| // Copyright 2018 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 <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <dbus/mock_bus.h> |
| #include <dbus/mock_object_proxy.h> |
| #include <brillo/dbus/exported_property_set.h> |
| #include <dbus/property.h> |
| #include <gtest/gtest.h> |
| |
| #include "bluetooth/common/property.h" |
| |
| using ::testing::_; |
| |
| namespace bluetooth { |
| |
| namespace { |
| |
| constexpr char kTestInterfaceName[] = "org.example.Interface"; |
| constexpr char kTestObjectPath[] = "/org/example/Object"; |
| constexpr char kTestPropertyName1[] = "SomeProperty1"; |
| constexpr char kTestPropertyName2[] = "SomeProperty2"; |
| constexpr char kTestServiceName[] = "org.example.Default"; |
| |
| } // namespace |
| |
| class PropertySetTest : public ::testing::Test { |
| public: |
| void SetUp() { bus_ = new dbus::MockBus(dbus::Bus::Options()); } |
| |
| void OnExportedPropertyUpdated( |
| const brillo::dbus_utils::ExportedPropertyBase* exported_property_base) { |
| exported_property_update_count_++; |
| } |
| |
| protected: |
| scoped_refptr<dbus::MockBus> bus_; |
| |
| // Counts the number of times an exported property is updated. |
| int exported_property_update_count_ = 0; |
| }; |
| |
| TEST_F(PropertySetTest, PropertyFactory) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory; |
| std::unique_ptr<dbus::PropertyBase> property_base = |
| property_factory.CreateProperty(); |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| dbus::Property<int>* property = |
| static_cast<dbus::Property<int>*>(property_base.get()); |
| brillo::dbus_utils::ExportedProperty<int>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<int>*>( |
| exported_property_base.get()); |
| dbus::PropertySet property_set( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base->Init(&property_set, kTestPropertyName1); |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| int expected_value = 3; |
| EXPECT_CALL(*object_proxy, CallMethodAndBlock(_, _)).Times(1); |
| property->SetAndBlock(expected_value); |
| property->ReplaceValueWithSetValue(); |
| property->set_valid(true); |
| ASSERT_EQ(expected_value, property->value()); |
| |
| EXPECT_EQ(0, exported_property_update_count_); |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base.get()}, exported_property_base.get()); |
| // exported_property should be updated to be the same as property. |
| EXPECT_EQ(expected_value, exported_property->value()); |
| EXPECT_EQ(1, exported_property_update_count_); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base.get()}, exported_property_base.get()); |
| // Now that the values are already the same, exported_property shouldn't get |
| // updated. |
| EXPECT_EQ(1, exported_property_update_count_); |
| |
| // Now set property to another value. |
| expected_value = 4; |
| EXPECT_CALL(*object_proxy, CallMethodAndBlock(_, _)).Times(1); |
| property->SetAndBlock(expected_value); |
| property->ReplaceValueWithSetValue(); |
| property->set_valid(true); |
| ASSERT_EQ(expected_value, property->value()); |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base.get()}, exported_property_base.get()); |
| // exported_property should be updated to be the same as property. |
| EXPECT_EQ(expected_value, exported_property->value()); |
| EXPECT_EQ(2, exported_property_update_count_); |
| } |
| |
| TEST_F(PropertySetTest, PropertySet) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertySet property_set(object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| |
| auto int_property = std::make_unique<dbus::Property<int>>(); |
| auto bool_property = std::make_unique<dbus::Property<int>>(); |
| dbus::PropertyBase* expected_int_property = int_property.get(); |
| dbus::PropertyBase* expected_bool_property = bool_property.get(); |
| // Before the properties are registered, name() should be empty strings. |
| ASSERT_EQ("", expected_int_property->name()); |
| ASSERT_EQ("", expected_bool_property->name()); |
| |
| property_set.RegisterProperty(kTestPropertyName1, std::move(int_property)); |
| property_set.RegisterProperty(kTestPropertyName2, std::move(bool_property)); |
| |
| // GetProperty should remember the registered properties. |
| EXPECT_EQ(expected_int_property, |
| property_set.GetProperty(kTestPropertyName1)); |
| EXPECT_EQ(expected_bool_property, |
| property_set.GetProperty(kTestPropertyName2)); |
| // The dbus::PropertyBase::name() should return the registered name. |
| EXPECT_EQ(kTestPropertyName1, expected_int_property->name()); |
| EXPECT_EQ(kTestPropertyName2, expected_bool_property->name()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingDefault) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory(MergingRule::DEFAULT); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property1 = |
| static_cast<dbus::Property<int>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<dbus::PropertyBase> property_base2 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property2 = |
| static_cast<dbus::Property<int>*>(property_base2.get()); |
| dbus::PropertySet property_set2( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base2->Init(&property_set2, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| brillo::dbus_utils::ExportedProperty<int>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<int>*>( |
| exported_property_base.get()); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| int value1 = 3; |
| int value2 = 5; |
| property1->SetAndBlock(value1); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| property2->SetAndBlock(value2); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(value1, exported_property->value()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingAnd) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<bool> property_factory(MergingRule::AND); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<bool>* property1 = |
| static_cast<dbus::Property<bool>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<dbus::PropertyBase> property_base2 = |
| property_factory.CreateProperty(); |
| dbus::Property<bool>* property2 = |
| static_cast<dbus::Property<bool>*>(property_base2.get()); |
| dbus::PropertySet property_set2( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base2->Init(&property_set2, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| brillo::dbus_utils::ExportedProperty<bool>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<bool>*>( |
| exported_property_base.get()); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(true); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| property2->SetAndBlock(false); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(false, exported_property->value()); |
| |
| property2->SetAndBlock(true); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(true, exported_property->value()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingAndInvalidType) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory(MergingRule::AND); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property1 = |
| static_cast<dbus::Property<int>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(1); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| EXPECT_DEATH(property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get()}, exported_property_base.get()), |
| "AND merging not supported for the given value type"); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingOr) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<bool> property_factory(MergingRule::OR); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<bool>* property1 = |
| static_cast<dbus::Property<bool>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<dbus::PropertyBase> property_base2 = |
| property_factory.CreateProperty(); |
| dbus::Property<bool>* property2 = |
| static_cast<dbus::Property<bool>*>(property_base2.get()); |
| dbus::PropertySet property_set2( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base2->Init(&property_set2, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| brillo::dbus_utils::ExportedProperty<bool>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<bool>*>( |
| exported_property_base.get()); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(false); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| property2->SetAndBlock(true); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(true, exported_property->value()); |
| |
| property2->SetAndBlock(false); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(false, exported_property->value()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingOrInvalidType) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory(MergingRule::OR); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property1 = |
| static_cast<dbus::Property<int>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(1); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| EXPECT_DEATH(property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get()}, exported_property_base.get()), |
| "OR merging not supported for the given value type"); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingUnion) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<std::vector<std::string>> property_factory( |
| MergingRule::UNION); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<std::vector<std::string>>* property1 = |
| static_cast<dbus::Property<std::vector<std::string>>*>( |
| property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<dbus::PropertyBase> property_base2 = |
| property_factory.CreateProperty(); |
| dbus::Property<std::vector<std::string>>* property2 = |
| static_cast<dbus::Property<std::vector<std::string>>*>( |
| property_base2.get()); |
| dbus::PropertySet property_set2( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base2->Init(&property_set2, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| brillo::dbus_utils::ExportedProperty<std::vector<std::string>>* |
| exported_property = static_cast< |
| brillo::dbus_utils::ExportedProperty<std::vector<std::string>>*>( |
| exported_property_base.get()); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock({"string1"}); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| property2->SetAndBlock({"string2"}); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| std::vector<std::string> expected_value = {"string1", "string2"}; |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(expected_value, exported_property->value()); |
| |
| property2->SetAndBlock({"string1"}); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| expected_value = {"string1"}; |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ(expected_value, exported_property->value()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingUnionInvalidType) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory(MergingRule::UNION); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property1 = |
| static_cast<dbus::Property<int>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(1); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| EXPECT_DEATH(property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get()}, exported_property_base.get()), |
| "UNION merging not supported for the given value type"); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingCancatenation) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<std::string> property_factory(MergingRule::CONCATENATION); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<std::string>* property1 = |
| static_cast<dbus::Property<std::string>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<dbus::PropertyBase> property_base2 = |
| property_factory.CreateProperty(); |
| dbus::Property<std::string>* property2 = |
| static_cast<dbus::Property<std::string>*>(property_base2.get()); |
| dbus::PropertySet property_set2( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base2->Init(&property_set2, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| brillo::dbus_utils::ExportedProperty<std::string>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<std::string>*>( |
| exported_property_base.get()); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock("string1"); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| property2->SetAndBlock("string2"); |
| property2->ReplaceValueWithSetValue(); |
| property2->set_valid(true); |
| |
| property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get(), property_base2.get()}, |
| exported_property_base.get()); |
| EXPECT_EQ("string1 string2", exported_property->value()); |
| } |
| |
| TEST_F(PropertySetTest, PropertyMergingConcatenationInvalidType) { |
| scoped_refptr<dbus::MockObjectProxy> object_proxy = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| |
| PropertyFactory<int> property_factory(MergingRule::CONCATENATION); |
| std::unique_ptr<dbus::PropertyBase> property_base1 = |
| property_factory.CreateProperty(); |
| dbus::Property<int>* property1 = |
| static_cast<dbus::Property<int>*>(property_base1.get()); |
| dbus::PropertySet property_set1( |
| object_proxy.get(), kTestInterfaceName, |
| base::Bind([](const std::string& property_name) {})); |
| property_base1->Init(&property_set1, kTestPropertyName1); |
| |
| std::unique_ptr<brillo::dbus_utils::ExportedPropertyBase> |
| exported_property_base = property_factory.CreateExportedProperty(); |
| |
| exported_property_base->SetUpdateCallback(base::Bind( |
| &PropertySetTest::OnExportedPropertyUpdated, base::Unretained(this))); |
| |
| property1->SetAndBlock(1); |
| property1->ReplaceValueWithSetValue(); |
| property1->set_valid(true); |
| |
| EXPECT_DEATH(property_factory.MergePropertiesToExportedProperty( |
| {property_base1.get()}, exported_property_base.get()), |
| "CONCATENATION merging not supported for the given value type"); |
| } |
| |
| } // namespace bluetooth |