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