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