| // 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 <brillo/bind_lambda.h> |
| #include <brillo/dbus/exported_property_set.h> |
| #include <brillo/dbus/mock_dbus_object.h> |
| #include <brillo/dbus/mock_exported_object_manager.h> |
| #include <dbus/mock_bus.h> |
| #include <dbus/mock_exported_object.h> |
| #include <dbus/mock_object_proxy.h> |
| #include <dbus/property.h> |
| #include <gtest/gtest.h> |
| |
| #include "bluetooth/common/exported_object_manager_wrapper.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::Mock; |
| using ::testing::Return; |
| |
| namespace bluetooth { |
| |
| namespace { |
| |
| constexpr char kTestInterfaceName1[] = "org.example.Interface1"; |
| constexpr char kTestInterfaceName2[] = "org.example.Interface2"; |
| constexpr char kTestObjectPath[] = "/org/example/Object"; |
| constexpr char kTestObjectManagerPath[] = "/"; |
| constexpr char kTestPropertyName[] = "SomeProperty"; |
| constexpr char kTestServiceName[] = "org.example.Default"; |
| |
| // Matcher to compare a D-Bus signal for properties changed. |
| MATCHER_P3(PropertySignalEq, |
| expected_property_interface, |
| expected_property_name, |
| expected_property_value, |
| "") { |
| std::string property_interface; |
| brillo::VariantDictionary dict; |
| dbus::MessageReader reader(arg); |
| |
| // Tries to read the message payload. |
| if (!brillo::dbus_utils::PopValueFromReader(&reader, &property_interface)) |
| return false; |
| if (!brillo::dbus_utils::PopValueFromReader(&reader, &dict)) |
| return false; |
| if (dict.empty()) |
| return false; |
| |
| // Reads the property name and value from the dictionary (second argument). |
| auto kv = *dict.begin(); |
| std::string property_name = kv.first; |
| decltype(expected_property_value) property_value = |
| kv.second.Get<decltype(expected_property_value)>(); |
| |
| // Checks if everything matches with expectations. |
| return (arg->GetInterface() == dbus::kPropertiesInterface && |
| arg->GetMember() == dbus::kPropertiesChanged && |
| property_interface == expected_property_interface && |
| property_name == expected_property_name && |
| property_value == expected_property_value); |
| } |
| |
| } // namespace |
| |
| class ExportedObjectManagerWrapperTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| bus_ = new dbus::MockBus(dbus::Bus::Options()); |
| EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber()); |
| exported_object_manager_ = |
| std::make_unique<brillo::dbus_utils::MockExportedObjectManager>( |
| bus_, dbus::ObjectPath(kTestObjectManagerPath)); |
| object_proxy_ = new dbus::MockObjectProxy( |
| bus_.get(), kTestServiceName, dbus::ObjectPath(kTestObjectPath)); |
| } |
| |
| void SetupPropertyMethodHandlers( |
| brillo::dbus_utils::DBusInterface* prop_interface, |
| brillo::dbus_utils::ExportedPropertySet* property_set) {} |
| |
| protected: |
| scoped_refptr<dbus::MockBus> bus_; |
| scoped_refptr<dbus::MockObjectProxy> object_proxy_; |
| std::unique_ptr<brillo::dbus_utils::MockExportedObjectManager> |
| exported_object_manager_; |
| }; |
| |
| TEST_F(ExportedObjectManagerWrapperTest, ExportedInterface) { |
| dbus::ObjectPath object_path = dbus::ObjectPath(kTestObjectPath); |
| |
| scoped_refptr<dbus::MockExportedObject> exported_object = |
| new dbus::MockExportedObject(bus_.get(), object_path); |
| EXPECT_CALL(*bus_, GetExportedObject(object_path)) |
| .WillOnce(Return(exported_object.get())); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, _, _)) |
| .WillRepeatedly(Return(true)); |
| |
| auto dbus_object = std::make_unique<brillo::dbus_utils::MockDBusObject>( |
| exported_object_manager_.get(), bus_, object_path); |
| EXPECT_CALL(*exported_object_manager_, |
| ClaimInterface(object_path, dbus::kPropertiesInterface, _)) |
| .Times(1); |
| dbus_object->RegisterAndBlock(); |
| |
| auto interface = std::make_unique<ExportedInterface>( |
| object_path, kTestInterfaceName1, dbus_object.get()); |
| // Instantiating ExportedInterface should add a DBusInterface to the |
| // underlying DBusObject. |
| EXPECT_TRUE(dbus_object->FindInterface(kTestInterfaceName1) != nullptr); |
| |
| // ExportedInterface::ExportAsync should trigger the underlying |
| // ExportedObjectManager to claim the interface of the specified object. |
| EXPECT_CALL(*exported_object_manager_, |
| ClaimInterface(object_path, kTestInterfaceName1, _)) |
| .Times(1); |
| interface->ExportAsync(base::Bind([](bool success) {})); |
| |
| // Registers a property to the interface. |
| PropertyFactory<int> property_factory; |
| brillo::dbus_utils::ExportedPropertyBase* exported_property_base = |
| interface->EnsureExportedPropertyRegistered(kTestPropertyName, |
| &property_factory); |
| brillo::dbus_utils::ExportedProperty<int>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<int>*>( |
| exported_property_base); |
| int set_value = 7; |
| // Checks that signal is emitted when the registered property changes value. |
| EXPECT_CALL(*exported_object, |
| SendSignal(PropertySignalEq(kTestInterfaceName1, |
| kTestPropertyName, set_value))) |
| .Times(1); |
| exported_property->SetValue(set_value); |
| |
| // Unexport() should remove the DBusInterface from the underlying DBusObject |
| // and trigger the ExportedObjectManager to release the interface. |
| EXPECT_CALL(*exported_object_manager_, |
| ReleaseInterface(object_path, dbus::kPropertiesInterface)) |
| .Times(1); |
| EXPECT_CALL(*exported_object_manager_, |
| ReleaseInterface(object_path, kTestInterfaceName1)) |
| .Times(1); |
| EXPECT_CALL(*exported_object, Unregister()).Times(1); |
| interface->Unexport(); |
| EXPECT_TRUE(dbus_object->FindInterface(kTestInterfaceName1) == nullptr); |
| |
| // Checks that signal is no longer emitted when property changes value. |
| // This makes sure that the property is unregistered after Unexport() above. |
| EXPECT_CALL(*exported_object, SendSignal(_)).Times(0); |
| exported_property->SetValue(set_value + 1); |
| } |
| |
| TEST_F(ExportedObjectManagerWrapperTest, CopyProperty) { |
| dbus::ObjectPath object_path(kTestObjectPath); |
| auto dbus_object = std::make_unique<brillo::dbus_utils::MockDBusObject>( |
| exported_object_manager_.get(), bus_, object_path); |
| |
| auto interface = std::make_unique<ExportedInterface>( |
| object_path, kTestInterfaceName1, dbus_object.get()); |
| // Instantiating ExportedInterface should add a DBusInterface to the |
| // underlying DBusObject. |
| EXPECT_TRUE(dbus_object->FindInterface(kTestInterfaceName1) != nullptr); |
| |
| // Prepares property for testing. |
| PropertyFactory<int> property_factory; |
| std::unique_ptr<dbus::PropertyBase> property_base = |
| property_factory.CreateProperty(); |
| auto property_set = std::make_unique<dbus::PropertySet>( |
| object_proxy_.get(), kTestInterfaceName1, |
| base::Bind([](const std::string& property_name) {})); |
| property_base->Init(property_set.get(), kTestPropertyName); |
| dbus::Property<int>* property = |
| static_cast<dbus::Property<int>*>(property_base.get()); |
| int expected_value = 3; |
| EXPECT_CALL(*object_proxy_, MockCallMethodAndBlock(_, _)).Times(1); |
| property->SetAndBlock(expected_value); |
| property->ReplaceValueWithSetValue(); |
| ASSERT_EQ(expected_value, property->value()); |
| |
| interface->CopyPropertyToExportedProperty( |
| kTestPropertyName, property_base.get(), &property_factory); |
| |
| // Checks that the property is exported and the exported property has the |
| // same value as the origin property. |
| brillo::dbus_utils::ExportedPropertyBase* exported_property_base = |
| interface->EnsureExportedPropertyRegistered( |
| kTestPropertyName, nullptr /* PropertyFactory no longer needed */); |
| brillo::dbus_utils::ExportedProperty<int>* exported_property = |
| static_cast<brillo::dbus_utils::ExportedProperty<int>*>( |
| exported_property_base); |
| EXPECT_EQ(expected_value, exported_property->value()); |
| } |
| |
| TEST_F(ExportedObjectManagerWrapperTest, ExportedObject) { |
| dbus::ObjectPath object_path(kTestObjectPath); |
| |
| scoped_refptr<dbus::MockExportedObject> exported_object = |
| new dbus::MockExportedObject(bus_.get(), object_path); |
| EXPECT_CALL(*bus_, GetExportedObject(object_path)) |
| .WillOnce(Return(exported_object.get())); |
| auto object = std::make_unique<ExportedObject>( |
| exported_object_manager_.get(), bus_, object_path, |
| base::Bind(&ExportedObjectManagerWrapperTest::SetupPropertyMethodHandlers, |
| base::Unretained(this))); |
| |
| // RegisterAsync should export org.freedesktop.DBus.Properties interface. |
| EXPECT_CALL(*exported_object_manager_, |
| ClaimInterface(object_path, dbus::kPropertiesInterface, _)) |
| .Times(1); |
| object->RegisterAsync(base::Bind([](bool success) {})); |
| |
| // Adds an exported interface. |
| object->AddExportedInterface(kTestInterfaceName1); |
| ExportedInterface* interface = |
| object->GetExportedInterface(kTestInterfaceName1); |
| // ExportedInterface::ExportAsync should trigger the underlying |
| // ExportedObjectManager to claim the interface of the specified object. |
| EXPECT_CALL(*exported_object_manager_, |
| ClaimInterface(object_path, kTestInterfaceName1, _)) |
| .Times(1); |
| interface->ExportAsync(base::Bind([](bool success) {})); |
| |
| // RemoveExportedInterface should unexport that interface. |
| EXPECT_CALL(*exported_object_manager_, |
| ReleaseInterface(object_path, kTestInterfaceName1)) |
| .Times(1); |
| object->RemoveExportedInterface(kTestInterfaceName1); |
| |
| // Destructing ExportedObject should unexport the previously |
| // exported interfaces and org.freedesktop.DBus.Properties interface. |
| EXPECT_CALL(*exported_object_manager_, |
| ReleaseInterface(object_path, dbus::kPropertiesInterface)) |
| .Times(1); |
| EXPECT_CALL(*exported_object, Unregister()).Times(1); |
| object.reset(); |
| } |
| |
| TEST_F(ExportedObjectManagerWrapperTest, ExportedObjectManagerWrapper) { |
| dbus::ObjectPath object_path(kTestObjectPath); |
| |
| scoped_refptr<dbus::MockExportedObject> exported_object = |
| new dbus::MockExportedObject(bus_.get(), object_path); |
| |
| brillo::dbus_utils::MockExportedObjectManager* exported_object_manager = |
| exported_object_manager_.get(); |
| EXPECT_CALL(*exported_object_manager, RegisterAsync(_)).Times(1); |
| ExportedObjectManagerWrapper exported_om_wrapper( |
| bus_, std::move(exported_object_manager_)); |
| |
| EXPECT_CALL(*bus_, GetExportedObject(object_path)) |
| .WillOnce(Return(exported_object.get())); |
| |
| exported_om_wrapper.SetPropertyHandlerSetupCallback( |
| base::Bind(&ExportedObjectManagerWrapperTest::SetupPropertyMethodHandlers, |
| base::Unretained(this))); |
| |
| // AddExportedInterface to the not-yet-exported object will trigger that |
| // object to be exported. |
| EXPECT_CALL(*exported_object_manager, |
| ClaimInterface(object_path, dbus::kPropertiesInterface, _)) |
| .Times(1); |
| exported_om_wrapper.AddExportedInterface(object_path, kTestInterfaceName1); |
| |
| EXPECT_CALL(*exported_object_manager, |
| ClaimInterface(object_path, kTestInterfaceName1, _)) |
| .Times(1); |
| exported_om_wrapper.GetExportedInterface(object_path, kTestInterfaceName1) |
| ->ExportAsync(base::Bind([](bool success) {})); |
| |
| EXPECT_CALL(*exported_object_manager, |
| ClaimInterface(object_path, kTestInterfaceName2, _)) |
| .Times(1); |
| exported_om_wrapper.AddExportedInterface(object_path, kTestInterfaceName2); |
| exported_om_wrapper.GetExportedInterface(object_path, kTestInterfaceName2) |
| ->ExportAsync(base::Bind([](bool success) {})); |
| |
| // RemoveExportedInterface on 1 of the 2 exported interfaces will only |
| // unregister the removed interface without unregistering the object. |
| EXPECT_CALL(*exported_object_manager, |
| ReleaseInterface(object_path, kTestInterfaceName1)) |
| .Times(1); |
| exported_om_wrapper.RemoveExportedInterface(object_path, kTestInterfaceName1); |
| |
| // RemoveExportedInterface on the last interface should unregister that |
| // interface and also unregister the object. |
| EXPECT_CALL(*exported_object_manager, |
| ReleaseInterface(object_path, kTestInterfaceName2)) |
| .Times(1); |
| EXPECT_CALL(*exported_object_manager, |
| ReleaseInterface(object_path, dbus::kPropertiesInterface)) |
| .Times(1); |
| EXPECT_CALL(*exported_object, Unregister()).Times(1); |
| exported_om_wrapper.RemoveExportedInterface(object_path, kTestInterfaceName2); |
| Mock::VerifyAndClearExpectations(exported_object_manager); |
| Mock::VerifyAndClearExpectations(exported_object.get()); |
| } |
| |
| } // namespace bluetooth |