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