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