blob: 1981a71ee5b675e7daeb65b2e09c624e74e37aa5 [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"
Peter Kasting383640f52018-02-13 06:22:408#include "base/bind_helpers.h"
fdorayebc379c2017-04-18 13:40:219#include "base/files/file_descriptor_watcher_posix.h"
avi22437c692015-12-22 18:12:4510#include "base/macros.h"
[email protected]2ef498f2011-08-23 19:25:2011#include "base/memory/ref_counted.h"
Carlos Caballerodd8bf7b042019-07-30 14:14:1512#include "base/message_loop/message_pump_type.h"
[email protected]049616e2013-06-10 22:52:3413#include "base/run_loop.h"
Carlos Caballero479d2872019-06-10 10:04:4314#include "base/test/scoped_task_environment.h"
[email protected]e20d39fe2011-09-02 06:56:2315#include "base/threading/thread.h"
[email protected]2ef498f2011-08-23 19:25:2016#include "dbus/exported_object.h"
[email protected]216ed0b2012-02-14 21:29:0617#include "dbus/object_path.h"
[email protected]2ef498f2011-08-23 19:25:2018#include "dbus/object_proxy.h"
[email protected]bae0f882013-01-31 06:08:0219#include "dbus/scoped_dbus_error.h"
[email protected]049616e2013-06-10 22:52:3420#include "dbus/test_service.h"
[email protected]2ef498f2011-08-23 19:25:2021
22#include "testing/gtest/include/gtest/gtest.h"
23
[email protected]2a57ca642013-06-13 06:37:1924namespace dbus {
25
[email protected]12e25992011-10-06 00:20:5326namespace {
27
[email protected]049616e2013-06-10 22:52:3428// Test helper for BusTest.ListenForServiceOwnerChange that wraps a
29// base::RunLoop. At Run() time, the caller pass in the expected number of
30// quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
31// if the expected number of quit calls have been reached.
32class RunLoopWithExpectedCount {
33 public:
34 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
Chris Watkins3740aae2017-11-29 07:44:1135 ~RunLoopWithExpectedCount() = default;
[email protected]049616e2013-06-10 22:52:3436
37 void Run(int expected_quit_calls) {
38 DCHECK_EQ(0, expected_quit_calls_);
39 DCHECK_EQ(0, actual_quit_calls_);
40 expected_quit_calls_ = expected_quit_calls;
41 run_loop_.reset(new base::RunLoop());
42 run_loop_->Run();
43 }
44
45 void QuitIfConditionIsSatisified() {
46 if (++actual_quit_calls_ != expected_quit_calls_)
47 return;
48 run_loop_->Quit();
49 expected_quit_calls_ = 0;
50 actual_quit_calls_ = 0;
51 }
52
53 private:
dcheng2a193282016-04-08 22:55:0454 std::unique_ptr<base::RunLoop> run_loop_;
[email protected]049616e2013-06-10 22:52:3455 int expected_quit_calls_;
56 int actual_quit_calls_;
57
58 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
59};
60
61// Test helper for BusTest.ListenForServiceOwnerChange.
62void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
63 std::string* service_owner,
64 int* num_of_owner_changes,
65 const std::string& new_service_owner) {
66 *service_owner = new_service_owner;
67 ++(*num_of_owner_changes);
68 run_loop_state->QuitIfConditionIsSatisified();
69}
70
[email protected]12e25992011-10-06 00:20:5371} // namespace
72
[email protected]2ef498f2011-08-23 19:25:2073TEST(BusTest, GetObjectProxy) {
[email protected]2a57ca642013-06-13 06:37:1974 Bus::Options options;
75 scoped_refptr<Bus> bus = new Bus(options);
[email protected]2ef498f2011-08-23 19:25:2076
[email protected]2a57ca642013-06-13 06:37:1977 ObjectProxy* object_proxy1 =
[email protected]2ef498f2011-08-23 19:25:2078 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1979 ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2080 ASSERT_TRUE(object_proxy1);
81
82 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:1983 ObjectProxy* object_proxy2 =
[email protected]2ef498f2011-08-23 19:25:2084 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1985 ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:2086 ASSERT_TRUE(object_proxy2);
87 EXPECT_EQ(object_proxy1, object_proxy2);
88
89 // This should not.
[email protected]2a57ca642013-06-13 06:37:1990 ObjectProxy* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:0691 bus->GetObjectProxy(
92 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:1993 ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:2094 ASSERT_TRUE(object_proxy3);
95 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:2696
97 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:2098}
99
[email protected]20bed012012-02-10 21:45:23100TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
[email protected]2a57ca642013-06-13 06:37:19101 Bus::Options options;
102 scoped_refptr<Bus> bus = new Bus(options);
[email protected]20bed012012-02-10 21:45:23103
[email protected]2a57ca642013-06-13 06:37:19104 ObjectProxy* object_proxy1 =
[email protected]20bed012012-02-10 21:45:23105 bus->GetObjectProxyWithOptions(
106 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19107 ObjectPath("/org/chromium/TestObject"),
108 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23109 ASSERT_TRUE(object_proxy1);
110
111 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:19112 ObjectProxy* object_proxy2 =
[email protected]20bed012012-02-10 21:45:23113 bus->GetObjectProxyWithOptions(
114 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19115 ObjectPath("/org/chromium/TestObject"),
116 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23117 ASSERT_TRUE(object_proxy2);
118 EXPECT_EQ(object_proxy1, object_proxy2);
119
120 // This should not.
[email protected]2a57ca642013-06-13 06:37:19121 ObjectProxy* object_proxy3 =
[email protected]20bed012012-02-10 21:45:23122 bus->GetObjectProxyWithOptions(
123 "org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19124 ObjectPath("/org/chromium/DifferentTestObject"),
125 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
[email protected]20bed012012-02-10 21:45:23126 ASSERT_TRUE(object_proxy3);
127 EXPECT_NE(object_proxy1, object_proxy3);
128
129 bus->ShutdownAndBlock();
130}
131
[email protected]38ecdc82013-01-29 20:29:12132TEST(BusTest, RemoveObjectProxy) {
Carlos Caballero479d2872019-06-10 10:04:43133 base::test::ScopedTaskEnvironment scoped_task_environment;
[email protected]38ecdc82013-01-29 20:29:12134
135 // Start the D-Bus thread.
136 base::Thread::Options thread_options;
Carlos Caballerodd8bf7b042019-07-30 14:14:15137 thread_options.message_pump_type = base::MessagePumpType::IO;
[email protected]38ecdc82013-01-29 20:29:12138 base::Thread dbus_thread("D-Bus thread");
139 dbus_thread.StartWithOptions(thread_options);
140
141 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19142 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04143 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19144 scoped_refptr<Bus> bus = new Bus(options);
[email protected]38ecdc82013-01-29 20:29:12145 ASSERT_FALSE(bus->shutdown_completed());
146
147 // Try to remove a non existant object proxy should return false.
Peter Kasting341e1fb2018-02-24 00:03:01148 ASSERT_FALSE(bus->RemoveObjectProxy("org.chromium.TestService",
149 ObjectPath("/org/chromium/TestObject"),
150 base::DoNothing()));
[email protected]38ecdc82013-01-29 20:29:12151
[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.
Peter Kasting341e1fb2018-02-24 00:03:01164 ASSERT_TRUE(bus->RemoveObjectProxy("org.chromium.TestService",
165 ObjectPath("/org/chromium/TestObject"),
166 base::DoNothing()));
[email protected]38ecdc82013-01-29 20:29:12167
168 // This should return a different object because the first object was removed
169 // from the bus, but not deleted from memory.
[email protected]2a57ca642013-06-13 06:37:19170 ObjectProxy* object_proxy2 =
[email protected]38ecdc82013-01-29 20:29:12171 bus->GetObjectProxy("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19172 ObjectPath("/org/chromium/TestObject"));
[email protected]38ecdc82013-01-29 20:29:12173 ASSERT_TRUE(object_proxy2);
174
175 // Compare the new object with the first object. The first object still exists
176 // thanks to the increased reference.
177 EXPECT_NE(object_proxy1, object_proxy2);
178
179 // Release object_proxy1.
180 object_proxy1->Release();
181
182 // Shut down synchronously.
183 bus->ShutdownOnDBusThreadAndBlock();
184 EXPECT_TRUE(bus->shutdown_completed());
185 dbus_thread.Stop();
186}
187
[email protected]2ef498f2011-08-23 19:25:20188TEST(BusTest, GetExportedObject) {
[email protected]2a57ca642013-06-13 06:37:19189 Bus::Options options;
190 scoped_refptr<Bus> bus = new Bus(options);
[email protected]2ef498f2011-08-23 19:25:20191
[email protected]2a57ca642013-06-13 06:37:19192 ExportedObject* object_proxy1 =
193 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20194 ASSERT_TRUE(object_proxy1);
195
196 // This should return the same object.
[email protected]2a57ca642013-06-13 06:37:19197 ExportedObject* object_proxy2 =
198 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]2ef498f2011-08-23 19:25:20199 ASSERT_TRUE(object_proxy2);
200 EXPECT_EQ(object_proxy1, object_proxy2);
201
202 // This should not.
[email protected]2a57ca642013-06-13 06:37:19203 ExportedObject* object_proxy3 =
[email protected]216ed0b2012-02-14 21:29:06204 bus->GetExportedObject(
[email protected]2a57ca642013-06-13 06:37:19205 ObjectPath("/org/chromium/DifferentTestObject"));
[email protected]2ef498f2011-08-23 19:25:20206 ASSERT_TRUE(object_proxy3);
207 EXPECT_NE(object_proxy1, object_proxy3);
[email protected]6477a412011-10-13 00:45:26208
209 bus->ShutdownAndBlock();
[email protected]2ef498f2011-08-23 19:25:20210}
[email protected]e20d39fe2011-09-02 06:56:23211
[email protected]bda57352013-01-24 00:58:35212TEST(BusTest, UnregisterExportedObject) {
[email protected]d7361fdc2012-03-14 01:18:35213 // Start the D-Bus thread.
214 base::Thread::Options thread_options;
Carlos Caballerodd8bf7b042019-07-30 14:14:15215 thread_options.message_pump_type = base::MessagePumpType::IO;
[email protected]d7361fdc2012-03-14 01:18:35216 base::Thread dbus_thread("D-Bus thread");
217 dbus_thread.StartWithOptions(thread_options);
218
219 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19220 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04221 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19222 scoped_refptr<Bus> bus = new Bus(options);
[email protected]d7361fdc2012-03-14 01:18:35223 ASSERT_FALSE(bus->shutdown_completed());
224
[email protected]2a57ca642013-06-13 06:37:19225 ExportedObject* object_proxy1 =
226 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35227 ASSERT_TRUE(object_proxy1);
228
[email protected]bda57352013-01-24 00:58:35229 // Increment the reference count to the object proxy to avoid destroying it
230 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
231 // not freed from memory. See http://crbug.com/137846 for details.
232 object_proxy1->AddRef();
233
[email protected]2a57ca642013-06-13 06:37:19234 bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35235
[email protected]bda57352013-01-24 00:58:35236 // This should return a new object because the object_proxy1 is still in
237 // alloc'ed memory.
[email protected]2a57ca642013-06-13 06:37:19238 ExportedObject* object_proxy2 =
239 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
[email protected]d7361fdc2012-03-14 01:18:35240 ASSERT_TRUE(object_proxy2);
241 EXPECT_NE(object_proxy1, object_proxy2);
242
[email protected]bda57352013-01-24 00:58:35243 // Release the incremented reference.
244 object_proxy1->Release();
245
[email protected]d7361fdc2012-03-14 01:18:35246 // Shut down synchronously.
247 bus->ShutdownOnDBusThreadAndBlock();
248 EXPECT_TRUE(bus->shutdown_completed());
249 dbus_thread.Stop();
250}
251
[email protected]e20d39fe2011-09-02 06:56:23252TEST(BusTest, ShutdownAndBlock) {
[email protected]2a57ca642013-06-13 06:37:19253 Bus::Options options;
254 scoped_refptr<Bus> bus = new Bus(options);
[email protected]e20d39fe2011-09-02 06:56:23255 ASSERT_FALSE(bus->shutdown_completed());
256
257 // Shut down synchronously.
258 bus->ShutdownAndBlock();
259 EXPECT_TRUE(bus->shutdown_completed());
260}
261
262TEST(BusTest, ShutdownAndBlockWithDBusThread) {
263 // Start the D-Bus thread.
264 base::Thread::Options thread_options;
Carlos Caballerodd8bf7b042019-07-30 14:14:15265 thread_options.message_pump_type = base::MessagePumpType::IO;
[email protected]e20d39fe2011-09-02 06:56:23266 base::Thread dbus_thread("D-Bus thread");
267 dbus_thread.StartWithOptions(thread_options);
268
269 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19270 Bus::Options options;
skyostil8a033aa2015-06-17 15:46:04271 options.dbus_task_runner = dbus_thread.task_runner();
[email protected]2a57ca642013-06-13 06:37:19272 scoped_refptr<Bus> bus = new Bus(options);
[email protected]e20d39fe2011-09-02 06:56:23273 ASSERT_FALSE(bus->shutdown_completed());
274
275 // Shut down synchronously.
276 bus->ShutdownOnDBusThreadAndBlock();
277 EXPECT_TRUE(bus->shutdown_completed());
278 dbus_thread.Stop();
279}
[email protected]12e25992011-10-06 00:20:53280
[email protected]bae0f882013-01-31 06:08:02281TEST(BusTest, DoubleAddAndRemoveMatch) {
[email protected]2a57ca642013-06-13 06:37:19282 Bus::Options options;
283 scoped_refptr<Bus> bus = new Bus(options);
284 ScopedDBusError error;
[email protected]bae0f882013-01-31 06:08:02285
286 bus->Connect();
287
288 // Adds the same rule twice.
289 bus->AddMatch(
290 "type='signal',interface='org.chromium.TestService',path='/'",
291 error.get());
292 ASSERT_FALSE(error.is_set());
293
294 bus->AddMatch(
295 "type='signal',interface='org.chromium.TestService',path='/'",
296 error.get());
297 ASSERT_FALSE(error.is_set());
298
299 // Removes the same rule twice.
300 ASSERT_TRUE(bus->RemoveMatch(
301 "type='signal',interface='org.chromium.TestService',path='/'",
302 error.get()));
303 ASSERT_FALSE(error.is_set());
304
305 // The rule should be still in the bus since it was removed only once.
306 // A second removal shouldn't give an error.
307 ASSERT_TRUE(bus->RemoveMatch(
308 "type='signal',interface='org.chromium.TestService',path='/'",
309 error.get()));
310 ASSERT_FALSE(error.is_set());
311
312 // A third attemp to remove the same rule should fail.
313 ASSERT_FALSE(bus->RemoveMatch(
314 "type='signal',interface='org.chromium.TestService',path='/'",
315 error.get()));
316
317 bus->ShutdownAndBlock();
318}
[email protected]049616e2013-06-10 22:52:34319
320TEST(BusTest, ListenForServiceOwnerChange) {
fdorayebc379c2017-04-18 13:40:21321 base::MessageLoopForIO message_loop;
322
323 // This enables FileDescriptorWatcher, which is required by dbus::Watch.
Alexander Timin6247f6a91a2018-10-27 15:40:10324 base::FileDescriptorWatcher file_descriptor_watcher(
325 message_loop.task_runner());
fdorayebc379c2017-04-18 13:40:21326
[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.
Sonny Sasaka59bc1c02018-08-16 05:09:20406 EXPECT_FALSE(bus->IsConnected());
zqiuf63bfe5e2015-07-08 02:08:30407 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.
Sonny Sasaka59bc1c02018-08-16 05:09:20413 EXPECT_TRUE(bus->IsConnected());
zqiuf63bfe5e2015-07-08 02:08:30414 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