• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium OS 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 <brillo/dbus/dbus_method_invoker.h>
6 
7 #include <string>
8 
9 #include <base/bind.h>
10 #include <dbus/mock_bus.h>
11 #include <dbus/mock_object_proxy.h>
12 #include <dbus/scoped_dbus_error.h>
13 #include <gmock/gmock.h>
14 #include <gtest/gtest.h>
15 
16 #include "brillo/dbus/test.pb.h"
17 
18 using testing::AnyNumber;
19 using testing::InSequence;
20 using testing::Invoke;
21 using testing::Return;
22 using testing::_;
23 
24 using dbus::MessageReader;
25 using dbus::MessageWriter;
26 using dbus::Response;
27 
28 namespace {
29 
SuccessCallback(const std::string & expected_result,int * counter,const std::string & actual_result)30 void SuccessCallback(const std::string& expected_result,
31                      int* counter,
32                      const std::string& actual_result) {
33   (*counter)++;
34   EXPECT_EQ(expected_result, actual_result);
35 }
36 
SimpleSuccessCallback(int * counter,const std::string & result)37 void SimpleSuccessCallback(int* counter, const std::string& result) {
38   (*counter)++;
39 }
40 
ErrorCallback(const std::string & domain,const std::string & code,const std::string & message,int * counter,brillo::Error * error)41 void ErrorCallback(const std::string& domain,
42                    const std::string& code,
43                    const std::string& message,
44                    int* counter,
45                    brillo::Error* error) {
46   (*counter)++;
47   ASSERT_NE(nullptr, error);
48   EXPECT_EQ(domain, error->GetDomain());
49   EXPECT_EQ(code, error->GetCode());
50   EXPECT_EQ(message, error->GetMessage());
51 }
52 
SimpleErrorCallback(int * counter,brillo::Error * error)53 void SimpleErrorCallback(int* counter, brillo::Error* error) {
54   (*counter)++;
55 }
56 
57 }  // namespace
58 
59 namespace brillo {
60 namespace dbus_utils {
61 
62 const char kTestPath[] = "/test/path";
63 const char kTestServiceName[] = "org.test.Object";
64 const char kTestInterface[] = "org.test.Object.TestInterface";
65 const char kTestMethod1[] = "TestMethod1";
66 const char kTestMethod2[] = "TestMethod2";
67 const char kTestMethod3[] = "TestMethod3";
68 const char kTestMethod4[] = "TestMethod4";
69 
70 class DBusMethodInvokerTest : public testing::Test {
71  public:
SetUp()72   void SetUp() override {
73     dbus::Bus::Options options;
74     options.bus_type = dbus::Bus::SYSTEM;
75     bus_ = new dbus::MockBus(options);
76     // By default, don't worry about threading assertions.
77     EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
78     EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
79     // Use a mock exported object.
80     mock_object_proxy_ = new dbus::MockObjectProxy(
81         bus_.get(), kTestServiceName, dbus::ObjectPath(kTestPath));
82     EXPECT_CALL(*bus_,
83                 GetObjectProxy(kTestServiceName, dbus::ObjectPath(kTestPath)))
84         .WillRepeatedly(Return(mock_object_proxy_.get()));
85     int def_timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT;
86     EXPECT_CALL(*mock_object_proxy_,
87                 CallMethodAndBlockWithErrorDetails(_, def_timeout_ms, _))
88         .WillRepeatedly(Invoke(this, &DBusMethodInvokerTest::CreateResponse));
89   }
90 
TearDown()91   void TearDown() override { bus_ = nullptr; }
92 
CreateResponse(dbus::MethodCall * method_call,int,dbus::ScopedDBusError * dbus_error)93   std::unique_ptr<Response> CreateResponse(dbus::MethodCall* method_call,
94                                            int /* timeout_ms */,
95                                            dbus::ScopedDBusError* dbus_error) {
96     if (method_call->GetInterface() == kTestInterface) {
97       if (method_call->GetMember() == kTestMethod1) {
98         MessageReader reader(method_call);
99         int v1, v2;
100         // Input: two ints.
101         // Output: sum of the ints converted to string.
102         if (reader.PopInt32(&v1) && reader.PopInt32(&v2)) {
103           auto response = Response::CreateEmpty();
104           MessageWriter writer(response.get());
105           writer.AppendString(std::to_string(v1 + v2));
106           return response;
107         }
108       } else if (method_call->GetMember() == kTestMethod2) {
109         method_call->SetSerial(123);
110         dbus_set_error(dbus_error->get(), "org.MyError", "My error message");
111         return std::unique_ptr<dbus::Response>();
112       } else if (method_call->GetMember() == kTestMethod3) {
113         MessageReader reader(method_call);
114         dbus_utils_test::TestMessage msg;
115         if (PopValueFromReader(&reader, &msg)) {
116           auto response = Response::CreateEmpty();
117           MessageWriter writer(response.get());
118           AppendValueToWriter(&writer, msg);
119           return response;
120         }
121       } else if (method_call->GetMember() == kTestMethod4) {
122         method_call->SetSerial(123);
123         MessageReader reader(method_call);
124         base::ScopedFD fd;
125         if (reader.PopFileDescriptor(&fd)) {
126           auto response = Response::CreateEmpty();
127           MessageWriter writer(response.get());
128           writer.AppendFileDescriptor(fd.get());
129           return response;
130         }
131       }
132     }
133 
134     LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
135     return std::unique_ptr<dbus::Response>();
136   }
137 
CallTestMethod(int v1,int v2)138   std::string CallTestMethod(int v1, int v2) {
139     std::unique_ptr<dbus::Response> response =
140         brillo::dbus_utils::CallMethodAndBlock(mock_object_proxy_.get(),
141                                                kTestInterface, kTestMethod1,
142                                                nullptr, v1, v2);
143     EXPECT_NE(nullptr, response.get());
144     std::string result;
145     using brillo::dbus_utils::ExtractMethodCallResults;
146     EXPECT_TRUE(ExtractMethodCallResults(response.get(), nullptr, &result));
147     return result;
148   }
149 
CallProtobufTestMethod(const dbus_utils_test::TestMessage & message)150   dbus_utils_test::TestMessage CallProtobufTestMethod(
151       const dbus_utils_test::TestMessage& message) {
152     std::unique_ptr<dbus::Response> response =
153         brillo::dbus_utils::CallMethodAndBlock(mock_object_proxy_.get(),
154                                                kTestInterface, kTestMethod3,
155                                                nullptr, message);
156     EXPECT_NE(nullptr, response.get());
157     dbus_utils_test::TestMessage result;
158     using brillo::dbus_utils::ExtractMethodCallResults;
159     EXPECT_TRUE(ExtractMethodCallResults(response.get(), nullptr, &result));
160     return result;
161   }
162 
163   // Sends a file descriptor received over D-Bus back to the caller using the
164   // new types.
EchoFD(int fd_in)165   base::ScopedFD EchoFD(int fd_in) {
166     std::unique_ptr<dbus::Response> response =
167         brillo::dbus_utils::CallMethodAndBlock(
168             mock_object_proxy_.get(), kTestInterface, kTestMethod4,
169             nullptr, brillo::dbus_utils::FileDescriptor{fd_in});
170     EXPECT_NE(nullptr, response.get());
171     base::ScopedFD fd_out;
172     using brillo::dbus_utils::ExtractMethodCallResults;
173     EXPECT_TRUE(ExtractMethodCallResults(response.get(), nullptr, &fd_out));
174     return fd_out;
175   }
176   scoped_refptr<dbus::MockBus> bus_;
177   scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_;
178 };
179 
TEST_F(DBusMethodInvokerTest,TestSuccess)180 TEST_F(DBusMethodInvokerTest, TestSuccess) {
181   EXPECT_EQ("4", CallTestMethod(2, 2));
182   EXPECT_EQ("10", CallTestMethod(3, 7));
183   EXPECT_EQ("-4", CallTestMethod(13, -17));
184 }
185 
TEST_F(DBusMethodInvokerTest,TestFailure)186 TEST_F(DBusMethodInvokerTest, TestFailure) {
187   brillo::ErrorPtr error;
188   std::unique_ptr<dbus::Response> response =
189       brillo::dbus_utils::CallMethodAndBlock(
190           mock_object_proxy_.get(), kTestInterface, kTestMethod2, &error);
191   EXPECT_EQ(nullptr, response.get());
192   EXPECT_EQ(brillo::errors::dbus::kDomain, error->GetDomain());
193   EXPECT_EQ("org.MyError", error->GetCode());
194   EXPECT_EQ("My error message", error->GetMessage());
195 }
196 
TEST_F(DBusMethodInvokerTest,TestProtobuf)197 TEST_F(DBusMethodInvokerTest, TestProtobuf) {
198   dbus_utils_test::TestMessage test_message;
199   test_message.set_foo(123);
200   test_message.set_bar("bar");
201 
202   dbus_utils_test::TestMessage resp = CallProtobufTestMethod(test_message);
203 
204   EXPECT_EQ(123, resp.foo());
205   EXPECT_EQ("bar", resp.bar());
206 }
207 
TEST_F(DBusMethodInvokerTest,TestFileDescriptors)208 TEST_F(DBusMethodInvokerTest, TestFileDescriptors) {
209   // Passing a file descriptor over D-Bus would effectively duplicate the fd.
210   // So the resulting file descriptor value would be different but it still
211   // should be valid.
212   int fd_stdin = 0;
213   base::ScopedFD out_fd = EchoFD(fd_stdin);
214   EXPECT_NE(fd_stdin, out_fd.get());
215   EXPECT_TRUE(out_fd.is_valid());
216   int fd_stdout = 1;
217   out_fd = EchoFD(fd_stdout);
218   EXPECT_NE(fd_stdout, out_fd.get());
219   EXPECT_TRUE(out_fd.is_valid());
220   int fd_stderr = 2;
221   out_fd = EchoFD(fd_stderr);
222   EXPECT_NE(fd_stderr, out_fd.get());
223   EXPECT_TRUE(out_fd.is_valid());
224 }
225 
226 //////////////////////////////////////////////////////////////////////////////
227 // Asynchronous method invocation support
228 
229 class AsyncDBusMethodInvokerTest : public testing::Test {
230  public:
SetUp()231   void SetUp() override {
232     dbus::Bus::Options options;
233     options.bus_type = dbus::Bus::SYSTEM;
234     bus_ = new dbus::MockBus(options);
235     // By default, don't worry about threading assertions.
236     EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
237     EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
238     // Use a mock exported object.
239     mock_object_proxy_ = new dbus::MockObjectProxy(
240         bus_.get(), kTestServiceName, dbus::ObjectPath(kTestPath));
241     EXPECT_CALL(*bus_,
242                 GetObjectProxy(kTestServiceName, dbus::ObjectPath(kTestPath)))
243         .WillRepeatedly(Return(mock_object_proxy_.get()));
244     int def_timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT;
245     EXPECT_CALL(*mock_object_proxy_,
246                 DoCallMethodWithErrorCallback(_, def_timeout_ms, _, _))
247         .WillRepeatedly(Invoke(this, &AsyncDBusMethodInvokerTest::HandleCall));
248   }
249 
TearDown()250   void TearDown() override { bus_ = nullptr; }
251 
HandleCall(dbus::MethodCall * method_call,int,dbus::ObjectProxy::ResponseCallback * success_callback,dbus::ObjectProxy::ErrorCallback * error_callback)252   void HandleCall(dbus::MethodCall* method_call,
253                   int /* timeout_ms */,
254                   dbus::ObjectProxy::ResponseCallback* success_callback,
255                   dbus::ObjectProxy::ErrorCallback* error_callback) {
256     if (method_call->GetInterface() == kTestInterface) {
257       if (method_call->GetMember() == kTestMethod1) {
258         MessageReader reader(method_call);
259         int v1, v2;
260         // Input: two ints.
261         // Output: sum of the ints converted to string.
262         if (reader.PopInt32(&v1) && reader.PopInt32(&v2)) {
263           auto response = Response::CreateEmpty();
264           MessageWriter writer(response.get());
265           writer.AppendString(std::to_string(v1 + v2));
266           std::move(*success_callback).Run(response.get());
267         }
268         return;
269       } else if (method_call->GetMember() == kTestMethod2) {
270         method_call->SetSerial(123);
271         auto error_response = dbus::ErrorResponse::FromMethodCall(
272             method_call, "org.MyError", "My error message");
273         std::move(*error_callback).Run(error_response.get());
274         return;
275       }
276     }
277 
278     LOG(FATAL) << "Unexpected method call: " << method_call->ToString();
279   }
280 
281   scoped_refptr<dbus::MockBus> bus_;
282   scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_;
283 };
284 
TEST_F(AsyncDBusMethodInvokerTest,TestSuccess)285 TEST_F(AsyncDBusMethodInvokerTest, TestSuccess) {
286   int error_count = 0;
287   int success_count = 0;
288   brillo::dbus_utils::CallMethod(
289       mock_object_proxy_.get(),
290       kTestInterface,
291       kTestMethod1,
292       base::Bind(SuccessCallback, "4", &success_count),
293       base::Bind(SimpleErrorCallback, &error_count),
294       2, 2);
295   brillo::dbus_utils::CallMethod(
296       mock_object_proxy_.get(),
297       kTestInterface,
298       kTestMethod1,
299       base::Bind(SuccessCallback, "10", &success_count),
300       base::Bind(SimpleErrorCallback, &error_count),
301       3, 7);
302   brillo::dbus_utils::CallMethod(
303       mock_object_proxy_.get(),
304       kTestInterface,
305       kTestMethod1,
306       base::Bind(SuccessCallback, "-4", &success_count),
307       base::Bind(SimpleErrorCallback, &error_count),
308       13, -17);
309   EXPECT_EQ(0, error_count);
310   EXPECT_EQ(3, success_count);
311 }
312 
TEST_F(AsyncDBusMethodInvokerTest,TestFailure)313 TEST_F(AsyncDBusMethodInvokerTest, TestFailure) {
314   int error_count = 0;
315   int success_count = 0;
316   brillo::dbus_utils::CallMethod(
317       mock_object_proxy_.get(),
318       kTestInterface,
319       kTestMethod2,
320       base::Bind(SimpleSuccessCallback, &success_count),
321       base::Bind(ErrorCallback,
322                  brillo::errors::dbus::kDomain,
323                  "org.MyError",
324                  "My error message",
325                  &error_count),
326       2, 2);
327   EXPECT_EQ(1, error_count);
328   EXPECT_EQ(0, success_count);
329 }
330 
331 }  // namespace dbus_utils
332 }  // namespace brillo
333