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