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