shill_property_handler_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.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_service_client.h"
20#include "dbus/object_path.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "third_party/cros_system_api/dbus/service_constants.h"
23
24namespace chromeos {
25
26namespace {
27
28void DoNothingWithCallStatus(DBusMethodCallStatus call_status) {
29}
30
31void ErrorCallbackFunction(const std::string& error_name,
32                           const std::string& error_message) {
33  LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
34}
35
36class TestListener : public internal::ShillPropertyHandler::Listener {
37 public:
38  TestListener() : manager_updates_(0), errors_(0) {
39  }
40
41  virtual void UpdateManagedList(ManagedState::ManagedType type,
42                                 const base::ListValue& entries) OVERRIDE {
43    UpdateEntries(GetTypeString(type), entries);
44  }
45
46  virtual void UpdateManagedStateProperties(
47      ManagedState::ManagedType type,
48      const std::string& path,
49      const base::DictionaryValue& properties) OVERRIDE {
50    AddPropertyUpdate(GetTypeString(type), path);
51  }
52
53  virtual void ProfileListChanged() OVERRIDE {
54  }
55
56  virtual void UpdateNetworkServiceProperty(
57      const std::string& service_path,
58      const std::string& key,
59      const base::Value& value) OVERRIDE {
60    AddPropertyUpdate(flimflam::kServicesProperty, service_path);
61  }
62
63  virtual void UpdateDeviceProperty(
64      const std::string& device_path,
65      const std::string& key,
66      const base::Value& value) OVERRIDE {
67    AddPropertyUpdate(flimflam::kDevicesProperty, device_path);
68  }
69
70  virtual void NotifyManagerPropertyChanged() OVERRIDE {
71    ++manager_updates_;
72  }
73
74  virtual void ManagedStateListChanged(
75      ManagedState::ManagedType type) OVERRIDE {
76    AddStateListUpdate(GetTypeString(type));
77  }
78
79  std::vector<std::string>& entries(const std::string& type) {
80    return entries_[type];
81  }
82  std::map<std::string, int>& property_updates(const std::string& type) {
83    return property_updates_[type];
84  }
85  int list_updates(const std::string& type) { return list_updates_[type]; }
86  int manager_updates() { return manager_updates_; }
87  int errors() { return errors_; }
88
89 private:
90  std::string GetTypeString(ManagedState::ManagedType type) {
91    if (type == ManagedState::MANAGED_TYPE_NETWORK) {
92      return flimflam::kServicesProperty;
93    } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
94      return flimflam::kDevicesProperty;
95    }
96    LOG(ERROR) << "UpdateManagedList called with unrecognized type: " << type;
97    ++errors_;
98    return std::string();
99  }
100
101  void UpdateEntries(const std::string& type, const base::ListValue& entries) {
102    if (type.empty())
103      return;
104    entries_[type].clear();
105    for (base::ListValue::const_iterator iter = entries.begin();
106         iter != entries.end(); ++iter) {
107      std::string path;
108      if ((*iter)->GetAsString(&path))
109        entries_[type].push_back(path);
110    }
111  }
112
113  void AddPropertyUpdate(const std::string& type, const std::string& path) {
114    if (type.empty())
115      return;
116    property_updates(type)[path] += 1;
117  }
118
119  void AddStateListUpdate(const std::string& type) {
120    if (type.empty())
121      return;
122    list_updates_[type] += 1;
123  }
124
125  // Map of list-type -> paths
126  std::map<std::string, std::vector<std::string> > entries_;
127  // Map of list-type -> map of paths -> update counts
128  std::map<std::string, std::map<std::string, int> > property_updates_;
129  // Map of list-type -> list update counts
130  std::map<std::string, int > list_updates_;
131  int manager_updates_;
132  int errors_;
133};
134
135}  // namespace
136
137class ShillPropertyHandlerTest : public testing::Test {
138 public:
139  ShillPropertyHandlerTest()
140      : manager_test_(NULL),
141        device_test_(NULL),
142        service_test_(NULL) {
143  }
144  virtual ~ShillPropertyHandlerTest() {
145  }
146
147  virtual void SetUp() OVERRIDE {
148    // Initialize DBusThreadManager with a stub implementation.
149    DBusThreadManager::InitializeWithStub();
150    // Get the test interface for manager / device / service and clear the
151    // default stub properties.
152    manager_test_ =
153        DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface();
154    ASSERT_TRUE(manager_test_);
155    device_test_ =
156        DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
157    ASSERT_TRUE(device_test_);
158    service_test_ =
159        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
160    ASSERT_TRUE(service_test_);
161    SetupShillPropertyHandler();
162    message_loop_.RunUntilIdle();
163  }
164
165  virtual void TearDown() OVERRIDE {
166    shill_property_handler_.reset();
167    listener_.reset();
168    DBusThreadManager::Shutdown();
169  }
170
171  void AddDevice(const std::string& type, const std::string& id) {
172    ASSERT_TRUE(IsValidType(type));
173    device_test_->AddDevice(id, type, std::string("/device/" + id));
174  }
175
176  void RemoveDevice(const std::string& id) {
177    device_test_->RemoveDevice(id);
178  }
179
180  void AddService(const std::string& type,
181                  const std::string& id,
182                  const std::string& state,
183                  bool add_to_watch_list) {
184    ASSERT_TRUE(IsValidType(type));
185    service_test_->AddService(id, id, type, state,
186                              add_to_watch_list);
187  }
188
189  void AddServiceWithIPConfig(const std::string& type,
190                              const std::string& id,
191                              const std::string& state,
192                              const std::string& ipconfig_path,
193                              bool add_to_watch_list) {
194    ASSERT_TRUE(IsValidType(type));
195    service_test_->AddServiceWithIPConfig(id, id, type, state,
196                                          ipconfig_path, add_to_watch_list);
197  }
198
199  void RemoveService(const std::string& id) {
200    service_test_->RemoveService(id);
201  }
202
203  // Call this after any initial Shill client setup
204  void SetupShillPropertyHandler() {
205    SetupDefaultShillState();
206    listener_.reset(new TestListener);
207    shill_property_handler_.reset(
208        new internal::ShillPropertyHandler(listener_.get()));
209    shill_property_handler_->Init();
210  }
211
212  bool IsValidType(const std::string& type) {
213    return (type == flimflam::kTypeEthernet ||
214            type == flimflam::kTypeWifi ||
215            type == flimflam::kTypeWimax ||
216            type == flimflam::kTypeBluetooth ||
217            type == flimflam::kTypeCellular ||
218            type == flimflam::kTypeVPN);
219  }
220
221 protected:
222  void SetupDefaultShillState() {
223    message_loop_.RunUntilIdle();  // Process any pending updates
224    device_test_->ClearDevices();
225    AddDevice(flimflam::kTypeWifi, "stub_wifi_device1");
226    AddDevice(flimflam::kTypeCellular, "stub_cellular_device1");
227    service_test_->ClearServices();
228    const bool add_to_watchlist = true;
229    AddService(flimflam::kTypeEthernet, "stub_ethernet",
230               flimflam::kStateOnline, add_to_watchlist);
231    AddService(flimflam::kTypeWifi, "stub_wifi1",
232               flimflam::kStateOnline, add_to_watchlist);
233    AddService(flimflam::kTypeWifi, "stub_wifi2",
234               flimflam::kStateIdle, add_to_watchlist);
235    AddService(flimflam::kTypeCellular, "stub_cellular1",
236               flimflam::kStateIdle, add_to_watchlist);
237  }
238
239  MessageLoopForUI message_loop_;
240  scoped_ptr<TestListener> listener_;
241  scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_;
242  ShillManagerClient::TestInterface* manager_test_;
243  ShillDeviceClient::TestInterface* device_test_;
244  ShillServiceClient::TestInterface* service_test_;
245
246 private:
247  DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest);
248};
249
250TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) {
251  EXPECT_EQ(1, listener_->manager_updates());
252  EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
253      flimflam::kTypeWifi));
254  EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(
255      flimflam::kTypeWifi));
256  const size_t kNumShillManagerClientStubImplDevices = 2;
257  EXPECT_EQ(kNumShillManagerClientStubImplDevices,
258            listener_->entries(flimflam::kDevicesProperty).size());
259  const size_t kNumShillManagerClientStubImplServices = 4;
260  EXPECT_EQ(kNumShillManagerClientStubImplServices,
261            listener_->entries(flimflam::kServicesProperty).size());
262
263  EXPECT_EQ(0, listener_->errors());
264}
265
266TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) {
267  EXPECT_EQ(1, listener_->manager_updates());
268  // Add a disabled technology.
269  manager_test_->AddTechnology(flimflam::kTypeWimax, false);
270  message_loop_.RunUntilIdle();
271  EXPECT_EQ(2, listener_->manager_updates());
272  EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
273      flimflam::kTypeWimax));
274  EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled(
275      flimflam::kTypeWimax));
276
277  // Enable the technology.
278  DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology(
279      flimflam::kTypeWimax,
280      base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
281  message_loop_.RunUntilIdle();
282  EXPECT_EQ(3, listener_->manager_updates());
283  EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(
284      flimflam::kTypeWimax));
285
286  EXPECT_EQ(0, listener_->errors());
287}
288
289TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) {
290  EXPECT_EQ(1, listener_->manager_updates());
291  EXPECT_EQ(1, listener_->list_updates(flimflam::kDevicesProperty));
292  const size_t kNumShillManagerClientStubImplDevices = 2;
293  EXPECT_EQ(kNumShillManagerClientStubImplDevices,
294            listener_->entries(flimflam::kDevicesProperty).size());
295  // Add a device.
296  const std::string kTestDevicePath("test_wifi_device1");
297  AddDevice(flimflam::kTypeWifi, kTestDevicePath);
298  message_loop_.RunUntilIdle();
299  EXPECT_EQ(1, listener_->manager_updates());  // No new manager updates.
300  EXPECT_EQ(2, listener_->list_updates(flimflam::kDevicesProperty));
301  EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1,
302            listener_->entries(flimflam::kDevicesProperty).size());
303  // Device changes are not observed.
304  // Remove a device
305  RemoveDevice(kTestDevicePath);
306  message_loop_.RunUntilIdle();
307  EXPECT_EQ(3, listener_->list_updates(flimflam::kDevicesProperty));
308  EXPECT_EQ(kNumShillManagerClientStubImplDevices,
309            listener_->entries(flimflam::kDevicesProperty).size());
310
311  EXPECT_EQ(0, listener_->errors());
312}
313
314TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) {
315  EXPECT_EQ(1, listener_->manager_updates());
316  EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty));
317  const size_t kNumShillManagerClientStubImplServices = 4;
318  EXPECT_EQ(kNumShillManagerClientStubImplServices,
319            listener_->entries(flimflam::kServicesProperty).size());
320
321  // Add an unwatched service.
322  const std::string kTestServicePath("test_wifi_service1");
323  AddService(flimflam::kTypeWifi, kTestServicePath,
324             flimflam::kStateIdle, false);
325  message_loop_.RunUntilIdle();
326  EXPECT_EQ(1, listener_->manager_updates());  // No new manager updates.
327  // Watched and unwatched services trigger a service list update.
328  EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
329  EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
330            listener_->entries(flimflam::kServicesProperty).size());
331  // Service receives an initial property update.
332  EXPECT_EQ(1, listener_->
333            property_updates(flimflam::kServicesProperty)[kTestServicePath]);
334  // Change a property.
335  base::FundamentalValue scan_interval(3);
336  DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
337      dbus::ObjectPath(kTestServicePath),
338      flimflam::kScanIntervalProperty,
339      scan_interval,
340      base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
341  message_loop_.RunUntilIdle();
342  // Property change triggers an update.
343  EXPECT_EQ(2, listener_->
344            property_updates(flimflam::kServicesProperty)[kTestServicePath]);
345
346  // Add the existing service to the watch list.
347  AddService(flimflam::kTypeWifi, kTestServicePath,
348             flimflam::kStateIdle, true);
349  message_loop_.RunUntilIdle();
350  // Service list update should be received when watch list changes.
351  EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
352  // Number of services shouldn't change.
353  EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
354            listener_->entries(flimflam::kServicesProperty).size());
355
356  // Change a property.
357  DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
358      dbus::ObjectPath(kTestServicePath),
359      flimflam::kScanIntervalProperty,
360      scan_interval,
361      base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
362  message_loop_.RunUntilIdle();
363  // Property change should trigger another update.
364  EXPECT_EQ(3, listener_->
365            property_updates(flimflam::kServicesProperty)[kTestServicePath]);
366
367  // Remove a service
368  RemoveService(kTestServicePath);
369  message_loop_.RunUntilIdle();
370  EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty));
371  EXPECT_EQ(kNumShillManagerClientStubImplServices,
372            listener_->entries(flimflam::kServicesProperty).size());
373
374  EXPECT_EQ(0, listener_->errors());
375}
376
377TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerIPConfigPropertyChanged) {
378  // Set the properties for an IP Config object.
379  const std::string kTestIPConfigPath("test_ip_config_path");
380  base::StringValue ip_address("192.168.1.1");
381  DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
382      dbus::ObjectPath(kTestIPConfigPath),
383      flimflam::kAddressProperty,
384      ip_address,
385      base::Bind(&DoNothingWithCallStatus));
386  base::ListValue dns_servers;
387  dns_servers.Append(base::Value::CreateStringValue("192.168.1.100"));
388  dns_servers.Append(base::Value::CreateStringValue("192.168.1.101"));
389  DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
390      dbus::ObjectPath(kTestIPConfigPath),
391      flimflam::kNameServersProperty,
392      dns_servers,
393      base::Bind(&DoNothingWithCallStatus));
394  message_loop_.RunUntilIdle();
395
396  // Add a service with an empty ipconfig and then update
397  // its ipconfig property.
398  const std::string kTestServicePath1("test_wifi_service1");
399  AddService(flimflam::kTypeWifi, kTestServicePath1,
400             flimflam::kStateIdle, true);
401  message_loop_.RunUntilIdle();
402  // This is the initial property update.
403  EXPECT_EQ(1, listener_->
404            property_updates(flimflam::kServicesProperty)[kTestServicePath1]);
405  DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
406      dbus::ObjectPath(kTestServicePath1),
407      shill::kIPConfigProperty,
408      base::StringValue(kTestIPConfigPath),
409      base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
410  message_loop_.RunUntilIdle();
411  // IPConfig property change on the service should trigger property updates for
412  // IP Address and DNS.
413  EXPECT_EQ(3, listener_->
414            property_updates(flimflam::kServicesProperty)[kTestServicePath1]);
415
416  // Now, Add a new watched service with the IPConfig already set.
417  const std::string kTestServicePath2("test_wifi_service2");
418  AddServiceWithIPConfig(flimflam::kTypeWifi, kTestServicePath2,
419                         flimflam::kStateIdle, kTestIPConfigPath, true);
420  message_loop_.RunUntilIdle();
421  // A watched service with the IPConfig property already set must
422  // trigger property updates for IP Address and DNS when added.
423  EXPECT_EQ(3, listener_->
424            property_updates(flimflam::kServicesProperty)[kTestServicePath2]);
425}
426
427}  // namespace chromeos
428