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
24namespace dbus {
25
26namespace {
27
28// See comments in ObjectProxy::RunResponseCallback() for why the number was
29// chosen.
30const 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.
36class 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
280TEST_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
297TEST_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.
316TEST_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
339TEST_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
356TEST_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
376TEST_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
397TEST_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
414TEST_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.
433TEST_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
450TEST_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
461TEST_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
473TEST_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
484TEST_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
496TEST_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
515TEST_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
534TEST_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
557TEST_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
567TEST_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
581TEST_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
591TEST_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
601class 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
646TEST_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