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