blob: f0d64c1aeefac4737ae6338e54551695cea73faa [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"
Gabriel Charettec7108742019-08-23 03:31:4014#include "base/test/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) {
Gabriel Charette1858a232019-09-09 07:50:49133 base::test::SingleThreadTaskEnvironment 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) {
Gabriel Charette1858a232019-09-09 07:50:49321 base::test::SingleThreadTaskEnvironment task_environment(
322 base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
fdorayebc379c2017-04-18 13:40:21323
[email protected]049616e2013-06-10 22:52:34324 RunLoopWithExpectedCount run_loop_state;
325
326 // Create the bus.
[email protected]2a57ca642013-06-13 06:37:19327 Bus::Options bus_options;
328 scoped_refptr<Bus> bus = new Bus(bus_options);
[email protected]049616e2013-06-10 22:52:34329
330 // Add a listener.
331 std::string service_owner1;
332 int num_of_owner_changes1 = 0;
Reilly Grantd4e66132019-11-22 17:14:50333 Bus::ServiceOwnerChangeCallback callback1 =
334 base::BindRepeating(&OnServiceOwnerChanged, &run_loop_state,
335 &service_owner1, &num_of_owner_changes1);
[email protected]049616e2013-06-10 22:52:34336 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
337 // This should be a no-op.
338 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
339 base::RunLoop().RunUntilIdle();
340
341 // Nothing has happened yet. Check initial state.
342 EXPECT_TRUE(service_owner1.empty());
343 EXPECT_EQ(0, num_of_owner_changes1);
344
345 // Make an ownership change.
[email protected]e2824902013-07-31 06:34:59346 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
347 Bus::REQUIRE_PRIMARY));
[email protected]049616e2013-06-10 22:52:34348 run_loop_state.Run(1);
349
350 {
351 // Get the current service owner and check to make sure the listener got
352 // the right value.
353 std::string current_service_owner =
354 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
[email protected]2a57ca642013-06-13 06:37:19355 Bus::REPORT_ERRORS);
[email protected]049616e2013-06-10 22:52:34356 ASSERT_FALSE(current_service_owner.empty());
357
358 // Make sure the listener heard about the new owner.
359 EXPECT_EQ(current_service_owner, service_owner1);
360
361 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
362 EXPECT_EQ(1, num_of_owner_changes1);
363 }
364
365 // Add a second listener.
366 std::string service_owner2;
367 int num_of_owner_changes2 = 0;
Reilly Grantd4e66132019-11-22 17:14:50368 Bus::ServiceOwnerChangeCallback callback2 =
369 base::BindRepeating(&OnServiceOwnerChanged, &run_loop_state,
370 &service_owner2, &num_of_owner_changes2);
[email protected]049616e2013-06-10 22:52:34371 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
372 base::RunLoop().RunUntilIdle();
373
374 // Release the ownership and make sure the service owner listeners fire with
375 // the right values and the right number of times.
376 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
377 run_loop_state.Run(2);
378
379 EXPECT_TRUE(service_owner1.empty());
380 EXPECT_TRUE(service_owner2.empty());
381 EXPECT_EQ(2, num_of_owner_changes1);
382 EXPECT_EQ(1, num_of_owner_changes2);
383
384 // Unlisten so shutdown can proceed correctly.
385 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
386 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
387 base::RunLoop().RunUntilIdle();
388
389 // Shut down synchronously.
390 bus->ShutdownAndBlock();
391 EXPECT_TRUE(bus->shutdown_completed());
392}
[email protected]2a57ca642013-06-13 06:37:19393
zqiuf63bfe5e2015-07-08 02:08:30394TEST(BusTest, GetConnectionName) {
395 Bus::Options options;
396 scoped_refptr<Bus> bus = new Bus(options);
397
398 // Connection name is empty since bus is not connected.
Sonny Sasaka59bc1c02018-08-16 05:09:20399 EXPECT_FALSE(bus->IsConnected());
zqiuf63bfe5e2015-07-08 02:08:30400 EXPECT_TRUE(bus->GetConnectionName().empty());
401
402 // Connect bus to D-Bus.
403 bus->Connect();
404
405 // Connection name is not empty after connection is established.
Sonny Sasaka59bc1c02018-08-16 05:09:20406 EXPECT_TRUE(bus->IsConnected());
zqiuf63bfe5e2015-07-08 02:08:30407 EXPECT_FALSE(bus->GetConnectionName().empty());
408
409 // Shut down synchronously.
410 bus->ShutdownAndBlock();
411 EXPECT_TRUE(bus->shutdown_completed());
412}
413
[email protected]2a57ca642013-06-13 06:37:19414} // namespace dbus