blob: f8dfa33d065087def6618b3ffde278e474c3dbc5 [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"
fdorayebc379c2017-04-18 13:40:218#include "base/files/file_descriptor_watcher_posix.h"
avi22437c692015-12-22 18:12:459#include "base/macros.h"
[email protected]2ef498f2011-08-23 19:25:2010#include "base/memory/ref_counted.h"
[email protected]2a9ec0e2013-07-17 23:00:3011#include "base/message_loop/message_loop.h"
[email protected]049616e2013-06-10 22:52:3412#include "base/run_loop.h"
[email protected]e20d39fe2011-09-02 06:56:2313#include "base/threading/thread.h"
[email protected]2ef498f2011-08-23 19:25:2014#include "dbus/exported_object.h"
[email protected]216ed0b2012-02-14 21:29:0615#include "dbus/object_path.h"
[email protected]2ef498f2011-08-23 19:25:2016#include "dbus/object_proxy.h"
[email protected]bae0f882013-01-31 06:08:0217#include "dbus/scoped_dbus_error.h"
[email protected]049616e2013-06-10 22:52:3418#include "dbus/test_service.h"
[email protected]2ef498f2011-08-23 19:25:2019
20#include "testing/gtest/include/gtest/gtest.h"
21
[email protected]2a57ca642013-06-13 06:37:1922namespace dbus {
23
[email protected]12e25992011-10-06 00:20:5324namespace {
25
[email protected]049616e2013-06-10 22:52:3426// Test helper for BusTest.ListenForServiceOwnerChange that wraps a
27// base::RunLoop. At Run() time, the caller pass in the expected number of
28// quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
29// if the expected number of quit calls have been reached.
30class RunLoopWithExpectedCount {
31 public:
32 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
Chris Watkins3740aae2017-11-29 07:44:1133 ~RunLoopWithExpectedCount() = default;
[email protected]049616e2013-06-10 22:52:3434
35 void Run(int expected_quit_calls) {
36 DCHECK_EQ(0, expected_quit_calls_);
37 DCHECK_EQ(0, actual_quit_calls_);
38 expected_quit_calls_ = expected_quit_calls;
39 run_loop_.reset(new base::RunLoop());
40 run_loop_->Run();
41 }
42
43 void QuitIfConditionIsSatisified() {
44 if (++actual_quit_calls_ != expected_quit_calls_)
45 return;
46 run_loop_->Quit();
47 expected_quit_calls_ = 0;
48 actual_quit_calls_ = 0;
49 }
50
51 private:
dcheng2a193282016-04-08 22:55:0452 std::unique_ptr<base::RunLoop> run_loop_;
[email protected]049616e2013-06-10 22:52:3453 int expected_quit_calls_;
54 int actual_quit_calls_;
55
56 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
57};
58
59// Test helper for BusTest.ListenForServiceOwnerChange.
60void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
61 std::string* service_owner,
62 int* num_of_owner_changes,
63 const std::string& new_service_owner) {
64 *service_owner = new_service_owner;
65 ++(*num_of_owner_changes);
66 run_loop_state->QuitIfConditionIsSatisified();
67}
68
[email protected]12e25992011-10-06 00:20:5369} // namespace
70
[email protected]2ef498f2011-08-23 19:25:2071TEST(BusTest, GetObjectProxy) {
[email protected]2a57ca642013-06-13 06:37:1972 Bus::Options options;
73 scoped_refptr<Bus> bus = new Bus(options);
[email protected]2ef498f2011-08-23 19:25:2074
[email protected]2a57ca642013-06-13 06:37:1975 ObjectProxy* object_proxy1 =
[email protected]2ef498f2011-08-23 19:25:2076 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1977 ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2078 ASSERT_TRUE(object_proxy1);
79
80 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:1981 ObjectProxy* object_proxy2 =
[email protected]2ef498f2011-08-23 19:25:2082 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1983 ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2084 ASSERT_TRUE(object_proxy2);
85 EXPECT_EQ(object_proxy1, object_proxy2);
86
87 // This should not.
[email protected]2a57ca642013-06-13 06:37:1988 ObjectProxy* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:0689 bus->GetObjectProxy(
90 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1991 ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:2092 ASSERT_TRUE(object_proxy3);
93 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:2694
95 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:2096}
97
[email protected]20bed012012-02-10 21:45:2398TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
[email protected]2a57ca642013-06-13 06:37:1999 Bus::Options options;
100 scoped_refptr<Bus> bus = new Bus(options);
[email protected]20bed012012-02-10 21:45:23101
[email protected]2a57ca642013-06-13 06:37:19102 ObjectProxy* object_proxy1 =
[email protected]20bed012012-02-10 21:45:23103 bus->GetObjectProxyWithOptions(
104 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19105 ObjectPath("/org/chromium/TestObject"),
106 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23107 ASSERT_TRUE(object_proxy1);
108
109 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:19110 ObjectProxy* object_proxy2 =
[email protected]20bed012012-02-10 21:45:23111 bus->GetObjectProxyWithOptions(
112 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19113 ObjectPath("/org/chromium/TestObject"),
114 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23115 ASSERT_TRUE(object_proxy2);
116 EXPECT_EQ(object_proxy1, object_proxy2);
117
118 // This should not.
[email protected]2a57ca642013-06-13 06:37:19119 ObjectProxy* object_proxy3 =
[email protected]20bed012012-02-10 21:45:23120 bus->GetObjectProxyWithOptions(
121 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19122 ObjectPath("/org/chromium/DifferentTestObject"),
123 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23124 ASSERT_TRUE(object_proxy3);
125 EXPECT_NE(object_proxy1, object_proxy3);
126
127 bus->ShutdownAndBlock();
128}
129
[email protected]38ecdc82013-01-29 20:29:12130TEST(BusTest, RemoveObjectProxy) {
131 // Setup the current thread's MessageLoop.
[email protected]ff33b18e2013-05-01 16:10:30132 base::MessageLoop message_loop;
[email protected]38ecdc82013-01-29 20:29:12133
134 // Start the D-Bus thread.
135 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30136 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]38ecdc82013-01-29 20:29:12137 base::Thread dbus_thread("D-Bus thread");
138 dbus_thread.StartWithOptions(thread_options);
139
140 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19141 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04142 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19143 scoped_refptr<Bus> bus = new Bus(options);
[email protected]38ecdc82013-01-29 20:29:12144 ASSERT_FALSE(bus->shutdown_completed());
145
146 // Try to remove a non existant object proxy should return false.
147 ASSERT_FALSE(
148 bus->RemoveObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19149 ObjectPath("/org/chromium/TestObject"),
[email protected]38ecdc82013-01-29 20:29:12150 base::Bind(&base::DoNothing)));
151
[email protected]2a57ca642013-06-13 06:37:19152 ObjectProxy* object_proxy1 =
[email protected]38ecdc82013-01-29 20:29:12153 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19154 ObjectPath("/org/chromium/TestObject"));
[email protected]38ecdc82013-01-29 20:29:12155 ASSERT_TRUE(object_proxy1);
156
157 // Increment the reference count to the object proxy to avoid destroying it
158 // while removing the object.
159 object_proxy1->AddRef();
160
161 // Remove the object from the bus. This will invalidate any other usage of
162 // object_proxy1 other than destroy it. We keep this object for a comparison
163 // at a later time.
164 ASSERT_TRUE(
165 bus->RemoveObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19166 ObjectPath("/org/chromium/TestObject"),
[email protected]38ecdc82013-01-29 20:29:12167 base::Bind(&base::DoNothing)));
168
169 // This should return a different object because the first object was removed
170 // from the bus, but not deleted from memory.
[email protected]2a57ca642013-06-13 06:37:19171 ObjectProxy* object_proxy2 =
[email protected]38ecdc82013-01-29 20:29:12172 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19173 ObjectPath("/org/chromium/TestObject"));
[email protected]38ecdc82013-01-29 20:29:12174 ASSERT_TRUE(object_proxy2);
175
176 // Compare the new object with the first object. The first object still exists
177 // thanks to the increased reference.
178 EXPECT_NE(object_proxy1, object_proxy2);
179
180 // Release object_proxy1.
181 object_proxy1->Release();
182
183 // Shut down synchronously.
184 bus->ShutdownOnDBusThreadAndBlock();
185 EXPECT_TRUE(bus->shutdown_completed());
186 dbus_thread.Stop();
187}
188
[email protected]2ef498f2011-08-23 19:25:20189TEST(BusTest, GetExportedObject) {
[email protected]2a57ca642013-06-13 06:37:19190 Bus::Options options;
191 scoped_refptr<Bus> bus = new Bus(options);
[email protected]2ef498f2011-08-23 19:25:20192
[email protected]2a57ca642013-06-13 06:37:19193 ExportedObject* object_proxy1 =
194 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20195 ASSERT_TRUE(object_proxy1);
196
197 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:19198 ExportedObject* object_proxy2 =
199 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20200 ASSERT_TRUE(object_proxy2);
201 EXPECT_EQ(object_proxy1, object_proxy2);
202
203 // This should not.
[email protected]2a57ca642013-06-13 06:37:19204 ExportedObject* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:06205 bus->GetExportedObject(
[email protected]2a57ca642013-06-13 06:37:19206 ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:20207 ASSERT_TRUE(object_proxy3);
208 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:26209
210 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:20211}
[email protected]e20d39fe2011-09-02 06:56:23212
[email protected]bda57352013-01-24 00:58:35213TEST(BusTest, UnregisterExportedObject) {
[email protected]d7361fdc2012-03-14 01:18:35214 // Start the D-Bus thread.
215 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30216 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]d7361fdc2012-03-14 01:18:35217 base::Thread dbus_thread("D-Bus thread");
218 dbus_thread.StartWithOptions(thread_options);
219
220 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19221 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04222 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19223 scoped_refptr<Bus> bus = new Bus(options);
[email protected]d7361fdc2012-03-14 01:18:35224 ASSERT_FALSE(bus->shutdown_completed());
225
[email protected]2a57ca642013-06-13 06:37:19226 ExportedObject* object_proxy1 =
227 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35228 ASSERT_TRUE(object_proxy1);
229
[email protected]bda57352013-01-24 00:58:35230 // Increment the reference count to the object proxy to avoid destroying it
231 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
232 // not freed from memory. See http://crbug.com/137846 for details.
233 object_proxy1->AddRef();
234
[email protected]2a57ca642013-06-13 06:37:19235 bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35236
[email protected]bda57352013-01-24 00:58:35237 // This should return a new object because the object_proxy1 is still in
238 // alloc'ed memory.
[email protected]2a57ca642013-06-13 06:37:19239 ExportedObject* object_proxy2 =
240 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35241 ASSERT_TRUE(object_proxy2);
242 EXPECT_NE(object_proxy1, object_proxy2);
243
[email protected]bda57352013-01-24 00:58:35244 // Release the incremented reference.
245 object_proxy1->Release();
246
[email protected]d7361fdc2012-03-14 01:18:35247 // Shut down synchronously.
248 bus->ShutdownOnDBusThreadAndBlock();
249 EXPECT_TRUE(bus->shutdown_completed());
250 dbus_thread.Stop();
251}
252
[email protected]e20d39fe2011-09-02 06:56:23253TEST(BusTest, ShutdownAndBlock) {
[email protected]2a57ca642013-06-13 06:37:19254 Bus::Options options;
255 scoped_refptr<Bus> bus = new Bus(options);
[email protected]e20d39fe2011-09-02 06:56:23256 ASSERT_FALSE(bus->shutdown_completed());
257
258 // Shut down synchronously.
259 bus->ShutdownAndBlock();
260 EXPECT_TRUE(bus->shutdown_completed());
261}
262
263TEST(BusTest, ShutdownAndBlockWithDBusThread) {
264 // Start the D-Bus thread.
265 base::Thread::Options thread_options;
[email protected]ff33b18e2013-05-01 16:10:30266 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
[email protected]e20d39fe2011-09-02 06:56:23267 base::Thread dbus_thread("D-Bus thread");
268 dbus_thread.StartWithOptions(thread_options);
269
270 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19271 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04272 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19273 scoped_refptr<Bus> bus = new Bus(options);
[email protected]e20d39fe2011-09-02 06:56:23274 ASSERT_FALSE(bus->shutdown_completed());
275
276 // Shut down synchronously.
277 bus->ShutdownOnDBusThreadAndBlock();
278 EXPECT_TRUE(bus->shutdown_completed());
279 dbus_thread.Stop();
280}
[email protected]12e25992011-10-06 00:20:53281
[email protected]bae0f882013-01-31 06:08:02282TEST(BusTest, DoubleAddAndRemoveMatch) {
[email protected]2a57ca642013-06-13 06:37:19283 Bus::Options options;
284 scoped_refptr<Bus> bus = new Bus(options);
285 ScopedDBusError error;
[email protected]bae0f882013-01-31 06:08:02286
287 bus->Connect();
288
289 // Adds the same rule twice.
290 bus->AddMatch(
291 "type='signal',interface='org.chromium.TestService',path='/'",
292 error.get());
293 ASSERT_FALSE(error.is_set());
294
295 bus->AddMatch(
296 "type='signal',interface='org.chromium.TestService',path='/'",
297 error.get());
298 ASSERT_FALSE(error.is_set());
299
300 // Removes the same rule twice.
301 ASSERT_TRUE(bus->RemoveMatch(
302 "type='signal',interface='org.chromium.TestService',path='/'",
303 error.get()));
304 ASSERT_FALSE(error.is_set());
305
306 // The rule should be still in the bus since it was removed only once.
307 // A second removal shouldn't give an error.
308 ASSERT_TRUE(bus->RemoveMatch(
309 "type='signal',interface='org.chromium.TestService',path='/'",
310 error.get()));
311 ASSERT_FALSE(error.is_set());
312
313 // A third attemp to remove the same rule should fail.
314 ASSERT_FALSE(bus->RemoveMatch(
315 "type='signal',interface='org.chromium.TestService',path='/'",
316 error.get()));
317
318 bus->ShutdownAndBlock();
319}
[email protected]049616e2013-06-10 22:52:34320
321TEST(BusTest, ListenForServiceOwnerChange) {
fdorayebc379c2017-04-18 13:40:21322 base::MessageLoopForIO message_loop;
323
324 // This enables FileDescriptorWatcher, which is required by dbus::Watch.
325 base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);
326
[email protected]049616e2013-06-10 22:52:34327 RunLoopWithExpectedCount run_loop_state;
328
329 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19330 Bus::Options bus_options;
331 scoped_refptr<Bus> bus = new Bus(bus_options);
[email protected]049616e2013-06-10 22:52:34332
333 // Add a listener.
334 std::string service_owner1;
335 int num_of_owner_changes1 = 0;
[email protected]2a57ca642013-06-13 06:37:19336 Bus::GetServiceOwnerCallback callback1 =
[email protected]049616e2013-06-10 22:52:34337 base::Bind(&OnServiceOwnerChanged,
338 &run_loop_state,
339 &service_owner1,
340 &num_of_owner_changes1);
341 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
342 // This should be a no-op.
343 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
344 base::RunLoop().RunUntilIdle();
345
346 // Nothing has happened yet. Check initial state.
347 EXPECT_TRUE(service_owner1.empty());
348 EXPECT_EQ(0, num_of_owner_changes1);
349
350 // Make an ownership change.
[email protected]e2824902013-07-31 06:34:59351 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
352 Bus::REQUIRE_PRIMARY));
[email protected]049616e2013-06-10 22:52:34353 run_loop_state.Run(1);
354
355 {
356 // Get the current service owner and check to make sure the listener got
357 // the right value.
358 std::string current_service_owner =
359 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19360 Bus::REPORT_ERRORS);
[email protected]049616e2013-06-10 22:52:34361 ASSERT_FALSE(current_service_owner.empty());
362
363 // Make sure the listener heard about the new owner.
364 EXPECT_EQ(current_service_owner, service_owner1);
365
366 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
367 EXPECT_EQ(1, num_of_owner_changes1);
368 }
369
370 // Add a second listener.
371 std::string service_owner2;
372 int num_of_owner_changes2 = 0;
[email protected]2a57ca642013-06-13 06:37:19373 Bus::GetServiceOwnerCallback callback2 =
[email protected]049616e2013-06-10 22:52:34374 base::Bind(&OnServiceOwnerChanged,
375 &run_loop_state,
376 &service_owner2,
377 &num_of_owner_changes2);
378 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
379 base::RunLoop().RunUntilIdle();
380
381 // Release the ownership and make sure the service owner listeners fire with
382 // the right values and the right number of times.
383 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
384 run_loop_state.Run(2);
385
386 EXPECT_TRUE(service_owner1.empty());
387 EXPECT_TRUE(service_owner2.empty());
388 EXPECT_EQ(2, num_of_owner_changes1);
389 EXPECT_EQ(1, num_of_owner_changes2);
390
391 // Unlisten so shutdown can proceed correctly.
392 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
393 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
394 base::RunLoop().RunUntilIdle();
395
396 // Shut down synchronously.
397 bus->ShutdownAndBlock();
398 EXPECT_TRUE(bus->shutdown_completed());
399}
[email protected]2a57ca642013-06-13 06:37:19400
zqiuf63bfe5e2015-07-08 02:08:30401TEST(BusTest, GetConnectionName) {
402 Bus::Options options;
403 scoped_refptr<Bus> bus = new Bus(options);
404
405 // Connection name is empty since bus is not connected.
406 EXPECT_FALSE(bus->is_connected());
407 EXPECT_TRUE(bus->GetConnectionName().empty());
408
409 // Connect bus to D-Bus.
410 bus->Connect();
411
412 // Connection name is not empty after connection is established.
413 EXPECT_TRUE(bus->is_connected());
414 EXPECT_FALSE(bus->GetConnectionName().empty());
415
416 // Shut down synchronously.
417 bus->ShutdownAndBlock();
418 EXPECT_TRUE(bus->shutdown_completed());
419}
420
[email protected]2a57ca642013-06-13 06:37:19421} // namespace dbus