Split Modem into ModemClassic and Modem1

Along the way:

  Restructure ModemManagerXxx to use the split modem classes.

  Add new mocks to limit scope of modem tests

  Change modem state enum to a neutral format and convert from MM1 and
  MMClassic to this format.

  Fix a bug where we weren't properly releasing a callback in
  DBusObjectManagerProxy.

  Add new DBus property matchers

BUG=chromium-os:27935,chromium-os:27936
TEST=unit tests

Change-Id: Ib78c7dfd9e30fe556f09a4427fd71c9d785210c9
Reviewed-on: https://gerrit.chromium.org/gerrit/19228
Commit-Ready: David Rochberg <rochberg@chromium.org>
Reviewed-by: David Rochberg <rochberg@chromium.org>
Tested-by: David Rochberg <rochberg@chromium.org>
diff --git a/modem_unittest.cc b/modem_unittest.cc
index a716e74..9e609e2 100644
--- a/modem_unittest.cc
+++ b/modem_unittest.cc
@@ -12,15 +12,18 @@
 
 #include "shill/cellular.h"
 #include "shill/cellular_capability_gsm.h"
+#include "shill/dbus_property_matchers.h"
 #include "shill/event_dispatcher.h"
 #include "shill/manager.h"
+#include "shill/mock_cellular.h"
 #include "shill/mock_control.h"
-#include "shill/mock_device_info.h"
 #include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_device_info.h"
 #include "shill/mock_glib.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_metrics.h"
-#include "shill/mock_sockets.h"
+#include "shill/mock_modem.h"
+#include "shill/mock_rtnl_handler.h"
 #include "shill/modem.h"
 #include "shill/proxy_factory.h"
 #include "shill/rtnl_handler.h"
@@ -31,6 +34,7 @@
 using testing::DoAll;
 using testing::Return;
 using testing::SetArgumentPointee;
+using testing::StrEq;
 using testing::StrictMock;
 using testing::Test;
 
