Home | History | Annotate | Download | only in network
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos/network/shill_property_handler.h"
      6 
      7 #include <map>
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/bind.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/values.h"
     15 #include "chromeos/dbus/dbus_thread_manager.h"
     16 #include "chromeos/dbus/shill_device_client.h"
     17 #include "chromeos/dbus/shill_ipconfig_client.h"
     18 #include "chromeos/dbus/shill_manager_client.h"
     19 #include "chromeos/dbus/shill_profile_client.h"
     20 #include "chromeos/dbus/shill_service_client.h"
     21 #include "dbus/object_path.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 #include "third_party/cros_system_api/dbus/service_constants.h"
     24 
     25 namespace chromeos {
     26 
     27 namespace {
     28 
     29 void DoNothingWithCallStatus(DBusMethodCallStatus call_status) {
     30 }
     31 
     32 void ErrorCallbackFunction(const std::string& error_name,
     33                            const std::string& error_message) {
     34   LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
     35 }
     36 
     37 class TestListener : public internal::ShillPropertyHandler::Listener {
     38  public:
     39   TestListener() : manager_updates_(0), errors_(0) {
     40   }
     41 
     42   virtual void UpdateManagedList(ManagedState::ManagedType type,
     43                                  const base::ListValue& entries) OVERRIDE {
     44     UpdateEntries(GetTypeString(type), entries);
     45   }
     46 
     47   virtual void UpdateManagedStateProperties(
     48       ManagedState::ManagedType type,
     49       const std::string& path,
     50       const base::DictionaryValue& properties) OVERRIDE {
     51     AddInitialPropertyUpdate(GetTypeString(type), path);
     52   }
     53 
     54   virtual void ProfileListChanged() OVERRIDE {
     55   }
     56 
     57   virtual void UpdateNetworkServiceProperty(
     58       const std::string& service_path,
     59       const std::string& key,
     60       const base::Value& value) OVERRIDE {
     61     AddPropertyUpdate(flimflam::kServicesProperty, service_path);
     62   }
     63 
     64   virtual void UpdateDeviceProperty(
     65       const std::string& device_path,
     66       const std::string& key,
     67       const base::Value& value) OVERRIDE {
     68     AddPropertyUpdate(flimflam::kDevicesProperty, device_path);
     69   }
     70 
     71   virtual void NotifyManagerPropertyChanged() OVERRIDE {
     72     ++manager_updates_;
     73   }
     74 
     75   virtual void CheckPortalListChanged(
     76       const std::string& check_portal_list) OVERRIDE {
     77   }
     78 
     79   virtual void ManagedStateListChanged(
     80       ManagedState::ManagedType type) OVERRIDE {
     81     AddStateListUpdate(GetTypeString(type));
     82   }
     83 
     84   std::vector<std::string>& entries(const std::string& type) {
     85     return entries_[type];
     86   }
     87   std::map<std::string, int>& property_updates(const std::string& type) {
     88     return property_updates_[type];
     89   }
     90   std::map<std::string, int>& initial_property_updates(
     91       const std::string& type) {
     92     return initial_property_updates_[type];
     93   }
     94   int list_updates(const std::string& type) { return list_updates_[type]; }
     95   int manager_updates() { return manager_updates_; }
     96   int errors() { return errors_; }
     97 
     98  private:
     99   std::string GetTypeString(ManagedState::ManagedType type) {
    100     if (type == ManagedState::MANAGED_TYPE_NETWORK) {
    101       return flimflam::kServicesProperty;
    102     } else if (type == ManagedState::MANAGED_TYPE_FAVORITE) {
    103       return shill::kServiceCompleteListProperty;
    104     } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
    105       return flimflam::kDevicesProperty;
    106     }
    107     LOG(ERROR) << "UpdateManagedList called with unrecognized type: " << type;
    108     ++errors_;
    109     return std::string();
    110   }
    111 
    112   void UpdateEntries(const std::string& type, const base::ListValue& entries) {
    113     if (type.empty())
    114       return;
    115     entries_[type].clear();
    116     for (base::ListValue::const_iterator iter = entries.begin();
    117          iter != entries.end(); ++iter) {
    118       std::string path;
    119       if ((*iter)->GetAsString(&path))
    120         entries_[type].push_back(path);
    121     }
    122   }
    123 
    124   void AddPropertyUpdate(const std::string& type, const std::string& path) {
    125     if (type.empty())
    126       return;
    127     property_updates(type)[path] += 1;
    128   }
    129 
    130   void AddInitialPropertyUpdate(const std::string& type,
    131                                 const std::string& path) {
    132     if (type.empty())
    133       return;
    134     initial_property_updates(type)[path] += 1;
    135   }
    136 
    137   void AddStateListUpdate(const std::string& type) {
    138     if (type.empty())
    139       return;
    140     list_updates_[type] += 1;
    141   }
    142 
    143   // Map of list-type -> paths
    144   std::map<std::string, std::vector<std::string> > entries_;
    145   // Map of list-type -> map of paths -> update counts
    146   std::map<std::string, std::map<std::string, int> > property_updates_;
    147   std::map<std::string, std::map<std::string, int> > initial_property_updates_;
    148   // Map of list-type -> list update counts
    149   std::map<std::string, int > list_updates_;
    150   int manager_updates_;
    151   int errors_;
    152 };
    153 
    154 }  // namespace
    155 
    156 class ShillPropertyHandlerTest : public testing::Test {
    157  public:
    158   ShillPropertyHandlerTest()
    159       : manager_test_(NULL),
    160         device_test_(NULL),
    161         service_test_(NULL),
    162         profile_test_(NULL) {
    163   }
    164   virtual ~ShillPropertyHandlerTest() {
    165   }
    166 
    167   virtual void SetUp() OVERRIDE {
    168     // Initialize DBusThreadManager with a stub implementation.
    169     DBusThreadManager::InitializeWithStub();
    170     // Get the test interface for manager / device / service and clear the
    171     // default stub properties.
    172     manager_test_ =
    173         DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface();
    174     ASSERT_TRUE(manager_test_);
    175     device_test_ =
    176         DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
    177     ASSERT_TRUE(device_test_);
    178     service_test_ =
    179         DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
    180     ASSERT_TRUE(service_test_);
    181     profile_test_ =
    182         DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
    183     ASSERT_TRUE(profile_test_);
    184     SetupShillPropertyHandler();
    185     message_loop_.RunUntilIdle();
    186   }
    187 
    188   virtual void TearDown() OVERRIDE {
    189     shill_property_handler_.reset();
    190     listener_.reset();
    191     DBusThreadManager::Shutdown();
    192   }
    193 
    194   void AddDevice(const std::string& type, const std::string& id) {
    195     ASSERT_TRUE(IsValidType(type));
    196     device_test_->AddDevice(id, type, std::string("/device/" + id));
    197   }
    198 
    199   void RemoveDevice(const std::string& id) {
    200     device_test_->RemoveDevice(id);
    201   }
    202 
    203   void AddService(const std::string& type,
    204                   const std::string& id,
    205                   const std::string& state,
    206                   bool add_to_watch_list) {
    207     ASSERT_TRUE(IsValidType(type));
    208     service_test_->AddService(id, id, type, state,
    209                               true /* visible */, add_to_watch_list);
    210   }
    211 
    212   void AddServiceWithIPConfig(const std::string& type,
    213                               const std::string& id,
    214                               const std::string& state,
    215                               const std::string& ipconfig_path,
    216                               bool add_to_watch_list) {
    217     ASSERT_TRUE(IsValidType(type));
    218     service_test_->AddServiceWithIPConfig(id, id, type, state,
    219                                           ipconfig_path,
    220                                           true /* visible */,
    221                                           add_to_watch_list);
    222   }
    223 
    224   void AddServiceToProfile(const std::string& type,
    225                            const std::string& id,
    226                            bool visible) {
    227     service_test_->AddService(id, id, type, flimflam::kStateIdle,
    228                               visible, false /* watch */);
    229     std::vector<std::string> profiles;
    230     profile_test_->GetProfilePaths(&profiles);
    231     ASSERT_TRUE(profiles.size() > 0);
    232     base::DictionaryValue properties;  // Empty entry
    233     profile_test_->AddService(profiles[0], id);
    234   }
    235 
    236   void RemoveService(const std::string& id) {
    237     service_test_->RemoveService(id);
    238   }
    239 
    240   // Call this after any initial Shill client setup
    241   void SetupShillPropertyHandler() {
    242     SetupDefaultShillState();
    243     listener_.reset(new TestListener);
    244     shill_property_handler_.reset(
    245         new internal::ShillPropertyHandler(listener_.get()));
    246     shill_property_handler_->Init();
    247   }
    248 
    249   bool IsValidType(const std::string& type) {
    250     return (type == flimflam::kTypeEthernet ||
    251             type == flimflam::kTypeWifi ||
    252             type == flimflam::kTypeWimax ||
    253             type == flimflam::kTypeBluetooth ||
    254             type == flimflam::kTypeCellular ||
    255             type == flimflam::kTypeVPN);
    256   }
    257 
    258  protected:
    259   void SetupDefaultShillState() {
    260     message_loop_.RunUntilIdle();  // Process any pending updates
    261     device_test_->ClearDevices();
    262     AddDevice(flimflam::kTypeWifi, "stub_wifi_device1");
    263     AddDevice(flimflam::kTypeCellular, "stub_cellular_device1");
    264     service_test_->ClearServices();
    265     const bool add_to_watchlist = true;
    266     AddService(flimflam::kTypeEthernet, "stub_ethernet",
    267                flimflam::kStateOnline, add_to_watchlist);
    268     AddService(flimflam::kTypeWifi, "stub_wifi1",
    269                flimflam::kStateOnline, add_to_watchlist);
    270     AddService(flimflam::kTypeWifi, "stub_wifi2",
    271                flimflam::kStateIdle, add_to_watchlist);
    272     AddService(flimflam::kTypeCellular, "stub_cellular1",
    273                flimflam::kStateIdle, add_to_watchlist);
    274   }
    275 
    276   base::MessageLoopForUI message_loop_;
    277   scoped_ptr<TestListener> listener_;
    278   scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_;
    279   ShillManagerClient::TestInterface* manager_test_;
    280   ShillDeviceClient::TestInterface* device_test_;
    281   ShillServiceClient::TestInterface* service_test_;
    282   ShillProfileClient::TestInterface* profile_test_;
    283 
    284  private:
    285   DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest);
    286 };
    287 
    288 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) {
    289   EXPECT_EQ(1, listener_->manager_updates());
    290   EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
    291       flimflam::kTypeWifi));
    292   EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(
    293       flimflam::kTypeWifi));
    294   const size_t kNumShillManagerClientStubImplDevices = 2;
    295   EXPECT_EQ(kNumShillManagerClientStubImplDevices,
    296             listener_->entries(flimflam::kDevicesProperty).size());
    297   const size_t kNumShillManagerClientStubImplServices = 4;
    298   EXPECT_EQ(kNumShillManagerClientStubImplServices,
    299             listener_->entries(flimflam::kServicesProperty).size());
    300 
    301   EXPECT_EQ(0, listener_->errors());
    302 }
    303 
    304 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) {
    305   EXPECT_EQ(1, listener_->manager_updates());
    306 
    307   // Remove a technology. Updates both the Available and Enabled lists.
    308   manager_test_->RemoveTechnology(flimflam::kTypeWimax);
    309   message_loop_.RunUntilIdle();
    310   EXPECT_EQ(3, listener_->manager_updates());
    311 
    312   // Add a disabled technology.
    313   manager_test_->AddTechnology(flimflam::kTypeWimax, false);
    314   message_loop_.RunUntilIdle();
    315   EXPECT_EQ(4, listener_->manager_updates());
    316   EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
    317       flimflam::kTypeWimax));
    318   EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled(
    319       flimflam::kTypeWimax));
    320 
    321   // Enable the technology.
    322   DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology(
    323       flimflam::kTypeWimax,
    324       base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
    325   message_loop_.RunUntilIdle();
    326   EXPECT_EQ(5, listener_->manager_updates());
    327   EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(
    328       flimflam::kTypeWimax));
    329 
    330   EXPECT_EQ(0, listener_->errors());
    331 }
    332 
    333 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) {
    334   EXPECT_EQ(1, listener_->manager_updates());
    335   EXPECT_EQ(1, listener_->list_updates(flimflam::kDevicesProperty));
    336   const size_t kNumShillManagerClientStubImplDevices = 2;
    337   EXPECT_EQ(kNumShillManagerClientStubImplDevices,
    338             listener_->entries(flimflam::kDevicesProperty).size());
    339   // Add a device.
    340   const std::string kTestDevicePath("test_wifi_device1");
    341   AddDevice(flimflam::kTypeWifi, kTestDevicePath);
    342   message_loop_.RunUntilIdle();
    343   EXPECT_EQ(1, listener_->manager_updates());  // No new manager updates.
    344   EXPECT_EQ(2, listener_->list_updates(flimflam::kDevicesProperty));
    345   EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1,
    346             listener_->entries(flimflam::kDevicesProperty).size());
    347   // Device changes are not observed.
    348   // Remove a device
    349   RemoveDevice(kTestDevicePath);
    350   message_loop_.RunUntilIdle();
    351   EXPECT_EQ(3, listener_->list_updates(flimflam::kDevicesProperty));
    352   EXPECT_EQ(kNumShillManagerClientStubImplDevices,
    353             listener_->entries(flimflam::kDevicesProperty).size());
    354 
    355   EXPECT_EQ(0, listener_->errors());
    356 }
    357 
    358 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) {
    359   EXPECT_EQ(1, listener_->manager_updates());
    360   EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty));
    361   const size_t kNumShillManagerClientStubImplServices = 4;
    362   EXPECT_EQ(kNumShillManagerClientStubImplServices,
    363             listener_->entries(flimflam::kServicesProperty).size());
    364 
    365   // Add an unwatched service.
    366   const std::string kTestServicePath("test_wifi_service1");
    367   AddService(flimflam::kTypeWifi, kTestServicePath,
    368              flimflam::kStateIdle, false);
    369   message_loop_.RunUntilIdle();
    370   EXPECT_EQ(1, listener_->manager_updates());  // No new manager updates.
    371   // Watched and unwatched services trigger a service list update.
    372   EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
    373   EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
    374             listener_->entries(flimflam::kServicesProperty).size());
    375   // Service receives an initial property update.
    376   EXPECT_EQ(1, listener_->initial_property_updates(
    377       flimflam::kServicesProperty)[kTestServicePath]);
    378   // Change a property.
    379   base::FundamentalValue scan_interval(3);
    380   DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
    381       dbus::ObjectPath(kTestServicePath),
    382       flimflam::kScanIntervalProperty,
    383       scan_interval,
    384       base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
    385   message_loop_.RunUntilIdle();
    386   // Property change triggers an update.
    387   EXPECT_EQ(1, listener_->property_updates(
    388       flimflam::kServicesProperty)[kTestServicePath]);
    389 
    390   // Add the existing service to the watch list.
    391   AddService(flimflam::kTypeWifi, kTestServicePath,
    392              flimflam::kStateIdle, true);
    393   message_loop_.RunUntilIdle();
    394   // Service list update should be received when watch list changes.
    395   EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
    396   // Number of services shouldn't change.
    397   EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
    398             listener_->entries(flimflam::kServicesProperty).size());
    399 
    400   // Change a property.
    401   DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
    402       dbus::ObjectPath(kTestServicePath),
    403       flimflam::kScanIntervalProperty,
    404       scan_interval,
    405       base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
    406   message_loop_.RunUntilIdle();
    407   // Property change should trigger another update.
    408   EXPECT_EQ(2, listener_->property_updates(
    409       flimflam::kServicesProperty)[kTestServicePath]);
    410 
    411   // Remove a service
    412   RemoveService(kTestServicePath);
    413   message_loop_.RunUntilIdle();
    414   EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty));
    415   EXPECT_EQ(kNumShillManagerClientStubImplServices,
    416             listener_->entries(flimflam::kServicesProperty).size());
    417 
    418   EXPECT_EQ(0, listener_->errors());
    419 }
    420 
    421 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerIPConfigPropertyChanged) {
    422   // Set the properties for an IP Config object.
    423   const std::string kTestIPConfigPath("test_ip_config_path");
    424 
    425   base::StringValue ip_address("192.168.1.1");
    426   DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
    427       dbus::ObjectPath(kTestIPConfigPath),
    428       flimflam::kAddressProperty, ip_address,
    429       base::Bind(&DoNothingWithCallStatus));
    430   base::ListValue dns_servers;
    431   dns_servers.Append(base::Value::CreateStringValue("192.168.1.100"));
    432   dns_servers.Append(base::Value::CreateStringValue("192.168.1.101"));
    433   DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
    434       dbus::ObjectPath(kTestIPConfigPath),
    435       flimflam::kNameServersProperty, dns_servers,
    436       base::Bind(&DoNothingWithCallStatus));
    437   base::FundamentalValue prefixlen(8);
    438   DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
    439       dbus::ObjectPath(kTestIPConfigPath),
    440       flimflam::kPrefixlenProperty, prefixlen,
    441       base::Bind(&DoNothingWithCallStatus));
    442   base::StringValue gateway("192.0.0.1");
    443   DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
    444       dbus::ObjectPath(kTestIPConfigPath),
    445       flimflam::kGatewayProperty, gateway,
    446       base::Bind(&DoNothingWithCallStatus));
    447   message_loop_.RunUntilIdle();
    448 
    449   // Add a service with an empty ipconfig and then update
    450   // its ipconfig property.
    451   const std::string kTestServicePath1("test_wifi_service1");
    452   AddService(flimflam::kTypeWifi, kTestServicePath1,
    453              flimflam::kStateIdle, true);
    454   message_loop_.RunUntilIdle();
    455   // This is the initial property update.
    456   EXPECT_EQ(1, listener_->initial_property_updates(
    457       flimflam::kServicesProperty)[kTestServicePath1]);
    458   DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
    459       dbus::ObjectPath(kTestServicePath1),
    460       shill::kIPConfigProperty,
    461       base::StringValue(kTestIPConfigPath),
    462       base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
    463   message_loop_.RunUntilIdle();
    464   // IPConfig property change on the service should trigger property updates for
    465   // IP Address, DNS, prefixlen, and gateway.
    466   EXPECT_EQ(4, listener_->property_updates(
    467       flimflam::kServicesProperty)[kTestServicePath1]);
    468 
    469   // Now, Add a new watched service with the IPConfig already set.
    470   const std::string kTestServicePath2("test_wifi_service2");
    471   AddServiceWithIPConfig(flimflam::kTypeWifi, kTestServicePath2,
    472                          flimflam::kStateIdle, kTestIPConfigPath, true);
    473   message_loop_.RunUntilIdle();
    474   // A watched service with the IPConfig property already set must trigger
    475   // property updates for IP Address, DNS, prefixlen, and gateway when added.
    476   EXPECT_EQ(4, listener_->property_updates(
    477       flimflam::kServicesProperty)[kTestServicePath2]);
    478 }
    479 
    480 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServiceCompleteList) {
    481   // Initial list updates.
    482   EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty));
    483   EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
    484 
    485   // Add a new entry to the profile only; should trigger a single list update
    486   // for both Services and ServiceCompleteList, and a single property update
    487   // for ServiceCompleteList.
    488   const std::string kTestServicePath1("stub_wifi_profile_only1");
    489   AddServiceToProfile(flimflam::kTypeWifi, kTestServicePath1, false);
    490   shill_property_handler_->UpdateManagerProperties();
    491   message_loop_.RunUntilIdle();
    492   EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
    493   EXPECT_EQ(2, listener_->list_updates(shill::kServiceCompleteListProperty));
    494   EXPECT_EQ(0, listener_->initial_property_updates(
    495       flimflam::kServicesProperty)[kTestServicePath1]);
    496   EXPECT_EQ(1, listener_->initial_property_updates(
    497       shill::kServiceCompleteListProperty)[kTestServicePath1]);
    498   EXPECT_EQ(0, listener_->property_updates(
    499       flimflam::kServicesProperty)[kTestServicePath1]);
    500   EXPECT_EQ(0, listener_->property_updates(
    501       shill::kServiceCompleteListProperty)[kTestServicePath1]);
    502 
    503   // Add a new entry to the services and the profile; should also trigger a
    504   // single list update for both Services and ServiceCompleteList, and should
    505   // trigger tow property updates for Services (one when the Profile propety
    506   // changes, and one for the Request) and one ServiceCompleteList change for
    507   // the Request.
    508   const std::string kTestServicePath2("stub_wifi_profile_only2");
    509   AddServiceToProfile(flimflam::kTypeWifi, kTestServicePath2, true);
    510   shill_property_handler_->UpdateManagerProperties();
    511   message_loop_.RunUntilIdle();
    512   EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty));
    513   EXPECT_EQ(3, listener_->list_updates(shill::kServiceCompleteListProperty));
    514   EXPECT_EQ(1, listener_->initial_property_updates(
    515       flimflam::kServicesProperty)[kTestServicePath2]);
    516   EXPECT_EQ(1, listener_->initial_property_updates(
    517       shill::kServiceCompleteListProperty)[kTestServicePath2]);
    518   // Expect one property update for the Profile property of the Network.
    519   EXPECT_EQ(1, listener_->property_updates(
    520       flimflam::kServicesProperty)[kTestServicePath2]);
    521   EXPECT_EQ(0, listener_->property_updates(
    522       shill::kServiceCompleteListProperty)[kTestServicePath2]);
    523 
    524   // Change a property of a Network in a Profile.
    525   base::FundamentalValue scan_interval(3);
    526   DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
    527       dbus::ObjectPath(kTestServicePath2),
    528       flimflam::kScanIntervalProperty,
    529       scan_interval,
    530       base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
    531   message_loop_.RunUntilIdle();
    532   // Property change should trigger an update for the Network only; no
    533   // property updates pushed by Shill affect Favorites.
    534   EXPECT_EQ(2, listener_->property_updates(
    535       flimflam::kServicesProperty)[kTestServicePath2]);
    536   EXPECT_EQ(0, listener_->property_updates(
    537       shill::kServiceCompleteListProperty)[kTestServicePath2]);
    538 }
    539 
    540 }  // namespace chromeos
    541