Home | History | Annotate | Download | only in dbus
      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