xref: /aosp_15_r20/external/libchrome/dbus/bus_unittest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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