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