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