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