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 "base/bind.h"
6#include "base/logging.h"
7#include "base/memory/ref_counted.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "dbus/message.h"
12#include "dbus/mock_bus.h"
13#include "dbus/mock_exported_object.h"
14#include "dbus/mock_object_proxy.h"
15#include "dbus/object_path.h"
16#include "dbus/scoped_dbus_error.h"
17#include "testing/gmock/include/gmock/gmock.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using ::testing::_;
21using ::testing::Invoke;
22using ::testing::Return;
23using ::testing::Unused;
24
25namespace dbus {
26
27class MockTest : public testing::Test {
28 public:
29  MockTest() {
30  }
31
32  virtual void SetUp() {
33    // Create a mock bus.
34    Bus::Options options;
35    options.bus_type = Bus::SYSTEM;
36    mock_bus_ = new MockBus(options);
37
38    // Create a mock proxy.
39    mock_proxy_ = new MockObjectProxy(
40        mock_bus_.get(),
41        "org.chromium.TestService",
42        ObjectPath("/org/chromium/TestObject"));
43
44    // Set an expectation so mock_proxy's CallMethodAndBlock() will use
45    // CreateMockProxyResponse() to return responses.
46    EXPECT_CALL(*mock_proxy_.get(), MockCallMethodAndBlock(_, _))
47        .WillRepeatedly(Invoke(this, &MockTest::CreateMockProxyResponse));
48    EXPECT_CALL(*mock_proxy_.get(),
49                MockCallMethodAndBlockWithErrorDetails(_, _, _))
50        .WillRepeatedly(
51            Invoke(this, &MockTest::CreateMockProxyResponseWithErrorDetails));
52
53    // Set an expectation so mock_proxy's CallMethod() will use
54    // HandleMockProxyResponseWithMessageLoop() to return responses.
55    EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _)).WillRepeatedly(
56        Invoke(this, &MockTest::HandleMockProxyResponseWithMessageLoop));
57
58    // Set an expectation so mock_bus's GetObjectProxy() for the given
59    // service name and the object path will return mock_proxy_.
60    EXPECT_CALL(*mock_bus_.get(),
61                GetObjectProxy("org.chromium.TestService",
62                               ObjectPath("/org/chromium/TestObject")))
63        .WillOnce(Return(mock_proxy_.get()));
64
65    // ShutdownAndBlock() will be called in TearDown().
66    EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
67  }
68
69  virtual void TearDown() {
70    mock_bus_->ShutdownAndBlock();
71  }
72
73  // Called when the response is received.
74  void OnResponse(Response* response) {
75    // |response| will be deleted on exit of the function. Copy the
76    // payload to |response_string_|.
77    if (response) {
78      MessageReader reader(response);
79      ASSERT_TRUE(reader.PopString(&response_string_));
80    }
81    run_loop_->Quit();
82  };
83
84 protected:
85  std::string response_string_;
86  base::MessageLoop message_loop_;
87  scoped_ptr<base::RunLoop> run_loop_;
88  scoped_refptr<MockBus> mock_bus_;
89  scoped_refptr<MockObjectProxy> mock_proxy_;
90
91 private:
92  // Returns a response for the given method call. Used to implement
93  // CallMethodAndBlock() for |mock_proxy_|.
94  Response* CreateMockProxyResponse(MethodCall* method_call,
95                                    int timeout_ms) {
96    if (method_call->GetInterface() == "org.chromium.TestInterface" &&
97        method_call->GetMember() == "Echo") {
98      MessageReader reader(method_call);
99      std::string text_message;
100      if (reader.PopString(&text_message)) {
101        scoped_ptr<Response> response = Response::CreateEmpty();
102        MessageWriter writer(response.get());
103        writer.AppendString(text_message);
104        return response.release();
105      }
106    }
107
108    LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
109    return NULL;
110  }
111
112  Response* CreateMockProxyResponseWithErrorDetails(
113      MethodCall* method_call, int timeout_ms, ScopedDBusError* error) {
114    dbus_set_error(error->get(), DBUS_ERROR_NOT_SUPPORTED, "Not implemented");
115    return NULL;
116  }
117
118  // Creates a response and runs the given response callback in the
119  // message loop with the response. Used to implement for |mock_proxy_|.
120  void HandleMockProxyResponseWithMessageLoop(
121      MethodCall* method_call,
122      int timeout_ms,
123      ObjectProxy::ResponseCallback response_callback) {
124    Response* response = CreateMockProxyResponse(method_call, timeout_ms);
125    message_loop_.PostTask(FROM_HERE,
126                           base::Bind(&MockTest::RunResponseCallback,
127                                      base::Unretained(this),
128                                      response_callback,
129                                      response));
130  }
131
132  // Runs the given response callback with the given response.
133  void RunResponseCallback(
134      ObjectProxy::ResponseCallback response_callback,
135      Response* response) {
136    response_callback.Run(response);
137    delete response;
138  }
139};
140
141// This test demonstrates how to mock a synchronous method call using the
142// mock classes.
143TEST_F(MockTest, CallMethodAndBlock) {
144  const char kHello[] = "Hello";
145  // Get an object proxy from the mock bus.
146  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
147      "org.chromium.TestService",
148      ObjectPath("/org/chromium/TestObject"));
149
150  // Create a method call.
151  MethodCall method_call("org.chromium.TestInterface", "Echo");
152  MessageWriter writer(&method_call);
153  writer.AppendString(kHello);
154
155  // Call the method.
156  scoped_ptr<Response> response(
157      proxy->CallMethodAndBlock(&method_call,
158                                ObjectProxy::TIMEOUT_USE_DEFAULT));
159
160  // Check the response.
161  ASSERT_TRUE(response.get());
162  MessageReader reader(response.get());
163  std::string text_message;
164  ASSERT_TRUE(reader.PopString(&text_message));
165  // The text message should be echo'ed back.
166  EXPECT_EQ(kHello, text_message);
167}
168
169TEST_F(MockTest, CallMethodAndBlockWithErrorDetails) {
170  // Get an object proxy from the mock bus.
171  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
172      "org.chromium.TestService",
173      ObjectPath("/org/chromium/TestObject"));
174
175  // Create a method call.
176  MethodCall method_call("org.chromium.TestInterface", "Echo");
177
178  ScopedDBusError error;
179  // Call the method.
180  scoped_ptr<Response> response(
181      proxy->CallMethodAndBlockWithErrorDetails(
182          &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT, &error));
183
184  // Check the response.
185  ASSERT_FALSE(response.get());
186  ASSERT_TRUE(error.is_set());
187  EXPECT_STREQ(DBUS_ERROR_NOT_SUPPORTED, error.name());
188  EXPECT_STREQ("Not implemented", error.message());
189}
190
191// This test demonstrates how to mock an asynchronous method call using the
192// mock classes.
193TEST_F(MockTest, CallMethod) {
194  const char kHello[] = "hello";
195
196  // Get an object proxy from the mock bus.
197  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
198      "org.chromium.TestService",
199      ObjectPath("/org/chromium/TestObject"));
200
201  // Create a method call.
202  MethodCall method_call("org.chromium.TestInterface", "Echo");
203  MessageWriter writer(&method_call);
204  writer.AppendString(kHello);
205
206  // Call the method.
207  run_loop_.reset(new base::RunLoop);
208  proxy->CallMethod(&method_call,
209                    ObjectProxy::TIMEOUT_USE_DEFAULT,
210                    base::Bind(&MockTest::OnResponse,
211                               base::Unretained(this)));
212  // Run the message loop to let OnResponse be called.
213  run_loop_->Run();
214
215  EXPECT_EQ(kHello, response_string_);
216}
217
218}  // namespace dbus
219