• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/memory/ref_counted.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/threading/thread.h"
12 #include "dbus/exported_object.h"
13 #include "dbus/object_path.h"
14 #include "dbus/object_proxy.h"
15 #include "dbus/scoped_dbus_error.h"
16 #include "dbus/test_service.h"
17 
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace dbus {
21 
22 namespace {
23 
24 // Used to test AddFilterFunction().
DummyHandler(DBusConnection * connection,DBusMessage * raw_message,void * user_data)25 DBusHandlerResult DummyHandler(DBusConnection* connection,
26                                DBusMessage* raw_message,
27                                void* user_data) {
28   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
29 }
30 
31 // Test helper for BusTest.ListenForServiceOwnerChange that wraps a
32 // base::RunLoop. At Run() time, the caller pass in the expected number of
33 // quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
34 // if the expected number of quit calls have been reached.
35 class RunLoopWithExpectedCount {
36  public:
RunLoopWithExpectedCount()37   RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
~RunLoopWithExpectedCount()38   ~RunLoopWithExpectedCount() {}
39 
Run(int expected_quit_calls)40   void Run(int expected_quit_calls) {
41     DCHECK_EQ(0, expected_quit_calls_);
42     DCHECK_EQ(0, actual_quit_calls_);
43     expected_quit_calls_ = expected_quit_calls;
44     run_loop_.reset(new base::RunLoop());
45     run_loop_->Run();
46   }
47 
QuitIfConditionIsSatisified()48   void QuitIfConditionIsSatisified() {
49     if (++actual_quit_calls_ != expected_quit_calls_)
50       return;
51     run_loop_->Quit();
52     expected_quit_calls_ = 0;
53     actual_quit_calls_ = 0;
54   }
55 
56  private:
57   scoped_ptr<base::RunLoop> run_loop_;
58   int expected_quit_calls_;
59   int actual_quit_calls_;
60 
61   DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
62 };
63 
64 // 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)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();
72 }
73 
74 }  // namespace
75 
TEST(BusTest,GetObjectProxy)76 TEST(BusTest, GetObjectProxy) {
77   Bus::Options options;
78   scoped_refptr<Bus> bus = new Bus(options);
79 
80   ObjectProxy* object_proxy1 =
81       bus->GetObjectProxy("org.chromium.TestService",
82                           ObjectPath("/org/chromium/TestObject"));
83   ASSERT_TRUE(object_proxy1);
84 
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);
91 
92   // This should not.
93   ObjectProxy* object_proxy3 =
94       bus->GetObjectProxy(
95           "org.chromium.TestService",
96           ObjectPath("/org/chromium/DifferentTestObject"));
97   ASSERT_TRUE(object_proxy3);
98   EXPECT_NE(object_proxy1, object_proxy3);
99 
100   bus->ShutdownAndBlock();
101 }
102 
TEST(BusTest,GetObjectProxyIgnoreUnknownService)103 TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
104   Bus::Options options;
105   scoped_refptr<Bus> bus = new Bus(options);
106 
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);
113 
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);
122 
123   // This should not.
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);
131 
132   bus->ShutdownAndBlock();
133 }
134 
TEST(BusTest,RemoveObjectProxy)135 TEST(BusTest, RemoveObjectProxy) {
136   // Setup the current thread's MessageLoop.
137   base::MessageLoop message_loop;
138 
139   // Start the D-Bus thread.
140   base::Thread::Options thread_options;
141   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
142   base::Thread dbus_thread("D-Bus thread");
143   dbus_thread.StartWithOptions(thread_options);
144 
145   // Create the bus.
146   Bus::Options options;
147   options.dbus_task_runner = dbus_thread.message_loop_proxy();
148   scoped_refptr<Bus> bus = new Bus(options);
149   ASSERT_FALSE(bus->shutdown_completed());
150 
151   // Try to remove a non existant object proxy should return false.
152   ASSERT_FALSE(
153       bus->RemoveObjectProxy("org.chromium.TestService",
154                              ObjectPath("/org/chromium/TestObject"),
155                              base::Bind(&base::DoNothing)));
156 
157   ObjectProxy* object_proxy1 =
158       bus->GetObjectProxy("org.chromium.TestService",
159                           ObjectPath("/org/chromium/TestObject"));
160   ASSERT_TRUE(object_proxy1);
161 
162   // Increment the reference count to the object proxy to avoid destroying it
163   // while removing the object.
164   object_proxy1->AddRef();
165 
166   // Remove the object from the bus. This will invalidate any other usage of
167   // object_proxy1 other than destroy it. We keep this object for a comparison
168   // at a later time.
169   ASSERT_TRUE(
170       bus->RemoveObjectProxy("org.chromium.TestService",
171                              ObjectPath("/org/chromium/TestObject"),
172                              base::Bind(&base::DoNothing)));
173 
174   // This should return a different object because the first object was removed
175   // from the bus, but not deleted from memory.
176   ObjectProxy* object_proxy2 =
177       bus->GetObjectProxy("org.chromium.TestService",
178                           ObjectPath("/org/chromium/TestObject"));
179   ASSERT_TRUE(object_proxy2);
180 
181   // Compare the new object with the first object. The first object still exists
182   // thanks to the increased reference.
183   EXPECT_NE(object_proxy1, object_proxy2);
184 
185   // Release object_proxy1.
186   object_proxy1->Release();
187 
188   // Shut down synchronously.
189   bus->ShutdownOnDBusThreadAndBlock();
190   EXPECT_TRUE(bus->shutdown_completed());
191   dbus_thread.Stop();
192 }
193 
TEST(BusTest,GetExportedObject)194 TEST(BusTest, GetExportedObject) {
195   Bus::Options options;
196   scoped_refptr<Bus> bus = new Bus(options);
197 
198   ExportedObject* object_proxy1 =
199       bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
200   ASSERT_TRUE(object_proxy1);
201 
202   // This should return the same object.
203   ExportedObject* object_proxy2 =
204       bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
205   ASSERT_TRUE(object_proxy2);
206   EXPECT_EQ(object_proxy1, object_proxy2);
207 
208   // This should not.
209   ExportedObject* object_proxy3 =
210       bus->GetExportedObject(
211           ObjectPath("/org/chromium/DifferentTestObject"));
212   ASSERT_TRUE(object_proxy3);
213   EXPECT_NE(object_proxy1, object_proxy3);
214 
215   bus->ShutdownAndBlock();
216 }
217 
TEST(BusTest,UnregisterExportedObject)218 TEST(BusTest, UnregisterExportedObject) {
219   // Start the D-Bus thread.
220   base::Thread::Options thread_options;
221   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
222   base::Thread dbus_thread("D-Bus thread");
223   dbus_thread.StartWithOptions(thread_options);
224 
225   // Create the bus.
226   Bus::Options options;
227   options.dbus_task_runner = dbus_thread.message_loop_proxy();
228   scoped_refptr<Bus> bus = new Bus(options);
229   ASSERT_FALSE(bus->shutdown_completed());
230 
231   ExportedObject* object_proxy1 =
232       bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
233   ASSERT_TRUE(object_proxy1);
234 
235   // Increment the reference count to the object proxy to avoid destroying it
236   // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
237   // not freed from memory. See http://crbug.com/137846 for details.
238   object_proxy1->AddRef();
239 
240   bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
241 
242   // This should return a new object because the object_proxy1 is still in
243   // alloc'ed memory.
244   ExportedObject* object_proxy2 =
245       bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
246   ASSERT_TRUE(object_proxy2);
247   EXPECT_NE(object_proxy1, object_proxy2);
248 
249   // Release the incremented reference.
250   object_proxy1->Release();
251 
252   // Shut down synchronously.
253   bus->ShutdownOnDBusThreadAndBlock();
254   EXPECT_TRUE(bus->shutdown_completed());
255   dbus_thread.Stop();
256 }
257 
TEST(BusTest,ShutdownAndBlock)258 TEST(BusTest, ShutdownAndBlock) {
259   Bus::Options options;
260   scoped_refptr<Bus> bus = new Bus(options);
261   ASSERT_FALSE(bus->shutdown_completed());
262 
263   // Shut down synchronously.
264   bus->ShutdownAndBlock();
265   EXPECT_TRUE(bus->shutdown_completed());
266 }
267 
TEST(BusTest,ShutdownAndBlockWithDBusThread)268 TEST(BusTest, ShutdownAndBlockWithDBusThread) {
269   // Start the D-Bus thread.
270   base::Thread::Options thread_options;
271   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
272   base::Thread dbus_thread("D-Bus thread");
273   dbus_thread.StartWithOptions(thread_options);
274 
275   // Create the bus.
276   Bus::Options options;
277   options.dbus_task_runner = dbus_thread.message_loop_proxy();
278   scoped_refptr<Bus> bus = new Bus(options);
279   ASSERT_FALSE(bus->shutdown_completed());
280 
281   // Shut down synchronously.
282   bus->ShutdownOnDBusThreadAndBlock();
283   EXPECT_TRUE(bus->shutdown_completed());
284   dbus_thread.Stop();
285 }
286 
TEST(BusTest,AddFilterFunction)287 TEST(BusTest, AddFilterFunction) {
288   Bus::Options options;
289   scoped_refptr<Bus> bus = new Bus(options);
290   // Should connect before calling AddFilterFunction().
291   bus->Connect();
292 
293   int data1 = 100;
294   int data2 = 200;
295   ASSERT_TRUE(bus->AddFilterFunction(&DummyHandler, &data1));
296   // Cannot add the same function with the same data.
297   ASSERT_FALSE(bus->AddFilterFunction(&DummyHandler, &data1));
298   // Can add the same function with different data.
299   ASSERT_TRUE(bus->AddFilterFunction(&DummyHandler, &data2));
300 
301   ASSERT_TRUE(bus->RemoveFilterFunction(&DummyHandler, &data1));
302   ASSERT_FALSE(bus->RemoveFilterFunction(&DummyHandler, &data1));
303   ASSERT_TRUE(bus->RemoveFilterFunction(&DummyHandler, &data2));
304 
305   bus->ShutdownAndBlock();
306 }
307 
TEST(BusTest,DoubleAddAndRemoveMatch)308 TEST(BusTest, DoubleAddAndRemoveMatch) {
309   Bus::Options options;
310   scoped_refptr<Bus> bus = new Bus(options);
311   ScopedDBusError error;
312 
313   bus->Connect();
314 
315   // Adds the same rule twice.
316   bus->AddMatch(
317       "type='signal',interface='org.chromium.TestService',path='/'",
318       error.get());
319   ASSERT_FALSE(error.is_set());
320 
321   bus->AddMatch(
322       "type='signal',interface='org.chromium.TestService',path='/'",
323       error.get());
324   ASSERT_FALSE(error.is_set());
325 
326   // Removes the same rule twice.
327   ASSERT_TRUE(bus->RemoveMatch(
328       "type='signal',interface='org.chromium.TestService',path='/'",
329       error.get()));
330   ASSERT_FALSE(error.is_set());
331 
332   // The rule should be still in the bus since it was removed only once.
333   // A second removal shouldn't give an error.
334   ASSERT_TRUE(bus->RemoveMatch(
335       "type='signal',interface='org.chromium.TestService',path='/'",
336       error.get()));
337   ASSERT_FALSE(error.is_set());
338 
339   // A third attemp to remove the same rule should fail.
340   ASSERT_FALSE(bus->RemoveMatch(
341       "type='signal',interface='org.chromium.TestService',path='/'",
342       error.get()));
343 
344   bus->ShutdownAndBlock();
345 }
346 
TEST(BusTest,ListenForServiceOwnerChange)347 TEST(BusTest, ListenForServiceOwnerChange) {
348   // Setup the current thread's MessageLoop. Must be of TYPE_IO for the
349   // listeners to work.
350   base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
351   RunLoopWithExpectedCount run_loop_state;
352 
353   // Create the bus.
354   Bus::Options bus_options;
355   scoped_refptr<Bus> bus = new Bus(bus_options);
356 
357   // Add a listener.
358   std::string service_owner1;
359   int num_of_owner_changes1 = 0;
360   Bus::GetServiceOwnerCallback callback1 =
361       base::Bind(&OnServiceOwnerChanged,
362                  &run_loop_state,
363                  &service_owner1,
364                  &num_of_owner_changes1);
365   bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
366   // This should be a no-op.
367   bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
368   base::RunLoop().RunUntilIdle();
369 
370   // Nothing has happened yet. Check initial state.
371   EXPECT_TRUE(service_owner1.empty());
372   EXPECT_EQ(0, num_of_owner_changes1);
373 
374   // Make an ownership change.
375   ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
376                                             Bus::REQUIRE_PRIMARY));
377   run_loop_state.Run(1);
378 
379   {
380     // Get the current service owner and check to make sure the listener got
381     // the right value.
382     std::string current_service_owner =
383         bus->GetServiceOwnerAndBlock("org.chromium.TestService",
384                                      Bus::REPORT_ERRORS);
385     ASSERT_FALSE(current_service_owner.empty());
386 
387     // Make sure the listener heard about the new owner.
388     EXPECT_EQ(current_service_owner, service_owner1);
389 
390     // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
391     EXPECT_EQ(1, num_of_owner_changes1);
392   }
393 
394   // Add a second listener.
395   std::string service_owner2;
396   int num_of_owner_changes2 = 0;
397   Bus::GetServiceOwnerCallback callback2 =
398       base::Bind(&OnServiceOwnerChanged,
399                  &run_loop_state,
400                  &service_owner2,
401                  &num_of_owner_changes2);
402   bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
403   base::RunLoop().RunUntilIdle();
404 
405   // Release the ownership and make sure the service owner listeners fire with
406   // the right values and the right number of times.
407   ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
408   run_loop_state.Run(2);
409 
410   EXPECT_TRUE(service_owner1.empty());
411   EXPECT_TRUE(service_owner2.empty());
412   EXPECT_EQ(2, num_of_owner_changes1);
413   EXPECT_EQ(1, num_of_owner_changes2);
414 
415   // Unlisten so shutdown can proceed correctly.
416   bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
417   bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
418   base::RunLoop().RunUntilIdle();
419 
420   // Shut down synchronously.
421   bus->ShutdownAndBlock();
422   EXPECT_TRUE(bus->shutdown_completed());
423 }
424 
425 }  // namespace dbus
426