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