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/message_loop.h"
9 #include "base/memory/ref_counted.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 // Used to test AddFilterFunction().
25 DBusHandlerResult
DummyHandler(DBusConnection
* connection
,
26 DBusMessage
* raw_message
,
28 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
31 // Test helper for BusTest.ListenForServiceOwnerChange that wraps a
32 // base::RunLoop. At Run() time, the caller pass in the expected number of
33 // quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
34 // if the expected number of quit calls have been reached.
35 class RunLoopWithExpectedCount
{
37 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
38 ~RunLoopWithExpectedCount() {}
40 void Run(int expected_quit_calls
) {
41 DCHECK_EQ(0, expected_quit_calls_
);
42 DCHECK_EQ(0, actual_quit_calls_
);
43 expected_quit_calls_
= expected_quit_calls
;
44 run_loop_
.reset(new base::RunLoop());
48 void QuitIfConditionIsSatisified() {
49 if (++actual_quit_calls_
!= expected_quit_calls_
)
52 expected_quit_calls_
= 0;
53 actual_quit_calls_
= 0;
57 scoped_ptr
<base::RunLoop
> run_loop_
;
58 int expected_quit_calls_
;
59 int actual_quit_calls_
;
61 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount
);
64 // Test helper for BusTest.ListenForServiceOwnerChange.
65 void OnServiceOwnerChanged(RunLoopWithExpectedCount
* run_loop_state
,
66 std::string
* service_owner
,
67 int* num_of_owner_changes
,
68 const std::string
& new_service_owner
) {
69 *service_owner
= new_service_owner
;
70 ++(*num_of_owner_changes
);
71 run_loop_state
->QuitIfConditionIsSatisified();
76 TEST(BusTest
, GetObjectProxy
) {
78 scoped_refptr
<Bus
> bus
= new Bus(options
);
80 ObjectProxy
* object_proxy1
=
81 bus
->GetObjectProxy("org.chromium.TestService",
82 ObjectPath("/org/chromium/TestObject"));
83 ASSERT_TRUE(object_proxy1
);
85 // This should return the same object.
86 ObjectProxy
* object_proxy2
=
87 bus
->GetObjectProxy("org.chromium.TestService",
88 ObjectPath("/org/chromium/TestObject"));
89 ASSERT_TRUE(object_proxy2
);
90 EXPECT_EQ(object_proxy1
, object_proxy2
);
93 ObjectProxy
* object_proxy3
=
95 "org.chromium.TestService",
96 ObjectPath("/org/chromium/DifferentTestObject"));
97 ASSERT_TRUE(object_proxy3
);
98 EXPECT_NE(object_proxy1
, object_proxy3
);
100 bus
->ShutdownAndBlock();
103 TEST(BusTest
, GetObjectProxyIgnoreUnknownService
) {
104 Bus::Options options
;
105 scoped_refptr
<Bus
> bus
= new Bus(options
);
107 ObjectProxy
* object_proxy1
=
108 bus
->GetObjectProxyWithOptions(
109 "org.chromium.TestService",
110 ObjectPath("/org/chromium/TestObject"),
111 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
112 ASSERT_TRUE(object_proxy1
);
114 // This should return the same object.
115 ObjectProxy
* object_proxy2
=
116 bus
->GetObjectProxyWithOptions(
117 "org.chromium.TestService",
118 ObjectPath("/org/chromium/TestObject"),
119 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
120 ASSERT_TRUE(object_proxy2
);
121 EXPECT_EQ(object_proxy1
, object_proxy2
);
124 ObjectProxy
* object_proxy3
=
125 bus
->GetObjectProxyWithOptions(
126 "org.chromium.TestService",
127 ObjectPath("/org/chromium/DifferentTestObject"),
128 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS
);
129 ASSERT_TRUE(object_proxy3
);
130 EXPECT_NE(object_proxy1
, object_proxy3
);
132 bus
->ShutdownAndBlock();
135 TEST(BusTest
, RemoveObjectProxy
) {
136 // Setup the current thread's MessageLoop.
137 base::MessageLoop message_loop
;
139 // Start the D-Bus thread.
140 base::Thread::Options thread_options
;
141 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
142 base::Thread
dbus_thread("D-Bus thread");
143 dbus_thread
.StartWithOptions(thread_options
);
146 Bus::Options options
;
147 options
.dbus_task_runner
= dbus_thread
.message_loop_proxy();
148 scoped_refptr
<Bus
> bus
= new Bus(options
);
149 ASSERT_FALSE(bus
->shutdown_completed());
151 // Try to remove a non existant object proxy should return false.
153 bus
->RemoveObjectProxy("org.chromium.TestService",
154 ObjectPath("/org/chromium/TestObject"),
155 base::Bind(&base::DoNothing
)));
157 ObjectProxy
* object_proxy1
=
158 bus
->GetObjectProxy("org.chromium.TestService",
159 ObjectPath("/org/chromium/TestObject"));
160 ASSERT_TRUE(object_proxy1
);
162 // Increment the reference count to the object proxy to avoid destroying it
163 // while removing the object.
164 object_proxy1
->AddRef();
166 // Remove the object from the bus. This will invalidate any other usage of
167 // object_proxy1 other than destroy it. We keep this object for a comparison
170 bus
->RemoveObjectProxy("org.chromium.TestService",
171 ObjectPath("/org/chromium/TestObject"),
172 base::Bind(&base::DoNothing
)));
174 // This should return a different object because the first object was removed
175 // from the bus, but not deleted from memory.
176 ObjectProxy
* object_proxy2
=
177 bus
->GetObjectProxy("org.chromium.TestService",
178 ObjectPath("/org/chromium/TestObject"));
179 ASSERT_TRUE(object_proxy2
);
181 // Compare the new object with the first object. The first object still exists
182 // thanks to the increased reference.
183 EXPECT_NE(object_proxy1
, object_proxy2
);
185 // Release object_proxy1.
186 object_proxy1
->Release();
188 // Shut down synchronously.
189 bus
->ShutdownOnDBusThreadAndBlock();
190 EXPECT_TRUE(bus
->shutdown_completed());
194 TEST(BusTest
, GetExportedObject
) {
195 Bus::Options options
;
196 scoped_refptr
<Bus
> bus
= new Bus(options
);
198 ExportedObject
* object_proxy1
=
199 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
200 ASSERT_TRUE(object_proxy1
);
202 // This should return the same object.
203 ExportedObject
* object_proxy2
=
204 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
205 ASSERT_TRUE(object_proxy2
);
206 EXPECT_EQ(object_proxy1
, object_proxy2
);
209 ExportedObject
* object_proxy3
=
210 bus
->GetExportedObject(
211 ObjectPath("/org/chromium/DifferentTestObject"));
212 ASSERT_TRUE(object_proxy3
);
213 EXPECT_NE(object_proxy1
, object_proxy3
);
215 bus
->ShutdownAndBlock();
218 TEST(BusTest
, UnregisterExportedObject
) {
219 // Start the D-Bus thread.
220 base::Thread::Options thread_options
;
221 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
222 base::Thread
dbus_thread("D-Bus thread");
223 dbus_thread
.StartWithOptions(thread_options
);
226 Bus::Options options
;
227 options
.dbus_task_runner
= dbus_thread
.message_loop_proxy();
228 scoped_refptr
<Bus
> bus
= new Bus(options
);
229 ASSERT_FALSE(bus
->shutdown_completed());
231 ExportedObject
* object_proxy1
=
232 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
233 ASSERT_TRUE(object_proxy1
);
235 // Increment the reference count to the object proxy to avoid destroying it
236 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
237 // not freed from memory. See http://crbug.com/137846 for details.
238 object_proxy1
->AddRef();
240 bus
->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
242 // This should return a new object because the object_proxy1 is still in
244 ExportedObject
* object_proxy2
=
245 bus
->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
246 ASSERT_TRUE(object_proxy2
);
247 EXPECT_NE(object_proxy1
, object_proxy2
);
249 // Release the incremented reference.
250 object_proxy1
->Release();
252 // Shut down synchronously.
253 bus
->ShutdownOnDBusThreadAndBlock();
254 EXPECT_TRUE(bus
->shutdown_completed());
258 TEST(BusTest
, ShutdownAndBlock
) {
259 Bus::Options options
;
260 scoped_refptr
<Bus
> bus
= new Bus(options
);
261 ASSERT_FALSE(bus
->shutdown_completed());
263 // Shut down synchronously.
264 bus
->ShutdownAndBlock();
265 EXPECT_TRUE(bus
->shutdown_completed());
268 TEST(BusTest
, ShutdownAndBlockWithDBusThread
) {
269 // Start the D-Bus thread.
270 base::Thread::Options thread_options
;
271 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
272 base::Thread
dbus_thread("D-Bus thread");
273 dbus_thread
.StartWithOptions(thread_options
);
276 Bus::Options options
;
277 options
.dbus_task_runner
= dbus_thread
.message_loop_proxy();
278 scoped_refptr
<Bus
> bus
= new Bus(options
);
279 ASSERT_FALSE(bus
->shutdown_completed());
281 // Shut down synchronously.
282 bus
->ShutdownOnDBusThreadAndBlock();
283 EXPECT_TRUE(bus
->shutdown_completed());
287 TEST(BusTest
, AddFilterFunction
) {
288 Bus::Options options
;
289 scoped_refptr
<Bus
> bus
= new Bus(options
);
290 // Should connect before calling AddFilterFunction().
295 ASSERT_TRUE(bus
->AddFilterFunction(&DummyHandler
, &data1
));
296 // Cannot add the same function with the same data.
297 ASSERT_FALSE(bus
->AddFilterFunction(&DummyHandler
, &data1
));
298 // Can add the same function with different data.
299 ASSERT_TRUE(bus
->AddFilterFunction(&DummyHandler
, &data2
));
301 ASSERT_TRUE(bus
->RemoveFilterFunction(&DummyHandler
, &data1
));
302 ASSERT_FALSE(bus
->RemoveFilterFunction(&DummyHandler
, &data1
));
303 ASSERT_TRUE(bus
->RemoveFilterFunction(&DummyHandler
, &data2
));
305 bus
->ShutdownAndBlock();
308 TEST(BusTest
, DoubleAddAndRemoveMatch
) {
309 Bus::Options options
;
310 scoped_refptr
<Bus
> bus
= new Bus(options
);
311 ScopedDBusError error
;
315 // Adds the same rule twice.
317 "type='signal',interface='org.chromium.TestService',path='/'",
319 ASSERT_FALSE(error
.is_set());
322 "type='signal',interface='org.chromium.TestService',path='/'",
324 ASSERT_FALSE(error
.is_set());
326 // Removes the same rule twice.
327 ASSERT_TRUE(bus
->RemoveMatch(
328 "type='signal',interface='org.chromium.TestService',path='/'",
330 ASSERT_FALSE(error
.is_set());
332 // The rule should be still in the bus since it was removed only once.
333 // A second removal shouldn't give an error.
334 ASSERT_TRUE(bus
->RemoveMatch(
335 "type='signal',interface='org.chromium.TestService',path='/'",
337 ASSERT_FALSE(error
.is_set());
339 // A third attemp to remove the same rule should fail.
340 ASSERT_FALSE(bus
->RemoveMatch(
341 "type='signal',interface='org.chromium.TestService',path='/'",
344 bus
->ShutdownAndBlock();
347 TEST(BusTest
, ListenForServiceOwnerChange
) {
348 // Setup the current thread's MessageLoop. Must be of TYPE_IO for the
349 // listeners to work.
350 base::MessageLoop
message_loop(base::MessageLoop::TYPE_IO
);
351 RunLoopWithExpectedCount run_loop_state
;
354 Bus::Options bus_options
;
355 scoped_refptr
<Bus
> bus
= new Bus(bus_options
);
358 std::string service_owner1
;
359 int num_of_owner_changes1
= 0;
360 Bus::GetServiceOwnerCallback callback1
=
361 base::Bind(&OnServiceOwnerChanged
,
364 &num_of_owner_changes1
);
365 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback1
);
366 // This should be a no-op.
367 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback1
);
368 base::RunLoop().RunUntilIdle();
370 // Nothing has happened yet. Check initial state.
371 EXPECT_TRUE(service_owner1
.empty());
372 EXPECT_EQ(0, num_of_owner_changes1
);
374 // Make an ownership change.
375 ASSERT_TRUE(bus
->RequestOwnershipAndBlock("org.chromium.TestService"));
376 run_loop_state
.Run(1);
379 // Get the current service owner and check to make sure the listener got
381 std::string current_service_owner
=
382 bus
->GetServiceOwnerAndBlock("org.chromium.TestService",
384 ASSERT_FALSE(current_service_owner
.empty());
386 // Make sure the listener heard about the new owner.
387 EXPECT_EQ(current_service_owner
, service_owner1
);
389 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
390 EXPECT_EQ(1, num_of_owner_changes1
);
393 // Add a second listener.
394 std::string service_owner2
;
395 int num_of_owner_changes2
= 0;
396 Bus::GetServiceOwnerCallback callback2
=
397 base::Bind(&OnServiceOwnerChanged
,
400 &num_of_owner_changes2
);
401 bus
->ListenForServiceOwnerChange("org.chromium.TestService", callback2
);
402 base::RunLoop().RunUntilIdle();
404 // Release the ownership and make sure the service owner listeners fire with
405 // the right values and the right number of times.
406 ASSERT_TRUE(bus
->ReleaseOwnership("org.chromium.TestService"));
407 run_loop_state
.Run(2);
409 EXPECT_TRUE(service_owner1
.empty());
410 EXPECT_TRUE(service_owner2
.empty());
411 EXPECT_EQ(2, num_of_owner_changes1
);
412 EXPECT_EQ(1, num_of_owner_changes2
);
414 // Unlisten so shutdown can proceed correctly.
415 bus
->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1
);
416 bus
->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2
);
417 base::RunLoop().RunUntilIdle();
419 // Shut down synchronously.
420 bus
->ShutdownAndBlock();
421 EXPECT_TRUE(bus
->shutdown_completed());