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/threading/thread.h" 14 #include "base/threading/thread_restrictions.h" 15 #include "dbus/bus.h" 16 #include "dbus/object_path.h" 17 #include "dbus/object_proxy.h" 18 #include "dbus/property.h" 19 #include "dbus/test_service.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 namespace dbus { 23 24 // The object manager test exercises the asynchronous APIs in ObjectManager, 25 // and by extension PropertySet and Property<>. 26 class ObjectManagerTest 27 : public testing::Test, 28 public ObjectManager::Interface { 29 public: 30 ObjectManagerTest() { 31 } 32 33 struct Properties : public PropertySet { 34 Property<std::string> name; 35 Property<int16> version; 36 Property<std::vector<std::string> > methods; 37 Property<std::vector<ObjectPath> > objects; 38 39 Properties(ObjectProxy* object_proxy, 40 const std::string& interface_name, 41 PropertyChangedCallback property_changed_callback) 42 : PropertySet(object_proxy, interface_name, property_changed_callback) { 43 RegisterProperty("Name", &name); 44 RegisterProperty("Version", &version); 45 RegisterProperty("Methods", &methods); 46 RegisterProperty("Objects", &objects); 47 } 48 }; 49 50 virtual PropertySet* CreateProperties( 51 ObjectProxy* object_proxy, 52 const ObjectPath& object_path, 53 const std::string& interface_name) OVERRIDE { 54 Properties* properties = new Properties( 55 object_proxy, interface_name, 56 base::Bind(&ObjectManagerTest::OnPropertyChanged, 57 base::Unretained(this), object_path)); 58 return static_cast<PropertySet*>(properties); 59 } 60 61 virtual void SetUp() { 62 // Make the main thread not to allow IO. 63 base::ThreadRestrictions::SetIOAllowed(false); 64 65 // Start the D-Bus thread. 66 dbus_thread_.reset(new base::Thread("D-Bus Thread")); 67 base::Thread::Options thread_options; 68 thread_options.message_loop_type = base::MessageLoop::TYPE_IO; 69 ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options)); 70 71 // Start the test service, using the D-Bus thread. 72 TestService::Options options; 73 options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 74 test_service_.reset(new TestService(options)); 75 ASSERT_TRUE(test_service_->StartService()); 76 ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted()); 77 ASSERT_TRUE(test_service_->HasDBusThread()); 78 79 // Create the client, using the D-Bus thread. 80 Bus::Options bus_options; 81 bus_options.bus_type = Bus::SESSION; 82 bus_options.connection_type = Bus::PRIVATE; 83 bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 84 bus_ = new Bus(bus_options); 85 ASSERT_TRUE(bus_->HasDBusThread()); 86 87 object_manager_ = bus_->GetObjectManager( 88 "org.chromium.TestService", 89 ObjectPath("/org/chromium/TestService")); 90 object_manager_->RegisterInterface("org.chromium.TestInterface", this); 91 92 object_manager_->GetManagedObjects(); 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 110 void MethodCallback(Response* response) { 111 method_callback_called_ = true; 112 message_loop_.Quit(); 113 } 114 115 protected: 116 // Called when an object is added. 117 virtual void ObjectAdded(const ObjectPath& object_path, 118 const std::string& interface_name) OVERRIDE { 119 added_objects_.push_back(std::make_pair(object_path, interface_name)); 120 message_loop_.Quit(); 121 } 122 123 // Called when an object is removed. 124 virtual void ObjectRemoved(const ObjectPath& object_path, 125 const std::string& interface_name) OVERRIDE { 126 removed_objects_.push_back(std::make_pair(object_path, interface_name)); 127 message_loop_.Quit(); 128 } 129 130 // Called when a property value is updated. 131 void OnPropertyChanged(const ObjectPath& object_path, 132 const std::string& name) { 133 updated_properties_.push_back(name); 134 message_loop_.Quit(); 135 } 136 137 static const size_t kExpectedObjects = 1; 138 static const size_t kExpectedProperties = 4; 139 140 void WaitForObject() { 141 while (added_objects_.size() < kExpectedObjects || 142 updated_properties_.size() < kExpectedProperties) 143 message_loop_.Run(); 144 for (size_t i = 0; i < kExpectedObjects; ++i) 145 added_objects_.erase(added_objects_.begin()); 146 for (size_t i = 0; i < kExpectedProperties; ++i) 147 updated_properties_.erase(updated_properties_.begin()); 148 } 149 150 void WaitForRemoveObject() { 151 while (removed_objects_.size() < kExpectedObjects) 152 message_loop_.Run(); 153 for (size_t i = 0; i < kExpectedObjects; ++i) 154 removed_objects_.erase(removed_objects_.begin()); 155 } 156 157 void WaitForMethodCallback() { 158 message_loop_.Run(); 159 method_callback_called_ = false; 160 } 161 162 void PerformAction(const std::string& action, const ObjectPath& object_path) { 163 ObjectProxy* object_proxy = bus_->GetObjectProxy( 164 "org.chromium.TestService", 165 ObjectPath("/org/chromium/TestObject")); 166 167 MethodCall method_call("org.chromium.TestInterface", "PerformAction"); 168 MessageWriter writer(&method_call); 169 writer.AppendString(action); 170 writer.AppendObjectPath(object_path); 171 172 object_proxy->CallMethod(&method_call, 173 ObjectProxy::TIMEOUT_USE_DEFAULT, 174 base::Bind(&ObjectManagerTest::MethodCallback, 175 base::Unretained(this))); 176 WaitForMethodCallback(); 177 } 178 179 base::MessageLoop message_loop_; 180 scoped_ptr<base::Thread> dbus_thread_; 181 scoped_refptr<Bus> bus_; 182 ObjectManager* object_manager_; 183 scoped_ptr<TestService> test_service_; 184 185 std::vector<std::pair<ObjectPath, std::string> > added_objects_; 186 std::vector<std::pair<ObjectPath, std::string> > removed_objects_; 187 std::vector<std::string> updated_properties_; 188 189 bool method_callback_called_; 190 }; 191 192 193 TEST_F(ObjectManagerTest, InitialObject) { 194 ObjectProxy* object_proxy = object_manager_->GetObjectProxy( 195 ObjectPath("/org/chromium/TestObject")); 196 EXPECT_TRUE(object_proxy != NULL); 197 198 Properties* properties = static_cast<Properties*>( 199 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"), 200 "org.chromium.TestInterface")); 201 EXPECT_TRUE(properties != NULL); 202 203 EXPECT_EQ("TestService", properties->name.value()); 204 EXPECT_EQ(10, properties->version.value()); 205 206 std::vector<std::string> methods = properties->methods.value(); 207 ASSERT_EQ(4U, methods.size()); 208 EXPECT_EQ("Echo", methods[0]); 209 EXPECT_EQ("SlowEcho", methods[1]); 210 EXPECT_EQ("AsyncEcho", methods[2]); 211 EXPECT_EQ("BrokenMethod", methods[3]); 212 213 std::vector<ObjectPath> objects = properties->objects.value(); 214 ASSERT_EQ(1U, objects.size()); 215 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]); 216 } 217 218 TEST_F(ObjectManagerTest, UnknownObjectProxy) { 219 ObjectProxy* object_proxy = object_manager_->GetObjectProxy( 220 ObjectPath("/org/chromium/UnknownObject")); 221 EXPECT_TRUE(object_proxy == NULL); 222 } 223 224 TEST_F(ObjectManagerTest, UnknownObjectProperties) { 225 Properties* properties = static_cast<Properties*>( 226 object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"), 227 "org.chromium.TestInterface")); 228 EXPECT_TRUE(properties == NULL); 229 } 230 231 TEST_F(ObjectManagerTest, UnknownInterfaceProperties) { 232 Properties* properties = static_cast<Properties*>( 233 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"), 234 "org.chromium.UnknownService")); 235 EXPECT_TRUE(properties == NULL); 236 } 237 238 TEST_F(ObjectManagerTest, GetObjects) { 239 std::vector<ObjectPath> object_paths = object_manager_->GetObjects(); 240 ASSERT_EQ(1U, object_paths.size()); 241 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]); 242 } 243 244 TEST_F(ObjectManagerTest, GetObjectsWithInterface) { 245 std::vector<ObjectPath> object_paths = 246 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface"); 247 ASSERT_EQ(1U, object_paths.size()); 248 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]); 249 } 250 251 TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) { 252 std::vector<ObjectPath> object_paths = 253 object_manager_->GetObjectsWithInterface("org.chromium.UnknownService"); 254 EXPECT_EQ(0U, object_paths.size()); 255 } 256 257 TEST_F(ObjectManagerTest, SameObject) { 258 ObjectManager* object_manager = bus_->GetObjectManager( 259 "org.chromium.TestService", 260 ObjectPath("/org/chromium/TestService")); 261 EXPECT_EQ(object_manager_, object_manager); 262 } 263 264 TEST_F(ObjectManagerTest, DifferentObjectForService) { 265 ObjectManager* object_manager = bus_->GetObjectManager( 266 "org.chromium.DifferentService", 267 ObjectPath("/org/chromium/TestService")); 268 EXPECT_NE(object_manager_, object_manager); 269 } 270 271 TEST_F(ObjectManagerTest, DifferentObjectForPath) { 272 ObjectManager* object_manager = bus_->GetObjectManager( 273 "org.chromium.TestService", 274 ObjectPath("/org/chromium/DifferentService")); 275 EXPECT_NE(object_manager_, object_manager); 276 } 277 278 TEST_F(ObjectManagerTest, SecondObject) { 279 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject")); 280 WaitForObject(); 281 282 ObjectProxy* object_proxy = object_manager_->GetObjectProxy( 283 ObjectPath("/org/chromium/SecondObject")); 284 EXPECT_TRUE(object_proxy != NULL); 285 286 Properties* properties = static_cast<Properties*>( 287 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"), 288 "org.chromium.TestInterface")); 289 EXPECT_TRUE(properties != NULL); 290 291 std::vector<ObjectPath> object_paths = object_manager_->GetObjects(); 292 ASSERT_EQ(2U, object_paths.size()); 293 294 std::sort(object_paths.begin(), object_paths.end()); 295 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]); 296 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]); 297 298 object_paths = 299 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface"); 300 ASSERT_EQ(2U, object_paths.size()); 301 302 std::sort(object_paths.begin(), object_paths.end()); 303 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]); 304 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]); 305 } 306 307 TEST_F(ObjectManagerTest, RemoveSecondObject) { 308 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject")); 309 WaitForObject(); 310 311 std::vector<ObjectPath> object_paths = object_manager_->GetObjects(); 312 ASSERT_EQ(2U, object_paths.size()); 313 314 PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject")); 315 WaitForRemoveObject(); 316 317 ObjectProxy* object_proxy = object_manager_->GetObjectProxy( 318 ObjectPath("/org/chromium/SecondObject")); 319 EXPECT_TRUE(object_proxy == NULL); 320 321 Properties* properties = static_cast<Properties*>( 322 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"), 323 "org.chromium.TestInterface")); 324 EXPECT_TRUE(properties == NULL); 325 326 object_paths = object_manager_->GetObjects(); 327 ASSERT_EQ(1U, object_paths.size()); 328 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]); 329 330 object_paths = 331 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface"); 332 ASSERT_EQ(1U, object_paths.size()); 333 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]); 334 } 335 336 } // namespace dbus 337