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