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