@@ -39,6 +43,11 @@
 namespace {
 
 const int kTestInterfaceIndex = 5;
+const char kLinkName[] = "usb0";
+const char kOwner[] = ":1.18";
+const char kPath[] = "/org/chromium/ModemManager/Gobi/0";
+const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+const char kAddressAsString[] = "000102030405";
 
 ACTION(SetInterfaceIndex) {
   if (arg2) {
@@ -55,21 +64,24 @@
         info_(&control_interface_, &dispatcher_, &metrics_, &manager_),
         proxy_(new MockDBusPropertiesProxy()),
         proxy_factory_(this),
-        modem_(new Modem(kOwner,
-                         kPath,
-                         &control_interface_,
-                         &dispatcher_,
-                         &metrics_,
-                         &manager_,
-                         NULL)) {}
-
+        modem_(
+            new StrictModem(
+                kOwner,
+                kPath,
+                &control_interface_,
+                &dispatcher_,
+                &metrics_,
+                &manager_,
+                static_cast<mobile_provider_db *>(NULL))) {}
   virtual void SetUp();
   virtual void TearDown();
 
-  void SetSockets(Sockets *sockets) {
-    RTNLHandler::GetInstance()->sockets_ = sockets;
+  void ReplaceSingletons() {
+    modem_->rtnl_handler_ = &rtnl_handler_;
   }
 
+  void ExpectLinkRelatedCalls();
+
  protected:
   class TestProxyFactory : public ProxyFactory {
    public:
@@ -86,9 +98,6 @@
     ModemTest *test_;
   };
 
-  static const char kOwner[];
-  static const char kPath[];
-
   CellularCapabilityGSM *GetCapabilityGSM() {
     return dynamic_cast<CellularCapabilityGSM *>(
         modem_->device_->capability_.get());
@@ -102,97 +111,114 @@
   MockDeviceInfo info_;
   scoped_ptr<MockDBusPropertiesProxy> proxy_;
   TestProxyFactory proxy_factory_;
-  scoped_ptr<Modem> modem_;
-  StrictMock<MockSockets> sockets_;
+  scoped_ptr<StrictModem> modem_;
+  MockRTNLHandler rtnl_handler_;
+  ByteString expected_address_;
 };
 
-const char ModemTest::kOwner[] = ":1.18";
-const char ModemTest::kPath[] = "/org/chromium/ModemManager/Gobi/0";
-
 void ModemTest::SetUp() {
   EXPECT_EQ(kOwner, modem_->owner_);
   EXPECT_EQ(kPath, modem_->path_);
-  modem_->proxy_factory_ = &proxy_factory_;
+  ReplaceSingletons();
+  expected_address_ = ByteString(kAddress, arraysize(kAddress));
+
+  EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(kLinkName)).
+      WillRepeatedly(Return(kTestInterfaceIndex));
+
+  EXPECT_CALL(*modem_, ConvertMmToCellularModemState(_)).
+      WillRepeatedly(Return(Cellular::kModemStateUnknown));
+
+  EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(&info_));
 }
 
 void ModemTest::TearDown() {
   modem_.reset();
-  SetSockets(NULL);
 }
 
-TEST_F(ModemTest, CreateDeviceFromProperties) {
-  DBusPropertiesMap props;
+TEST_F(ModemTest, PendingDevicePropertiesAndCreate) {
+  static const char kSentinel[] = "sentinel";
+  static const uint32 kSentinelValue = 17;
 
-  modem_->CreateDeviceFromProperties(props);
+  DBusPropertiesMap properties;
+  properties[kSentinel].writer().append_uint32(kSentinelValue);
+
+  EXPECT_CALL(*modem_, GetLinkName(_, _)).WillRepeatedly(DoAll(
+      SetArgumentPointee<1>(string(kLinkName)),
+      Return(true)));
+  EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).
+      WillRepeatedly(Return(kTestInterfaceIndex));
+
+  // The first time we call CreateDeviceFromModemProperties,
+  // GetMACAddress will fail.
+  EXPECT_CALL(info_, GetMACAddress(kTestInterfaceIndex, _)).
+      WillOnce(Return(false));
+  modem_->CreateDeviceFromModemProperties(properties);
   EXPECT_FALSE(modem_->device_.get());
 
-  props[Modem::kPropertyIPMethod].writer().append_uint32(
-      MM_MODEM_IP_METHOD_PPP);
-  modem_->CreateDeviceFromProperties(props);
+  // On the second time, we allow GetMACAddress to succeed.  Now we
+  // expect a device to be built
+  EXPECT_CALL(info_, GetMACAddress(kTestInterfaceIndex, _)).
+      WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
+                     Return(true)));
+
+  // modem will take ownership
+  MockCellular* cellular = new MockCellular(
+      &control_interface_,
+      &dispatcher_,
+      &metrics_,
+      &manager_,
+      kLinkName,
+      kAddressAsString,
+      kTestInterfaceIndex,
+      Cellular::kTypeGSM,
+      kOwner,
+      kPath,
+      static_cast<mobile_provider_db *>(NULL));
+
+  EXPECT_CALL(*modem_,
+              ConstructCellular(StrEq(kLinkName),
+                                StrEq(kAddressAsString),
+                                kTestInterfaceIndex)).
+      WillOnce(Return(cellular));
+
+  EXPECT_CALL(*cellular, OnModemManagerPropertiesChanged(
+      HasDBusPropertyWithValueU32(kSentinel, kSentinelValue)));
+  EXPECT_CALL(info_, RegisterDevice(_));
+  EXPECT_CALL(info_, DeregisterDevice(_));
+  modem_->OnDeviceInfoAvailable(kLinkName);
+
+  EXPECT_TRUE(modem_->device_.get());
+}
+
+TEST_F(ModemTest, EarlyDeviceProperties) {
+  // OnDeviceInfoAvailable called before
+  // CreateDeviceFromModemProperties: Do nothing
+  modem_->OnDeviceInfoAvailable(kLinkName);
+  EXPECT_FALSE(modem_->device_.get());
+}
+
+TEST_F(ModemTest, CreateDeviceEarlyFailures) {
+  DBusPropertiesMap properties;
+
+  EXPECT_CALL(*modem_, ConstructCellular(_, _, _)).Times(0);
+
+  // No link name:  no device created
+  EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(Return(false));
+  modem_->CreateDeviceFromModemProperties(properties);
   EXPECT_FALSE(modem_->device_.get());
 
-  props.erase(Modem::kPropertyIPMethod);
-  props[Modem::kPropertyIPMethod].writer().append_uint32(
-      MM_MODEM_IP_METHOD_DHCP);
-  modem_->CreateDeviceFromProperties(props);
+  // Link name, but no ifindex: no device created
+  EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll(
+      SetArgumentPointee<1>(string(kLinkName)),
+      Return(true)));
+  EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).WillOnce(
+      Return(-1));
+  modem_->CreateDeviceFromModemProperties(properties);
   EXPECT_FALSE(modem_->device_.get());
