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/exported_property_set.h>
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include <base/bind.h>
     11 #include <base/macros.h>
     12 #include <brillo/dbus/dbus_object.h>
     13 #include <brillo/dbus/dbus_object_test_helpers.h>
     14 #include <brillo/errors/error_codes.h>
     15 #include <dbus/message.h>
     16 #include <dbus/property.h>
     17 #include <dbus/object_path.h>
     18 #include <dbus/mock_bus.h>
     19 #include <dbus/mock_exported_object.h>
     20 #include <gmock/gmock.h>
     21 #include <gtest/gtest.h>
     22 
     23 using ::testing::AnyNumber;
     24 using ::testing::Return;
     25 using ::testing::Invoke;
     26 using ::testing::_;
     27 using ::testing::Unused;
     28 
     29 namespace brillo {
     30 
     31 namespace dbus_utils {
     32 
     33 namespace {
     34 
     35 const char kBoolPropName[] = "BoolProp";
     36 const char kUint8PropName[] = "Uint8Prop";
     37 const char kInt16PropName[] = "Int16Prop";
     38 const char kUint16PropName[] = "Uint16Prop";
     39 const char kInt32PropName[] = "Int32Prop";
     40 const char kUint32PropName[] = "Uint32Prop";
     41 const char kInt64PropName[] = "Int64Prop";
     42 const char kUint64PropName[] = "Uint64Prop";
     43 const char kDoublePropName[] = "DoubleProp";
     44 const char kStringPropName[] = "StringProp";
     45 const char kPathPropName[] = "PathProp";
     46 const char kStringListPropName[] = "StringListProp";
     47 const char kPathListPropName[] = "PathListProp";
     48 const char kUint8ListPropName[] = "Uint8ListProp";
     49 
     50 const char kTestInterface1[] = "org.chromium.TestInterface1";
     51 const char kTestInterface2[] = "org.chromium.TestInterface2";
     52 const char kTestInterface3[] = "org.chromium.TestInterface3";
     53 
     54 const std::string kTestString("lies");
     55 const dbus::ObjectPath kMethodsExportedOnPath(std::string("/export"));
     56 const dbus::ObjectPath kTestObjectPathInit(std::string("/path_init"));
     57 const dbus::ObjectPath kTestObjectPathUpdate(std::string("/path_update"));
     58 
     59 }  // namespace
     60 
     61 class ExportedPropertySetTest : public ::testing::Test {
     62  public:
     63   struct Properties {
     64    public:
     65     ExportedProperty<bool> bool_prop_;
     66     ExportedProperty<uint8_t> uint8_prop_;
     67     ExportedProperty<int16_t> int16_prop_;
     68     ExportedProperty<uint16_t> uint16_prop_;
     69     ExportedProperty<int32_t> int32_prop_;
     70     ExportedProperty<uint32_t> uint32_prop_;
     71     ExportedProperty<int64_t> int64_prop_;
     72     ExportedProperty<uint64_t> uint64_prop_;
     73     ExportedProperty<double> double_prop_;
     74     ExportedProperty<std::string> string_prop_;
     75     ExportedProperty<dbus::ObjectPath> path_prop_;
     76     ExportedProperty<std::vector<std::string>> stringlist_prop_;
     77     ExportedProperty<std::vector<dbus::ObjectPath>> pathlist_prop_;
     78     ExportedProperty<std::vector<uint8_t>> uint8list_prop_;
     79 
     80     Properties(scoped_refptr<dbus::Bus> bus, const dbus::ObjectPath& path)
     81         : dbus_object_(nullptr, bus, path) {
     82       // The empty string is not a valid value for an ObjectPath.
     83       path_prop_.SetValue(kTestObjectPathInit);
     84       DBusInterface* itf1 = dbus_object_.AddOrGetInterface(kTestInterface1);
     85       itf1->AddProperty(kBoolPropName, &bool_prop_);
     86       itf1->AddProperty(kUint8PropName, &uint8_prop_);
     87       itf1->AddProperty(kInt16PropName, &int16_prop_);
     88       // I chose this weird grouping because N=2 is about all the permutations
     89       // of GetAll that I want to anticipate.
     90       DBusInterface* itf2 = dbus_object_.AddOrGetInterface(kTestInterface2);
     91       itf2->AddProperty(kUint16PropName, &uint16_prop_);
     92       itf2->AddProperty(kInt32PropName, &int32_prop_);
     93       DBusInterface* itf3 = dbus_object_.AddOrGetInterface(kTestInterface3);
     94       itf3->AddProperty(kUint32PropName, &uint32_prop_);
     95       itf3->AddProperty(kInt64PropName, &int64_prop_);
     96       itf3->AddProperty(kUint64PropName, &uint64_prop_);
     97       itf3->AddProperty(kDoublePropName, &double_prop_);
     98       itf3->AddProperty(kStringPropName, &string_prop_);
     99       itf3->AddProperty(kPathPropName, &path_prop_);
    100       itf3->AddProperty(kStringListPropName, &stringlist_prop_);
    101       itf3->AddProperty(kPathListPropName, &pathlist_prop_);
    102       itf3->AddProperty(kUint8ListPropName, &uint8list_prop_);
    103       dbus_object_.RegisterAsync(
    104           AsyncEventSequencer::GetDefaultCompletionAction());
    105     }
    106     virtual ~Properties() {}
    107 
    108     DBusObject dbus_object_;
    109   };
    110 
    111   void SetUp() override {
    112     dbus::Bus::Options options;
    113     options.bus_type = dbus::Bus::SYSTEM;
    114     bus_ = new dbus::MockBus(options);
    115     // By default, don't worry about threading assertions.
    116     EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
    117     EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
    118     // Use a mock exported object.
    119     mock_exported_object_ =
    120         new dbus::MockExportedObject(bus_.get(), kMethodsExportedOnPath);
    121     EXPECT_CALL(*bus_, GetExportedObject(kMethodsExportedOnPath))
    122         .Times(1).WillOnce(Return(mock_exported_object_.get()));
    123 
    124     EXPECT_CALL(*mock_exported_object_,
    125                 ExportMethod(dbus::kPropertiesInterface, _, _, _)).Times(3);
    126     p_.reset(new Properties(bus_, kMethodsExportedOnPath));
    127   }
    128 
    129   void TearDown() override {
    130     EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
    131   }
    132 
    133   void AssertMethodReturnsError(dbus::MethodCall* method_call) {
    134     method_call->SetSerial(123);
    135     auto response = testing::CallMethod(p_->dbus_object_, method_call);
    136     ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    137   }
    138 
    139   std::unique_ptr<dbus::Response> GetPropertyOnInterface(
    140       const std::string& interface_name,
    141       const std::string& property_name) {
    142     dbus::MethodCall method_call(dbus::kPropertiesInterface,
    143                                  dbus::kPropertiesGet);
    144     method_call.SetSerial(123);
    145     dbus::MessageWriter writer(&method_call);
    146     writer.AppendString(interface_name);
    147     writer.AppendString(property_name);
    148     return testing::CallMethod(p_->dbus_object_, &method_call);
    149   }
    150 
    151   std::unique_ptr<dbus::Response> SetPropertyOnInterface(
    152       const std::string& interface_name,
    153       const std::string& property_name,
    154       const brillo::Any& value) {
    155     dbus::MethodCall method_call(dbus::kPropertiesInterface,
    156                                  dbus::kPropertiesSet);
    157     method_call.SetSerial(123);
    158     dbus::MessageWriter writer(&method_call);
    159     writer.AppendString(interface_name);
    160     writer.AppendString(property_name);
    161     dbus_utils::AppendValueToWriter(&writer, value);
    162     return testing::CallMethod(p_->dbus_object_, &method_call);
    163   }
    164 
    165   std::unique_ptr<dbus::Response> last_response_;
    166   scoped_refptr<dbus::MockBus> bus_;
    167   scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
    168   std::unique_ptr<Properties> p_;
    169 };
    170 
    171 template<typename T>
    172 class PropertyValidatorObserver {
    173  public:
    174   PropertyValidatorObserver()
    175       : validate_property_callback_(
    176             base::Bind(&PropertyValidatorObserver::ValidateProperty,
    177                        base::Unretained(this))) {}
    178   virtual ~PropertyValidatorObserver() {}
    179 
    180   MOCK_METHOD2_T(ValidateProperty,
    181                  bool(brillo::ErrorPtr* error, const T& value));
    182 
    183   const base::Callback<bool(brillo::ErrorPtr*, const T&)>&
    184   validate_property_callback() const {
    185     return validate_property_callback_;
    186   }
    187 
    188  private:
    189   base::Callback<bool(brillo::ErrorPtr*, const T&)>
    190       validate_property_callback_;
    191 
    192   DISALLOW_COPY_AND_ASSIGN(PropertyValidatorObserver);
    193 };
    194 
    195 TEST_F(ExportedPropertySetTest, UpdateNotifications) {
    196   EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(14);
    197   p_->bool_prop_.SetValue(true);
    198   p_->uint8_prop_.SetValue(1);
    199   p_->int16_prop_.SetValue(1);
    200   p_->uint16_prop_.SetValue(1);
    201   p_->int32_prop_.SetValue(1);
    202   p_->uint32_prop_.SetValue(1);
    203   p_->int64_prop_.SetValue(1);
    204   p_->uint64_prop_.SetValue(1);
    205   p_->double_prop_.SetValue(1.0);
    206   p_->string_prop_.SetValue(kTestString);
    207   p_->path_prop_.SetValue(kTestObjectPathUpdate);
    208   p_->stringlist_prop_.SetValue({kTestString});
    209   p_->pathlist_prop_.SetValue({kTestObjectPathUpdate});
    210   p_->uint8list_prop_.SetValue({1});
    211 }
    212 
    213 TEST_F(ExportedPropertySetTest, UpdateToSameValue) {
    214   EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
    215   p_->bool_prop_.SetValue(true);
    216   p_->bool_prop_.SetValue(true);
    217 }
    218 
    219 TEST_F(ExportedPropertySetTest, GetAllNoArgs) {
    220   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    221                                dbus::kPropertiesGetAll);
    222   AssertMethodReturnsError(&method_call);
    223 }
    224 
    225 TEST_F(ExportedPropertySetTest, GetAllInvalidInterface) {
    226   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    227                                dbus::kPropertiesGetAll);
    228   method_call.SetSerial(123);
    229   dbus::MessageWriter writer(&method_call);
    230   writer.AppendString("org.chromium.BadInterface");
    231   auto response = testing::CallMethod(p_->dbus_object_, &method_call);
    232   dbus::MessageReader response_reader(response.get());
    233   dbus::MessageReader dict_reader(nullptr);
    234   ASSERT_TRUE(response_reader.PopArray(&dict_reader));
    235   // The response should just be a an empty array, since there are no properties
    236   // on this interface.  The spec doesn't say much about error conditions here,
    237   // so I'm going to assume this is a valid implementation.
    238   ASSERT_FALSE(dict_reader.HasMoreData());
    239   ASSERT_FALSE(response_reader.HasMoreData());
    240 }
    241 
    242 TEST_F(ExportedPropertySetTest, GetAllExtraArgs) {
    243   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    244                                dbus::kPropertiesGetAll);
    245   dbus::MessageWriter writer(&method_call);
    246   writer.AppendString(kTestInterface1);
    247   writer.AppendString(kTestInterface1);
    248   AssertMethodReturnsError(&method_call);
    249 }
    250 
    251 TEST_F(ExportedPropertySetTest, GetAllCorrectness) {
    252   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    253                                dbus::kPropertiesGetAll);
    254   method_call.SetSerial(123);
    255   dbus::MessageWriter writer(&method_call);
    256   writer.AppendString(kTestInterface2);
    257   auto response = testing::CallMethod(p_->dbus_object_, &method_call);
    258   dbus::MessageReader response_reader(response.get());
    259   dbus::MessageReader dict_reader(nullptr);
    260   dbus::MessageReader entry_reader(nullptr);
    261   ASSERT_TRUE(response_reader.PopArray(&dict_reader));
    262   ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
    263   std::string property_name;
    264   ASSERT_TRUE(entry_reader.PopString(&property_name));
    265   uint16_t value16;
    266   int32_t value32;
    267   if (property_name.compare(kUint16PropName) == 0) {
    268     ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
    269     ASSERT_FALSE(entry_reader.HasMoreData());
    270     ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
    271     ASSERT_TRUE(entry_reader.PopString(&property_name));
    272     ASSERT_EQ(property_name.compare(kInt32PropName), 0);
    273     ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
    274   } else {
    275     ASSERT_EQ(property_name.compare(kInt32PropName), 0);
    276     ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
    277     ASSERT_FALSE(entry_reader.HasMoreData());
    278     ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
    279     ASSERT_TRUE(entry_reader.PopString(&property_name));
    280     ASSERT_EQ(property_name.compare(kUint16PropName), 0);
    281     ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
    282   }
    283   ASSERT_FALSE(entry_reader.HasMoreData());
    284   ASSERT_FALSE(dict_reader.HasMoreData());
    285   ASSERT_FALSE(response_reader.HasMoreData());
    286 }
    287 
    288 TEST_F(ExportedPropertySetTest, GetNoArgs) {
    289   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    290                                dbus::kPropertiesGet);
    291   AssertMethodReturnsError(&method_call);
    292 }
    293 
    294 TEST_F(ExportedPropertySetTest, GetInvalidInterface) {
    295   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    296                                dbus::kPropertiesGet);
    297   dbus::MessageWriter writer(&method_call);
    298   writer.AppendString("org.chromium.BadInterface");
    299   writer.AppendString(kInt16PropName);
    300   AssertMethodReturnsError(&method_call);
    301 }
    302 
    303 TEST_F(ExportedPropertySetTest, GetBadPropertyName) {
    304   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    305                                dbus::kPropertiesGet);
    306   dbus::MessageWriter writer(&method_call);
    307   writer.AppendString(kTestInterface1);
    308   writer.AppendString("IAmNotAProperty");
    309   AssertMethodReturnsError(&method_call);
    310 }
    311 
    312 TEST_F(ExportedPropertySetTest, GetPropIfMismatch) {
    313   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    314                                dbus::kPropertiesGet);
    315   dbus::MessageWriter writer(&method_call);
    316   writer.AppendString(kTestInterface1);
    317   writer.AppendString(kStringPropName);
    318   AssertMethodReturnsError(&method_call);
    319 }
    320 
    321 TEST_F(ExportedPropertySetTest, GetNoPropertyName) {
    322   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    323                                dbus::kPropertiesGet);
    324   dbus::MessageWriter writer(&method_call);
    325   writer.AppendString(kTestInterface1);
    326   AssertMethodReturnsError(&method_call);
    327 }
    328 
    329 TEST_F(ExportedPropertySetTest, GetExtraArgs) {
    330   dbus::MethodCall method_call(dbus::kPropertiesInterface,
    331                                dbus::kPropertiesGet);
    332   dbus::MessageWriter writer(&method_call);
    333   writer.AppendString(kTestInterface1);
    334   writer.AppendString(kBoolPropName);
    335   writer.AppendString("Extra param");
    336   AssertMethodReturnsError(&method_call);
    337 }
    338 
    339 TEST_F(ExportedPropertySetTest, GetWorksWithBool) {
    340   auto response = GetPropertyOnInterface(kTestInterface1, kBoolPropName);
    341   dbus::MessageReader reader(response.get());
    342   bool value;
    343   ASSERT_TRUE(reader.PopVariantOfBool(&value));
    344   ASSERT_FALSE(reader.HasMoreData());
    345 }
    346 
    347 TEST_F(ExportedPropertySetTest, GetWorksWithUint8) {
    348   auto response = GetPropertyOnInterface(kTestInterface1, kUint8PropName);
    349   dbus::MessageReader reader(response.get());
    350   uint8_t value;
    351   ASSERT_TRUE(reader.PopVariantOfByte(&value));
    352   ASSERT_FALSE(reader.HasMoreData());
    353 }
    354 
    355 TEST_F(ExportedPropertySetTest, GetWorksWithInt16) {
    356   auto response = GetPropertyOnInterface(kTestInterface1, kInt16PropName);
    357   dbus::MessageReader reader(response.get());
    358   int16_t value;
    359   ASSERT_TRUE(reader.PopVariantOfInt16(&value));
    360   ASSERT_FALSE(reader.HasMoreData());
    361 }
    362 
    363 TEST_F(ExportedPropertySetTest, GetWorksWithUint16) {
    364   auto response = GetPropertyOnInterface(kTestInterface2, kUint16PropName);
    365   dbus::MessageReader reader(response.get());
    366   uint16_t value;
    367   ASSERT_TRUE(reader.PopVariantOfUint16(&value));
    368   ASSERT_FALSE(reader.HasMoreData());
    369 }
    370 
    371 TEST_F(ExportedPropertySetTest, GetWorksWithInt32) {
    372   auto response = GetPropertyOnInterface(kTestInterface2, kInt32PropName);
    373   dbus::MessageReader reader(response.get());
    374   int32_t value;
    375   ASSERT_TRUE(reader.PopVariantOfInt32(&value));
    376   ASSERT_FALSE(reader.HasMoreData());
    377 }
    378 
    379 TEST_F(ExportedPropertySetTest, GetWorksWithUint32) {
    380   auto response = GetPropertyOnInterface(kTestInterface3, kUint32PropName);
    381   dbus::MessageReader reader(response.get());
    382   uint32_t value;
    383   ASSERT_TRUE(reader.PopVariantOfUint32(&value));
    384   ASSERT_FALSE(reader.HasMoreData());
    385 }
    386 
    387 TEST_F(ExportedPropertySetTest, GetWorksWithInt64) {
    388   auto response = GetPropertyOnInterface(kTestInterface3, kInt64PropName);
    389   dbus::MessageReader reader(response.get());
    390   int64_t value;
    391   ASSERT_TRUE(reader.PopVariantOfInt64(&value));
    392   ASSERT_FALSE(reader.HasMoreData());
    393 }
    394 
    395 TEST_F(ExportedPropertySetTest, GetWorksWithUint64) {
    396   auto response = GetPropertyOnInterface(kTestInterface3, kUint64PropName);
    397   dbus::MessageReader reader(response.get());
    398   uint64_t value;
    399   ASSERT_TRUE(reader.PopVariantOfUint64(&value));
    400   ASSERT_FALSE(reader.HasMoreData());
    401 }
    402 
    403 TEST_F(ExportedPropertySetTest, GetWorksWithDouble) {
    404   auto response = GetPropertyOnInterface(kTestInterface3, kDoublePropName);
    405   dbus::MessageReader reader(response.get());
    406   double value;
    407   ASSERT_TRUE(reader.PopVariantOfDouble(&value));
    408   ASSERT_FALSE(reader.HasMoreData());
    409 }
    410 
    411 TEST_F(ExportedPropertySetTest, GetWorksWithString) {
    412   auto response = GetPropertyOnInterface(kTestInterface3, kStringPropName);
    413   dbus::MessageReader reader(response.get());
    414   std::string value;
    415   ASSERT_TRUE(reader.PopVariantOfString(&value));
    416   ASSERT_FALSE(reader.HasMoreData());
    417 }
    418 
    419 TEST_F(ExportedPropertySetTest, GetWorksWithPath) {
    420   auto response = GetPropertyOnInterface(kTestInterface3, kPathPropName);
    421   dbus::MessageReader reader(response.get());
    422   dbus::ObjectPath value;
    423   ASSERT_TRUE(reader.PopVariantOfObjectPath(&value));
    424   ASSERT_FALSE(reader.HasMoreData());
    425 }
    426 
    427 TEST_F(ExportedPropertySetTest, GetWorksWithStringList) {
    428   auto response = GetPropertyOnInterface(kTestInterface3, kStringListPropName);
    429   dbus::MessageReader reader(response.get());
    430   dbus::MessageReader variant_reader(nullptr);
    431   std::vector<std::string> value;
    432   ASSERT_TRUE(reader.PopVariant(&variant_reader));
    433   ASSERT_TRUE(variant_reader.PopArrayOfStrings(&value));
    434   ASSERT_FALSE(variant_reader.HasMoreData());
    435   ASSERT_FALSE(reader.HasMoreData());
    436 }
    437 
    438 TEST_F(ExportedPropertySetTest, GetWorksWithPathList) {
    439   auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
    440   dbus::MessageReader reader(response.get());
    441   dbus::MessageReader variant_reader(nullptr);
    442   std::vector<dbus::ObjectPath> value;
    443   ASSERT_TRUE(reader.PopVariant(&variant_reader));
    444   ASSERT_TRUE(variant_reader.PopArrayOfObjectPaths(&value));
    445   ASSERT_FALSE(variant_reader.HasMoreData());
    446   ASSERT_FALSE(reader.HasMoreData());
    447 }
    448 
    449 TEST_F(ExportedPropertySetTest, GetWorksWithUint8List) {
    450   auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
    451   dbus::MessageReader reader(response.get());
    452   dbus::MessageReader variant_reader(nullptr);
    453   const uint8_t* buffer;
    454   size_t buffer_len;
    455   ASSERT_TRUE(reader.PopVariant(&variant_reader));
    456   // |buffer| remains under the control of the MessageReader.
    457   ASSERT_TRUE(variant_reader.PopArrayOfBytes(&buffer, &buffer_len));
    458   ASSERT_FALSE(variant_reader.HasMoreData());
    459   ASSERT_FALSE(reader.HasMoreData());
    460 }
    461 
    462 TEST_F(ExportedPropertySetTest, SetInvalidInterface) {
    463   auto response = SetPropertyOnInterface(
    464       "BadInterfaceName", kStringPropName, brillo::Any(kTestString));
    465   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    466   ASSERT_EQ(DBUS_ERROR_UNKNOWN_INTERFACE, response->GetErrorName());
    467 }
    468 
    469 TEST_F(ExportedPropertySetTest, SetBadPropertyName) {
    470   auto response = SetPropertyOnInterface(
    471       kTestInterface3, "IAmNotAProperty", brillo::Any(kTestString));
    472   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    473   ASSERT_EQ(DBUS_ERROR_UNKNOWN_PROPERTY, response->GetErrorName());
    474 }
    475 
    476 TEST_F(ExportedPropertySetTest, SetFailsWithReadOnlyProperty) {
    477   auto response = SetPropertyOnInterface(
    478       kTestInterface3, kStringPropName, brillo::Any(kTestString));
    479   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    480   ASSERT_EQ(DBUS_ERROR_PROPERTY_READ_ONLY, response->GetErrorName());
    481 }
    482 
    483 TEST_F(ExportedPropertySetTest, SetFailsWithMismatchedValueType) {
    484   p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
    485   auto response = SetPropertyOnInterface(
    486       kTestInterface3, kStringPropName, brillo::Any(true));
    487   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    488   ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
    489 }
    490 
    491 namespace {
    492 
    493 bool SetInvalidProperty(brillo::ErrorPtr* error, Unused) {
    494   brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
    495                        DBUS_ERROR_INVALID_ARGS, "Invalid value");
    496   return false;
    497 }
    498 
    499 }  // namespace
    500 
    501 TEST_F(ExportedPropertySetTest, SetFailsWithValidator) {
    502   PropertyValidatorObserver<std::string> property_validator;
    503   p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
    504   p_->string_prop_.SetValidator(
    505       property_validator.validate_property_callback());
    506 
    507   brillo::ErrorPtr error = brillo::Error::Create(
    508       FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "");
    509   EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
    510       .WillOnce(Invoke(SetInvalidProperty));
    511   auto response = SetPropertyOnInterface(
    512       kTestInterface3, kStringPropName, brillo::Any(kTestString));
    513   ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    514   ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
    515 }
    516 
    517 TEST_F(ExportedPropertySetTest, SetWorksWithValidator) {
    518   PropertyValidatorObserver<std::string> property_validator;
    519   p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
    520   p_->string_prop_.SetValidator(
    521       property_validator.validate_property_callback());
    522 
    523   EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
    524       .WillOnce(Return(true));
    525   auto response = SetPropertyOnInterface(
    526       kTestInterface3, kStringPropName, brillo::Any(kTestString));
    527   ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    528   ASSERT_EQ(kTestString, p_->string_prop_.value());
    529 }
    530 
    531 TEST_F(ExportedPropertySetTest, SetWorksWithSameValue) {
    532   PropertyValidatorObserver<std::string> property_validator;
    533   p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
    534   p_->string_prop_.SetValidator(
    535       property_validator.validate_property_callback());
    536   EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
    537   p_->string_prop_.SetValue(kTestString);
    538 
    539   // No need to validate the value if it is the same as the current one.
    540   EXPECT_CALL(property_validator, ValidateProperty(_, _)).Times(0);
    541   auto response = SetPropertyOnInterface(
    542       kTestInterface3, kStringPropName, brillo::Any(kTestString));
    543   ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    544   ASSERT_EQ(kTestString, p_->string_prop_.value());
    545 }
    546 
    547 TEST_F(ExportedPropertySetTest, SetWorksWithoutValidator) {
    548   p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
    549   auto response = SetPropertyOnInterface(
    550       kTestInterface3, kStringPropName, brillo::Any(kTestString));
    551   ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
    552   ASSERT_EQ(kTestString, p_->string_prop_.value());
    553 }
    554 
    555 namespace {
    556 
    557 void VerifySignal(dbus::Signal* signal) {
    558   ASSERT_NE(signal, nullptr);
    559   std::string interface_name;
    560   std::string property_name;
    561   uint8_t value;
    562   dbus::MessageReader reader(signal);
    563   dbus::MessageReader array_reader(signal);
    564   dbus::MessageReader dict_reader(signal);
    565   ASSERT_TRUE(reader.PopString(&interface_name));
    566   ASSERT_TRUE(reader.PopArray(&array_reader));
    567   ASSERT_TRUE(array_reader.PopDictEntry(&dict_reader));
    568   ASSERT_TRUE(dict_reader.PopString(&property_name));
    569   ASSERT_TRUE(dict_reader.PopVariantOfByte(&value));
    570   ASSERT_FALSE(dict_reader.HasMoreData());
    571   ASSERT_FALSE(array_reader.HasMoreData());
    572   ASSERT_TRUE(reader.HasMoreData());
    573   // Read the (empty) list of invalidated property names.
    574   ASSERT_TRUE(reader.PopArray(&array_reader));
    575   ASSERT_FALSE(array_reader.HasMoreData());
    576   ASSERT_FALSE(reader.HasMoreData());
    577   ASSERT_EQ(value, 57);
    578   ASSERT_EQ(property_name, std::string(kUint8PropName));
    579   ASSERT_EQ(interface_name, std::string(kTestInterface1));
    580 }
    581 
    582 }  // namespace
    583 
    584 TEST_F(ExportedPropertySetTest, SignalsAreParsable) {
    585   EXPECT_CALL(*mock_exported_object_, SendSignal(_))
    586       .Times(1).WillOnce(Invoke(&VerifySignal));
    587   p_->uint8_prop_.SetValue(57);
    588 }
    589 
    590 }  // namespace dbus_utils
    591 
    592 }  // namespace brillo
    593