1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // 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
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/files/file_descriptor_watcher_posix.h"
10 #include "base/macros.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/threading/thread.h"
15 #include "dbus/exported_object.h"
16 #include "dbus/object_path.h"
17 #include "dbus/object_proxy.h"
18 #include "dbus/scoped_dbus_error.h"
19 #include "dbus/test_service.h"
20
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace dbus {
24
25 namespace {
26
27 // Test helper for BusTest.ListenForServiceOwnerChange that wraps a
28 // base::RunLoop. At Run() time, the caller pass in the expected number of
29 // quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
30 // if the expected number of quit calls have been reached.
31 class RunLoopWithExpectedCount {
32 public:
RunLoopWithExpectedCount()33 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
34 ~RunLoopWithExpectedCount() = default;
35
Run(int expected_quit_calls)36 void Run(int expected_quit_calls) {
37 DCHECK_EQ(0, expected_quit_calls_);
38 DCHECK_EQ(0, actual_quit_calls_);
39 expected_quit_calls_ = expected_quit_calls;
40 run_loop_.reset(new base::RunLoop());
41 run_loop_->Run();
42 }
43
QuitIfConditionIsSatisified()44 void QuitIfConditionIsSatisified() {
45 if (++actual_quit_calls_ != expected_quit_calls_)
46 return;
47 run_loop_->Quit();
48 expected_quit_calls_ = 0;
49 actual_quit_calls_ = 0;
50 }
51
52 private:
53 std::unique_ptr<base::RunLoop> run_loop_;
54 int expected_quit_calls_;
55 int actual_quit_calls_;
56
57 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
58 };
59
60 // Test helper for BusTest.ListenForServiceOwnerChange.
OnServiceOwnerChanged(RunLoopWithExpectedCount * run_loop_state,std::string * service_owner,int * num_of_owner_changes,const std::string & new_service_owner)61 void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
62 std::string* service_owner,
63 int* num_of_owner_changes,
64 const std::string& new_service_owner) {
65 *service_owner = new_service_owner;
66 ++(*num_of_owner_changes);
67 run_loop_state->QuitIfConditionIsSatisified();
68 }
69
70 } // namespace
71
TEST(BusTest,GetObjectProxy)72 TEST(BusTest, GetObjectProxy) {
73 Bus::Options options;
74 scoped_refptr<Bus> bus = new Bus(options);
75
76 ObjectProxy* object_proxy1 =
77 bus->GetObjectProxy("org.chromium.TestService",
78 ObjectPath("/org/chromium/TestObject"));
79 ASSERT_TRUE(object_proxy1);
80
81 // This should return the same object.
82 ObjectProxy* object_proxy2 =
83 bus->GetObjectProxy("org.chromium.TestService",
84 ObjectPath("/org/chromium/TestObject"));
85 ASSERT_TRUE(object_proxy2);
86 EXPECT_EQ(object_proxy1, object_proxy2);
87
88 // This should not.
89 ObjectProxy* object_proxy3 =
90 bus->GetObjectProxy(
91 "org.chromium.TestService",
92 ObjectPath("/org/chromium/DifferentTestObject"));
93 ASSERT_TRUE(object_proxy3);
94 EXPECT_NE(object_proxy1, object_proxy3);
95
96 bus->ShutdownAndBlock();
97 }
98
TEST(BusTest,GetObjectProxyIgnoreUnknownService)99 TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
100 Bus::Options options;
101 scoped_refptr<Bus> bus = new Bus(options);
102
103 ObjectProxy* object_proxy1 =
104 bus->GetObjectProxyWithOptions(
105 "org.chromium.TestService",
106 ObjectPath("/org/chromium/TestObject"),
107 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
108 ASSERT_TRUE(object_proxy1);
109
110 // This should return the same object.
111 ObjectProxy* object_proxy2 =
112 bus->GetObjectProxyWithOptions(
113 "org.chromium.TestService",
114 ObjectPath("/org/chromium/TestObject"),
115 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
116 ASSERT_TRUE(object_proxy2);
117 EXPECT_EQ(object_proxy1, object_proxy2);
118
119 // This should not.
120 ObjectProxy* object_proxy3 =
121 bus->GetObjectProxyWithOptions(
122 "org.chromium.TestService",
123 ObjectPath("/org/chromium/DifferentTestObject"),
124 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
125 ASSERT_TRUE(object_proxy3);
126 EXPECT_NE(object_proxy1, object_proxy3);
127
128 bus->ShutdownAndBlock();
129 }
130
TEST(BusTest,RemoveObjectProxy)131 TEST(BusTest, RemoveObjectProxy) {
132 // Setup the current thread's MessageLoop.
133 base::MessageLoop message_loop;
134
135 // Start the D-Bus thread.
136 base::Thread::Options thread_options;
137 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
138 base::Thread dbus_thread("D-Bus thread");
139 dbus_thread.StartWithOptions(thread_options);
140
141 // Create the bus.
142 Bus::Options options;
143 options.dbus_task_runner = dbus_thread.task_runner();
144 scoped_refptr<Bus> bus = new Bus(options);
145 ASSERT_FALSE(bus->shutdown_completed());
146
147 // Try to remove a non existant object proxy should return false.
148 ASSERT_FALSE(bus->RemoveObjectProxy("org.chromium.TestService",
149 ObjectPath("/org/chromium/TestObject"),
150 base::DoNothing()));
151
152 ObjectProxy* object_proxy1 =
153 bus->GetObjectProxy("org.chromium.TestService",
154 ObjectPath("/org/chromium/TestObject"));
155 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(bus->RemoveObjectProxy("org.chromium.TestService",
165 ObjectPath("/org/chromium/TestObject"),
166 base::DoNothing()));
167
168 // This should return a different object because the first object was removed
169 // from the bus, but not deleted from memory.
170 ObjectProxy* object_proxy2 =
171 bus->GetObjectProxy("org.chromium.TestService",
172 ObjectPath("/org/chromium/TestObject"));
173 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
TEST(BusTest,GetExportedObject)188 TEST(BusTest, GetExportedObject) {
189 Bus::Options options;
190 scoped_refptr<Bus> bus = new Bus(options);
191
192 ExportedObject* object_proxy1 =
193 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
194 ASSERT_TRUE(object_proxy1);
195
196 // This should return the same object.
197 ExportedObject* object_proxy2 =
198 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
199 ASSERT_TRUE(object_proxy2);
200 EXPECT_EQ(object_proxy1, object_proxy2);
201
202 // This should not.
203 ExportedObject* object_proxy3 =
204 bus->GetExportedObject(
205 ObjectPath("/org/chromium/DifferentTestObject"));
206 ASSERT_TRUE(object_proxy3);
207 EXPECT_NE(object_proxy1, object_proxy3);
208
209 bus->ShutdownAndBlock();
210 }
211
TEST(BusTest,UnregisterExportedObject)212 TEST(BusTest, UnregisterExportedObject) {
213 // Start the D-Bus thread.
214 base::Thread::Options thread_options;
215 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
216 base::Thread dbus_thread("D-Bus thread");
217 dbus_thread.StartWithOptions(thread_options);
218
219 // Create the bus.
220 Bus::Options options;
221 options.dbus_task_runner = dbus_thread.task_runner();
222 scoped_refptr<Bus> bus = new Bus(options);
223 ASSERT_FALSE(bus->shutdown_completed());
224
225 ExportedObject* object_proxy1 =
226 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
227 ASSERT_TRUE(object_proxy1);
228
229 // 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
234 bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
235
236 // This should return a new object because the object_proxy1 is still in
237 // alloc'ed memory.
238 ExportedObject* object_proxy2 =
239 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
240 ASSERT_TRUE(object_proxy2);
241 EXPECT_NE(object_proxy1, object_proxy2);
242
243 // Release the incremented reference.
244 object_proxy1->Release();
245
246 // Shut down synchronously.
247 bus->ShutdownOnDBusThreadAndBlock();
248 EXPECT_TRUE(bus->shutdown_completed());
249 dbus_thread.Stop();
250 }
251
TEST(BusTest,ShutdownAndBlock)252 TEST(BusTest, ShutdownAndBlock) {
253 Bus::Options options;
254 scoped_refptr<Bus> bus = new Bus(options);
255 ASSERT_FALSE(bus->shutdown_completed());
256
257 // Shut down synchronously.
258 bus->ShutdownAndBlock();
259 EXPECT_TRUE(bus->shutdown_completed());
260 }
261
TEST(BusTest,ShutdownAndBlockWithDBusThread)262 TEST(BusTest, ShutdownAndBlockWithDBusThread) {
263 // Start the D-Bus thread.
264 base::Thread::Options thread_options;
265 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
266 base::Thread dbus_thread("D-Bus thread");
267 dbus_thread.StartWithOptions(thread_options);
268
269 // Create the bus.
270 Bus::Options options;
271 options.dbus_task_runner = dbus_thread.task_runner();
272 scoped_refptr<Bus> bus = new Bus(options);
273 ASSERT_FALSE(bus->shutdown_completed());
274
275 // Shut down synchronously.
276 bus->ShutdownOnDBusThreadAndBlock();
277 EXPECT_TRUE(bus->shutdown_completed());
278 dbus_thread.Stop();
279 }
280
TEST(BusTest,DoubleAddAndRemoveMatch)281 TEST(BusTest, DoubleAddAndRemoveMatch) {
282 Bus::Options options;
283 scoped_refptr<Bus> bus = new Bus(options);
284 ScopedDBusError error;
285
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 }
319
TEST(BusTest,ListenForServiceOwnerChange)320 TEST(BusTest, ListenForServiceOwnerChange) {
321 base::MessageLoopForIO message_loop;
322
323 // This enables FileDescriptorWatcher, which is required by dbus::Watch.
324 base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);
325
326 RunLoopWithExpectedCount run_loop_state;
327
328 // Create the bus.
329 Bus::Options bus_options;
330 scoped_refptr<Bus> bus = new Bus(bus_options);
331
332 // Add a listener.
333 std::string service_owner1;
334 int num_of_owner_changes1 = 0;
335 Bus::GetServiceOwnerCallback callback1 =
336 base::Bind(&OnServiceOwnerChanged,
337 &run_loop_state,
338 &service_owner1,
339 &num_of_owner_changes1);
340 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
341 // This should be a no-op.
342 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
343 base::RunLoop().RunUntilIdle();
344
345 // Nothing has happened yet. Check initial state.
346 EXPECT_TRUE(service_owner1.empty());
347 EXPECT_EQ(0, num_of_owner_changes1);
348
349 // Make an ownership change.
350 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
351 Bus::REQUIRE_PRIMARY));
352 run_loop_state.Run(1);
353
354 {
355 // Get the current service owner and check to make sure the listener got
356 // the right value.
357 std::string current_service_owner =
358 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
359 Bus::REPORT_ERRORS);
360 ASSERT_FALSE(current_service_owner.empty());
361
362 // Make sure the listener heard about the new owner.
363 EXPECT_EQ(current_service_owner, service_owner1);
364
365 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
366 EXPECT_EQ(1, num_of_owner_changes1);
367 }
368
369 // Add a second listener.
370 std::string service_owner2;
371 int num_of_owner_changes2 = 0;
372 Bus::GetServiceOwnerCallback callback2 =
373 base::Bind(&OnServiceOwnerChanged,
374 &run_loop_state,
375 &service_owner2,
376 &num_of_owner_changes2);
377 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
378 base::RunLoop().RunUntilIdle();
379
380 // Release the ownership and make sure the service owner listeners fire with
381 // the right values and the right number of times.
382 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
383 run_loop_state.Run(2);
384
385 EXPECT_TRUE(service_owner1.empty());
386 EXPECT_TRUE(service_owner2.empty());
387 EXPECT_EQ(2, num_of_owner_changes1);
388 EXPECT_EQ(1, num_of_owner_changes2);
389
390 // Unlisten so shutdown can proceed correctly.
391 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
392 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
393 base::RunLoop().RunUntilIdle();
394
395 // Shut down synchronously.
396 bus->ShutdownAndBlock();
397 EXPECT_TRUE(bus->shutdown_completed());
398 }
399
TEST(BusTest,GetConnectionName)400 TEST(BusTest, GetConnectionName) {
401 Bus::Options options;
402 scoped_refptr<Bus> bus = new Bus(options);
403
404 // Connection name is empty since bus is not connected.
405 EXPECT_FALSE(bus->is_connected());
406 EXPECT_TRUE(bus->GetConnectionName().empty());
407
408 // Connect bus to D-Bus.
409 bus->Connect();
410
411 // Connection name is not empty after connection is established.
412 EXPECT_TRUE(bus->is_connected());
413 EXPECT_FALSE(bus->GetConnectionName().empty());
414
415 // Shut down synchronously.
416 bus->ShutdownAndBlock();
417 EXPECT_TRUE(bus->shutdown_completed());
418 }
419
420 } // namespace dbus
421