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.
8 #include "base/memory/ref_counted.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/threading/thread.h"
12 #include "dbus/exported_object.h"
13 #include "dbus/object_path.h"
14 #include "dbus/object_proxy.h"
15 #include "dbus/scoped_dbus_error.h"
16 #include "dbus/test_service.h"
18 #include "testing/gtest/include/gtest/gtest.h"
24 // Test helper for BusTest.ListenForServiceOwnerChange that wraps a
25 // base::RunLoop. At Run() time, the caller pass in the expected number of
26 // quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
27 // if the expected number of quit calls have been reached.
28 class RunLoopWithExpectedCount
{
30 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
31 ~RunLoopWithExpectedCount() {}
33 void Run(int expected_quit_calls
) {
34 DCHECK_EQ(0, expected_quit_calls_
);
35 DCHECK_EQ(0, actual_quit_calls_
);
36 expected_quit_calls_
= expected_quit_calls
;
37 run_loop_
.reset(new base::RunLoop());
41 void QuitIfConditionIsSatisified() {
42 if (++actual_quit_calls_
!= expected_quit_calls_
)
45 expected_quit_calls_
= 0;
46 actual_quit_calls_
= 0;
50 scoped_ptr
<base::RunLoop
> run_loop_
;
51 int expected_quit_calls_
;
52 int actual_quit_calls_
;
54 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount
);
57 // Test helper for BusTest.ListenForServiceOwnerChange.
58 void OnServiceOwnerChanged(RunLoopWithExpectedCount
* run_loop_state
,
59 std::string
* service_owner
,
60 int* num_of_owner_changes
,
61 const std::string
& new_service_owner
) {
62 *service_owner
= new_service_owner
;
63 ++(*num_of_owner_changes
);
64 run_loop_state
->QuitIfConditionIsSatisified();
69 TEST(BusTest
, GetObjectProxy
) {
71 scoped_refptr
<Bus
> bus
= new Bus(options
);
73 ObjectProxy
* object_proxy1
=
74 bus
->GetObjectProxy("org.chromium.TestService",
75 ObjectPath("/org/chromium/TestObject"));
76 ASSERT_TRUE(object_proxy1
);
78 // This should return the same object.
79 ObjectProxy
* object_proxy2
=
80 bus
->GetObjectProxy("org.chromium.TestService",
81 ObjectPath("/org/chromium/TestObject"));
82 ASSERT_TRUE(object_proxy2
);
83 EXPECT_EQ(object_proxy1
, object_proxy2
);
86 ObjectProxy
* object_proxy3
=
88 "org.chromium.TestService",
89 ObjectPath("/org/chromium/DifferentTestObject"));
90 ASSERT_TRUE(object_proxy3
);
91 EXPECT_NE(object_proxy1
, object_proxy3
);
93 bus
->ShutdownAndBlock();
96 TEST(BusTest
, GetObjectProxyIgnoreUnknownService
) {
98 scoped_refptr
<Bus
> bus
= new Bus(options
);
100 ObjectProxy
* object_proxy1
=
101 bus
->GetObjectProxyWithOptions(
102 "org.chromium.TestService",
103 ObjectPath("/org/chromium/TestObject"),
104 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
105 ASSERT_TRUE(object_proxy1
);
107 // This should return the same object.
108 ObjectProxy
* object_proxy2
=
109 bus
->GetObjectProxyWithOptions(
110 "org.chromium.TestService",
111 ObjectPath("/org/chromium/TestObject"),
112 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
113 ASSERT_TRUE(object_proxy2
);
114 EXPECT_EQ(object_proxy1
, object_proxy2
);
117 ObjectProxy
* object_proxy3
=
118 bus
->GetObjectProxyWithOptions(
119 "org.chromium.TestService",
120 ObjectPath("/org/chromium/DifferentTestObject"),
121 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
122 ASSERT_TRUE(object_proxy3
);
123 EXPECT_NE(object_proxy1
, object_proxy3
);
125 bus
->ShutdownAndBlock();
128 TEST(BusTest
, RemoveObjectProxy
) {
129 // Setup the current thread's MessageLoop.
130 base::MessageLoop message_loop
;
132 // Start the D-Bus thread.
133 base::Thread::Options thread_options
;
134 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
135 base::Thread
dbus_thread("D-Bus thread");
136 dbus_thread
.StartWithOptions(thread_options
);
139 Bus::Options options
;
140 options
.dbus_task_runner
= dbus_thread
.task_runner();
141 scoped_refptr
<Bus
> bus
= new Bus(options
);
142 ASSERT_FALSE(bus
->shutdown_completed());
144 // Try to remove a non existant object proxy should return false.
146 bus
->RemoveObjectProxy("org.chromium.TestService",
147 ObjectPath("/org/chromium/TestObject"),
148 base::Bind(&base::DoNothing
)));
150 ObjectProxy
* object_proxy1
=
151 bus
->GetObjectProxy("org.chromium.TestService",
152 ObjectPath("/org/chromium/TestObject"));
153 ASSERT_TRUE(object_proxy1
);
155 // Increment the reference count to the object proxy to avoid destroying it
156 // while removing the object.
157 object_proxy1
->AddRef();
159 // Remove the object from the bus. This will invalidate any other usage of
160 // object_proxy1 other than destroy it. We keep this object for a comparison
163 bus
->RemoveObjectProxy("org.chromium.TestService",
164 ObjectPath("/org/chromium/TestObject"),
165 base::Bind(&base::DoNothing
)));
167 // This should return a different object because the first object was removed
168 // from the bus, but not deleted from memory.
169 ObjectProxy
* object_proxy2
=
170 bus
->GetObjectProxy("org.chromium.TestService",
171 ObjectPath("/org/chromium/TestObject"));
172 ASSERT_TRUE(object_proxy2
);
174 // Compare the new object with the first object. The first object still exists
175 // thanks to the increased reference.
176 EXPECT_NE(object_proxy1
, object_proxy2
);
178 // Release object_proxy1.
179 object_proxy1
->Release();
181 // Shut down synchronously.
182 bus
->ShutdownOnDBusThreadAndBlock();
183 EXPECT_TRUE(bus
->shutdown_completed());
187 TEST(BusTest
, GetExportedObject
) {
188 Bus::Options options
;
189 scoped_refptr
<Bus
> bus
= new Bus(options
);
191 ExportedObject
* object_proxy1
=
192 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
193 ASSERT_TRUE(object_proxy1
);
195 // This should return the same object.
196 ExportedObject
* object_proxy2
=
197 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
198 ASSERT_TRUE(object_proxy2
);
199 EXPECT_EQ(object_proxy1
, object_proxy2
);
202 ExportedObject
* object_proxy3
=
203 bus
->GetExportedObject(
204 ObjectPath("/org/chromium/DifferentTestObject"));
205 ASSERT_TRUE(object_proxy3
);
206 EXPECT_NE(object_proxy1
, object_proxy3
);
208 bus
->ShutdownAndBlock();
211 TEST(BusTest
, UnregisterExportedObject
) {
212 // Start the D-Bus thread.
213 base::Thread::Options thread_options
;
214 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
215 base::Thread
dbus_thread("D-Bus thread");
216 dbus_thread
.StartWithOptions(thread_options
);
219 Bus::Options options
;
220 options
.dbus_task_runner
= dbus_thread
.task_runner();
221 scoped_refptr
<Bus
> bus
= new Bus(options
);
222 ASSERT_FALSE(bus
->shutdown_completed());
224 ExportedObject
* object_proxy1
=
225 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
226 ASSERT_TRUE(object_proxy1
);
228 // Increment the reference count to the object proxy to avoid destroying it
229 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
230 // not freed from memory. See http://crbug.com/137846 for details.
231 object_proxy1
->AddRef();
233 bus
->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
235 // This should return a new object because the object_proxy1 is still in
237 ExportedObject
* object_proxy2
=
238 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
239 ASSERT_TRUE(object_proxy2
);
240 EXPECT_NE(object_proxy1
, object_proxy2
);
242 // Release the incremented reference.
243 object_proxy1
->Release();
245 // Shut down synchronously.
246 bus
->ShutdownOnDBusThreadAndBlock();
247 EXPECT_TRUE(bus
->shutdown_completed());
251 TEST(BusTest
, ShutdownAndBlock
) {
252 Bus::Options options
;
253 scoped_refptr
<Bus
> bus
= new Bus(options
);
254 ASSERT_FALSE(bus
->shutdown_completed());
256 // Shut down synchronously.
257 bus
->ShutdownAndBlock();
258 EXPECT_TRUE(bus
->shutdown_completed());
261 TEST(BusTest
, ShutdownAndBlockWithDBusThread
) {
262 // Start the D-Bus thread.
263 base::Thread::Options thread_options
;
264 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
265 base::Thread
dbus_thread("D-Bus thread");
266 dbus_thread
.StartWithOptions(thread_options
);
269 Bus::Options options
;
270 options
.dbus_task_runner
= dbus_thread
.task_runner();
271 scoped_refptr
<Bus
> bus
= new Bus(options
);
272 ASSERT_FALSE(bus
->shutdown_completed());
274 // Shut down synchronously.
275 bus
->ShutdownOnDBusThreadAndBlock();
276 EXPECT_TRUE(bus
->shutdown_completed());
280 TEST(BusTest
, DoubleAddAndRemoveMatch
) {
281 Bus::Options options
;
282 scoped_refptr
<Bus
> bus
= new Bus(options
);
283 ScopedDBusError error
;
287 // Adds the same rule twice.
289 "type='signal',interface='org.chromium.TestService',path='/'",
291 ASSERT_FALSE(error
.is_set());
294 "type='signal',interface='org.chromium.TestService',path='/'",
296 ASSERT_FALSE(error
.is_set());
298 // Removes the same rule twice.
299 ASSERT_TRUE(bus
->RemoveMatch(
300 "type='signal',interface='org.chromium.TestService',path='/'",
302 ASSERT_FALSE(error
.is_set());
304 // The rule should be still in the bus since it was removed only once.
305 // A second removal shouldn't give an error.
306 ASSERT_TRUE(bus
->RemoveMatch(
307 "type='signal',interface='org.chromium.TestService',path='/'",
309 ASSERT_FALSE(error
.is_set());
311 // A third attemp to remove the same rule should fail.
312 ASSERT_FALSE(bus
->RemoveMatch(
313 "type='signal',interface='org.chromium.TestService',path='/'",
316 bus
->ShutdownAndBlock();
319 TEST(BusTest
, ListenForServiceOwnerChange
) {
320 // Setup the current thread's MessageLoop. Must be of TYPE_IO for the
321 // listeners to work.
322 base::MessageLoop
message_loop(base::MessageLoop::TYPE_IO
);
323 RunLoopWithExpectedCount run_loop_state
;
326 Bus::Options bus_options
;
327 scoped_refptr
<Bus
> bus
= new Bus(bus_options
);
330 std::string service_owner1
;
331 int num_of_owner_changes1
= 0;
332 Bus::GetServiceOwnerCallback callback1
=
333 base::Bind(&OnServiceOwnerChanged
,
336 &num_of_owner_changes1
);
337 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback1
);
338 // This should be a no-op.
339 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback1
);
340 base::RunLoop().RunUntilIdle();
342 // Nothing has happened yet. Check initial state.
343 EXPECT_TRUE(service_owner1
.empty());
344 EXPECT_EQ(0, num_of_owner_changes1
);
346 // Make an ownership change.
347 ASSERT_TRUE(bus
->RequestOwnershipAndBlock("org.chromium.TestService",
348 Bus::REQUIRE_PRIMARY
));
349 run_loop_state
.Run(1);
352 // Get the current service owner and check to make sure the listener got
354 std::string current_service_owner
=
355 bus
->GetServiceOwnerAndBlock("org.chromium.TestService",
357 ASSERT_FALSE(current_service_owner
.empty());
359 // Make sure the listener heard about the new owner.
360 EXPECT_EQ(current_service_owner
, service_owner1
);
362 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
363 EXPECT_EQ(1, num_of_owner_changes1
);
366 // Add a second listener.
367 std::string service_owner2
;
368 int num_of_owner_changes2
= 0;
369 Bus::GetServiceOwnerCallback callback2
=
370 base::Bind(&OnServiceOwnerChanged
,
373 &num_of_owner_changes2
);
374 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback2
);
375 base::RunLoop().RunUntilIdle();
377 // Release the ownership and make sure the service owner listeners fire with
378 // the right values and the right number of times.
379 ASSERT_TRUE(bus
->ReleaseOwnership("org.chromium.TestService"));
380 run_loop_state
.Run(2);
382 EXPECT_TRUE(service_owner1
.empty());
383 EXPECT_TRUE(service_owner2
.empty());
384 EXPECT_EQ(2, num_of_owner_changes1
);
385 EXPECT_EQ(1, num_of_owner_changes2
);
387 // Unlisten so shutdown can proceed correctly.
388 bus
->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1
);
389 bus
->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2
);
390 base::RunLoop().RunUntilIdle();
392 // Shut down synchronously.
393 bus
->ShutdownAndBlock();
394 EXPECT_TRUE(bus
->shutdown_completed());
397 TEST(BusTest
, GetConnectionName
) {
398 Bus::Options options
;
399 scoped_refptr
<Bus
> bus
= new Bus(options
);
401 // Connection name is empty since bus is not connected.
402 EXPECT_FALSE(bus
->is_connected());
403 EXPECT_TRUE(bus
->GetConnectionName().empty());
405 // Connect bus to D-Bus.
408 // Connection name is not empty after connection is established.
409 EXPECT_TRUE(bus
->is_connected());
410 EXPECT_FALSE(bus
->GetConnectionName().empty());
412 // Shut down synchronously.
413 bus
->ShutdownAndBlock();
414 EXPECT_TRUE(bus
->shutdown_completed());