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.
5 #include "dbus/object_manager.h"
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"
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"
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
{
31 ObjectManagerTest() : timeout_expired_(false) {
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
;
40 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
);
51 PropertySet
* CreateProperties(ObjectProxy
* object_proxy
,
52 const ObjectPath
& object_path
,
53 const std::string
& interface_name
) override
{
54 Properties
* properties
= new Properties(
55 object_proxy
, interface_name
,
56 base::Bind(&ObjectManagerTest::OnPropertyChanged
,
57 base::Unretained(this), object_path
));
58 return static_cast<PropertySet
*>(properties
);
61 void SetUp() override
{
62 // Make the main thread not to allow IO.
63 base::ThreadRestrictions::SetIOAllowed(false);
65 // Start the D-Bus thread.
66 dbus_thread_
.reset(new base::Thread("D-Bus Thread"));
67 base::Thread::Options thread_options
;
68 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
69 ASSERT_TRUE(dbus_thread_
->StartWithOptions(thread_options
));
71 // Start the test service, using the D-Bus thread.
72 TestService::Options options
;
73 options
.dbus_task_runner
= dbus_thread_
->message_loop_proxy();
74 test_service_
.reset(new TestService(options
));
75 ASSERT_TRUE(test_service_
->StartService());
76 ASSERT_TRUE(test_service_
->WaitUntilServiceIsStarted());
77 ASSERT_TRUE(test_service_
->HasDBusThread());
79 // Create the client, using the D-Bus thread.
80 Bus::Options bus_options
;
81 bus_options
.bus_type
= Bus::SESSION
;
82 bus_options
.connection_type
= Bus::PRIVATE
;
83 bus_options
.dbus_task_runner
= dbus_thread_
->message_loop_proxy();
84 bus_
= new Bus(bus_options
);
85 ASSERT_TRUE(bus_
->HasDBusThread());
87 object_manager_
= bus_
->GetObjectManager(
88 "org.chromium.TestService",
89 ObjectPath("/org/chromium/TestService"));
90 object_manager_
->RegisterInterface("org.chromium.TestInterface", this);
95 void TearDown() override
{
96 bus_
->ShutdownOnDBusThreadAndBlock();
98 // Shut down the service.
99 test_service_
->ShutdownAndBlock();
101 // Reset to the default.
102 base::ThreadRestrictions::SetIOAllowed(true);
104 // Stopping a thread is considered an IO operation, so do this after
106 test_service_
->Stop();
108 base::RunLoop().RunUntilIdle();
111 void MethodCallback(Response
* response
) {
112 method_callback_called_
= true;
116 // Called from the PropertiesChangedAsObjectsReceived test case. The test will
117 // not run the message loop if it receives the expected PropertiesChanged
118 // signal before the timeout. This method immediately fails the test.
119 void PropertiesChangedTestTimeout() {
120 timeout_expired_
= true;
123 FAIL() << "Never received PropertiesChanged";
127 // Called when an object is added.
128 void ObjectAdded(const ObjectPath
& object_path
,
129 const std::string
& interface_name
) override
{
130 added_objects_
.push_back(std::make_pair(object_path
, interface_name
));
134 // Called when an object is removed.
135 void ObjectRemoved(const ObjectPath
& object_path
,
136 const std::string
& interface_name
) override
{
137 removed_objects_
.push_back(std::make_pair(object_path
, interface_name
));
141 // Called when a property value is updated.
142 void OnPropertyChanged(const ObjectPath
& object_path
,
143 const std::string
& name
) {
144 // Store the value of the "Name" property if that's the one that
146 Properties
* properties
= static_cast<Properties
*>(
147 object_manager_
->GetProperties(
149 "org.chromium.TestInterface"));
150 if (name
== properties
->name
.name())
151 last_name_value_
= properties
->name
.value();
153 // Store the updated property.
154 updated_properties_
.push_back(name
);
158 static const size_t kExpectedObjects
= 1;
159 static const size_t kExpectedProperties
= 4;
161 void WaitForObject() {
162 while (added_objects_
.size() < kExpectedObjects
||
163 updated_properties_
.size() < kExpectedProperties
) {
164 run_loop_
.reset(new base::RunLoop
);
167 for (size_t i
= 0; i
< kExpectedObjects
; ++i
)
168 added_objects_
.erase(added_objects_
.begin());
169 for (size_t i
= 0; i
< kExpectedProperties
; ++i
)
170 updated_properties_
.erase(updated_properties_
.begin());
173 void WaitForRemoveObject() {
174 while (removed_objects_
.size() < kExpectedObjects
) {
175 run_loop_
.reset(new base::RunLoop
);
178 for (size_t i
= 0; i
< kExpectedObjects
; ++i
)
179 removed_objects_
.erase(removed_objects_
.begin());
182 void WaitForMethodCallback() {
183 run_loop_
.reset(new base::RunLoop
);
185 method_callback_called_
= false;
188 void PerformAction(const std::string
& action
, const ObjectPath
& object_path
) {
189 ObjectProxy
* object_proxy
= bus_
->GetObjectProxy(
190 "org.chromium.TestService",
191 ObjectPath("/org/chromium/TestObject"));
193 MethodCall
method_call("org.chromium.TestInterface", "PerformAction");
194 MessageWriter
writer(&method_call
);
195 writer
.AppendString(action
);
196 writer
.AppendObjectPath(object_path
);
198 object_proxy
->CallMethod(&method_call
,
199 ObjectProxy::TIMEOUT_USE_DEFAULT
,
200 base::Bind(&ObjectManagerTest::MethodCallback
,
201 base::Unretained(this)));
202 WaitForMethodCallback();
205 base::MessageLoop message_loop_
;
206 scoped_ptr
<base::RunLoop
> run_loop_
;
207 scoped_ptr
<base::Thread
> dbus_thread_
;
208 scoped_refptr
<Bus
> bus_
;
209 ObjectManager
* object_manager_
;
210 scoped_ptr
<TestService
> test_service_
;
212 std::string last_name_value_
;
213 bool timeout_expired_
;
215 std::vector
<std::pair
<ObjectPath
, std::string
> > added_objects_
;
216 std::vector
<std::pair
<ObjectPath
, std::string
> > removed_objects_
;
217 std::vector
<std::string
> updated_properties_
;
219 bool method_callback_called_
;
223 TEST_F(ObjectManagerTest
, InitialObject
) {
224 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
225 ObjectPath("/org/chromium/TestObject"));
226 EXPECT_TRUE(object_proxy
!= NULL
);
228 Properties
* properties
= static_cast<Properties
*>(
229 object_manager_
->GetProperties(ObjectPath("/org/chromium/TestObject"),
230 "org.chromium.TestInterface"));
231 EXPECT_TRUE(properties
!= NULL
);
233 EXPECT_EQ("TestService", properties
->name
.value());
234 EXPECT_EQ(10, properties
->version
.value());
236 std::vector
<std::string
> methods
= properties
->methods
.value();
237 ASSERT_EQ(4U, methods
.size());
238 EXPECT_EQ("Echo", methods
[0]);
239 EXPECT_EQ("SlowEcho", methods
[1]);
240 EXPECT_EQ("AsyncEcho", methods
[2]);
241 EXPECT_EQ("BrokenMethod", methods
[3]);
243 std::vector
<ObjectPath
> objects
= properties
->objects
.value();
244 ASSERT_EQ(1U, objects
.size());
245 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects
[0]);
248 TEST_F(ObjectManagerTest
, UnknownObjectProxy
) {
249 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
250 ObjectPath("/org/chromium/UnknownObject"));
251 EXPECT_TRUE(object_proxy
== NULL
);
254 TEST_F(ObjectManagerTest
, UnknownObjectProperties
) {
255 Properties
* properties
= static_cast<Properties
*>(
256 object_manager_
->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
257 "org.chromium.TestInterface"));
258 EXPECT_TRUE(properties
== NULL
);
261 TEST_F(ObjectManagerTest
, UnknownInterfaceProperties
) {
262 Properties
* properties
= static_cast<Properties
*>(
263 object_manager_
->GetProperties(ObjectPath("/org/chromium/TestObject"),
264 "org.chromium.UnknownService"));
265 EXPECT_TRUE(properties
== NULL
);
268 TEST_F(ObjectManagerTest
, GetObjects
) {
269 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
270 ASSERT_EQ(1U, object_paths
.size());
271 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
274 TEST_F(ObjectManagerTest
, GetObjectsWithInterface
) {
275 std::vector
<ObjectPath
> object_paths
=
276 object_manager_
->GetObjectsWithInterface("org.chromium.TestInterface");
277 ASSERT_EQ(1U, object_paths
.size());
278 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
281 TEST_F(ObjectManagerTest
, GetObjectsWithUnknownInterface
) {
282 std::vector
<ObjectPath
> object_paths
=
283 object_manager_
->GetObjectsWithInterface("org.chromium.UnknownService");
284 EXPECT_EQ(0U, object_paths
.size());
287 TEST_F(ObjectManagerTest
, SameObject
) {
288 ObjectManager
* object_manager
= bus_
->GetObjectManager(
289 "org.chromium.TestService",
290 ObjectPath("/org/chromium/TestService"));
291 EXPECT_EQ(object_manager_
, object_manager
);
294 TEST_F(ObjectManagerTest
, DifferentObjectForService
) {
295 ObjectManager
* object_manager
= bus_
->GetObjectManager(
296 "org.chromium.DifferentService",
297 ObjectPath("/org/chromium/TestService"));
298 EXPECT_NE(object_manager_
, object_manager
);
301 TEST_F(ObjectManagerTest
, DifferentObjectForPath
) {
302 ObjectManager
* object_manager
= bus_
->GetObjectManager(
303 "org.chromium.TestService",
304 ObjectPath("/org/chromium/DifferentService"));
305 EXPECT_NE(object_manager_
, object_manager
);
308 TEST_F(ObjectManagerTest
, SecondObject
) {
309 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
312 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
313 ObjectPath("/org/chromium/SecondObject"));
314 EXPECT_TRUE(object_proxy
!= NULL
);
316 Properties
* properties
= static_cast<Properties
*>(
317 object_manager_
->GetProperties(ObjectPath("/org/chromium/SecondObject"),
318 "org.chromium.TestInterface"));
319 EXPECT_TRUE(properties
!= NULL
);
321 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
322 ASSERT_EQ(2U, object_paths
.size());
324 std::sort(object_paths
.begin(), object_paths
.end());
325 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths
[0]);
326 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[1]);
329 object_manager_
->GetObjectsWithInterface("org.chromium.TestInterface");
330 ASSERT_EQ(2U, object_paths
.size());
332 std::sort(object_paths
.begin(), object_paths
.end());
333 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths
[0]);
334 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[1]);
337 TEST_F(ObjectManagerTest
, RemoveSecondObject
) {
338 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
341 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
342 ASSERT_EQ(2U, object_paths
.size());
344 PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
345 WaitForRemoveObject();
347 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
348 ObjectPath("/org/chromium/SecondObject"));
349 EXPECT_TRUE(object_proxy
== NULL
);
351 Properties
* properties
= static_cast<Properties
*>(
352 object_manager_
->GetProperties(ObjectPath("/org/chromium/SecondObject"),
353 "org.chromium.TestInterface"));
354 EXPECT_TRUE(properties
== NULL
);
356 object_paths
= object_manager_
->GetObjects();
357 ASSERT_EQ(1U, object_paths
.size());
358 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
361 object_manager_
->GetObjectsWithInterface("org.chromium.TestInterface");
362 ASSERT_EQ(1U, object_paths
.size());
363 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
366 TEST_F(ObjectManagerTest
, OwnershipLost
) {
367 PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
368 WaitForRemoveObject();
370 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
371 ASSERT_EQ(0U, object_paths
.size());
374 TEST_F(ObjectManagerTest
, OwnershipLostAndRegained
) {
375 PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
376 WaitForRemoveObject();
379 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
380 ASSERT_EQ(1U, object_paths
.size());
383 TEST_F(ObjectManagerTest
, PropertiesChangedAsObjectsReceived
) {
384 // Remove the existing object manager.
385 object_manager_
->UnregisterInterface("org.chromium.TestInterface");
386 run_loop_
.reset(new base::RunLoop
);
387 EXPECT_TRUE(bus_
->RemoveObjectManager(
388 "org.chromium.TestService",
389 ObjectPath("/org/chromium/TestService"),
390 run_loop_
->QuitClosure()));
393 PerformAction("SetSendImmediatePropertiesChanged",
394 ObjectPath("/org/chromium/TestService"));
396 object_manager_
= bus_
->GetObjectManager(
397 "org.chromium.TestService",
398 ObjectPath("/org/chromium/TestService"));
399 object_manager_
->RegisterInterface("org.chromium.TestInterface", this);
401 // The newly created object manager should call GetManagedObjects immediately
402 // after setting up the match rule for PropertiesChanged. We should process
403 // the PropertiesChanged event right after that. If we don't receive it within
404 // 2 seconds, then fail the test.
405 message_loop_
.PostDelayedTask(
407 base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout
,
408 base::Unretained(this)),
409 base::TimeDelta::FromSeconds(2));
411 while (last_name_value_
!= "ChangedTestServiceName" && !timeout_expired_
) {
412 run_loop_
.reset(new base::RunLoop
);