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 struct Calc {
Addbrillo::dbus_utils::__anon531d6bc20111::Calc47 int Add(int x, int y) { return x + y; }
Negatebrillo::dbus_utils::__anon531d6bc20111::Calc48 int Negate(int x) { return -x; }
Positivebrillo::dbus_utils::__anon531d6bc20111::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::__anon531d6bc20111::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
92 } // namespace
93
94 class DBusObjectTest : public ::testing::Test {
95 public:
SetUp()96 virtual void SetUp() {
97 dbus::Bus::Options options;
98 options.bus_type = dbus::Bus::SYSTEM;
99 bus_ = new dbus::MockBus(options);
100 // By default, don't worry about threading assertions.
101 EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
102 EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
103 // Use a mock exported object.
104 const dbus::ObjectPath kMethodsExportedOnPath{
105 std::string{kMethodsExportedOn}};
106 mock_exported_object_ =
107 new dbus::MockExportedObject(bus_.get(), kMethodsExportedOnPath);
108 EXPECT_CALL(*bus_, GetExportedObject(kMethodsExportedOnPath))
109 .Times(AnyNumber())
110 .WillRepeatedly(Return(mock_exported_object_.get()));
111 EXPECT_CALL(*mock_exported_object_, ExportMethod(_, _, _, _))
112 .Times(AnyNumber());
113 EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
114
115 dbus_object_ = std::unique_ptr<DBusObject>(
116 new DBusObject(nullptr, bus_, kMethodsExportedOnPath));
117
118 DBusInterface* itf1 = dbus_object_->AddOrGetInterface(kTestInterface1);
119 itf1->AddSimpleMethodHandler(
120 kTestMethod_Add, base::Unretained(&calc_), &Calc::Add);
121 itf1->AddSimpleMethodHandler(
122 kTestMethod_Negate, base::Unretained(&calc_), &Calc::Negate);
123 itf1->AddMethodHandler(
124 kTestMethod_Positive, base::Unretained(&calc_), &Calc::Positive);
125 itf1->AddSimpleMethodHandler(
126 kTestMethod_AddSubtract, base::Unretained(&calc_), &Calc::AddSubtract);
127 DBusInterface* itf2 = dbus_object_->AddOrGetInterface(kTestInterface2);
128 itf2->AddSimpleMethodHandler(kTestMethod_StrLen, StrLen);
129 itf2->AddSimpleMethodHandlerWithError(kTestMethod_CheckNonEmpty,
130 CheckNonEmpty);
131 DBusInterface* itf3 = dbus_object_->AddOrGetInterface(kTestInterface3);
132 base::Callback<void()> noop_callback = base::Bind(NoOp);
133 itf3->AddSimpleMethodHandler(kTestMethod_NoOp, noop_callback);
134 itf3->AddSimpleMethodHandlerWithErrorAndMessage(
135 kTestMethod_WithMessage, base::Bind(&TestWithMessage));
136 itf3->AddMethodHandlerWithMessage(kTestMethod_WithMessageAsync,
137 base::Bind(&TestWithMessageAsync));
138
139 dbus_object_->RegisterAsync(
140 AsyncEventSequencer::GetDefaultCompletionAction());
141 }
142
ExpectError(dbus::Response * response,const std::string & expected_code)143 void ExpectError(dbus::Response* response, const std::string& expected_code) {
144 EXPECT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
145 EXPECT_EQ(expected_code, response->GetErrorName());
146 }
147
148 scoped_refptr<dbus::MockBus> bus_;
149 scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
150 std::unique_ptr<DBusObject> dbus_object_;
151 Calc calc_;
152 };
153
TEST_F(DBusObjectTest,Add)154 TEST_F(DBusObjectTest, Add) {
155 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
156 method_call.SetSerial(123);
157 dbus::MessageWriter writer(&method_call);
158 writer.AppendInt32(2);
159 writer.AppendInt32(3);
160 auto response = testing::CallMethod(*dbus_object_, &method_call);
161 dbus::MessageReader reader(response.get());
162 int result;
163 ASSERT_TRUE(reader.PopInt32(&result));
164 ASSERT_FALSE(reader.HasMoreData());
165 ASSERT_EQ(5, result);
166 }
167
TEST_F(DBusObjectTest,Negate)168 TEST_F(DBusObjectTest, Negate) {
169 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Negate);
170 method_call.SetSerial(123);
171 dbus::MessageWriter writer(&method_call);
172 writer.AppendInt32(98765);
173 auto response = testing::CallMethod(*dbus_object_, &method_call);
174 dbus::MessageReader reader(response.get());
175 int result;
176 ASSERT_TRUE(reader.PopInt32(&result));
177 ASSERT_FALSE(reader.HasMoreData());
178 ASSERT_EQ(-98765, result);
179 }
180
TEST_F(DBusObjectTest,PositiveSuccess)181 TEST_F(DBusObjectTest, PositiveSuccess) {
182 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Positive);
183 method_call.SetSerial(123);
184 dbus::MessageWriter writer(&method_call);
185 writer.AppendDouble(17.5);
186 auto response = testing::CallMethod(*dbus_object_, &method_call);
187 dbus::MessageReader reader(response.get());
188 double result;
189 ASSERT_TRUE(reader.PopDouble(&result));
190 ASSERT_FALSE(reader.HasMoreData());
191 ASSERT_DOUBLE_EQ(17.5, result);
192 }
193
TEST_F(DBusObjectTest,PositiveFailure)194 TEST_F(DBusObjectTest, PositiveFailure) {
195 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Positive);
196 method_call.SetSerial(123);
197 dbus::MessageWriter writer(&method_call);
198 writer.AppendDouble(-23.2);
199 auto response = testing::CallMethod(*dbus_object_, &method_call);
200 ExpectError(response.get(), DBUS_ERROR_FAILED);
201 }
202
TEST_F(DBusObjectTest,AddSubtract)203 TEST_F(DBusObjectTest, AddSubtract) {
204 dbus::MethodCall method_call(kTestInterface1, kTestMethod_AddSubtract);
205 method_call.SetSerial(123);
206 dbus::MessageWriter writer(&method_call);
207 writer.AppendInt32(2);
208 writer.AppendInt32(3);
209 auto response = testing::CallMethod(*dbus_object_, &method_call);
210 dbus::MessageReader reader(response.get());
211 int sum = 0, diff = 0;
212 ASSERT_TRUE(reader.PopInt32(&sum));
213 ASSERT_TRUE(reader.PopInt32(&diff));
214 ASSERT_FALSE(reader.HasMoreData());
215 EXPECT_EQ(5, sum);
216 EXPECT_EQ(-1, diff);
217 }
218
TEST_F(DBusObjectTest,StrLen0)219 TEST_F(DBusObjectTest, StrLen0) {
220 dbus::MethodCall method_call(kTestInterface2, kTestMethod_StrLen);
221 method_call.SetSerial(123);
222 dbus::MessageWriter writer(&method_call);
223 writer.AppendString("");
224 auto response = testing::CallMethod(*dbus_object_, &method_call);
225 dbus::MessageReader reader(response.get());
226 int result;
227 ASSERT_TRUE(reader.PopInt32(&result));
228 ASSERT_FALSE(reader.HasMoreData());
229 ASSERT_EQ(0, result);
230 }
231
TEST_F(DBusObjectTest,StrLen4)232 TEST_F(DBusObjectTest, StrLen4) {
233 dbus::MethodCall method_call(kTestInterface2, kTestMethod_StrLen);
234 method_call.SetSerial(123);
235 dbus::MessageWriter writer(&method_call);
236 writer.AppendString("test");
237 auto response = testing::CallMethod(*dbus_object_, &method_call);
238 dbus::MessageReader reader(response.get());
239 int result;
240 ASSERT_TRUE(reader.PopInt32(&result));
241 ASSERT_FALSE(reader.HasMoreData());
242 ASSERT_EQ(4, result);
243 }
244
TEST_F(DBusObjectTest,CheckNonEmpty_Success)245 TEST_F(DBusObjectTest, CheckNonEmpty_Success) {
246 dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
247 method_call.SetSerial(123);
248 dbus::MessageWriter writer(&method_call);
249 writer.AppendString("test");
250 auto response = testing::CallMethod(*dbus_object_, &method_call);
251 ASSERT_EQ(dbus::Message::MESSAGE_METHOD_RETURN, response->GetMessageType());
252 dbus::MessageReader reader(response.get());
253 EXPECT_FALSE(reader.HasMoreData());
254 }
255
TEST_F(DBusObjectTest,CheckNonEmpty_Failure)256 TEST_F(DBusObjectTest, CheckNonEmpty_Failure) {
257 dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
258 method_call.SetSerial(123);
259 dbus::MessageWriter writer(&method_call);
260 writer.AppendString("");
261 auto response = testing::CallMethod(*dbus_object_, &method_call);
262 ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
263 ErrorPtr error;
264 ExtractMethodCallResults(response.get(), &error);
265 ASSERT_NE(nullptr, error.get());
266 EXPECT_EQ("test", error->GetDomain());
267 EXPECT_EQ("string_empty", error->GetCode());
268 EXPECT_EQ("String is empty", error->GetMessage());
269 }
270
TEST_F(DBusObjectTest,CheckNonEmpty_MissingParams)271 TEST_F(DBusObjectTest, CheckNonEmpty_MissingParams) {
272 dbus::MethodCall method_call(kTestInterface2, kTestMethod_CheckNonEmpty);
273 method_call.SetSerial(123);
274 auto response = testing::CallMethod(*dbus_object_, &method_call);
275 ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
276 dbus::MessageReader reader(response.get());
277 std::string message;
278 ASSERT_TRUE(reader.PopString(&message));
279 EXPECT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
280 EXPECT_EQ("Too few parameters in a method call", message);
281 EXPECT_FALSE(reader.HasMoreData());
282 }
283
TEST_F(DBusObjectTest,NoOp)284 TEST_F(DBusObjectTest, NoOp) {
285 dbus::MethodCall method_call(kTestInterface3, kTestMethod_NoOp);
286 method_call.SetSerial(123);
287 auto response = testing::CallMethod(*dbus_object_, &method_call);
288 dbus::MessageReader reader(response.get());
289 ASSERT_FALSE(reader.HasMoreData());
290 }
291
TEST_F(DBusObjectTest,TestWithMessage)292 TEST_F(DBusObjectTest, TestWithMessage) {
293 const std::string sender{":1.2345"};
294 dbus::MethodCall method_call(kTestInterface3, kTestMethod_WithMessage);
295 method_call.SetSerial(123);
296 method_call.SetSender(sender);
297 auto response = testing::CallMethod(*dbus_object_, &method_call);
298 dbus::MessageReader reader(response.get());
299 std::string message;
300 ASSERT_TRUE(reader.PopString(&message));
301 ASSERT_FALSE(reader.HasMoreData());
302 EXPECT_EQ(sender, message);
303 }
304
TEST_F(DBusObjectTest,TestWithMessageAsync)305 TEST_F(DBusObjectTest, TestWithMessageAsync) {
306 const std::string sender{":6.7890"};
307 dbus::MethodCall method_call(kTestInterface3, kTestMethod_WithMessageAsync);
308 method_call.SetSerial(123);
309 method_call.SetSender(sender);
310 auto response = testing::CallMethod(*dbus_object_, &method_call);
311 dbus::MessageReader reader(response.get());
312 std::string message;
313 ASSERT_TRUE(reader.PopString(&message));
314 ASSERT_FALSE(reader.HasMoreData());
315 EXPECT_EQ(sender, message);
316 }
317
TEST_F(DBusObjectTest,TooFewParams)318 TEST_F(DBusObjectTest, TooFewParams) {
319 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
320 method_call.SetSerial(123);
321 dbus::MessageWriter writer(&method_call);
322 writer.AppendInt32(2);
323 auto response = testing::CallMethod(*dbus_object_, &method_call);
324 ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
325 }
326
TEST_F(DBusObjectTest,TooManyParams)327 TEST_F(DBusObjectTest, TooManyParams) {
328 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
329 method_call.SetSerial(123);
330 dbus::MessageWriter writer(&method_call);
331 writer.AppendInt32(1);
332 writer.AppendInt32(2);
333 writer.AppendInt32(3);
334 auto response = testing::CallMethod(*dbus_object_, &method_call);
335 ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
336 }
337
TEST_F(DBusObjectTest,ParamTypeMismatch)338 TEST_F(DBusObjectTest, ParamTypeMismatch) {
339 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
340 method_call.SetSerial(123);
341 dbus::MessageWriter writer(&method_call);
342 writer.AppendInt32(1);
343 writer.AppendBool(false);
344 auto response = testing::CallMethod(*dbus_object_, &method_call);
345 ExpectError(response.get(), DBUS_ERROR_INVALID_ARGS);
346 }
347
TEST_F(DBusObjectTest,ParamAsVariant)348 TEST_F(DBusObjectTest, ParamAsVariant) {
349 dbus::MethodCall method_call(kTestInterface1, kTestMethod_Add);
350 method_call.SetSerial(123);
351 dbus::MessageWriter writer(&method_call);
352 writer.AppendVariantOfInt32(10);
353 writer.AppendVariantOfInt32(3);
354 auto response = testing::CallMethod(*dbus_object_, &method_call);
355 dbus::MessageReader reader(response.get());
356 int result;
357 ASSERT_TRUE(reader.PopInt32(&result));
358 ASSERT_FALSE(reader.HasMoreData());
359 ASSERT_EQ(13, result);
360 }
361
TEST_F(DBusObjectTest,UnknownMethod)362 TEST_F(DBusObjectTest, UnknownMethod) {
363 dbus::MethodCall method_call(kTestInterface2, kTestMethod_Add);
364 method_call.SetSerial(123);
365 dbus::MessageWriter writer(&method_call);
366 writer.AppendInt32(1);
367 writer.AppendBool(false);
368 auto response = testing::CallMethod(*dbus_object_, &method_call);
369 ExpectError(response.get(), DBUS_ERROR_UNKNOWN_METHOD);
370 }
371
TEST_F(DBusObjectTest,ShouldReleaseOnlyClaimedInterfaces)372 TEST_F(DBusObjectTest, ShouldReleaseOnlyClaimedInterfaces) {
373 const dbus::ObjectPath kObjectManagerPath{std::string{"/"}};
374 const dbus::ObjectPath kMethodsExportedOnPath{
375 std::string{kMethodsExportedOn}};
376 MockExportedObjectManager mock_object_manager{bus_, kObjectManagerPath};
377 dbus_object_ = std::unique_ptr<DBusObject>(
378 new DBusObject(&mock_object_manager, bus_, kMethodsExportedOnPath));
379 EXPECT_CALL(mock_object_manager, ClaimInterface(_, _, _)).Times(0);
380 EXPECT_CALL(mock_object_manager, ReleaseInterface(_, _)).Times(0);
381 DBusInterface* itf1 = dbus_object_->AddOrGetInterface(kTestInterface1);
382 itf1->AddSimpleMethodHandler(
383 kTestMethod_Add, base::Unretained(&calc_), &Calc::Add);
384 // When we tear down our DBusObject, it should release only interfaces it has
385 // previously claimed. This prevents a check failing inside the
386 // ExportedObjectManager. Since no interfaces have finished exporting
387 // handlers, nothing should be released.
388 dbus_object_.reset();
389 }
390
391 } // namespace dbus_utils
392 } // namespace brillo
393