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