Home | History | Annotate | Download | only in dbus
      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().
     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:
     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.
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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