shill_property_handler_unittest.cc revision 2385ea399aae016c0806a4f9ef3c9cfe3d2a39df
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() : 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
156class 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
288TEST_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
304TEST_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
333TEST_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
358TEST_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
421TEST_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
480TEST_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