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