Home | History | Annotate | Download | only in dbus
      1 // Copyright (c) 2013 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/object_manager.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/bind.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/run_loop.h"
     14 #include "base/threading/thread.h"
     15 #include "base/threading/thread_restrictions.h"
     16 #include "dbus/bus.h"
     17 #include "dbus/object_path.h"
     18 #include "dbus/object_proxy.h"
     19 #include "dbus/property.h"
     20 #include "dbus/test_service.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 namespace dbus {
     24 
     25 // The object manager test exercises the asynchronous APIs in ObjectManager,
     26 // and by extension PropertySet and Property<>.
     27 class ObjectManagerTest
     28     : public testing::Test,
     29       public ObjectManager::Interface {
     30  public:
     31   ObjectManagerTest() : timeout_expired_(false) {
     32   }
     33 
     34   struct Properties : public PropertySet {
     35     Property<std::string> name;
     36     Property<int16> version;
     37     Property<std::vector<std::string> > methods;
     38     Property<std::vector<ObjectPath> > objects;
     39 
     40     Properties(ObjectProxy* object_proxy,
     41                const std::string& interface_name,
     42                PropertyChangedCallback property_changed_callback)
     43         : PropertySet(object_proxy, interface_name, property_changed_callback) {
     44       RegisterProperty("Name", &name);
     45       RegisterProperty("Version", &version);
     46       RegisterProperty("Methods", &methods);
     47       RegisterProperty("Objects", &objects);
     48     }
     49   };
     50 
     51   virtual PropertySet* CreateProperties(
     52       ObjectProxy* object_proxy,
     53       const ObjectPath& object_path,
     54       const std::string& interface_name) OVERRIDE {
     55     Properties* properties = new Properties(
     56         object_proxy, interface_name,
     57         base::Bind(&ObjectManagerTest::OnPropertyChanged,
     58                    base::Unretained(this), object_path));
     59     return static_cast<PropertySet*>(properties);
     60   }
     61 
     62   virtual void SetUp() {
     63     // Make the main thread not to allow IO.
     64     base::ThreadRestrictions::SetIOAllowed(false);
     65 
     66     // Start the D-Bus thread.
     67     dbus_thread_.reset(new base::Thread("D-Bus Thread"));
     68     base::Thread::Options thread_options;
     69     thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
     70     ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
     71 
     72     // Start the test service, using the D-Bus thread.
     73     TestService::Options options;
     74     options.dbus_task_runner = dbus_thread_->message_loop_proxy();
     75     test_service_.reset(new TestService(options));
     76     ASSERT_TRUE(test_service_->StartService());
     77     ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
     78     ASSERT_TRUE(test_service_->HasDBusThread());
     79 
     80     // Create the client, using the D-Bus thread.
     81     Bus::Options bus_options;
     82     bus_options.bus_type = Bus::SESSION;
     83     bus_options.connection_type = Bus::PRIVATE;
     84     bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy();
     85     bus_ = new Bus(bus_options);
     86     ASSERT_TRUE(bus_->HasDBusThread());
     87 
     88     object_manager_ = bus_->GetObjectManager(
     89         "org.chromium.TestService",
     90         ObjectPath("/org/chromium/TestService"));
     91     object_manager_->RegisterInterface("org.chromium.TestInterface", this);
     92 
     93     WaitForObject();
     94   }
     95 
     96   virtual void TearDown() {
     97     bus_->ShutdownOnDBusThreadAndBlock();
     98 
     99     // Shut down the service.
    100     test_service_->ShutdownAndBlock();
    101 
    102     // Reset to the default.
    103     base::ThreadRestrictions::SetIOAllowed(true);
    104 
    105     // Stopping a thread is considered an IO operation, so do this after
    106     // allowing IO.
    107     test_service_->Stop();
    108 
    109     base::RunLoop().RunUntilIdle();
    110   }
    111 
    112   void MethodCallback(Response* response) {
    113     method_callback_called_ = true;
    114     run_loop_->Quit();
    115   }
    116 
    117   // Called from the PropertiesChangedAsObjectsReceived test case. The test will
    118   // not run the message loop if it receives the expected PropertiesChanged
    119   // signal before the timeout. This method immediately fails the test.
    120   void PropertiesChangedTestTimeout() {
    121     timeout_expired_ = true;
    122     run_loop_->Quit();
    123 
    124     FAIL() << "Never received PropertiesChanged";
    125   }
    126 
    127  protected:
    128   // Called when an object is added.
    129   virtual void ObjectAdded(const ObjectPath& object_path,
    130                            const std::string& interface_name) OVERRIDE {
    131     added_objects_.push_back(std::make_pair(object_path, interface_name));
    132     run_loop_->Quit();
    133   }
    134 
    135   // Called when an object is removed.
    136   virtual void ObjectRemoved(const ObjectPath& object_path,
    137                              const std::string& interface_name) OVERRIDE {
    138     removed_objects_.push_back(std::make_pair(object_path, interface_name));
    139     run_loop_->Quit();
    140   }
    141 
    142   // Called when a property value is updated.
    143   void OnPropertyChanged(const ObjectPath& object_path,
    144                          const std::string& name) {
    145     // Store the value of the "Name" property if that's the one that
    146     // changed.
    147     Properties* properties = static_cast<Properties*>(
    148         object_manager_->GetProperties(
    149             object_path,
    150             "org.chromium.TestInterface"));
    151     if (name == properties->name.name())
    152       last_name_value_ = properties->name.value();
    153 
    154     // Store the updated property.
    155     updated_properties_.push_back(name);
    156     run_loop_->Quit();
    157   }
    158 
    159   static const size_t kExpectedObjects = 1;
    160   static const size_t kExpectedProperties = 4;
    161 
    162   void WaitForObject() {
    163     while (added_objects_.size() < kExpectedObjects ||
    164            updated_properties_.size() < kExpectedProperties) {
    165       run_loop_.reset(new base::RunLoop);
    166       run_loop_->Run();
    167     }
    168     for (size_t i = 0; i < kExpectedObjects; ++i)
    169       added_objects_.erase(added_objects_.begin());
    170     for (size_t i = 0; i < kExpectedProperties; ++i)
    171       updated_properties_.erase(updated_properties_.begin());
    172   }
    173 
    174   void WaitForRemoveObject() {
    175     while (removed_objects_.size() < kExpectedObjects) {
    176       run_loop_.reset(new base::RunLoop);
    177       run_loop_->Run();
    178     }
    179     for (size_t i = 0; i < kExpectedObjects; ++i)
    180       removed_objects_.erase(removed_objects_.begin());
    181   }
    182 
    183   void WaitForMethodCallback() {
    184     run_loop_.reset(new base::RunLoop);
    185     run_loop_->Run();
    186     method_callback_called_ = false;
    187   }
    188 
    189   void PerformAction(const std::string& action, const ObjectPath& object_path) {
    190     ObjectProxy* object_proxy = bus_->GetObjectProxy(
    191         "org.chromium.TestService",
    192         ObjectPath("/org/chromium/TestObject"));
    193 
    194     MethodCall method_call("org.chromium.TestInterface", "PerformAction");
    195     MessageWriter writer(&method_call);
    196     writer.AppendString(action);
    197     writer.AppendObjectPath(object_path);
    198 
    199     object_proxy->CallMethod(&method_call,
    200                              ObjectProxy::TIMEOUT_USE_DEFAULT,
    201                              base::Bind(&ObjectManagerTest::MethodCallback,
    202                                         base::Unretained(this)));
    203     WaitForMethodCallback();
    204   }
    205 
    206   base::MessageLoop message_loop_;
    207   scoped_ptr<base::RunLoop> run_loop_;
    208   scoped_ptr<base::Thread> dbus_thread_;
    209   scoped_refptr<Bus> bus_;
    210   ObjectManager* object_manager_;
    211   scoped_ptr<TestService> test_service_;
    212 
    213   std::string last_name_value_;
    214   bool timeout_expired_;
    215 
    216   std::vector<std::pair<ObjectPath, std::string> > added_objects_;
    217   std::vector<std::pair<ObjectPath, std::string> > removed_objects_;
    218   std::vector<std::string> updated_properties_;
    219 
    220   bool method_callback_called_;
    221 };
    222 
    223 
    224 TEST_F(ObjectManagerTest, InitialObject) {
    225   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
    226       ObjectPath("/org/chromium/TestObject"));
    227   EXPECT_TRUE(object_proxy != NULL);
    228 
    229   Properties* properties = static_cast<Properties*>(
    230       object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
    231                                      "org.chromium.TestInterface"));
    232   EXPECT_TRUE(properties != NULL);
    233 
    234   EXPECT_EQ("TestService", properties->name.value());
    235   EXPECT_EQ(10, properties->version.value());
    236 
    237   std::vector<std::string> methods = properties->methods.value();
    238   ASSERT_EQ(4U, methods.size());
    239   EXPECT_EQ("Echo", methods[0]);
    240   EXPECT_EQ("SlowEcho", methods[1]);
    241   EXPECT_EQ("AsyncEcho", methods[2]);
    242   EXPECT_EQ("BrokenMethod", methods[3]);
    243 
    244   std::vector<ObjectPath> objects = properties->objects.value();
    245   ASSERT_EQ(1U, objects.size());
    246   EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
    247 }
    248 
    249 TEST_F(ObjectManagerTest, UnknownObjectProxy) {
    250   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
    251       ObjectPath("/org/chromium/UnknownObject"));
    252   EXPECT_TRUE(object_proxy == NULL);
    253 }
    254 
    255 TEST_F(ObjectManagerTest, UnknownObjectProperties) {
    256   Properties* properties = static_cast<Properties*>(
    257       object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
    258                                      "org.chromium.TestInterface"));
    259   EXPECT_TRUE(properties == NULL);
    260 }
    261 
    262 TEST_F(ObjectManagerTest, UnknownInterfaceProperties) {
    263   Properties* properties = static_cast<Properties*>(
    264       object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
    265                                      "org.chromium.UnknownService"));
    266   EXPECT_TRUE(properties == NULL);
    267 }
    268 
    269 TEST_F(ObjectManagerTest, GetObjects) {
    270   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
    271   ASSERT_EQ(1U, object_paths.size());
    272   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
    273 }
    274 
    275 TEST_F(ObjectManagerTest, GetObjectsWithInterface) {
    276   std::vector<ObjectPath> object_paths =
    277       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
    278   ASSERT_EQ(1U, object_paths.size());
    279   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
    280 }
    281 
    282 TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) {
    283   std::vector<ObjectPath> object_paths =
    284       object_manager_->GetObjectsWithInterface("org.chromium.UnknownService");
    285   EXPECT_EQ(0U, object_paths.size());
    286 }
    287 
    288 TEST_F(ObjectManagerTest, SameObject) {
    289   ObjectManager* object_manager = bus_->GetObjectManager(
    290       "org.chromium.TestService",
    291       ObjectPath("/org/chromium/TestService"));
    292   EXPECT_EQ(object_manager_, object_manager);
    293 }
    294 
    295 TEST_F(ObjectManagerTest, DifferentObjectForService) {
    296   ObjectManager* object_manager = bus_->GetObjectManager(
    297       "org.chromium.DifferentService",
    298       ObjectPath("/org/chromium/TestService"));
    299   EXPECT_NE(object_manager_, object_manager);
    300 }
    301 
    302 TEST_F(ObjectManagerTest, DifferentObjectForPath) {
    303   ObjectManager* object_manager = bus_->GetObjectManager(
    304       "org.chromium.TestService",
    305       ObjectPath("/org/chromium/DifferentService"));
    306   EXPECT_NE(object_manager_, object_manager);
    307 }
    308 
    309 TEST_F(ObjectManagerTest, SecondObject) {
    310   PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
    311   WaitForObject();
    312 
    313   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
    314       ObjectPath("/org/chromium/SecondObject"));
    315   EXPECT_TRUE(object_proxy != NULL);
    316 
    317   Properties* properties = static_cast<Properties*>(
    318       object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
    319                                      "org.chromium.TestInterface"));
    320   EXPECT_TRUE(properties != NULL);
    321 
    322   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
    323   ASSERT_EQ(2U, object_paths.size());
    324 
    325   std::sort(object_paths.begin(), object_paths.end());
    326   EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
    327   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
    328 
    329   object_paths =
    330       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
    331   ASSERT_EQ(2U, object_paths.size());
    332 
    333   std::sort(object_paths.begin(), object_paths.end());
    334   EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
    335   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
    336 }
    337 
    338 TEST_F(ObjectManagerTest, RemoveSecondObject) {
    339   PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
    340   WaitForObject();
    341 
    342   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
    343   ASSERT_EQ(2U, object_paths.size());
    344 
    345   PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
    346   WaitForRemoveObject();
    347 
    348   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
    349       ObjectPath("/org/chromium/SecondObject"));
    350   EXPECT_TRUE(object_proxy == NULL);
    351 
    352   Properties* properties = static_cast<Properties*>(
    353       object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
    354                                      "org.chromium.TestInterface"));
    355   EXPECT_TRUE(properties == NULL);
    356 
    357   object_paths = object_manager_->GetObjects();
    358   ASSERT_EQ(1U, object_paths.size());
    359   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
    360 
    361   object_paths =
    362       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
    363   ASSERT_EQ(1U, object_paths.size());
    364   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
    365 }
    366 
    367 TEST_F(ObjectManagerTest, OwnershipLost) {
    368   PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
    369   WaitForRemoveObject();
    370 
    371   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
    372   ASSERT_EQ(0U, object_paths.size());
    373 }
    374 
    375 TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
    376   PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
    377   WaitForRemoveObject();
    378   WaitForObject();
    379 
    380   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
    381   ASSERT_EQ(1U, object_paths.size());
    382 }
    383 
    384 TEST_F(ObjectManagerTest, PropertiesChangedAsObjectsReceived) {
    385   // Remove the existing object manager.
    386   object_manager_->UnregisterInterface("org.chromium.TestInterface");
    387   run_loop_.reset(new base::RunLoop);
    388   EXPECT_TRUE(bus_->RemoveObjectManager(
    389       "org.chromium.TestService",
    390       ObjectPath("/org/chromium/TestService"),
    391       run_loop_->QuitClosure()));
    392   run_loop_->Run();
    393 
    394   PerformAction("SetSendImmediatePropertiesChanged",
    395                 ObjectPath("/org/chromium/TestService"));
    396 
    397   object_manager_ = bus_->GetObjectManager(
    398       "org.chromium.TestService",
    399       ObjectPath("/org/chromium/TestService"));
    400   object_manager_->RegisterInterface("org.chromium.TestInterface", this);
    401 
    402   // The newly created object manager should call GetManagedObjects immediately
    403   // after setting up the match rule for PropertiesChanged. We should process
    404   // the PropertiesChanged event right after that. If we don't receive it within
    405   // 2 seconds, then fail the test.
    406   message_loop_.PostDelayedTask(
    407       FROM_HERE,
    408       base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout,
    409                  base::Unretained(this)),
    410       base::TimeDelta::FromSeconds(2));
    411 
    412   while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) {
    413     run_loop_.reset(new base::RunLoop);
    414     run_loop_->Run();
    415   }
    416 }
    417 
    418 }  // namespace dbus
    419