blob: 1e27aacbf804af149ba6ecab58e627763d397f48 [file] [log] [blame]
// 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 "buffet/commands/dbus_command_dispatcher.h"
#include <memory>
#include <string>
#include <chromeos/dbus/exported_object_manager.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>
#include "buffet/commands/command_dictionary.h"
#include "buffet/commands/command_queue.h"
#include "buffet/commands/unittest_utils.h"
#include "buffet/dbus_constants.h"
using buffet::unittests::CreateDictionaryValue;
using chromeos::dbus_utils::AsyncEventSequencer;
using testing::AnyNumber;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using testing::_;
namespace buffet {
namespace {
const char kCommandCategory[] = "test_category";
} // anonymous namespace
class DBusCommandDispacherTest : public testing::Test {
public:
void SetUp() override {
command_queue_.SetNowForTest(base::Time::Max());
const dbus::ObjectPath kExportedObjectManagerPath("/test/om_path");
std::string cmd_path = dbus_constants::kCommandServicePathPrefix;
cmd_path += "1";
const dbus::ObjectPath kCmdObjPath(cmd_path);
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 manager.
mock_exported_object_manager_ = new dbus::MockExportedObject(
bus_.get(), kExportedObjectManagerPath);
EXPECT_CALL(*bus_, GetExportedObject(kExportedObjectManagerPath))
.WillRepeatedly(Return(mock_exported_object_manager_.get()));
EXPECT_CALL(*mock_exported_object_manager_,
ExportMethod(_, _, _, _)).Times(AnyNumber());
om_.reset(new chromeos::dbus_utils::ExportedObjectManager(
bus_.get(), kExportedObjectManagerPath));
om_->RegisterAsync(AsyncEventSequencer::GetDefaultCompletionAction());
command_dispatcher_.reset(
new DBusCommandDispacher(om_->GetBus(), om_.get()));
command_queue_.SetCommandDispachInterface(command_dispatcher_.get());
// Use a mock exported object for command proxy.
mock_exported_command_proxy_ = new dbus::MockExportedObject(
bus_.get(), kCmdObjPath);
EXPECT_CALL(*bus_, GetExportedObject(kCmdObjPath))
.WillRepeatedly(Return(mock_exported_command_proxy_.get()));
EXPECT_CALL(*mock_exported_command_proxy_, ExportMethod(_, _, _, _))
.WillRepeatedly(Invoke(MockExportMethod));
auto json = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'integer'},
'results': {}
},
'shutdown': {
'parameters': {},
'results': {}
}
}
})");
CHECK(dictionary_.LoadCommands(*json, kCommandCategory, nullptr, nullptr))
<< "Failed to load command dictionary";
}
void TearDown() override {
EXPECT_CALL(*mock_exported_object_manager_, Unregister()).Times(1);
om_.reset();
bus_ = nullptr;
}
static void MockExportMethod(
const std::string& interface_name,
const std::string& method_name,
dbus::ExportedObject::MethodCallCallback method_call_callback,
dbus::ExportedObject::OnExportedCallback on_exported_callback) {
on_exported_callback.Run(interface_name, method_name, true);
}
void AddNewCommand(const std::string& json, const std::string& id) {
auto command_instance = CommandInstance::FromJson(
CreateDictionaryValue(json.c_str()).get(), dictionary_, nullptr);
command_instance->SetID(id);
// Two interfaces are added - Command and Properties.
EXPECT_CALL(*mock_exported_object_manager_, SendSignal(_)).Times(2);
command_queue_.Add(std::move(command_instance));
}
DBusCommandProxy* FindProxy(CommandInstance* command_instance) {
CHECK_EQ(command_instance->proxies_.size(), 1U);
return static_cast<DBusCommandProxy*>(command_instance->proxies_[0].get());
}
void FinishCommand(DBusCommandProxy* proxy) {
proxy->Done();
}
void SetProgress(DBusCommandProxy* proxy, int progress) {
proxy->SetProgress(nullptr, progress);
}
scoped_refptr<dbus::MockBus> bus_;
scoped_refptr<dbus::MockExportedObject> mock_exported_object_manager_;
scoped_refptr<dbus::MockExportedObject> mock_exported_command_proxy_;
std::unique_ptr<chromeos::dbus_utils::ExportedObjectManager> om_;
CommandDictionary dictionary_;
CommandQueue command_queue_;
std::unique_ptr<DBusCommandDispacher> command_dispatcher_;
};
TEST_F(DBusCommandDispacherTest, Test_Command_Base_Shutdown) {
const std::string id = "id0000";
AddNewCommand("{'name':'base.shutdown'}", id);
CommandInstance* command_instance = command_queue_.Find(id);
ASSERT_NE(nullptr, command_instance);
DBusCommandProxy* command_proxy = FindProxy(command_instance);
ASSERT_NE(nullptr, command_proxy);
EXPECT_EQ(CommandInstance::kStatusQueued, command_instance->GetStatus());
// Two properties are set, Progress = 50%, Status = "inProgress"
EXPECT_CALL(*mock_exported_command_proxy_, SendSignal(_)).Times(2);
SetProgress(command_proxy, 50);
EXPECT_EQ(CommandInstance::kStatusInProgress, command_instance->GetStatus());
EXPECT_EQ(50, command_instance->GetProgress());
// Command must be removed from the queue and proxy destroyed after calling
// FinishCommand().
// Two properties are set, Progress = 100%, Status = "done"
EXPECT_CALL(*mock_exported_command_proxy_, SendSignal(_)).Times(2);
// D-Bus command proxy is going away.
EXPECT_CALL(*mock_exported_command_proxy_, Unregister()).Times(1);
// Two interfaces are being removed on the D-Bus command object.
EXPECT_CALL(*mock_exported_object_manager_, SendSignal(_)).Times(2);
FinishCommand(command_proxy);
EXPECT_EQ(nullptr, command_queue_.Find(id));
}
TEST_F(DBusCommandDispacherTest, Test_Command_Base_Reboot) {
const std::string id = "id0001";
AddNewCommand(R"({
'name': 'base.reboot',
'parameters': {
'delay': 20
}
})", id);
CommandInstance* command_instance = command_queue_.Find(id);
ASSERT_NE(nullptr, command_instance);
DBusCommandProxy* command_proxy = FindProxy(command_instance);
ASSERT_NE(nullptr, command_proxy);
EXPECT_EQ(CommandInstance::kStatusQueued, command_instance->GetStatus());
// Two properties are set, Progress = 50%, Status = "inProgress"
EXPECT_CALL(*mock_exported_command_proxy_, SendSignal(_)).Times(2);
SetProgress(command_proxy, 50);
EXPECT_EQ(CommandInstance::kStatusInProgress, command_instance->GetStatus());
EXPECT_EQ(50, command_instance->GetProgress());
// Command must be removed from the queue and proxy destroyed after calling
// FinishCommand().
// Two properties are set, Progress = 100%, Status = "done"
EXPECT_CALL(*mock_exported_command_proxy_, SendSignal(_)).Times(2);
// D-Bus command proxy is going away.
EXPECT_CALL(*mock_exported_command_proxy_, Unregister()).Times(1);
// Two interfaces are being removed on the D-Bus command object.
EXPECT_CALL(*mock_exported_object_manager_, SendSignal(_)).Times(2);
FinishCommand(command_proxy);
EXPECT_EQ(nullptr, command_queue_.Find(id));
}
} // namespace buffet