// 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_object_manager.h>
#include <base/bind.h>
#include <brillo/dbus/dbus_object_test_helpers.h>
#include <brillo/dbus/utils.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_exported_object.h>
#include <dbus/object_manager.h>
#include <dbus/object_path.h>
#include <gtest/gtest.h>
using ::testing::AnyNumber;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::_;
namespace brillo {
namespace dbus_utils {
namespace {
const dbus::ObjectPath kTestPath(std::string("/test/om_path"));
const dbus::ObjectPath kClaimedTestPath(std::string("/test/claimed_path"));
const std::string kClaimedInterface("claimed.interface");
const std::string kTestPropertyName("PropertyName");
const std::string kTestPropertyValue("PropertyValue");
void WriteTestPropertyDict(VariantDictionary* dict) {
dict->insert(std::make_pair(kTestPropertyName, Any(kTestPropertyValue)));
}
void ReadTestPropertyDict(dbus::MessageReader* reader) {
dbus::MessageReader all_properties(nullptr);
dbus::MessageReader each_property(nullptr);
ASSERT_TRUE(reader->PopArray(&all_properties));
ASSERT_TRUE(all_properties.PopDictEntry(&each_property));
std::string property_name;
std::string property_value;
ASSERT_TRUE(each_property.PopString(&property_name));
ASSERT_TRUE(each_property.PopVariantOfString(&property_value));
EXPECT_FALSE(each_property.HasMoreData());
EXPECT_FALSE(all_properties.HasMoreData());
EXPECT_EQ(property_name, kTestPropertyName);
EXPECT_EQ(property_value, kTestPropertyValue);
}
void VerifyInterfaceClaimSignal(dbus::Signal* signal) {
EXPECT_EQ(signal->GetInterface(), std::string(dbus::kObjectManagerInterface));
EXPECT_EQ(signal->GetMember(),
std::string(dbus::kObjectManagerInterfacesAdded));
// org.freedesktop.DBus.ObjectManager.InterfacesAdded (
// OBJPATH object_path,
// DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties);
dbus::MessageReader reader(signal);
dbus::MessageReader all_interfaces(nullptr);
dbus::MessageReader each_interface(nullptr);
dbus::ObjectPath path;
ASSERT_TRUE(reader.PopObjectPath(&path));
ASSERT_TRUE(reader.PopArray(&all_interfaces));
ASSERT_TRUE(all_interfaces.PopDictEntry(&each_interface));
std::string interface_name;
ASSERT_TRUE(each_interface.PopString(&interface_name));
ReadTestPropertyDict(&each_interface);
EXPECT_FALSE(each_interface.HasMoreData());
EXPECT_FALSE(all_interfaces.HasMoreData());
EXPECT_FALSE(reader.HasMoreData());
EXPECT_EQ(interface_name, kClaimedInterface);
EXPECT_EQ(path, kClaimedTestPath);
}
void VerifyInterfaceDropSignal(dbus::Signal* signal) {
EXPECT_EQ(signal->GetInterface(), std::string(dbus::kObjectManagerInterface));
EXPECT_EQ(signal->GetMember(),
std::string(dbus::kObjectManagerInterfacesRemoved));
// org.freedesktop.DBus.ObjectManager.InterfacesRemoved (
// OBJPATH object_path, ARRAY<STRING> interfaces);
dbus::MessageReader reader(signal);
dbus::MessageReader each_interface(nullptr);
dbus::ObjectPath path;
ASSERT_TRUE(reader.PopObjectPath(&path));
ASSERT_TRUE(reader.PopArray(&each_interface));
std::string interface_name;
ASSERT_TRUE(each_interface.PopString(&interface_name));
EXPECT_FALSE(each_interface.HasMoreData());
EXPECT_FALSE(reader.HasMoreData());
EXPECT_EQ(interface_name, kClaimedInterface);
EXPECT_EQ(path, kClaimedTestPath);
}
} // namespace
class ExportedObjectManagerTest : public ::testing::Test {
public:
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(), kTestPath);
EXPECT_CALL(*bus_, GetExportedObject(kTestPath)).Times(1).WillOnce(
Return(mock_exported_object_.get()));
EXPECT_CALL(*mock_exported_object_, ExportMethod(_, _, _, _))
.Times(AnyNumber());
om_.reset(new ExportedObjectManager(bus_.get(), kTestPath));
property_writer_ = base::Bind(&WriteTestPropertyDict);
om_->RegisterAsync(AsyncEventSequencer::GetDefaultCompletionAction());
}
void TearDown() override {
EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
om_.reset();
bus_ = nullptr;
}
std::unique_ptr<dbus::Response> CallHandleGetManagedObjects() {
dbus::MethodCall method_call(dbus::kObjectManagerInterface,
dbus::kObjectManagerGetManagedObjects);
method_call.SetSerial(1234);
return brillo::dbus_utils::testing::CallMethod(om_->dbus_object_,
&method_call);
}
scoped_refptr<dbus::MockBus> bus_;
scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
std::unique_ptr<ExportedObjectManager> om_;
ExportedPropertySet::PropertyWriter property_writer_;
};
TEST_F(ExportedObjectManagerTest, ClaimInterfaceSendsSignals) {
EXPECT_CALL(*mock_exported_object_, SendSignal(_))
.Times(1).WillOnce(Invoke(&VerifyInterfaceClaimSignal));
om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
}
TEST_F(ExportedObjectManagerTest, ReleaseInterfaceSendsSignals) {
InSequence dummy;
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
EXPECT_CALL(*mock_exported_object_, SendSignal(_))
.Times(1).WillOnce(Invoke(&VerifyInterfaceDropSignal));
om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
om_->ReleaseInterface(kClaimedTestPath, kClaimedInterface);
}
TEST_F(ExportedObjectManagerTest, GetManagedObjectsResponseEmptyCorrectness) {
auto response = CallHandleGetManagedObjects();
dbus::MessageReader reader(response.get());
dbus::MessageReader all_paths(nullptr);
ASSERT_TRUE(reader.PopArray(&all_paths));
EXPECT_FALSE(reader.HasMoreData());
}
TEST_F(ExportedObjectManagerTest, GetManagedObjectsResponseCorrectness) {
// org.freedesktop.DBus.ObjectManager.GetManagedObjects (
// out DICT<OBJPATH,
// DICT<STRING,
// DICT<STRING,VARIANT>>> )
EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
auto response = CallHandleGetManagedObjects();
dbus::MessageReader reader(response.get());
dbus::MessageReader all_paths(nullptr);
dbus::MessageReader each_path(nullptr);
dbus::MessageReader all_interfaces(nullptr);
dbus::MessageReader each_interface(nullptr);
ASSERT_TRUE(reader.PopArray(&all_paths));
ASSERT_TRUE(all_paths.PopDictEntry(&each_path));
dbus::ObjectPath path;
ASSERT_TRUE(each_path.PopObjectPath(&path));
ASSERT_TRUE(each_path.PopArray(&all_interfaces));
ASSERT_TRUE(all_interfaces.PopDictEntry(&each_interface));
std::string interface_name;
ASSERT_TRUE(each_interface.PopString(&interface_name));
ReadTestPropertyDict(&each_interface);
EXPECT_FALSE(each_interface.HasMoreData());
EXPECT_FALSE(all_interfaces.HasMoreData());
EXPECT_FALSE(each_path.HasMoreData());
EXPECT_FALSE(all_paths.HasMoreData());
EXPECT_FALSE(reader.HasMoreData());
EXPECT_EQ(path, kClaimedTestPath);
EXPECT_EQ(interface_name, kClaimedInterface);
}
} // namespace dbus_utils
} // namespace brillo