1 // Copyright (c) 2013 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 "dbus/object_manager.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/threading/thread.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "dbus/bus.h"
17 #include "dbus/object_path.h"
18 #include "dbus/object_proxy.h"
19 #include "dbus/property.h"
20 #include "dbus/test_service.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace dbus {
24
25 // The object manager test exercises the asynchronous APIs in ObjectManager,
26 // and by extension PropertySet and Property<>.
27 class ObjectManagerTest
28 : public testing::Test,
29 public ObjectManager::Interface {
30 public:
ObjectManagerTest()31 ObjectManagerTest() : timeout_expired_(false) {
32 }
33
34 struct Properties : public PropertySet {
35 Property<std::string> name;
36 Property<int16> version;
37 Property<std::vector<std::string> > methods;
38 Property<std::vector<ObjectPath> > objects;
39
Propertiesdbus::ObjectManagerTest::Properties40 Properties(ObjectProxy* object_proxy,
41 const std::string& interface_name,
42 PropertyChangedCallback property_changed_callback)
43 : PropertySet(object_proxy, interface_name, property_changed_callback) {
44 RegisterProperty("Name", &name);
45 RegisterProperty("Version", &version);
46 RegisterProperty("Methods", &methods);
47 RegisterProperty("Objects", &objects);
48 }
49 };
50
CreateProperties(ObjectProxy * object_proxy,const ObjectPath & object_path,const std::string & interface_name)51 virtual PropertySet* CreateProperties(
52 ObjectProxy* object_proxy,
53 const ObjectPath& object_path,
54 const std::string& interface_name) OVERRIDE {
55 Properties* properties = new Properties(
56 object_proxy, interface_name,
57 base::Bind(&ObjectManagerTest::OnPropertyChanged,
58 base::Unretained(this), object_path));
59 return static_cast<PropertySet*>(properties);
60 }
61
SetUp()62 virtual void SetUp() {
63 // Make the main thread not to allow IO.
64 base::ThreadRestrictions::SetIOAllowed(false);
65
66 // Start the D-Bus thread.
67 dbus_thread_.reset(new base::Thread("D-Bus Thread"));
68 base::Thread::Options thread_options;
69 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
70 ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
71
72 // Start the test service, using the D-Bus thread.
73 TestService::Options options;
74 options.dbus_task_runner = dbus_thread_->message_loop_proxy();
75 test_service_.reset(new TestService(options));
76 ASSERT_TRUE(test_service_->StartService());
77 ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
78 ASSERT_TRUE(test_service_->HasDBusThread());
79
80 // Create the client, using the D-Bus thread.
81 Bus::Options bus_options;
82 bus_options.bus_type = Bus::SESSION;
83 bus_options.connection_type = Bus::PRIVATE;
84 bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy();
85 bus_ = new Bus(bus_options);
86 ASSERT_TRUE(bus_->HasDBusThread());
87
88 object_manager_ = bus_->GetObjectManager(
89 "org.chromium.TestService",
90 ObjectPath("/org/chromium/TestService"));
91 object_manager_->RegisterInterface("org.chromium.TestInterface", this);
92
93 WaitForObject();
94 }
95
TearDown()96 virtual void TearDown() {
97 bus_->ShutdownOnDBusThreadAndBlock();
98
99 // Shut down the service.
100 test_service_->ShutdownAndBlock();
101
102 // Reset to the default.
103 base::ThreadRestrictions::SetIOAllowed(true);
104
105 // Stopping a thread is considered an IO operation, so do this after
106 // allowing IO.
107 test_service_->Stop();
108
109 base::RunLoop().RunUntilIdle();
110 }
111
MethodCallback(Response * response)112 void MethodCallback(Response* response) {
113 method_callback_called_ = true;
114 run_loop_->Quit();
115 }
116
117 // Called from the PropertiesChangedAsObjectsReceived test case. The test will
118 // not run the message loop if it receives the expected PropertiesChanged
119 // signal before the timeout. This method immediately fails the test.
PropertiesChangedTestTimeout()120 void PropertiesChangedTestTimeout() {
121 timeout_expired_ = true;
122 run_loop_->Quit();
123
124 FAIL() << "Never received PropertiesChanged";
125 }
126
127 protected:
128 // Called when an object is added.
ObjectAdded(const ObjectPath & object_path,const std::string & interface_name)129 virtual void ObjectAdded(const ObjectPath& object_path,
130 const std::string& interface_name) OVERRIDE {
131 added_objects_.push_back(std::make_pair(object_path, interface_name));
132 run_loop_->Quit();
133 }
134
135 // Called when an object is removed.
ObjectRemoved(const ObjectPath & object_path,const std::string & interface_name)136 virtual void ObjectRemoved(const ObjectPath& object_path,
137 const std::string& interface_name) OVERRIDE {
138 removed_objects_.push_back(std::make_pair(object_path, interface_name));
139 run_loop_->Quit();
140 }
141
142 // Called when a property value is updated.
OnPropertyChanged(const ObjectPath & object_path,const std::string & name)143 void OnPropertyChanged(const ObjectPath& object_path,
144 const std::string& name) {
145 // Store the value of the "Name" property if that's the one that
146 // changed.
147 Properties* properties = static_cast<Properties*>(
148 object_manager_->GetProperties(
149 object_path,
150 "org.chromium.TestInterface"));
151 if (name == properties->name.name())
152 last_name_value_ = properties->name.value();
153
154 // Store the updated property.
155 updated_properties_.push_back(name);
156 run_loop_->Quit();
157 }
158
159 static const size_t kExpectedObjects = 1;
160 static const size_t kExpectedProperties = 4;
161
WaitForObject()162 void WaitForObject() {
163 while (added_objects_.size() < kExpectedObjects ||
164 updated_properties_.size() < kExpectedProperties) {
165 run_loop_.reset(new base::RunLoop);
166 run_loop_->Run();
167 }
168 for (size_t i = 0; i < kExpectedObjects; ++i)
169 added_objects_.erase(added_objects_.begin());
170 for (size_t i = 0; i < kExpectedProperties; ++i)
171 updated_properties_.erase(updated_properties_.begin());
172 }
173
WaitForRemoveObject()174 void WaitForRemoveObject() {
175 while (removed_objects_.size() < kExpectedObjects) {
176 run_loop_.reset(new base::RunLoop);
177 run_loop_->Run();
178 }
179 for (size_t i = 0; i < kExpectedObjects; ++i)
180 removed_objects_.erase(removed_objects_.begin());
181 }
182
WaitForMethodCallback()183 void WaitForMethodCallback() {
184 run_loop_.reset(new base::RunLoop);
185 run_loop_->Run();
186 method_callback_called_ = false;
187 }
188
PerformAction(const std::string & action,const ObjectPath & object_path)189 void PerformAction(const std::string& action, const ObjectPath& object_path) {
190 ObjectProxy* object_proxy = bus_->GetObjectProxy(
191 "org.chromium.TestService",
192 ObjectPath("/org/chromium/TestObject"));
193
194 MethodCall method_call("org.chromium.TestInterface", "PerformAction");
195 MessageWriter writer(&method_call);
196 writer.AppendString(action);
197 writer.AppendObjectPath(object_path);
198
199 object_proxy->CallMethod(&method_call,
200 ObjectProxy::TIMEOUT_USE_DEFAULT,
201 base::Bind(&ObjectManagerTest::MethodCallback,
202 base::Unretained(this)));
203 WaitForMethodCallback();
204 }
205
206 base::MessageLoop message_loop_;
207 scoped_ptr<base::RunLoop> run_loop_;
208 scoped_ptr<base::Thread> dbus_thread_;
209 scoped_refptr<Bus> bus_;
210 ObjectManager* object_manager_;
211 scoped_ptr<TestService> test_service_;
212
213 std::string last_name_value_;
214 bool timeout_expired_;
215
216 std::vector<std::pair<ObjectPath, std::string> > added_objects_;
217 std::vector<std::pair<ObjectPath, std::string> > removed_objects_;
218 std::vector<std::string> updated_properties_;
219
220 bool method_callback_called_;
221 };
222
223
TEST_F(ObjectManagerTest,InitialObject)224 TEST_F(ObjectManagerTest, InitialObject) {
225 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
226 ObjectPath("/org/chromium/TestObject"));
227 EXPECT_TRUE(object_proxy != NULL);
228
229 Properties* properties = static_cast<Properties*>(
230 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
231 "org.chromium.TestInterface"));
232 EXPECT_TRUE(properties != NULL);
233
234 EXPECT_EQ("TestService", properties->name.value());
235 EXPECT_EQ(10, properties->version.value());
236
237 std::vector<std::string> methods = properties->methods.value();
238 ASSERT_EQ(4U, methods.size());
239 EXPECT_EQ("Echo", methods[0]);
240 EXPECT_EQ("SlowEcho", methods[1]);
241 EXPECT_EQ("AsyncEcho", methods[2]);
242 EXPECT_EQ("BrokenMethod", methods[3]);
243
244 std::vector<ObjectPath> objects = properties->objects.value();
245 ASSERT_EQ(1U, objects.size());
246 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
247 }
248
TEST_F(ObjectManagerTest,UnknownObjectProxy)249 TEST_F(ObjectManagerTest, UnknownObjectProxy) {
250 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
251 ObjectPath("/org/chromium/UnknownObject"));
252 EXPECT_TRUE(object_proxy == NULL);
253 }
254
TEST_F(ObjectManagerTest,UnknownObjectProperties)255 TEST_F(ObjectManagerTest, UnknownObjectProperties) {
256 Properties* properties = static_cast<Properties*>(
257 object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
258 "org.chromium.TestInterface"));
259 EXPECT_TRUE(properties == NULL);
260 }
261
TEST_F(ObjectManagerTest,UnknownInterfaceProperties)262 TEST_F(ObjectManagerTest, UnknownInterfaceProperties) {
263 Properties* properties = static_cast<Properties*>(
264 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
265 "org.chromium.UnknownService"));
266 EXPECT_TRUE(properties == NULL);
267 }
268
TEST_F(ObjectManagerTest,GetObjects)269 TEST_F(ObjectManagerTest, GetObjects) {
270 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
271 ASSERT_EQ(1U, object_paths.size());
272 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
273 }
274
TEST_F(ObjectManagerTest,GetObjectsWithInterface)275 TEST_F(ObjectManagerTest, GetObjectsWithInterface) {
276 std::vector<ObjectPath> object_paths =
277 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
278 ASSERT_EQ(1U, object_paths.size());
279 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
280 }
281
TEST_F(ObjectManagerTest,GetObjectsWithUnknownInterface)282 TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) {
283 std::vector<ObjectPath> object_paths =
284 object_manager_->GetObjectsWithInterface("org.chromium.UnknownService");
285 EXPECT_EQ(0U, object_paths.size());
286 }
287
TEST_F(ObjectManagerTest,SameObject)288 TEST_F(ObjectManagerTest, SameObject) {
289 ObjectManager* object_manager = bus_->GetObjectManager(
290 "org.chromium.TestService",
291 ObjectPath("/org/chromium/TestService"));
292 EXPECT_EQ(object_manager_, object_manager);
293 }
294
TEST_F(ObjectManagerTest,DifferentObjectForService)295 TEST_F(ObjectManagerTest, DifferentObjectForService) {
296 ObjectManager* object_manager = bus_->GetObjectManager(
297 "org.chromium.DifferentService",
298 ObjectPath("/org/chromium/TestService"));
299 EXPECT_NE(object_manager_, object_manager);
300 }
301
TEST_F(ObjectManagerTest,DifferentObjectForPath)302 TEST_F(ObjectManagerTest, DifferentObjectForPath) {
303 ObjectManager* object_manager = bus_->GetObjectManager(
304 "org.chromium.TestService",
305 ObjectPath("/org/chromium/DifferentService"));
306 EXPECT_NE(object_manager_, object_manager);
307 }
308
TEST_F(ObjectManagerTest,SecondObject)309 TEST_F(ObjectManagerTest, SecondObject) {
310 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
311 WaitForObject();
312
313 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
314 ObjectPath("/org/chromium/SecondObject"));
315 EXPECT_TRUE(object_proxy != NULL);
316
317 Properties* properties = static_cast<Properties*>(
318 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
319 "org.chromium.TestInterface"));
320 EXPECT_TRUE(properties != NULL);
321
322 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
323 ASSERT_EQ(2U, object_paths.size());
324
325 std::sort(object_paths.begin(), object_paths.end());
326 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
327 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
328
329 object_paths =
330 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
331 ASSERT_EQ(2U, object_paths.size());
332
333 std::sort(object_paths.begin(), object_paths.end());
334 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
335 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
336 }
337
TEST_F(ObjectManagerTest,RemoveSecondObject)338 TEST_F(ObjectManagerTest, RemoveSecondObject) {
339 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
340 WaitForObject();
341
342 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
343 ASSERT_EQ(2U, object_paths.size());
344
345 PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
346 WaitForRemoveObject();
347
348 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
349 ObjectPath("/org/chromium/SecondObject"));
350 EXPECT_TRUE(object_proxy == NULL);
351
352 Properties* properties = static_cast<Properties*>(
353 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
354 "org.chromium.TestInterface"));
355 EXPECT_TRUE(properties == NULL);
356
357 object_paths = object_manager_->GetObjects();
358 ASSERT_EQ(1U, object_paths.size());
359 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
360
361 object_paths =
362 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
363 ASSERT_EQ(1U, object_paths.size());
364 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
365 }
366
TEST_F(ObjectManagerTest,OwnershipLost)367 TEST_F(ObjectManagerTest, OwnershipLost) {
368 PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
369 WaitForRemoveObject();
370
371 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
372 ASSERT_EQ(0U, object_paths.size());
373 }
374
TEST_F(ObjectManagerTest,OwnershipLostAndRegained)375 TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
376 PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
377 WaitForRemoveObject();
378 WaitForObject();
379
380 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
381 ASSERT_EQ(1U, object_paths.size());
382 }
383
TEST_F(ObjectManagerTest,PropertiesChangedAsObjectsReceived)384 TEST_F(ObjectManagerTest, PropertiesChangedAsObjectsReceived) {
385 // Remove the existing object manager.
386 object_manager_->UnregisterInterface("org.chromium.TestInterface");
387 run_loop_.reset(new base::RunLoop);
388 EXPECT_TRUE(bus_->RemoveObjectManager(
389 "org.chromium.TestService",
390 ObjectPath("/org/chromium/TestService"),
391 run_loop_->QuitClosure()));
392 run_loop_->Run();
393
394 PerformAction("SetSendImmediatePropertiesChanged",
395 ObjectPath("/org/chromium/TestService"));
396
397 object_manager_ = bus_->GetObjectManager(
398 "org.chromium.TestService",
399 ObjectPath("/org/chromium/TestService"));
400 object_manager_->RegisterInterface("org.chromium.TestInterface", this);
401
402 // The newly created object manager should call GetManagedObjects immediately
403 // after setting up the match rule for PropertiesChanged. We should process
404 // the PropertiesChanged event right after that. If we don't receive it within
405 // 2 seconds, then fail the test.
406 message_loop_.PostDelayedTask(
407 FROM_HERE,
408 base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout,
409 base::Unretained(this)),
410 base::TimeDelta::FromSeconds(2));
411
412 while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) {
413 run_loop_.reset(new base::RunLoop);
414 run_loop_->Run();
415 }
416 }
417
418 } // namespace dbus
419