blob: 3c453f748290b24d12968603393a412cea7a759a [file] [log] [blame]
[email protected]20bed012012-02-10 21:45:231// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]2ef498f2011-08-23 19:25:202// 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/bus.h"
6
[email protected]e20d39fe2011-09-02 06:56:237#include "base/bind.h"
8#include "base/message_loop.h"
[email protected]2ef498f2011-08-23 19:25:209#include "base/memory/ref_counted.h"
[email protected]049616e2013-06-10 22:52:3410#include "base/run_loop.h"
[email protected]e20d39fe2011-09-02 06:56:2311#include "base/threading/thread.h"
[email protected]2ef498f2011-08-23 19:25:2012#include "dbus/exported_object.h"
[email protected]216ed0b2012-02-14 21:29:0613#include "dbus/object_path.h"
[email protected]2ef498f2011-08-23 19:25:2014#include "dbus/object_proxy.h"
[email protected]bae0f882013-01-31 06:08:0215#include "dbus/scoped_dbus_error.h"
[email protected]049616e2013-06-10 22:52:3416#include "dbus/test_service.h"
[email protected]2ef498f2011-08-23 19:25:2017
18#include "testing/gtest/include/gtest/gtest.h"
19
[email protected]12e25992011-10-06 00:20:5320namespace {
21
22// Used to test AddFilterFunction().
23DBusHandlerResult DummyHandler(DBusConnection* connection,
24 DBusMessage* raw_message,
25 void* user_data) {
26 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
27}
28
[email protected]049616e2013-06-10 22:52:3429// Test helper for BusTest.ListenForServiceOwnerChange that wraps a
30// base::RunLoop. At Run() time, the caller pass in the expected number of
31// quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
32// if the expected number of quit calls have been reached.
33class RunLoopWithExpectedCount {
34 public:
35 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
36 ~RunLoopWithExpectedCount() {}
37
38 void Run(int expected_quit_calls) {
39 DCHECK_EQ(0, expected_quit_calls_);
40 DCHECK_EQ(0, actual_quit_calls_);
41 expected_quit_calls_ = expected_quit_calls;
42 run_loop_.reset(new base::RunLoop());
43 run_loop_->Run();
44 }
45
46 void QuitIfConditionIsSatisified() {
47 if (++actual_quit_calls_ != expected_quit_calls_)
48 return;
49 run_loop_->Quit();
50 expected_quit_calls_ = 0;
51 actual_quit_calls_ = 0;
52 }
53
54 private:
55 scoped_ptr<base::RunLoop> run_loop_;
56 int expected_quit_calls_;
57 int actual_quit_calls_;
58
59 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
60};
61
62// Test helper for BusTest.ListenForServiceOwnerChange.
63void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
64 std::string* service_owner,
65 int* num_of_owner_changes,
66 const std::string& new_service_owner) {
67 *service_owner = new_service_owner;
68 ++(*num_of_owner_changes);
69 run_loop_state->QuitIfConditionIsSatisified();
70}
71
[email protected]12e25992011-10-06 00:20:5372} // namespace
73
[email protected]2ef498f2011-08-23 19:25:2074TEST(BusTest, GetObjectProxy) {
75 dbus::Bus::Options options;
76 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
77
78 dbus::ObjectProxy* object_proxy1 =
79 bus->GetObjectProxy("org.chromium.TestService",
[email protected]216ed0b2012-02-14 21:29:0680 dbus::ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2081 ASSERT_TRUE(object_proxy1);
82
83 // This should return the same object.
84 dbus::ObjectProxy* object_proxy2 =
85 bus->GetObjectProxy("org.chromium.TestService",
[email protected]216ed0b2012-02-14 21:29:0686 dbus::ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2087 ASSERT_TRUE(object_proxy2);
88 EXPECT_EQ(object_proxy1, object_proxy2);
89
90 // This should not.
91 dbus::ObjectProxy* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:0692 bus->GetObjectProxy(
93 "org.chromium.TestService",
94 dbus::ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:2095 ASSERT_TRUE(object_proxy3);
96 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:2697
98 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:2099}
100
[email protected]20bed012012-02-10 21:45:23101TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
102 dbus::Bus::Options options;
103 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
104
105 dbus::ObjectProxy* object_proxy1 =
106 bus->GetObjectProxyWithOptions(
107 "org.chromium.TestService",
[email protected]216ed0b2012-02-14 21:29:06108 dbus::ObjectPath("/org/chromium/TestObject"),
[email protected]20bed012012-02-10 21:45:23109 dbus::ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
110 ASSERT_TRUE(object_proxy1);
111
112 // This should return the same object.
113 dbus::ObjectProxy* object_proxy2 =
114 bus->GetObjectProxyWithOptions(
115 "org.chromium.TestService",
[email protected]216ed0b2012-02-14 21:29:06116 dbus::ObjectPath("/org/chromium/TestObject"),
[email protected]20bed012012-02-10 21:45:23117 dbus::ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
118 ASSERT_TRUE(object_proxy2);
119 EXPECT_EQ(object_proxy1, object_proxy2);
120
121 // This should not.
122 dbus::ObjectProxy* object_proxy3 =
123 bus->GetObjectProxyWithOptions(
124 "org.chromium.TestService",
[email protected]216ed0b2012-02-14 21:29:06125 dbus::ObjectPath("/org/chromium/DifferentTestObject"),
[email protected]20bed012012-02-10 21:45:23126 dbus::ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
127 ASSERT_TRUE(object_proxy3);
128 EXPECT_NE(object_proxy1, object_proxy3);
129
130 bus->ShutdownAndBlock();
131}
132
[email protected]38ecdc82013-01-29 20:29:12133TEST(BusTest, RemoveObjectProxy) {
134 // Setup the current thread's MessageLoop.
[email protected]ff33b18e2013-05-01 16:10:30135 base::MessageLoop message_loop;
[email protected]38ecdc82013-01-29 20:29:12136
137 // Start the D-Bus thread.
138 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30139 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]38ecdc82013-01-29 20:29:12140 base::Thread dbus_thread("D-Bus thread");
141 dbus_thread.StartWithOptions(thread_options);
142
143 // Create the bus.
144 dbus::Bus::Options options;
[email protected]200328a2013-02-20 01:36:53145 options.dbus_task_runner = dbus_thread.message_loop_proxy();
[email protected]38ecdc82013-01-29 20:29:12146 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
147 ASSERT_FALSE(bus->shutdown_completed());
148
149 // Try to remove a non existant object proxy should return false.
150 ASSERT_FALSE(
151 bus->RemoveObjectProxy("org.chromium.TestService",
152 dbus::ObjectPath("/org/chromium/TestObject"),
153 base::Bind(&base::DoNothing)));
154
155 dbus::ObjectProxy* object_proxy1 =
156 bus->GetObjectProxy("org.chromium.TestService",
157 dbus::ObjectPath("/org/chromium/TestObject"));
158 ASSERT_TRUE(object_proxy1);
159
160 // Increment the reference count to the object proxy to avoid destroying it
161 // while removing the object.
162 object_proxy1->AddRef();
163
164 // Remove the object from the bus. This will invalidate any other usage of
165 // object_proxy1 other than destroy it. We keep this object for a comparison
166 // at a later time.
167 ASSERT_TRUE(
168 bus->RemoveObjectProxy("org.chromium.TestService",
169 dbus::ObjectPath("/org/chromium/TestObject"),
170 base::Bind(&base::DoNothing)));
171
172 // This should return a different object because the first object was removed
173 // from the bus, but not deleted from memory.
174 dbus::ObjectProxy* object_proxy2 =
175 bus->GetObjectProxy("org.chromium.TestService",
176 dbus::ObjectPath("/org/chromium/TestObject"));
177 ASSERT_TRUE(object_proxy2);
178
179 // Compare the new object with the first object. The first object still exists
180 // thanks to the increased reference.
181 EXPECT_NE(object_proxy1, object_proxy2);
182
183 // Release object_proxy1.
184 object_proxy1->Release();
185
186 // Shut down synchronously.
187 bus->ShutdownOnDBusThreadAndBlock();
188 EXPECT_TRUE(bus->shutdown_completed());
189 dbus_thread.Stop();
190}
191
[email protected]2ef498f2011-08-23 19:25:20192TEST(BusTest, GetExportedObject) {
193 dbus::Bus::Options options;
194 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
195
196 dbus::ExportedObject* object_proxy1 =
[email protected]15e7b162012-03-10 01:12:52197 bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20198 ASSERT_TRUE(object_proxy1);
199
200 // This should return the same object.
201 dbus::ExportedObject* object_proxy2 =
[email protected]15e7b162012-03-10 01:12:52202 bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20203 ASSERT_TRUE(object_proxy2);
204 EXPECT_EQ(object_proxy1, object_proxy2);
205
206 // This should not.
207 dbus::ExportedObject* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:06208 bus->GetExportedObject(
[email protected]216ed0b2012-02-14 21:29:06209 dbus::ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:20210 ASSERT_TRUE(object_proxy3);
211 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:26212
213 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:20214}
[email protected]e20d39fe2011-09-02 06:56:23215
[email protected]bda57352013-01-24 00:58:35216TEST(BusTest, UnregisterExportedObject) {
[email protected]d7361fdc2012-03-14 01:18:35217 // Start the D-Bus thread.
218 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30219 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]d7361fdc2012-03-14 01:18:35220 base::Thread dbus_thread("D-Bus thread");
221 dbus_thread.StartWithOptions(thread_options);
222
223 // Create the bus.
224 dbus::Bus::Options options;
[email protected]200328a2013-02-20 01:36:53225 options.dbus_task_runner = dbus_thread.message_loop_proxy();
[email protected]d7361fdc2012-03-14 01:18:35226 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
227 ASSERT_FALSE(bus->shutdown_completed());
228
229 dbus::ExportedObject* object_proxy1 =
230 bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
231 ASSERT_TRUE(object_proxy1);
232
[email protected]bda57352013-01-24 00:58:35233 // Increment the reference count to the object proxy to avoid destroying it
234 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
235 // not freed from memory. See http://crbug.com/137846 for details.
236 object_proxy1->AddRef();
237
[email protected]d7361fdc2012-03-14 01:18:35238 bus->UnregisterExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
239
[email protected]bda57352013-01-24 00:58:35240 // This should return a new object because the object_proxy1 is still in
241 // alloc'ed memory.
[email protected]d7361fdc2012-03-14 01:18:35242 dbus::ExportedObject* object_proxy2 =
243 bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
244 ASSERT_TRUE(object_proxy2);
245 EXPECT_NE(object_proxy1, object_proxy2);
246
[email protected]bda57352013-01-24 00:58:35247 // Release the incremented reference.
248 object_proxy1->Release();
249
[email protected]d7361fdc2012-03-14 01:18:35250 // Shut down synchronously.
251 bus->ShutdownOnDBusThreadAndBlock();
252 EXPECT_TRUE(bus->shutdown_completed());
253 dbus_thread.Stop();
254}
255
[email protected]e20d39fe2011-09-02 06:56:23256TEST(BusTest, ShutdownAndBlock) {
257 dbus::Bus::Options options;
258 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
259 ASSERT_FALSE(bus->shutdown_completed());
260
261 // Shut down synchronously.
262 bus->ShutdownAndBlock();
263 EXPECT_TRUE(bus->shutdown_completed());
264}
265
266TEST(BusTest, ShutdownAndBlockWithDBusThread) {
267 // Start the D-Bus thread.
268 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30269 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]e20d39fe2011-09-02 06:56:23270 base::Thread dbus_thread("D-Bus thread");
271 dbus_thread.StartWithOptions(thread_options);
272
273 // Create the bus.
274 dbus::Bus::Options options;
[email protected]200328a2013-02-20 01:36:53275 options.dbus_task_runner = dbus_thread.message_loop_proxy();
[email protected]e20d39fe2011-09-02 06:56:23276 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
277 ASSERT_FALSE(bus->shutdown_completed());
278
279 // Shut down synchronously.
280 bus->ShutdownOnDBusThreadAndBlock();
281 EXPECT_TRUE(bus->shutdown_completed());
282 dbus_thread.Stop();
283}
[email protected]12e25992011-10-06 00:20:53284
285TEST(BusTest, AddFilterFunction) {
286 dbus::Bus::Options options;
287 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
288 // Should connect before calling AddFilterFunction().
289 bus->Connect();
290
291 int data1 = 100;
292 int data2 = 200;
293 ASSERT_TRUE(bus->AddFilterFunction(&DummyHandler, &data1));
294 // Cannot add the same function with the same data.
295 ASSERT_FALSE(bus->AddFilterFunction(&DummyHandler, &data1));
296 // Can add the same function with different data.
297 ASSERT_TRUE(bus->AddFilterFunction(&DummyHandler, &data2));
298
299 ASSERT_TRUE(bus->RemoveFilterFunction(&DummyHandler, &data1));
300 ASSERT_FALSE(bus->RemoveFilterFunction(&DummyHandler, &data1));
301 ASSERT_TRUE(bus->RemoveFilterFunction(&DummyHandler, &data2));
302
303 bus->ShutdownAndBlock();
304}
[email protected]bae0f882013-01-31 06:08:02305
306TEST(BusTest, DoubleAddAndRemoveMatch) {
307 dbus::Bus::Options options;
308 scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
309 dbus::ScopedDBusError error;
310
311 bus->Connect();
312
313 // Adds the same rule twice.
314 bus->AddMatch(
315 "type='signal',interface='org.chromium.TestService',path='/'",
316 error.get());
317 ASSERT_FALSE(error.is_set());
318
319 bus->AddMatch(
320 "type='signal',interface='org.chromium.TestService',path='/'",
321 error.get());
322 ASSERT_FALSE(error.is_set());
323
324 // Removes the same rule twice.
325 ASSERT_TRUE(bus->RemoveMatch(
326 "type='signal',interface='org.chromium.TestService',path='/'",
327 error.get()));
328 ASSERT_FALSE(error.is_set());
329
330 // The rule should be still in the bus since it was removed only once.
331 // A second removal shouldn't give an error.
332 ASSERT_TRUE(bus->RemoveMatch(
333 "type='signal',interface='org.chromium.TestService',path='/'",
334 error.get()));
335 ASSERT_FALSE(error.is_set());
336
337 // A third attemp to remove the same rule should fail.
338 ASSERT_FALSE(bus->RemoveMatch(
339 "type='signal',interface='org.chromium.TestService',path='/'",
340 error.get()));
341
342 bus->ShutdownAndBlock();
343}
[email protected]049616e2013-06-10 22:52:34344
345TEST(BusTest, ListenForServiceOwnerChange) {
346 // Setup the current thread's MessageLoop. Must be of TYPE_IO for the
347 // listeners to work.
348 base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
349 RunLoopWithExpectedCount run_loop_state;
350
351 // Create the bus.
352 dbus::Bus::Options bus_options;
353 scoped_refptr<dbus::Bus> bus = new dbus::Bus(bus_options);
354
355 // Add a listener.
356 std::string service_owner1;
357 int num_of_owner_changes1 = 0;
358 dbus::Bus::GetServiceOwnerCallback callback1 =
359 base::Bind(&OnServiceOwnerChanged,
360 &run_loop_state,
361 &service_owner1,
362 &num_of_owner_changes1);
363 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
364 // This should be a no-op.
365 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
366 base::RunLoop().RunUntilIdle();
367
368 // Nothing has happened yet. Check initial state.
369 EXPECT_TRUE(service_owner1.empty());
370 EXPECT_EQ(0, num_of_owner_changes1);
371
372 // Make an ownership change.
373 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService"));
374 run_loop_state.Run(1);
375
376 {
377 // Get the current service owner and check to make sure the listener got
378 // the right value.
379 std::string current_service_owner =
380 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
381 dbus::Bus::REPORT_ERRORS);
382 ASSERT_FALSE(current_service_owner.empty());
383
384 // Make sure the listener heard about the new owner.
385 EXPECT_EQ(current_service_owner, service_owner1);
386
387 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
388 EXPECT_EQ(1, num_of_owner_changes1);
389 }
390
391 // Add a second listener.
392 std::string service_owner2;
393 int num_of_owner_changes2 = 0;
394 dbus::Bus::GetServiceOwnerCallback callback2 =
395 base::Bind(&OnServiceOwnerChanged,
396 &run_loop_state,
397 &service_owner2,
398 &num_of_owner_changes2);
399 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
400 base::RunLoop().RunUntilIdle();
401
402 // Release the ownership and make sure the service owner listeners fire with
403 // the right values and the right number of times.
404 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
405 run_loop_state.Run(2);
406
407 EXPECT_TRUE(service_owner1.empty());
408 EXPECT_TRUE(service_owner2.empty());
409 EXPECT_EQ(2, num_of_owner_changes1);
410 EXPECT_EQ(1, num_of_owner_changes2);
411
412 // Unlisten so shutdown can proceed correctly.
413 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
414 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
415 base::RunLoop().RunUntilIdle();
416
417 // Shut down synchronously.
418 bus->ShutdownAndBlock();
419 EXPECT_TRUE(bus->shutdown_completed());
420}