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 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
);
62 virtual void SetUp() {
63 // Make the main thread not to allow IO.
64 base::ThreadRestrictions::SetIOAllowed(false);
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
));
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());
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());
88 object_manager_
= bus_
->GetObjectManager(
89 "org.chromium.TestService",
90 ObjectPath("/org/chromium/TestService"));
91 object_manager_
->RegisterInterface("org.chromium.TestInterface", this);
96 virtual void TearDown() {
97 bus_
->ShutdownOnDBusThreadAndBlock();
99 // Shut down the service.
100 test_service_
->ShutdownAndBlock();
102 // Reset to the default.
103 base::ThreadRestrictions::SetIOAllowed(true);
105 // Stopping a thread is considered an IO operation, so do this after
107 test_service_
->Stop();
109 base::RunLoop().RunUntilIdle();
112 void MethodCallback(Response
* response
) {
113 method_callback_called_
= true;
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.
120 void PropertiesChangedTestTimeout() {
121 timeout_expired_
= true;
124 FAIL() << "Never received PropertiesChanged";
128 // Called when an object is added.
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
));
135 // Called when an object is removed.
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
));
142 // Called when a property value is updated.
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
147 Properties
* properties
= static_cast<Properties
*>(
148 object_manager_
->GetProperties(
150 "org.chromium.TestInterface"));
151 if (name
== properties
->name
.name())
152 last_name_value_
= properties
->name
.value();
154 // Store the updated property.
155 updated_properties_
.push_back(name
);
159 static const size_t kExpectedObjects
= 1;
160 static const size_t kExpectedProperties
= 4;
162 void WaitForObject() {
163 while (added_objects_
.size() < kExpectedObjects
||
164 updated_properties_
.size() < kExpectedProperties
) {
165 run_loop_
.reset(new base::RunLoop
);
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());
174 void WaitForRemoveObject() {
175 while (removed_objects_
.size() < kExpectedObjects
) {
176 run_loop_
.reset(new base::RunLoop
);
179 for (size_t i
= 0; i
< kExpectedObjects
; ++i
)
180 removed_objects_
.erase(removed_objects_
.begin());
183 void WaitForMethodCallback() {
184 run_loop_
.reset(new base::RunLoop
);
186 method_callback_called_
= false;
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"));
194 MethodCall
method_call("org.chromium.TestInterface", "PerformAction");
195 MessageWriter
writer(&method_call
);
196 writer
.AppendString(action
);
197 writer
.AppendObjectPath(object_path
);
199 object_proxy
->CallMethod(&method_call
,
200 ObjectProxy::TIMEOUT_USE_DEFAULT
,
201 base::Bind(&ObjectManagerTest::MethodCallback
,
202 base::Unretained(this)));
203 WaitForMethodCallback();
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_
;
213 std::string last_name_value_
;
214 bool timeout_expired_
;
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_
;
220 bool method_callback_called_
;
224 TEST_F(ObjectManagerTest
, InitialObject
) {
225 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
226 ObjectPath("/org/chromium/TestObject"));
227 EXPECT_TRUE(object_proxy
!= NULL
);
229 Properties
* properties
= static_cast<Properties
*>(
230 object_manager_
->GetProperties(ObjectPath("/org/chromium/TestObject"),
231 "org.chromium.TestInterface"));
232 EXPECT_TRUE(properties
!= NULL
);
234 EXPECT_EQ("TestService", properties
->name
.value());
235 EXPECT_EQ(10, properties
->version
.value());
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]);
244 std::vector
<ObjectPath
> objects
= properties
->objects
.value();
245 ASSERT_EQ(1U, objects
.size());
246 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects
[0]);
249 TEST_F(ObjectManagerTest
, UnknownObjectProxy
) {
250 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
251 ObjectPath("/org/chromium/UnknownObject"));
252 EXPECT_TRUE(object_proxy
== NULL
);
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
);
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
);
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]);
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]);
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());
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
);
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
);
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
);
309 TEST_F(ObjectManagerTest
, SecondObject
) {
310 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
313 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
314 ObjectPath("/org/chromium/SecondObject"));
315 EXPECT_TRUE(object_proxy
!= NULL
);
317 Properties
* properties
= static_cast<Properties
*>(
318 object_manager_
->GetProperties(ObjectPath("/org/chromium/SecondObject"),
319 "org.chromium.TestInterface"));
320 EXPECT_TRUE(properties
!= NULL
);
322 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
323 ASSERT_EQ(2U, object_paths
.size());
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]);
330 object_manager_
->GetObjectsWithInterface("org.chromium.TestInterface");
331 ASSERT_EQ(2U, object_paths
.size());
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]);
338 TEST_F(ObjectManagerTest
, RemoveSecondObject
) {
339 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
342 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
343 ASSERT_EQ(2U, object_paths
.size());
345 PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
346 WaitForRemoveObject();
348 ObjectProxy
* object_proxy
= object_manager_
->GetObjectProxy(
349 ObjectPath("/org/chromium/SecondObject"));
350 EXPECT_TRUE(object_proxy
== NULL
);
352 Properties
* properties
= static_cast<Properties
*>(
353 object_manager_
->GetProperties(ObjectPath("/org/chromium/SecondObject"),
354 "org.chromium.TestInterface"));
355 EXPECT_TRUE(properties
== NULL
);
357 object_paths
= object_manager_
->GetObjects();
358 ASSERT_EQ(1U, object_paths
.size());
359 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
362 object_manager_
->GetObjectsWithInterface("org.chromium.TestInterface");
363 ASSERT_EQ(1U, object_paths
.size());
364 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths
[0]);
367 TEST_F(ObjectManagerTest
, OwnershipLost
) {
368 PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
369 WaitForRemoveObject();
371 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
372 ASSERT_EQ(0U, object_paths
.size());
375 TEST_F(ObjectManagerTest
, OwnershipLostAndRegained
) {
376 PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
377 WaitForRemoveObject();
380 std::vector
<ObjectPath
> object_paths
= object_manager_
->GetObjects();
381 ASSERT_EQ(1U, object_paths
.size());
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()));
394 PerformAction("SetSendImmediatePropertiesChanged",
395 ObjectPath("/org/chromium/TestService"));
397 object_manager_
= bus_
->GetObjectManager(
398 "org.chromium.TestService",
399 ObjectPath("/org/chromium/TestService"));
400 object_manager_
->RegisterInterface("org.chromium.TestInterface", this);
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(
408 base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout
,
409 base::Unretained(this)),
410 base::TimeDelta::FromSeconds(2));
412 while (last_name_value_
!= "ChangedTestServiceName" && !timeout_expired_
) {
413 run_loop_
.reset(new base::RunLoop
);