• 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_object.h>
6 
7 #include <memory>
8 
9 #include <base/bind.h>
10 #include <brillo/dbus/dbus_object_test_helpers.h>
11 #include <brillo/dbus/mock_exported_object_manager.h>
12 #include <dbus/message.h>
13 #include <dbus/property.h>
14 #include <dbus/object_path.h>
15 #include <dbus/mock_bus.h>
16 #include <dbus/mock_exported_object.h>
17 
18 using ::testing::AnyNumber;
19 using ::testing::Return;
20 using ::testing::Invoke;
21 using ::testing::Mock;
22 using ::testing::_;
23 
24 namespace brillo {
25 namespace dbus_utils {
26 
27 namespace {
28 
29 const char kMethodsExportedOn[] = "/export";
30 
31 const char kTestInterface1[] = "org.chromium.Test.MathInterface";
32 const char kTestMethod_Add[] = "Add";
33 const char kTestMethod_Negate[] = "Negate";
34 const char kTestMethod_Positive[] = "Positive";
35 const char kTestMethod_AddSubtract[] = "AddSubtract";
36 
37 const char kTestInterface2[] = "org.chromium.Test.StringInterface";
38 const char kTestMethod_StrLen[] = "StrLen";
39 const char kTestMethod_CheckNonEmpty[] = "CheckNonEmpty";
40 
41 const char kTestInterface3[] = "org.chromium.Test.NoOpInterface";
42 const char kTestMethod_NoOp[] = "NoOp";
43 const char kTestMethod_WithMessage[] = "TestWithMessage";
44 const char kTestMethod_WithMessageAsync[] = "TestWithMessageAsync";
45 
46 const char kTestInterface4[] = "org.chromium.Test.LateInterface";
47 
48 struct Calc {
Addbrillo::dbus_utils::__anon1a02b28d0111::Calc49   int Add(int x, int y) { return x + y; }
Negatebrillo::dbus_utils::__anon1a02b28d0111::Calc50   int Negate(int x) { return -x; }
Positivebrillo::dbus_utils::__anon1a02b28d0111::Calc51   void Positive(std::unique_ptr<DBusMethodResponse<double>> response,
52                 double x) {
53     if (x >= 0.0) {
54       response->Return(x);
55       return;
56     }
57     ErrorPtr error;
58     Error::AddTo(&error, FROM_HERE, "test", "not_positive",
59                  "Negative value passed in");
60     response->ReplyWithError(error.get());
61   }
AddSubtractbrillo::dbus_utils::__anon1a02b28d0111::Calc62   void AddSubtract(int x, int y, int* sum, int* diff) {
63     *sum = x + y;
64     *diff = x - y;
65   }
66 };
67 
StrLen(const std::string & str)68 int StrLen(const std::string& str) {
69   return str.size();
70 }
71 
CheckNonEmpty(ErrorPtr * error,const std::string & str)72 bool CheckNonEmpty(ErrorPtr* error, const std::string& str) {
73   if (!str.empty())
74     return true;
75   Error::AddTo(error, FROM_HERE, "test", "string_empty", "String is empty");
76   return false;
77 }
78 
NoOp()79 void NoOp() {}
80 
TestWithMessage(ErrorPtr *,dbus::Message * message,std::string * str)81 bool TestWithMessage(ErrorPtr* /* error */,
82                      dbus::Message* message,
83                      std::string* str) {
84   *str = message->GetSender();
85   return true;
86 }
87 
TestWithMessageAsync(std::unique_ptr<DBusMethodResponse<std::string>> response,dbus::Message * message)88 void TestWithMessageAsync(
89     std::unique_ptr<DBusMethodResponse<std::string>> response,
90     dbus::Message* message) {
91   response->Return(message->GetSender());
92 }
93 
OnInterfaceExported(bool success)94 void OnInterfaceExported(bool success) {
95   // Does nothing.
96 }
97 
98 }  // namespace
99 
100 class DBusObjectTest : public ::testing::Test {
101  public:
SetUp()102   virtual void SetUp() {
103     dbus::Bus::Options options;
104     options.bus_type = dbus::Bus::SYSTEM;
105     bus_ = new dbus::MockBus(options);
106     // By default, don't worry about threading assertions.
107     EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
108     EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
109     // Use a mock exported object.
110     const dbus::ObjectPath kMethodsExportedOnPath{
111         std::string{kMethodsExportedOn}};
112     mock_exported_object_ =
113         new dbus::MockExportedObject(bus_.get(), kMethodsExportedOnPath);
114     EXPECT_CALL(*bus_, GetExportedObject(kMethodsExportedOnPath))
115         .Times(AnyNumber())
116         .WillRepeatedly(Return(mock_exported_object_.get()));
117     EXPECT_CALL(*mock_exported_object_, ExportMethod(_, _, _, _))
118         .Times(AnyNumber());
119     EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
120 
121     dbus_object_ = std::unique_ptr<DBusObject>(
122         new DBusObject(nullptr, bus_, kMethodsExportedOnPath));
123 
124     DBusInterface* itf1 = dbus_object_->AddOrGetInterface(kTestInterface1);
125     itf1->AddSimpleMethodHandler(
126         kTestMethod_Add, base::Unretained(&calc_), &Calc::Add);
127     itf1->AddSimpleMethodHandler(
128         kTestMethod_Negate, base::Unretained(&calc_), &Calc::Negate);
129     itf1->AddMethodHandler(
130         kTestMethod_Positive, base::Unretained(&calc_), &Calc::Positive);
131     itf1->AddSimpleMethodHandler(
132         kTestMethod_AddSubtract, base::Unretained(&calc_), &Calc::AddSubtract);
133     DBusInterface* itf2 = dbus_object_->AddOrGetInterface(kTestInterface2);
134     itf2->AddSimpleMethodHandler(kTestMethod_StrLen, StrLen);
135     itf2->AddSimpleMethodHandlerWithError(kTestMethod_CheckNonEmpty,
136                                           CheckNonEmpty);
137     DBusInterface* itf3 = dbus_object_->AddOrGetInterface(kTestInterface3);
138     base::Callback<void()> noop_callback = base::Bind(NoOp);
139     itf3->AddSimpleMethodHandler(kTestMethod_NoOp, noop_callback);
140     itf3->AddSimpleMethodHandlerWithErrorAndMessage(
141         kTestMethod_WithMessage, base::Bind(&TestWithMessage));
142     itf3->AddMethodHandlerWithMessage(kTestMethod_WithMessageAsync,
143                                       base::Bind(&TestWithMessageAsync));
144 
145     dbus_object_->RegisterAsync(
146         AsyncEventSequencer::GetDefaultCompletionAction());
147   }
148 
ExpectError(dbus::Response * response,const std::string & expected_code)149   void ExpectError(dbus::Response* response, const std::string& expected_code) {
150     EXPECT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
151     EXPECT_EQ(expected_code, response->GetErrorName());
152   }
153 
154   scoped_refptr<dbus::MockBus> bus_;
155   scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
156   std::unique_ptr<DBusObject> dbus_object_;
157   Calc calc_;
158 };
159 
TEST_F(DBusObjectTest,Add)160 TEST_F(DBusObjectTest, Add) {
161   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
162   method_call.SetSerial(123);
163   dbus::MessageWriter writer(&method_call);
164   writer.AppendInt32(2);
165   writer.AppendInt32(3);
166   auto response = testing::CallMethod(*dbus_object_, &method_call);
167   dbus::MessageReader reader(response.get());
168   int result;
169   ASSERT_TRUE(reader.PopInt32(&result));
170   ASSERT_FALSE(reader.HasMoreData());
171   ASSERT_EQ(5, result);
172 }
173 
TEST_F(DBusObjectTest,Negate)174 TEST_F(DBusObjectTest, Negate) {
175   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Negate);
176   method_call.SetSerial(123);
177   dbus::MessageWriter writer(&method_call);
178   writer.AppendInt32(98765);
179   auto response = testing::CallMethod(*dbus_object_, &method_call);
180   dbus::MessageReader reader(response.get());
181   int result;
182   ASSERT_TRUE(reader.PopInt32(&result));
183   ASSERT_FALSE(reader.HasMoreData());
184   ASSERT_EQ(-98765, result);
185 }
186 
TEST_F(DBusObjectTest,PositiveSuccess)187 TEST_F(DBusObjectTest, PositiveSuccess) {
188   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Positive);
189   method_call.SetSerial(123);
190   dbus::MessageWriter writer(&method_call);
191   writer.AppendDouble(17.5);
192   auto response = testing::CallMethod(*dbus_object_, &method_call);
193   dbus::MessageReader reader(response.get());
194   double result;
195   ASSERT_TRUE(reader.PopDouble(&result));
196   ASSERT_FALSE(reader.HasMoreData());
197   ASSERT_DOUBLE_EQ(17.5, result);
198 }
199 
TEST_F(DBusObjectTest,PositiveFailure)200 TEST_F(DBusObjectTest, PositiveFailure) {
201   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Positive);
202   method_call.SetSerial(123);
203   dbus::MessageWriter writer(&method_call);
204   writer.AppendDouble(-23.2);
205   auto response = testing::CallMethod(*dbus_object_, &method_call);
206   ExpectError(response.get(), DBUS_ERROR_FAILED);
207 }
208 
TEST_F(DBusObjectTest,AddSubtract)209 TEST_F(DBusObjectTest, AddSubtract) {
210   dbus::MethodCall method_call(kTestInterface1, kTestMethod_AddSubtract);
211   method_call.SetSerial(123);
212   dbus::MessageWriter writer(&method_call);
213   writer.AppendInt32(2);
214   writer.AppendInt32(3);
215   auto response = testing::CallMethod(*dbus_object_, &method_call);
216   dbus::MessageReader reader(response.get());
217   int sum = 0, diff = 0;
218   ASSERT_TRUE(reader.PopInt32(&sum));
219   ASSERT_TRUE(reader.PopInt32(&diff));
220   ASSERT_FALSE(reader.HasMoreData());
221   EXPECT_EQ(5, sum);
222   EXPECT_EQ(-1, diff);
223 }
224 
TEST_F(DBusObjectTest,StrLen0)225 TEST_F(DBusObjectTest, StrLen0) {
226   dbus::MethodCall method_call(kTestInterface2, kTestMethod_StrLen);
227   method_call.SetSerial(123);
228   dbus::MessageWriter writer(&method_call);
229   writer.AppendString("");
230   auto response = testing::CallMethod(*dbus_object_, &method_call);
231   dbus::MessageReader reader(response.get());
232   int result;
233   ASSERT_TRUE(reader.PopInt32(&result));
234   ASSERT_FALSE(reader.HasMoreData());
235   ASSERT_EQ(0, result);
236 }
237 
TEST_F(DBusObjectTest,StrLen4)238 TEST_F(DBusObjectTest, StrLen4) {
239   dbus::MethodCall method_call(kTestInterface2, kTestMethod_StrLen);
240   method_call.SetSerial(123);
241   dbus::MessageWriter writer(&method_call);
242   writer.AppendString("test");
243   auto response = testing::CallMethod(*dbus_object_, &method_call);
244   dbus::MessageReader reader(response.get());
245   int result;
246   ASSERT_TRUE(reader.PopInt32(&result));
247   ASSERT_FALSE(reader.HasMoreData());
248   ASSERT_EQ(4, result);
249 }
250 
TEST_F(DBusObjectTest,CheckNonEmpty_Success)251 TEST_F(DBusObjectTest, CheckNonEmpty_Success) {
252   dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
253   method_call.SetSerial(123);
254   dbus::MessageWriter writer(&method_call);
255   writer.AppendString("test");
256   auto response = testing::CallMethod(*dbus_object_, &method_call);
257   ASSERT_EQ(dbus::Message::MESSAGE_METHOD_RETURN, response->GetMessageType());
258   dbus::MessageReader reader(response.get());
259   EXPECT_FALSE(reader.HasMoreData());
260 }
261 
TEST_F(DBusObjectTest,CheckNonEmpty_Failure)262 TEST_F(DBusObjectTest, CheckNonEmpty_Failure) {
263   dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
264   method_call.SetSerial(123);
265   dbus::MessageWriter writer(&method_call);
266   writer.AppendString("");
267   auto response = testing::CallMethod(*dbus_object_, &method_call);
268   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
269   ErrorPtr error;
270   ExtractMethodCallResults(response.get(), &error);
271   ASSERT_NE(nullptr, error.get());
272   EXPECT_EQ("test", error->GetDomain());
273   EXPECT_EQ("string_empty", error->GetCode());
274   EXPECT_EQ("String is empty", error->GetMessage());
275 }
276 
TEST_F(DBusObjectTest,CheckNonEmpty_MissingParams)277 TEST_F(DBusObjectTest, CheckNonEmpty_MissingParams) {
278   dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
279   method_call.SetSerial(123);
280   auto response = testing::CallMethod(*dbus_object_, &method_call);
281   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
282   dbus::MessageReader reader(response.get());
283   std::string message;
284   ASSERT_TRUE(reader.PopString(&message));
285   EXPECT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
286   EXPECT_EQ("Too few parameters in a method call", message);
287   EXPECT_FALSE(reader.HasMoreData());
288 }
289 
TEST_F(DBusObjectTest,NoOp)290 TEST_F(DBusObjectTest, NoOp) {
291   dbus::MethodCall method_call(kTestInterface3, kTestMethod_NoOp);
292   method_call.SetSerial(123);
293   auto response = testing::CallMethod(*dbus_object_, &method_call);
294   dbus::MessageReader reader(response.get());
295   ASSERT_FALSE(reader.HasMoreData());
296 }
297 
TEST_F(DBusObjectTest,TestWithMessage)298 TEST_F(DBusObjectTest, TestWithMessage) {
299   const std::string sender{":1.2345"};
300   dbus::MethodCall method_call(kTestInterface3, kTestMethod_WithMessage);
301   method_call.SetSerial(123);
302   method_call.SetSender(sender);
303   auto response = testing::CallMethod(*dbus_object_, &method_call);
304   dbus::MessageReader reader(response.get());
305   std::string message;
306   ASSERT_TRUE(reader.PopString(&message));
307   ASSERT_FALSE(reader.HasMoreData());
308   EXPECT_EQ(sender, message);
309 }
310 
TEST_F(DBusObjectTest,TestWithMessageAsync)311 TEST_F(DBusObjectTest, TestWithMessageAsync) {
312   const std::string sender{":6.7890"};
313   dbus::MethodCall method_call(kTestInterface3, kTestMethod_WithMessageAsync);
314   method_call.SetSerial(123);
315   method_call.SetSender(sender);
316   auto response = testing::CallMethod(*dbus_object_, &method_call);
317   dbus::MessageReader reader(response.get());
318   std::string message;
319   ASSERT_TRUE(reader.PopString(&message));
320   ASSERT_FALSE(reader.HasMoreData());
321   EXPECT_EQ(sender, message);
322 }
323 
TEST_F(DBusObjectTest,TestRemovedInterface)324 TEST_F(DBusObjectTest, TestRemovedInterface) {
325   // Removes the interface to be tested.
326   dbus_object_->RemoveInterface(kTestInterface3);
327 
328   const std::string sender{":1.2345"};
329   dbus::MethodCall method_call(kTestInterface3, kTestMethod_WithMessage);
330   method_call.SetSerial(123);
331   method_call.SetSender(sender);
332   auto response = testing::CallMethod(*dbus_object_, &method_call);
333   // The response should contain error UnknownInterface since the interface has
334   // been intentionally removed.
335   EXPECT_EQ(DBUS_ERROR_UNKNOWN_INTERFACE, response->GetErrorName());
336 }
337 
TEST_F(DBusObjectTest,TestInterfaceExportedLate)338 TEST_F(DBusObjectTest, TestInterfaceExportedLate) {
339   // Registers a new interface late.
340   dbus_object_->ExportInterfaceAsync(kTestInterface4,
341                                      base::Bind(&OnInterfaceExported));
342 
343   const std::string sender{":1.2345"};
344   dbus::MethodCall method_call(kTestInterface4, kTestMethod_WithMessage);
345   method_call.SetSerial(123);
346   method_call.SetSender(sender);
347   auto response = testing::CallMethod(*dbus_object_, &method_call);
348   // The response should contain error UnknownMethod rather than
349   // UnknownInterface since the interface has been registered late.
350   EXPECT_EQ(DBUS_ERROR_UNKNOWN_METHOD, response->GetErrorName());
351 }
352 
TEST_F(DBusObjectTest,TooFewParams)353 TEST_F(DBusObjectTest, TooFewParams) {
354   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
355   method_call.SetSerial(123);
356   dbus::MessageWriter writer(&method_call);
357   writer.AppendInt32(2);
358   auto response = testing::CallMethod(*dbus_object_, &method_call);
359   ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
360 }
361 
TEST_F(DBusObjectTest,TooManyParams)362 TEST_F(DBusObjectTest, TooManyParams) {
363   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
364   method_call.SetSerial(123);
365   dbus::MessageWriter writer(&method_call);
366   writer.AppendInt32(1);
367   writer.AppendInt32(2);
368   writer.AppendInt32(3);
369   auto response = testing::CallMethod(*dbus_object_, &method_call);
370   ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
371 }
372 
TEST_F(DBusObjectTest,ParamTypeMismatch)373 TEST_F(DBusObjectTest, ParamTypeMismatch) {
374   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
375   method_call.SetSerial(123);
376   dbus::MessageWriter writer(&method_call);
377   writer.AppendInt32(1);
378   writer.AppendBool(false);
379   auto response = testing::CallMethod(*dbus_object_, &method_call);
380   ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
381 }
382 
TEST_F(DBusObjectTest,ParamAsVariant)383 TEST_F(DBusObjectTest, ParamAsVariant) {
384   dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
385   method_call.SetSerial(123);
386   dbus::MessageWriter writer(&method_call);
387   writer.AppendVariantOfInt32(10);
388   writer.AppendVariantOfInt32(3);
389   auto response = testing::CallMethod(*dbus_object_, &method_call);
390   dbus::MessageReader reader(response.get());
391   int result;
392   ASSERT_TRUE(reader.PopInt32(&result));
393   ASSERT_FALSE(reader.HasMoreData());
394   ASSERT_EQ(13, result);
395 }
396 
TEST_F(DBusObjectTest,UnknownMethod)397 TEST_F(DBusObjectTest, UnknownMethod) {
398   dbus::MethodCall method_call(kTestInterface2, kTestMethod_Add);
399   method_call.SetSerial(123);
400   dbus::MessageWriter writer(&method_call);
401   writer.AppendInt32(1);
402   writer.AppendBool(false);
403   auto response = testing::CallMethod(*dbus_object_, &method_call);
404   ExpectError(response.get(), DBUS_ERROR_UNKNOWN_METHOD);
405 }
406 
TEST_F(DBusObjectTest,ShouldReleaseOnlyClaimedInterfaces)407 TEST_F(DBusObjectTest, ShouldReleaseOnlyClaimedInterfaces) {
408   const dbus::ObjectPath kObjectManagerPath{std::string{"/"}};
409   const dbus::ObjectPath kMethodsExportedOnPath{
410       std::string{kMethodsExportedOn}};
411   MockExportedObjectManager mock_object_manager{bus_, kObjectManagerPath};
412   dbus_object_ = std::unique_ptr<DBusObject>(
413       new DBusObject(&mock_object_manager, bus_, kMethodsExportedOnPath));
414   EXPECT_CALL(mock_object_manager, ClaimInterface(_, _, _)).Times(0);
415   EXPECT_CALL(mock_object_manager, ReleaseInterface(_, _)).Times(0);
416   DBusInterface* itf1 = dbus_object_->AddOrGetInterface(kTestInterface1);
417   itf1->AddSimpleMethodHandler(
418       kTestMethod_Add, base::Unretained(&calc_), &Calc::Add);
419   // When we tear down our DBusObject, it should release only interfaces it has
420   // previously claimed.  This prevents a check failing inside the
421   // ExportedObjectManager.  Since no interfaces have finished exporting
422   // handlers, nothing should be released.
423   dbus_object_.reset();
424 }
425 
426 }  // namespace dbus_utils
427 }  // namespace brillo
428