bus_unittest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/message_loop.h"
9#include "base/memory/ref_counted.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
20namespace dbus {
21
22namespace {
23
24// Used to test AddFilterFunction().
25DBusHandlerResult 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.
35class RunLoopWithExpectedCount {
36 public:
37  RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
38  ~RunLoopWithExpectedCount() {}
39
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
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.
65void 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
76TEST(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
103TEST(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
135TEST(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
194TEST(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
218TEST(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
258TEST(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
268TEST(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
287TEST(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
308TEST(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
347TEST(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  run_loop_state.Run(1);
377
378  {
379    // Get the current service owner and check to make sure the listener got
380    // the right value.
381    std::string current_service_owner =
382        bus->GetServiceOwnerAndBlock("org.chromium.TestService",
383                                     Bus::REPORT_ERRORS);
384    ASSERT_FALSE(current_service_owner.empty());
385
386    // Make sure the listener heard about the new owner.
387    EXPECT_EQ(current_service_owner, service_owner1);
388
389    // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
390    EXPECT_EQ(1, num_of_owner_changes1);
391  }
392
393  // Add a second listener.
394  std::string service_owner2;
395  int num_of_owner_changes2 = 0;
396  Bus::GetServiceOwnerCallback callback2 =
397      base::Bind(&OnServiceOwnerChanged,
398                 &run_loop_state,
399                 &service_owner2,
400                 &num_of_owner_changes2);
401  bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
402  base::RunLoop().RunUntilIdle();
403
404  // Release the ownership and make sure the service owner listeners fire with
405  // the right values and the right number of times.
406  ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
407  run_loop_state.Run(2);
408
409  EXPECT_TRUE(service_owner1.empty());
410  EXPECT_TRUE(service_owner2.empty());
411  EXPECT_EQ(2, num_of_owner_changes1);
412  EXPECT_EQ(1, num_of_owner_changes2);
413
414  // Unlisten so shutdown can proceed correctly.
415  bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
416  bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
417  base::RunLoop().RunUntilIdle();
418
419  // Shut down synchronously.
420  bus->ShutdownAndBlock();
421  EXPECT_TRUE(bus->shutdown_completed());
422}
423
424}  // namespace dbus
425