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 <algorithm> 6 #include <string> 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/run_loop.h" 13 #include "base/stl_util.h" 14 #include "base/test/test_timeouts.h" 15 #include "base/threading/thread.h" 16 #include "base/threading/thread_restrictions.h" 17 #include "dbus/bus.h" 18 #include "dbus/message.h" 19 #include "dbus/object_path.h" 20 #include "dbus/object_proxy.h" 21 #include "dbus/test_service.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 24 namespace dbus { 25 26 namespace { 27 28 // See comments in ObjectProxy::RunResponseCallback() for why the number was 29 // chosen. 30 const int kHugePayloadSize = 64 << 20; // 64 MB 31 32 } // namespace 33 34 // The end-to-end test exercises the asynchronous APIs in ObjectProxy and 35 // ExportedObject. 36 class EndToEndAsyncTest : public testing::Test { 37 public: 38 EndToEndAsyncTest() : on_disconnected_call_count_(0) {} 39 40 virtual void SetUp() { 41 // Make the main thread not to allow IO. 42 base::ThreadRestrictions::SetIOAllowed(false); 43 44 // Start the D-Bus thread. 45 dbus_thread_.reset(new base::Thread("D-Bus Thread")); 46 base::Thread::Options thread_options; 47 thread_options.message_loop_type = base::MessageLoop::TYPE_IO; 48 ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options)); 49 50 // Start the test service, using the D-Bus thread. 51 TestService::Options options; 52 options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 53 test_service_.reset(new TestService(options)); 54 ASSERT_TRUE(test_service_->StartService()); 55 ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted()); 56 ASSERT_TRUE(test_service_->HasDBusThread()); 57 58 // Create the client, using the D-Bus thread. 59 Bus::Options bus_options; 60 bus_options.bus_type = Bus::SESSION; 61 bus_options.connection_type = Bus::PRIVATE; 62 bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 63 bus_options.disconnected_callback = 64 base::Bind(&EndToEndAsyncTest::OnDisconnected, base::Unretained(this)); 65 bus_ = new Bus(bus_options); 66 object_proxy_ = bus_->GetObjectProxy( 67 "org.chromium.TestService", 68 ObjectPath("/org/chromium/TestObject")); 69 ASSERT_TRUE(bus_->HasDBusThread()); 70 71 // Connect to the "Test" signal of "org.chromium.TestInterface" from 72 // the remote object. 73 object_proxy_->ConnectToSignal( 74 "org.chromium.TestInterface", 75 "Test", 76 base::Bind(&EndToEndAsyncTest::OnTestSignal, 77 base::Unretained(this)), 78 base::Bind(&EndToEndAsyncTest::OnConnected, 79 base::Unretained(this))); 80 // Wait until the object proxy is connected to the signal. 81 run_loop_.reset(new base::RunLoop()); 82 run_loop_->Run(); 83 84 // Connect to the "Test2" signal of "org.chromium.TestInterface" from 85 // the remote object. There was a bug where we were emitting error 86 // messages like "Requested to remove an unknown match rule: ..." at 87 // the shutdown of Bus when an object proxy is connected to more than 88 // one signal of the same interface. See crosbug.com/23382 for details. 89 object_proxy_->ConnectToSignal( 90 "org.chromium.TestInterface", 91 "Test2", 92 base::Bind(&EndToEndAsyncTest::OnTest2Signal, 93 base::Unretained(this)), 94 base::Bind(&EndToEndAsyncTest::OnConnected, 95 base::Unretained(this))); 96 // Wait until the object proxy is connected to the signal. 97 run_loop_.reset(new base::RunLoop()); 98 run_loop_->Run(); 99 100 // Create a second object proxy for the root object. 101 root_object_proxy_ = bus_->GetObjectProxy("org.chromium.TestService", 102 ObjectPath("/")); 103 ASSERT_TRUE(bus_->HasDBusThread()); 104 105 // Connect to the "Test" signal of "org.chromium.TestInterface" from 106 // the root remote object too. 107 root_object_proxy_->ConnectToSignal( 108 "org.chromium.TestInterface", 109 "Test", 110 base::Bind(&EndToEndAsyncTest::OnRootTestSignal, 111 base::Unretained(this)), 112 base::Bind(&EndToEndAsyncTest::OnConnected, 113 base::Unretained(this))); 114 // Wait until the root object proxy is connected to the signal. 115 run_loop_.reset(new base::RunLoop()); 116 run_loop_->Run(); 117 } 118 119 virtual void TearDown() { 120 bus_->ShutdownOnDBusThreadAndBlock(); 121 122 // Shut down the service. 123 test_service_->ShutdownAndBlock(); 124 125 // Reset to the default. 126 base::ThreadRestrictions::SetIOAllowed(true); 127 128 // Stopping a thread is considered an IO operation, so do this after 129 // allowing IO. 130 test_service_->Stop(); 131 } 132 133 protected: 134 // Replaces the bus with a broken one. 135 void SetUpBrokenBus() { 136 // Shut down the existing bus. 137 bus_->ShutdownOnDBusThreadAndBlock(); 138 139 // Create new bus with invalid address. 140 const char kInvalidAddress[] = ""; 141 Bus::Options bus_options; 142 bus_options.bus_type = Bus::CUSTOM_ADDRESS; 143 bus_options.address = kInvalidAddress; 144 bus_options.connection_type = Bus::PRIVATE; 145 bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 146 bus_ = new Bus(bus_options); 147 ASSERT_TRUE(bus_->HasDBusThread()); 148 149 // Create new object proxy. 150 object_proxy_ = bus_->GetObjectProxy( 151 "org.chromium.TestService", 152 ObjectPath("/org/chromium/TestObject")); 153 } 154 155 // Calls the method asynchronously. OnResponse() will be called once the 156 // response is received. 157 void CallMethod(MethodCall* method_call, 158 int timeout_ms) { 159 object_proxy_->CallMethod(method_call, 160 timeout_ms, 161 base::Bind(&EndToEndAsyncTest::OnResponse, 162 base::Unretained(this))); 163 } 164 165 // Calls the method asynchronously. OnResponse() will be called once the 166 // response is received without error, otherwise OnError() will be called. 167 void CallMethodWithErrorCallback(MethodCall* method_call, 168 int timeout_ms) { 169 object_proxy_->CallMethodWithErrorCallback( 170 method_call, 171 timeout_ms, 172 base::Bind(&EndToEndAsyncTest::OnResponse, base::Unretained(this)), 173 base::Bind(&EndToEndAsyncTest::OnError, base::Unretained(this))); 174 } 175 176 // Wait for the give number of responses. 177 void WaitForResponses(size_t num_responses) { 178 while (response_strings_.size() < num_responses) { 179 run_loop_.reset(new base::RunLoop); 180 run_loop_->Run(); 181 } 182 } 183 184 // Called when the response is received. 185 void OnResponse(Response* response) { 186 // |response| will be deleted on exit of the function. Copy the 187 // payload to |response_strings_|. 188 if (response) { 189 MessageReader reader(response); 190 std::string response_string; 191 ASSERT_TRUE(reader.PopString(&response_string)); 192 response_strings_.push_back(response_string); 193 } else { 194 response_strings_.push_back(std::string()); 195 } 196 run_loop_->Quit(); 197 }; 198 199 // Wait for the given number of errors. 200 void WaitForErrors(size_t num_errors) { 201 while (error_names_.size() < num_errors) { 202 run_loop_.reset(new base::RunLoop); 203 run_loop_->Run(); 204 } 205 } 206 207 // Called when an error is received. 208 void OnError(ErrorResponse* error) { 209 // |error| will be deleted on exit of the function. Copy the payload to 210 // |error_names_|. 211 if (error) { 212 ASSERT_NE("", error->GetErrorName()); 213 error_names_.push_back(error->GetErrorName()); 214 } else { 215 error_names_.push_back(std::string()); 216 } 217 run_loop_->Quit(); 218 } 219 220 // Called when the "Test" signal is received, in the main thread. 221 // Copy the string payload to |test_signal_string_|. 222 void OnTestSignal(Signal* signal) { 223 MessageReader reader(signal); 224 ASSERT_TRUE(reader.PopString(&test_signal_string_)); 225 run_loop_->Quit(); 226 } 227 228 // Called when the "Test" signal is received, in the main thread, by 229 // the root object proxy. Copy the string payload to 230 // |root_test_signal_string_|. 231 void OnRootTestSignal(Signal* signal) { 232 MessageReader reader(signal); 233 ASSERT_TRUE(reader.PopString(&root_test_signal_string_)); 234 run_loop_->Quit(); 235 } 236 237 // Called when the "Test2" signal is received, in the main thread. 238 void OnTest2Signal(Signal* signal) { 239 MessageReader reader(signal); 240 run_loop_->Quit(); 241 } 242 243 // Called when connected to the signal. 244 void OnConnected(const std::string& interface_name, 245 const std::string& signal_name, 246 bool success) { 247 ASSERT_TRUE(success); 248 run_loop_->Quit(); 249 } 250 251 // Called when the connection with dbus-daemon is disconnected. 252 void OnDisconnected() { 253 run_loop_->Quit(); 254 ++on_disconnected_call_count_; 255 } 256 257 // Wait for the hey signal to be received. 258 void WaitForTestSignal() { 259 // OnTestSignal() will quit the message loop. 260 run_loop_.reset(new base::RunLoop); 261 run_loop_->Run(); 262 } 263 264 base::MessageLoop message_loop_; 265 scoped_ptr<base::RunLoop> run_loop_; 266 std::vector<std::string> response_strings_; 267 std::vector<std::string> error_names_; 268 scoped_ptr<base::Thread> dbus_thread_; 269 scoped_refptr<Bus> bus_; 270 ObjectProxy* object_proxy_; 271 ObjectProxy* root_object_proxy_; 272 scoped_ptr<TestService> test_service_; 273 // Text message from "Test" signal. 274 std::string test_signal_string_; 275 // Text message from "Test" signal delivered to root. 276 std::string root_test_signal_string_; 277 int on_disconnected_call_count_; 278 }; 279 280 TEST_F(EndToEndAsyncTest, Echo) { 281 const char* kHello = "hello"; 282 283 // Create the method call. 284 MethodCall method_call("org.chromium.TestInterface", "Echo"); 285 MessageWriter writer(&method_call); 286 writer.AppendString(kHello); 287 288 // Call the method. 289 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 290 CallMethod(&method_call, timeout_ms); 291 292 // Check the response. 293 WaitForResponses(1); 294 EXPECT_EQ(kHello, response_strings_[0]); 295 } 296 297 TEST_F(EndToEndAsyncTest, EchoWithErrorCallback) { 298 const char* kHello = "hello"; 299 300 // Create the method call. 301 MethodCall method_call("org.chromium.TestInterface", "Echo"); 302 MessageWriter writer(&method_call); 303 writer.AppendString(kHello); 304 305 // Call the method. 306 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 307 CallMethodWithErrorCallback(&method_call, timeout_ms); 308 309 // Check the response. 310 WaitForResponses(1); 311 EXPECT_EQ(kHello, response_strings_[0]); 312 EXPECT_TRUE(error_names_.empty()); 313 } 314 315 // Call Echo method three times. 316 TEST_F(EndToEndAsyncTest, EchoThreeTimes) { 317 const char* kMessages[] = { "foo", "bar", "baz" }; 318 319 for (size_t i = 0; i < arraysize(kMessages); ++i) { 320 // Create the method call. 321 MethodCall method_call("org.chromium.TestInterface", "Echo"); 322 MessageWriter writer(&method_call); 323 writer.AppendString(kMessages[i]); 324 325 // Call the method. 326 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 327 CallMethod(&method_call, timeout_ms); 328 } 329 330 // Check the responses. 331 WaitForResponses(3); 332 // Sort as the order of the returned messages is not deterministic. 333 std::sort(response_strings_.begin(), response_strings_.end()); 334 EXPECT_EQ("bar", response_strings_[0]); 335 EXPECT_EQ("baz", response_strings_[1]); 336 EXPECT_EQ("foo", response_strings_[2]); 337 } 338 339 TEST_F(EndToEndAsyncTest, Echo_HugePayload) { 340 const std::string kHugePayload(kHugePayloadSize, 'o'); 341 342 // Create the method call with a huge payload. 343 MethodCall method_call("org.chromium.TestInterface", "Echo"); 344 MessageWriter writer(&method_call); 345 writer.AppendString(kHugePayload); 346 347 // Call the method. 348 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 349 CallMethod(&method_call, timeout_ms); 350 351 // This caused a DCHECK failure before. Ensure that the issue is fixed. 352 WaitForResponses(1); 353 EXPECT_EQ(kHugePayload, response_strings_[0]); 354 } 355 356 TEST_F(EndToEndAsyncTest, BrokenBus) { 357 const char* kHello = "hello"; 358 359 // Set up a broken bus. 360 SetUpBrokenBus(); 361 362 // Create the method call. 363 MethodCall method_call("org.chromium.TestInterface", "Echo"); 364 MessageWriter writer(&method_call); 365 writer.AppendString(kHello); 366 367 // Call the method. 368 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 369 CallMethod(&method_call, timeout_ms); 370 WaitForResponses(1); 371 372 // Should fail because of the broken bus. 373 ASSERT_EQ("", response_strings_[0]); 374 } 375 376 TEST_F(EndToEndAsyncTest, BrokenBusWithErrorCallback) { 377 const char* kHello = "hello"; 378 379 // Set up a broken bus. 380 SetUpBrokenBus(); 381 382 // Create the method call. 383 MethodCall method_call("org.chromium.TestInterface", "Echo"); 384 MessageWriter writer(&method_call); 385 writer.AppendString(kHello); 386 387 // Call the method. 388 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 389 CallMethodWithErrorCallback(&method_call, timeout_ms); 390 WaitForErrors(1); 391 392 // Should fail because of the broken bus. 393 ASSERT_TRUE(response_strings_.empty()); 394 ASSERT_EQ("", error_names_[0]); 395 } 396 397 TEST_F(EndToEndAsyncTest, Timeout) { 398 const char* kHello = "hello"; 399 400 // Create the method call. 401 MethodCall method_call("org.chromium.TestInterface", "SlowEcho"); 402 MessageWriter writer(&method_call); 403 writer.AppendString(kHello); 404 405 // Call the method with timeout of 0ms. 406 const int timeout_ms = 0; 407 CallMethod(&method_call, timeout_ms); 408 WaitForResponses(1); 409 410 // Should fail because of timeout. 411 ASSERT_EQ("", response_strings_[0]); 412 } 413 414 TEST_F(EndToEndAsyncTest, TimeoutWithErrorCallback) { 415 const char* kHello = "hello"; 416 417 // Create the method call. 418 MethodCall method_call("org.chromium.TestInterface", "SlowEcho"); 419 MessageWriter writer(&method_call); 420 writer.AppendString(kHello); 421 422 // Call the method with timeout of 0ms. 423 const int timeout_ms = 0; 424 CallMethodWithErrorCallback(&method_call, timeout_ms); 425 WaitForErrors(1); 426 427 // Should fail because of timeout. 428 ASSERT_TRUE(response_strings_.empty()); 429 ASSERT_EQ(DBUS_ERROR_NO_REPLY, error_names_[0]); 430 } 431 432 // Tests calling a method that sends its reply asynchronously. 433 TEST_F(EndToEndAsyncTest, AsyncEcho) { 434 const char* kHello = "hello"; 435 436 // Create the method call. 437 MethodCall method_call("org.chromium.TestInterface", "AsyncEcho"); 438 MessageWriter writer(&method_call); 439 writer.AppendString(kHello); 440 441 // Call the method. 442 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 443 CallMethod(&method_call, timeout_ms); 444 445 // Check the response. 446 WaitForResponses(1); 447 EXPECT_EQ(kHello, response_strings_[0]); 448 } 449 450 TEST_F(EndToEndAsyncTest, NonexistentMethod) { 451 MethodCall method_call("org.chromium.TestInterface", "Nonexistent"); 452 453 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 454 CallMethod(&method_call, timeout_ms); 455 WaitForResponses(1); 456 457 // Should fail because the method is nonexistent. 458 ASSERT_EQ("", response_strings_[0]); 459 } 460 461 TEST_F(EndToEndAsyncTest, NonexistentMethodWithErrorCallback) { 462 MethodCall method_call("org.chromium.TestInterface", "Nonexistent"); 463 464 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 465 CallMethodWithErrorCallback(&method_call, timeout_ms); 466 WaitForErrors(1); 467 468 // Should fail because the method is nonexistent. 469 ASSERT_TRUE(response_strings_.empty()); 470 ASSERT_EQ(DBUS_ERROR_UNKNOWN_METHOD, error_names_[0]); 471 } 472 473 TEST_F(EndToEndAsyncTest, BrokenMethod) { 474 MethodCall method_call("org.chromium.TestInterface", "BrokenMethod"); 475 476 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 477 CallMethod(&method_call, timeout_ms); 478 WaitForResponses(1); 479 480 // Should fail because the method is broken. 481 ASSERT_EQ("", response_strings_[0]); 482 } 483 484 TEST_F(EndToEndAsyncTest, BrokenMethodWithErrorCallback) { 485 MethodCall method_call("org.chromium.TestInterface", "BrokenMethod"); 486 487 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 488 CallMethodWithErrorCallback(&method_call, timeout_ms); 489 WaitForErrors(1); 490 491 // Should fail because the method is broken. 492 ASSERT_TRUE(response_strings_.empty()); 493 ASSERT_EQ(DBUS_ERROR_FAILED, error_names_[0]); 494 } 495 496 TEST_F(EndToEndAsyncTest, InvalidObjectPath) { 497 // Trailing '/' is only allowed for the root path. 498 const ObjectPath invalid_object_path("/org/chromium/TestObject/"); 499 500 // Replace object proxy with new one. 501 object_proxy_ = bus_->GetObjectProxy("org.chromium.TestService", 502 invalid_object_path); 503 504 MethodCall method_call("org.chromium.TestInterface", "Echo"); 505 506 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 507 CallMethodWithErrorCallback(&method_call, timeout_ms); 508 WaitForErrors(1); 509 510 // Should fail because of the invalid path. 511 ASSERT_TRUE(response_strings_.empty()); 512 ASSERT_EQ("", error_names_[0]); 513 } 514 515 TEST_F(EndToEndAsyncTest, InvalidServiceName) { 516 // Bus name cannot contain '/'. 517 const std::string invalid_service_name = ":1/2"; 518 519 // Replace object proxy with new one. 520 object_proxy_ = bus_->GetObjectProxy( 521 invalid_service_name, ObjectPath("org.chromium.TestObject")); 522 523 MethodCall method_call("org.chromium.TestInterface", "Echo"); 524 525 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 526 CallMethodWithErrorCallback(&method_call, timeout_ms); 527 WaitForErrors(1); 528 529 // Should fail because of the invalid bus name. 530 ASSERT_TRUE(response_strings_.empty()); 531 ASSERT_EQ("", error_names_[0]); 532 } 533 534 TEST_F(EndToEndAsyncTest, EmptyResponseCallback) { 535 const char* kHello = "hello"; 536 537 // Create the method call. 538 MethodCall method_call("org.chromium.TestInterface", "Echo"); 539 MessageWriter writer(&method_call); 540 writer.AppendString(kHello); 541 542 // Call the method with an empty callback. 543 const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT; 544 object_proxy_->CallMethod(&method_call, 545 timeout_ms, 546 ObjectProxy::EmptyResponseCallback()); 547 // Post a delayed task to quit the message loop. 548 run_loop_.reset(new base::RunLoop); 549 message_loop_.PostDelayedTask(FROM_HERE, 550 run_loop_->QuitClosure(), 551 TestTimeouts::tiny_timeout()); 552 run_loop_->Run(); 553 // We cannot tell if the empty callback is called, but at least we can 554 // check if the test does not crash. 555 } 556 557 TEST_F(EndToEndAsyncTest, TestSignal) { 558 const char kMessage[] = "hello, world"; 559 // Send the test signal from the exported object. 560 test_service_->SendTestSignal(kMessage); 561 // Receive the signal with the object proxy. The signal is handled in 562 // EndToEndAsyncTest::OnTestSignal() in the main thread. 563 WaitForTestSignal(); 564 ASSERT_EQ(kMessage, test_signal_string_); 565 } 566 567 TEST_F(EndToEndAsyncTest, TestSignalFromRoot) { 568 const char kMessage[] = "hello, world"; 569 // Object proxies are tied to a particular object path, if a signal 570 // arrives from a different object path like "/" the first object proxy 571 // |object_proxy_| should not handle it, and should leave it for the root 572 // object proxy |root_object_proxy_|. 573 test_service_->SendTestSignalFromRoot(kMessage); 574 WaitForTestSignal(); 575 // Verify the signal was not received by the specific proxy. 576 ASSERT_TRUE(test_signal_string_.empty()); 577 // Verify the string WAS received by the root proxy. 578 ASSERT_EQ(kMessage, root_test_signal_string_); 579 } 580 581 TEST_F(EndToEndAsyncTest, TestHugeSignal) { 582 const std::string kHugeMessage(kHugePayloadSize, 'o'); 583 584 // Send the huge signal from the exported object. 585 test_service_->SendTestSignal(kHugeMessage); 586 // This caused a DCHECK failure before. Ensure that the issue is fixed. 587 WaitForTestSignal(); 588 ASSERT_EQ(kHugeMessage, test_signal_string_); 589 } 590 591 TEST_F(EndToEndAsyncTest, DisconnectedSignal) { 592 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, 593 base::Bind(&Bus::ClosePrivateConnection, 594 base::Unretained(bus_.get()))); 595 // OnDisconnected callback quits message loop. 596 run_loop_.reset(new base::RunLoop); 597 run_loop_->Run(); 598 EXPECT_EQ(1, on_disconnected_call_count_); 599 } 600 601 class SignalMultipleHandlerTest : public EndToEndAsyncTest { 602 public: 603 SignalMultipleHandlerTest() { 604 } 605 606 virtual void SetUp() { 607 // Set up base class. 608 EndToEndAsyncTest::SetUp(); 609 610 // Connect the root object proxy's signal handler to a new handler 611 // so that we can verify that a second call to ConnectSignal() delivers 612 // to both our new handler and the old. 613 object_proxy_->ConnectToSignal( 614 "org.chromium.TestInterface", 615 "Test", 616 base::Bind(&SignalMultipleHandlerTest::OnAdditionalTestSignal, 617 base::Unretained(this)), 618 base::Bind(&SignalMultipleHandlerTest::OnAdditionalConnected, 619 base::Unretained(this))); 620 // Wait until the object proxy is connected to the signal. 621 run_loop_.reset(new base::RunLoop); 622 run_loop_->Run(); 623 } 624 625 protected: 626 // Called when the "Test" signal is received, in the main thread. 627 // Copy the string payload to |additional_test_signal_string_|. 628 void OnAdditionalTestSignal(Signal* signal) { 629 MessageReader reader(signal); 630 ASSERT_TRUE(reader.PopString(&additional_test_signal_string_)); 631 run_loop_->Quit(); 632 } 633 634 // Called when connected to the signal. 635 void OnAdditionalConnected(const std::string& interface_name, 636 const std::string& signal_name, 637 bool success) { 638 ASSERT_TRUE(success); 639 run_loop_->Quit(); 640 } 641 642 // Text message from "Test" signal delivered to additional handler. 643 std::string additional_test_signal_string_; 644 }; 645 646 TEST_F(SignalMultipleHandlerTest, TestMultipleHandlers) { 647 const char kMessage[] = "hello, world"; 648 // Send the test signal from the exported object. 649 test_service_->SendTestSignal(kMessage); 650 // Receive the signal with the object proxy. 651 WaitForTestSignal(); 652 // Verify the string WAS received by the original handler. 653 ASSERT_EQ(kMessage, test_signal_string_); 654 // Verify the signal WAS ALSO received by the additional handler. 655 ASSERT_EQ(kMessage, additional_test_signal_string_); 656 } 657 658 } // namespace dbus 659