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