1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/files/file_descriptor_watcher_posix.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop/message_pump_type.h"
14 #include "base/run_loop.h"
15 #include "base/test/task_environment.h"
16 #include "base/threading/thread.h"
17 #include "dbus/error.h"
18 #include "dbus/exported_object.h"
19 #include "dbus/object_path.h"
20 #include "dbus/object_proxy.h"
21 #include "dbus/test_service.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 // 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.
33 class RunLoopWithExpectedCount {
35 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
37 RunLoopWithExpectedCount(const RunLoopWithExpectedCount&) = delete;
38 RunLoopWithExpectedCount& operator=(const RunLoopWithExpectedCount&) = delete;
40 ~RunLoopWithExpectedCount() = default;
42 void Run(int expected_quit_calls) {
43 DCHECK_EQ(0, expected_quit_calls_);
44 DCHECK_EQ(0, actual_quit_calls_);
45 expected_quit_calls_ = expected_quit_calls;
46 run_loop_ = std::make_unique<base::RunLoop>();
50 void QuitIfConditionIsSatisified() {
51 if (++actual_quit_calls_ != expected_quit_calls_)
54 expected_quit_calls_ = 0;
55 actual_quit_calls_ = 0;
59 std::unique_ptr<base::RunLoop> run_loop_;
60 int expected_quit_calls_;
61 int actual_quit_calls_;
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 base::test::SingleThreadTaskEnvironment task_environment;
138 // Start the D-Bus thread.
139 base::Thread::Options thread_options;
140 thread_options.message_pump_type = base::MessagePumpType::IO;
141 base::Thread dbus_thread("D-Bus thread");
142 dbus_thread.StartWithOptions(std::move(thread_options));
145 Bus::Options options;
146 options.dbus_task_runner = dbus_thread.task_runner();
147 scoped_refptr<Bus> bus = new Bus(options);
148 ASSERT_FALSE(bus->shutdown_completed());
150 // Try to remove a non existant object proxy should return false.
151 ASSERT_FALSE(bus->RemoveObjectProxy("org.chromium.TestService",
152 ObjectPath("/org/chromium/TestObject"),
155 ObjectProxy* object_proxy1 =
156 bus->GetObjectProxy("org.chromium.TestService",
157 ObjectPath("/org/chromium/TestObject"));
158 ASSERT_TRUE(object_proxy1);
160 // Increment the reference count to the object proxy to avoid destroying it
161 // while removing the object.
162 object_proxy1->AddRef();
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
167 ASSERT_TRUE(bus->RemoveObjectProxy("org.chromium.TestService",
168 ObjectPath("/org/chromium/TestObject"),
171 // This should return a different object because the first object was removed
172 // from the bus, but not deleted from memory.
173 ObjectProxy* object_proxy2 =
174 bus->GetObjectProxy("org.chromium.TestService",
175 ObjectPath("/org/chromium/TestObject"));
176 ASSERT_TRUE(object_proxy2);
178 // Compare the new object with the first object. The first object still exists
179 // thanks to the increased reference.
180 EXPECT_NE(object_proxy1, object_proxy2);
182 // Release object_proxy1.
183 object_proxy1->Release();
185 // Shut down synchronously.
186 bus->ShutdownOnDBusThreadAndBlock();
187 EXPECT_TRUE(bus->shutdown_completed());
191 TEST(BusTest, GetExportedObject) {
192 Bus::Options options;
193 scoped_refptr<Bus> bus = new Bus(options);
195 ExportedObject* object_proxy1 =
196 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
197 ASSERT_TRUE(object_proxy1);
199 // This should return the same object.
200 ExportedObject* object_proxy2 =
201 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
202 ASSERT_TRUE(object_proxy2);
203 EXPECT_EQ(object_proxy1, object_proxy2);
206 ExportedObject* object_proxy3 =
207 bus->GetExportedObject(
208 ObjectPath("/org/chromium/DifferentTestObject"));
209 ASSERT_TRUE(object_proxy3);
210 EXPECT_NE(object_proxy1, object_proxy3);
212 bus->ShutdownAndBlock();
215 TEST(BusTest, UnregisterExportedObject) {
216 // Start the D-Bus thread.
217 base::Thread::Options thread_options;
218 thread_options.message_pump_type = base::MessagePumpType::IO;
219 base::Thread dbus_thread("D-Bus thread");
220 dbus_thread.StartWithOptions(std::move(thread_options));
223 Bus::Options options;
224 options.dbus_task_runner = dbus_thread.task_runner();
225 scoped_refptr<Bus> bus = new Bus(options);
226 ASSERT_FALSE(bus->shutdown_completed());
228 ExportedObject* object_proxy1 =
229 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
230 ASSERT_TRUE(object_proxy1);
232 // Increment the reference count to the object proxy to avoid destroying it
233 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
234 // not freed from memory. See http://crbug.com/137846 for details.
235 object_proxy1->AddRef();
237 bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
239 // This should return a new object because the object_proxy1 is still in
241 ExportedObject* object_proxy2 =
242 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
243 ASSERT_TRUE(object_proxy2);
244 EXPECT_NE(object_proxy1, object_proxy2);
246 // Release the incremented reference.
247 object_proxy1->Release();
249 // Shut down synchronously.
250 bus->ShutdownOnDBusThreadAndBlock();
251 EXPECT_TRUE(bus->shutdown_completed());
255 TEST(BusTest, ShutdownAndBlock) {
256 Bus::Options options;
257 scoped_refptr<Bus> bus = new Bus(options);
258 ASSERT_FALSE(bus->shutdown_completed());
260 // Shut down synchronously.
261 bus->ShutdownAndBlock();
262 EXPECT_TRUE(bus->shutdown_completed());
265 TEST(BusTest, ShutdownAndBlockWithDBusThread) {
266 // Start the D-Bus thread.
267 base::Thread::Options thread_options;
268 thread_options.message_pump_type = base::MessagePumpType::IO;
269 base::Thread dbus_thread("D-Bus thread");
270 dbus_thread.StartWithOptions(std::move(thread_options));
273 Bus::Options options;
274 options.dbus_task_runner = dbus_thread.task_runner();
275 scoped_refptr<Bus> bus = new Bus(options);
276 ASSERT_FALSE(bus->shutdown_completed());
278 // Shut down synchronously.
279 bus->ShutdownOnDBusThreadAndBlock();
280 EXPECT_TRUE(bus->shutdown_completed());
284 TEST(BusTest, DoubleAddAndRemoveMatch) {
285 Bus::Options options;
286 scoped_refptr<Bus> bus = new Bus(options);
291 // Adds the same rule twice.
292 bus->AddMatch("type='signal',interface='org.chromium.TestService',path='/'",
294 ASSERT_FALSE(error.IsValid());
296 bus->AddMatch("type='signal',interface='org.chromium.TestService',path='/'",
298 ASSERT_FALSE(error.IsValid());
300 // Removes the same rule twice.
301 ASSERT_TRUE(bus->RemoveMatch(
302 "type='signal',interface='org.chromium.TestService',path='/'", &error));
303 ASSERT_FALSE(error.IsValid());
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='/'", &error));
309 ASSERT_FALSE(error.IsValid());
311 // A third attemp to remove the same rule should fail.
312 ASSERT_FALSE(bus->RemoveMatch(
313 "type='signal',interface='org.chromium.TestService',path='/'", &error));
315 bus->ShutdownAndBlock();
318 TEST(BusTest, ListenForServiceOwnerChange) {
319 base::test::SingleThreadTaskEnvironment task_environment(
320 base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
322 RunLoopWithExpectedCount run_loop_state;
325 Bus::Options bus_options;
326 scoped_refptr<Bus> bus = new Bus(bus_options);
329 std::string service_owner1;
330 int num_of_owner_changes1 = 0;
331 Bus::ServiceOwnerChangeCallback callback1 =
332 base::BindRepeating(&OnServiceOwnerChanged, &run_loop_state,
333 &service_owner1, &num_of_owner_changes1);
334 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
335 // This should be a no-op.
336 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
337 base::RunLoop().RunUntilIdle();
339 // Nothing has happened yet. Check initial state.
340 EXPECT_TRUE(service_owner1.empty());
341 EXPECT_EQ(0, num_of_owner_changes1);
343 // Make an ownership change.
344 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
345 Bus::REQUIRE_PRIMARY));
346 run_loop_state.Run(1);
349 // Get the current service owner and check to make sure the listener got
351 std::string current_service_owner =
352 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
354 ASSERT_FALSE(current_service_owner.empty());
356 // Make sure the listener heard about the new owner.
357 EXPECT_EQ(current_service_owner, service_owner1);
359 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
360 EXPECT_EQ(1, num_of_owner_changes1);
363 // Add a second listener.
364 std::string service_owner2;
365 int num_of_owner_changes2 = 0;
366 Bus::ServiceOwnerChangeCallback callback2 =
367 base::BindRepeating(&OnServiceOwnerChanged, &run_loop_state,
368 &service_owner2, &num_of_owner_changes2);
369 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
370 base::RunLoop().RunUntilIdle();
372 // Release the ownership and make sure the service owner listeners fire with
373 // the right values and the right number of times.
374 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
375 run_loop_state.Run(2);
377 EXPECT_TRUE(service_owner1.empty());
378 EXPECT_TRUE(service_owner2.empty());
379 EXPECT_EQ(2, num_of_owner_changes1);
380 EXPECT_EQ(1, num_of_owner_changes2);
382 // Unlisten so shutdown can proceed correctly.
383 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
384 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
385 base::RunLoop().RunUntilIdle();
387 // Shut down synchronously.
388 bus->ShutdownAndBlock();
389 EXPECT_TRUE(bus->shutdown_completed());
392 TEST(BusTest, GetConnectionName) {
393 Bus::Options options;
394 scoped_refptr<Bus> bus = new Bus(options);
396 // Connection name is empty since bus is not connected.
397 EXPECT_FALSE(bus->IsConnected());
398 EXPECT_TRUE(bus->GetConnectionName().empty());
400 // Connect bus to D-Bus.
403 // Connection name is not empty after connection is established.
404 EXPECT_TRUE(bus->IsConnected());
405 EXPECT_FALSE(bus->GetConnectionName().empty());
407 // Shut down synchronously.
408 bus->ShutdownAndBlock();
409 EXPECT_TRUE(bus->shutdown_completed());