604 lines
23 KiB
C++
604 lines
23 KiB
C++
// Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <brillo/dbus/exported_property_set.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/macros.h>
|
|
#include <brillo/dbus/dbus_object.h>
|
|
#include <brillo/dbus/dbus_object_test_helpers.h>
|
|
#include <brillo/errors/error_codes.h>
|
|
#include <dbus/message.h>
|
|
#include <dbus/property.h>
|
|
#include <dbus/object_path.h>
|
|
#include <dbus/mock_bus.h>
|
|
#include <dbus/mock_exported_object.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using ::testing::AnyNumber;
|
|
using ::testing::Return;
|
|
using ::testing::Invoke;
|
|
using ::testing::_;
|
|
using ::testing::Unused;
|
|
|
|
namespace brillo {
|
|
|
|
namespace dbus_utils {
|
|
|
|
namespace {
|
|
|
|
const char kBoolPropName[] = "BoolProp";
|
|
const char kUint8PropName[] = "Uint8Prop";
|
|
const char kInt16PropName[] = "Int16Prop";
|
|
const char kUint16PropName[] = "Uint16Prop";
|
|
const char kInt32PropName[] = "Int32Prop";
|
|
const char kUint32PropName[] = "Uint32Prop";
|
|
const char kInt64PropName[] = "Int64Prop";
|
|
const char kUint64PropName[] = "Uint64Prop";
|
|
const char kDoublePropName[] = "DoubleProp";
|
|
const char kStringPropName[] = "StringProp";
|
|
const char kPathPropName[] = "PathProp";
|
|
const char kStringListPropName[] = "StringListProp";
|
|
const char kPathListPropName[] = "PathListProp";
|
|
const char kUint8ListPropName[] = "Uint8ListProp";
|
|
|
|
const char kTestInterface1[] = "org.chromium.TestInterface1";
|
|
const char kTestInterface2[] = "org.chromium.TestInterface2";
|
|
const char kTestInterface3[] = "org.chromium.TestInterface3";
|
|
|
|
const std::string kTestString("lies");
|
|
const dbus::ObjectPath kMethodsExportedOnPath(std::string("/export"));
|
|
const dbus::ObjectPath kTestObjectPathInit(std::string("/path_init"));
|
|
const dbus::ObjectPath kTestObjectPathUpdate(std::string("/path_update"));
|
|
|
|
} // namespace
|
|
|
|
class ExportedPropertySetTest : public ::testing::Test {
|
|
public:
|
|
struct Properties {
|
|
public:
|
|
ExportedProperty<bool> bool_prop_;
|
|
ExportedProperty<uint8_t> uint8_prop_;
|
|
ExportedProperty<int16_t> int16_prop_;
|
|
ExportedProperty<uint16_t> uint16_prop_;
|
|
ExportedProperty<int32_t> int32_prop_;
|
|
ExportedProperty<uint32_t> uint32_prop_;
|
|
ExportedProperty<int64_t> int64_prop_;
|
|
ExportedProperty<uint64_t> uint64_prop_;
|
|
ExportedProperty<double> double_prop_;
|
|
ExportedProperty<std::string> string_prop_;
|
|
ExportedProperty<dbus::ObjectPath> path_prop_;
|
|
ExportedProperty<std::vector<std::string>> stringlist_prop_;
|
|
ExportedProperty<std::vector<dbus::ObjectPath>> pathlist_prop_;
|
|
ExportedProperty<std::vector<uint8_t>> uint8list_prop_;
|
|
|
|
Properties(scoped_refptr<dbus::Bus> bus, const dbus::ObjectPath& path)
|
|
: dbus_object_(nullptr, bus, path) {
|
|
// The empty string is not a valid value for an ObjectPath.
|
|
path_prop_.SetValue(kTestObjectPathInit);
|
|
DBusInterface* itf1 = dbus_object_.AddOrGetInterface(kTestInterface1);
|
|
itf1->AddProperty(kBoolPropName, &bool_prop_);
|
|
itf1->AddProperty(kUint8PropName, &uint8_prop_);
|
|
itf1->AddProperty(kInt16PropName, &int16_prop_);
|
|
// I chose this weird grouping because N=2 is about all the permutations
|
|
// of GetAll that I want to anticipate.
|
|
DBusInterface* itf2 = dbus_object_.AddOrGetInterface(kTestInterface2);
|
|
itf2->AddProperty(kUint16PropName, &uint16_prop_);
|
|
itf2->AddProperty(kInt32PropName, &int32_prop_);
|
|
DBusInterface* itf3 = dbus_object_.AddOrGetInterface(kTestInterface3);
|
|
itf3->AddProperty(kUint32PropName, &uint32_prop_);
|
|
itf3->AddProperty(kInt64PropName, &int64_prop_);
|
|
itf3->AddProperty(kUint64PropName, &uint64_prop_);
|
|
itf3->AddProperty(kDoublePropName, &double_prop_);
|
|
itf3->AddProperty(kStringPropName, &string_prop_);
|
|
itf3->AddProperty(kPathPropName, &path_prop_);
|
|
itf3->AddProperty(kStringListPropName, &stringlist_prop_);
|
|
itf3->AddProperty(kPathListPropName, &pathlist_prop_);
|
|
itf3->AddProperty(kUint8ListPropName, &uint8list_prop_);
|
|
dbus_object_.RegisterAsync(
|
|
AsyncEventSequencer::GetDefaultCompletionAction());
|
|
}
|
|
virtual ~Properties() {}
|
|
|
|
DBusObject dbus_object_;
|
|
};
|
|
|
|
void SetUp() override {
|
|
dbus::Bus::Options options;
|
|
options.bus_type = dbus::Bus::SYSTEM;
|
|
bus_ = new dbus::MockBus(options);
|
|
// By default, don't worry about threading assertions.
|
|
EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
|
|
EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
|
|
// Use a mock exported object.
|
|
mock_exported_object_ =
|
|
new dbus::MockExportedObject(bus_.get(), kMethodsExportedOnPath);
|
|
EXPECT_CALL(*bus_, GetExportedObject(kMethodsExportedOnPath))
|
|
.Times(1).WillOnce(Return(mock_exported_object_.get()));
|
|
|
|
EXPECT_CALL(*mock_exported_object_,
|
|
ExportMethod(dbus::kPropertiesInterface, _, _, _)).Times(3);
|
|
p_.reset(new Properties(bus_, kMethodsExportedOnPath));
|
|
}
|
|
|
|
void TearDown() override {
|
|
EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
|
|
}
|
|
|
|
void AssertMethodReturnsError(dbus::MethodCall* method_call) {
|
|
method_call->SetSerial(123);
|
|
auto response = testing::CallMethod(p_->dbus_object_, method_call);
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
}
|
|
|
|
std::unique_ptr<dbus::Response> GetPropertyOnInterface(
|
|
const std::string& interface_name,
|
|
const std::string& property_name) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
method_call.SetSerial(123);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(interface_name);
|
|
writer.AppendString(property_name);
|
|
return testing::CallMethod(p_->dbus_object_, &method_call);
|
|
}
|
|
|
|
std::unique_ptr<dbus::Response> SetPropertyOnInterface(
|
|
const std::string& interface_name,
|
|
const std::string& property_name,
|
|
const brillo::Any& value) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesSet);
|
|
method_call.SetSerial(123);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(interface_name);
|
|
writer.AppendString(property_name);
|
|
dbus_utils::AppendValueToWriter(&writer, value);
|
|
return testing::CallMethod(p_->dbus_object_, &method_call);
|
|
}
|
|
|
|
std::unique_ptr<dbus::Response> last_response_;
|
|
scoped_refptr<dbus::MockBus> bus_;
|
|
scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
|
|
std::unique_ptr<Properties> p_;
|
|
};
|
|
|
|
template<typename T>
|
|
class PropertyValidatorObserver {
|
|
public:
|
|
PropertyValidatorObserver()
|
|
: validate_property_callback_(
|
|
base::Bind(&PropertyValidatorObserver::ValidateProperty,
|
|
base::Unretained(this))) {}
|
|
virtual ~PropertyValidatorObserver() {}
|
|
|
|
MOCK_METHOD(bool, ValidateProperty, (brillo::ErrorPtr*, const T&));
|
|
|
|
const base::Callback<bool(brillo::ErrorPtr*, const T&)>&
|
|
validate_property_callback() const {
|
|
return validate_property_callback_;
|
|
}
|
|
|
|
private:
|
|
base::Callback<bool(brillo::ErrorPtr*, const T&)>
|
|
validate_property_callback_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PropertyValidatorObserver);
|
|
};
|
|
|
|
TEST_F(ExportedPropertySetTest, UpdateNotifications) {
|
|
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(14);
|
|
p_->bool_prop_.SetValue(true);
|
|
p_->uint8_prop_.SetValue(1);
|
|
p_->int16_prop_.SetValue(1);
|
|
p_->uint16_prop_.SetValue(1);
|
|
p_->int32_prop_.SetValue(1);
|
|
p_->uint32_prop_.SetValue(1);
|
|
p_->int64_prop_.SetValue(1);
|
|
p_->uint64_prop_.SetValue(1);
|
|
p_->double_prop_.SetValue(1.0);
|
|
p_->string_prop_.SetValue(kTestString);
|
|
p_->path_prop_.SetValue(kTestObjectPathUpdate);
|
|
p_->stringlist_prop_.SetValue({kTestString});
|
|
p_->pathlist_prop_.SetValue({kTestObjectPathUpdate});
|
|
p_->uint8list_prop_.SetValue({1});
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, UpdateToSameValue) {
|
|
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
|
|
p_->bool_prop_.SetValue(true);
|
|
p_->bool_prop_.SetValue(true);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetAllNoArgs) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGetAll);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetAllInvalidInterface) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGetAll);
|
|
method_call.SetSerial(123);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString("org.chromium.BadInterface");
|
|
auto response = testing::CallMethod(p_->dbus_object_, &method_call);
|
|
dbus::MessageReader response_reader(response.get());
|
|
dbus::MessageReader dict_reader(nullptr);
|
|
ASSERT_TRUE(response_reader.PopArray(&dict_reader));
|
|
// The response should just be a an empty array, since there are no properties
|
|
// on this interface. The spec doesn't say much about error conditions here,
|
|
// so I'm going to assume this is a valid implementation.
|
|
ASSERT_FALSE(dict_reader.HasMoreData());
|
|
ASSERT_FALSE(response_reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetAllExtraArgs) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGetAll);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface1);
|
|
writer.AppendString(kTestInterface1);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetAllCorrectness) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGetAll);
|
|
method_call.SetSerial(123);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface2);
|
|
auto response = testing::CallMethod(p_->dbus_object_, &method_call);
|
|
dbus::MessageReader response_reader(response.get());
|
|
dbus::MessageReader dict_reader(nullptr);
|
|
dbus::MessageReader entry_reader(nullptr);
|
|
ASSERT_TRUE(response_reader.PopArray(&dict_reader));
|
|
ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
|
|
std::string property_name;
|
|
ASSERT_TRUE(entry_reader.PopString(&property_name));
|
|
uint16_t value16;
|
|
int32_t value32;
|
|
if (property_name.compare(kUint16PropName) == 0) {
|
|
ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
|
|
ASSERT_FALSE(entry_reader.HasMoreData());
|
|
ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
|
|
ASSERT_TRUE(entry_reader.PopString(&property_name));
|
|
ASSERT_EQ(property_name.compare(kInt32PropName), 0);
|
|
ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
|
|
} else {
|
|
ASSERT_EQ(property_name.compare(kInt32PropName), 0);
|
|
ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
|
|
ASSERT_FALSE(entry_reader.HasMoreData());
|
|
ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
|
|
ASSERT_TRUE(entry_reader.PopString(&property_name));
|
|
ASSERT_EQ(property_name.compare(kUint16PropName), 0);
|
|
ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
|
|
}
|
|
ASSERT_FALSE(entry_reader.HasMoreData());
|
|
ASSERT_FALSE(dict_reader.HasMoreData());
|
|
ASSERT_FALSE(response_reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetNoArgs) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetInvalidInterface) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString("org.chromium.BadInterface");
|
|
writer.AppendString(kInt16PropName);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetBadPropertyName) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface1);
|
|
writer.AppendString("IAmNotAProperty");
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetPropIfMismatch) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface1);
|
|
writer.AppendString(kStringPropName);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetNoPropertyName) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface1);
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetExtraArgs) {
|
|
dbus::MethodCall method_call(dbus::kPropertiesInterface,
|
|
dbus::kPropertiesGet);
|
|
dbus::MessageWriter writer(&method_call);
|
|
writer.AppendString(kTestInterface1);
|
|
writer.AppendString(kBoolPropName);
|
|
writer.AppendString("Extra param");
|
|
AssertMethodReturnsError(&method_call);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetRemovedProperty) {
|
|
DBusInterface* itf1 = p_->dbus_object_.AddOrGetInterface(kTestInterface1);
|
|
itf1->RemoveProperty(kBoolPropName);
|
|
|
|
auto response = GetPropertyOnInterface(kTestInterface1, kBoolPropName);
|
|
ASSERT_EQ(DBUS_ERROR_UNKNOWN_PROPERTY, response->GetErrorName());
|
|
|
|
// Signal should not be emitted for removed property.
|
|
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(0);
|
|
p_->bool_prop_.SetValue(true);
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithBool) {
|
|
auto response = GetPropertyOnInterface(kTestInterface1, kBoolPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
bool value;
|
|
ASSERT_TRUE(reader.PopVariantOfBool(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithUint8) {
|
|
auto response = GetPropertyOnInterface(kTestInterface1, kUint8PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
uint8_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfByte(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithInt16) {
|
|
auto response = GetPropertyOnInterface(kTestInterface1, kInt16PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
int16_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfInt16(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithUint16) {
|
|
auto response = GetPropertyOnInterface(kTestInterface2, kUint16PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
uint16_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfUint16(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithInt32) {
|
|
auto response = GetPropertyOnInterface(kTestInterface2, kInt32PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
int32_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfInt32(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithUint32) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kUint32PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
uint32_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfUint32(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithInt64) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kInt64PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
int64_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfInt64(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithUint64) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kUint64PropName);
|
|
dbus::MessageReader reader(response.get());
|
|
uint64_t value;
|
|
ASSERT_TRUE(reader.PopVariantOfUint64(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithDouble) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kDoublePropName);
|
|
dbus::MessageReader reader(response.get());
|
|
double value;
|
|
ASSERT_TRUE(reader.PopVariantOfDouble(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithString) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kStringPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
std::string value;
|
|
ASSERT_TRUE(reader.PopVariantOfString(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithPath) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kPathPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
dbus::ObjectPath value;
|
|
ASSERT_TRUE(reader.PopVariantOfObjectPath(&value));
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithStringList) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kStringListPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
dbus::MessageReader variant_reader(nullptr);
|
|
std::vector<std::string> value;
|
|
ASSERT_TRUE(reader.PopVariant(&variant_reader));
|
|
ASSERT_TRUE(variant_reader.PopArrayOfStrings(&value));
|
|
ASSERT_FALSE(variant_reader.HasMoreData());
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithPathList) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
dbus::MessageReader variant_reader(nullptr);
|
|
std::vector<dbus::ObjectPath> value;
|
|
ASSERT_TRUE(reader.PopVariant(&variant_reader));
|
|
ASSERT_TRUE(variant_reader.PopArrayOfObjectPaths(&value));
|
|
ASSERT_FALSE(variant_reader.HasMoreData());
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, GetWorksWithUint8List) {
|
|
auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
|
|
dbus::MessageReader reader(response.get());
|
|
dbus::MessageReader variant_reader(nullptr);
|
|
const uint8_t* buffer;
|
|
size_t buffer_len;
|
|
ASSERT_TRUE(reader.PopVariant(&variant_reader));
|
|
// |buffer| remains under the control of the MessageReader.
|
|
ASSERT_TRUE(variant_reader.PopArrayOfBytes(&buffer, &buffer_len));
|
|
ASSERT_FALSE(variant_reader.HasMoreData());
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetInvalidInterface) {
|
|
auto response = SetPropertyOnInterface(
|
|
"BadInterfaceName", kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(DBUS_ERROR_UNKNOWN_INTERFACE, response->GetErrorName());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetBadPropertyName) {
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, "IAmNotAProperty", brillo::Any(kTestString));
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(DBUS_ERROR_UNKNOWN_PROPERTY, response->GetErrorName());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetFailsWithReadOnlyProperty) {
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(DBUS_ERROR_PROPERTY_READ_ONLY, response->GetErrorName());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetFailsWithMismatchedValueType) {
|
|
p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(true));
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool SetInvalidProperty(brillo::ErrorPtr* error, Unused) {
|
|
brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
|
|
DBUS_ERROR_INVALID_ARGS, "Invalid value");
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ExportedPropertySetTest, SetFailsWithValidator) {
|
|
PropertyValidatorObserver<std::string> property_validator;
|
|
p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
|
|
p_->string_prop_.SetValidator(
|
|
property_validator.validate_property_callback());
|
|
|
|
brillo::ErrorPtr error = brillo::Error::Create(
|
|
FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "");
|
|
EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
|
|
.WillOnce(Invoke(SetInvalidProperty));
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetWorksWithValidator) {
|
|
PropertyValidatorObserver<std::string> property_validator;
|
|
p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
|
|
p_->string_prop_.SetValidator(
|
|
property_validator.validate_property_callback());
|
|
|
|
EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
|
|
.WillOnce(Return(true));
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(kTestString, p_->string_prop_.value());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetWorksWithSameValue) {
|
|
PropertyValidatorObserver<std::string> property_validator;
|
|
p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
|
|
p_->string_prop_.SetValidator(
|
|
property_validator.validate_property_callback());
|
|
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
|
|
p_->string_prop_.SetValue(kTestString);
|
|
|
|
// No need to validate the value if it is the same as the current one.
|
|
EXPECT_CALL(property_validator, ValidateProperty(_, _)).Times(0);
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(kTestString, p_->string_prop_.value());
|
|
}
|
|
|
|
TEST_F(ExportedPropertySetTest, SetWorksWithoutValidator) {
|
|
p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
|
|
auto response = SetPropertyOnInterface(
|
|
kTestInterface3, kStringPropName, brillo::Any(kTestString));
|
|
ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
|
|
ASSERT_EQ(kTestString, p_->string_prop_.value());
|
|
}
|
|
|
|
namespace {
|
|
|
|
void VerifySignal(dbus::Signal* signal) {
|
|
ASSERT_NE(signal, nullptr);
|
|
std::string interface_name;
|
|
std::string property_name;
|
|
uint8_t value;
|
|
dbus::MessageReader reader(signal);
|
|
dbus::MessageReader array_reader(signal);
|
|
dbus::MessageReader dict_reader(signal);
|
|
ASSERT_TRUE(reader.PopString(&interface_name));
|
|
ASSERT_TRUE(reader.PopArray(&array_reader));
|
|
ASSERT_TRUE(array_reader.PopDictEntry(&dict_reader));
|
|
ASSERT_TRUE(dict_reader.PopString(&property_name));
|
|
ASSERT_TRUE(dict_reader.PopVariantOfByte(&value));
|
|
ASSERT_FALSE(dict_reader.HasMoreData());
|
|
ASSERT_FALSE(array_reader.HasMoreData());
|
|
ASSERT_TRUE(reader.HasMoreData());
|
|
// Read the (empty) list of invalidated property names.
|
|
ASSERT_TRUE(reader.PopArray(&array_reader));
|
|
ASSERT_FALSE(array_reader.HasMoreData());
|
|
ASSERT_FALSE(reader.HasMoreData());
|
|
ASSERT_EQ(value, 57);
|
|
ASSERT_EQ(property_name, std::string(kUint8PropName));
|
|
ASSERT_EQ(interface_name, std::string(kTestInterface1));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ExportedPropertySetTest, SignalsAreParsable) {
|
|
EXPECT_CALL(*mock_exported_object_, SendSignal(_))
|
|
.Times(1).WillOnce(Invoke(&VerifySignal));
|
|
p_->uint8_prop_.SetValue(57);
|
|
}
|
|
|
|
} // namespace dbus_utils
|
|
|
|
} // namespace brillo
|