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 { 47 int Add(int x, int y) { return x + y; } 48 int Negate(int x) { return -x; } 49 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 } 60 void AddSubtract(int x, int y, int* sum, int* diff) { 61 *sum = x + y; 62 *diff = x - y; 63 } 64 }; 65 66 int StrLen(const std::string& str) { 67 return str.size(); 68 } 69 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 77 void NoOp() {} 78 79 bool TestWithMessage(ErrorPtr* /* error */, 80 dbus::Message* message, 81 std::string* str) { 82 *str = message->GetSender(); 83 return true; 84 } 85 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: 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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