+}
 
-  static const char kLinkName[] = "usb0";
-  static const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-  const int kTestSocket = 10;
-  const int kTestLinkSocket = 11;
-  props[Modem::kPropertyLinkName].writer().append_string(kLinkName);
-  EXPECT_CALL(sockets_, Socket(PF_INET, SOCK_DGRAM, 0))
-      .Times(2)
-      .WillRepeatedly(Return(kTestSocket));
-  EXPECT_CALL(sockets_, Ioctl(kTestSocket, SIOCGIFINDEX, _))
-      .WillRepeatedly(DoAll(SetInterfaceIndex(), Return(0)));
-  EXPECT_CALL(sockets_, Close(kTestSocket))
-      .WillRepeatedly(Return(0));
-
-  EXPECT_CALL(sockets_, Socket(PF_NETLINK, _, _))
-      .WillRepeatedly(Return(kTestLinkSocket));
-  EXPECT_CALL(sockets_, Bind(kTestLinkSocket, _, _))
-      .WillRepeatedly(Return(0));
-  EXPECT_CALL(sockets_, Send(kTestLinkSocket, _, _, _))
-      .WillRepeatedly(Return(0));
-  RTNLHandler::GetInstance()->Start(&dispatcher_, &sockets_);
-
-  ByteString expected_address(kAddress, arraysize(kAddress));
-  EXPECT_CALL(info_, GetMACAddress(kTestInterfaceIndex, _))
-      .WillOnce(DoAll(SetArgumentPointee<1>(expected_address), Return(true)))
-      .WillOnce(DoAll(SetArgumentPointee<1>(expected_address), Return(true)));
-  EXPECT_CALL(info_, GetDevice(kTestInterfaceIndex))
-      .WillRepeatedly(Return(modem_->device_));
-  EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(&info_));
-
-  modem_->CreateDeviceFromProperties(props);
-  EXPECT_FALSE(modem_->device_.get());
-
-  props[Modem::kPropertyType].writer().append_uint32(MM_MODEM_TYPE_GSM);
-  props[Modem::kPropertyState].writer().append_uint32(
-      Cellular::kModemStateDisabled);
-  static const char kLockType[] = "sim-pin";
-  const int kRetries = 2;
-  props[CellularCapabilityGSM::kPropertyEnabledFacilityLocks].writer().
-      append_uint32(MM_MODEM_GSM_FACILITY_SIM);
-  props[CellularCapabilityGSM::kPropertyUnlockRequired].writer().append_string(
-      kLockType);
-  props[CellularCapabilityGSM::kPropertyUnlockRetries].writer().append_uint32(
-      kRetries);
-  modem_->CreateDeviceFromProperties(props);
-  ASSERT_TRUE(modem_->device_.get());
-  EXPECT_EQ(kLinkName, modem_->device_->link_name());
-  EXPECT_EQ(kTestInterfaceIndex, modem_->device_->interface_index());
-  EXPECT_EQ(Cellular::kModemStateDisabled, modem_->device_->modem_state());
-  EXPECT_TRUE(GetCapabilityGSM()->sim_lock_status_.enabled);
-  EXPECT_EQ(kLockType, GetCapabilityGSM()->sim_lock_status_.lock_type);
-  EXPECT_EQ(kRetries, GetCapabilityGSM()->sim_lock_status_.retries_left);
-
-  vector<DeviceRefPtr> devices;
-  manager_.FilterByTechnology(Technology::kCellular, &devices);
-  EXPECT_EQ(1, devices.size());
-  EXPECT_TRUE(devices[0].get() == modem_->device_.get());
+TEST_F(ModemTest, RejectPPPModem) {
+  // TODO(rochberg):  Port this to ModemClassic
 }
 
 }  // namespace shill