buffet: Add DBus proxy class for command instance object
Added DBusCommandProxy class that implements
org.chromium.Buffet.Command DBus interface, including command
methods and properties.
BUG=chromium:374864
TEST=USE=buffet P2_TEST_FILTER="buffet::*" FEATURES=test emerge-link platform2
Change-Id: Iaf17f2b7c276edc1e9f3ca09a759a4a7d4dc3b10
Reviewed-on: https://chromium-review.googlesource.com/213267
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index b95637f..74c1564 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -21,6 +21,7 @@
'commands/command_instance.cc',
'commands/command_manager.cc',
'commands/command_queue.cc',
+ 'commands/dbus_command_proxy.cc',
'commands/object_schema.cc',
'commands/prop_constraints.cc',
'commands/prop_types.cc',
@@ -68,6 +69,11 @@
'dependencies': [
'buffet_common',
],
+ 'variables': {
+ 'deps': [
+ 'libchrome-test-<(libbase_ver)',
+ ],
+ },
'includes': ['../common-mk/common_test.gypi'],
'sources': [
'any_unittest.cc',
@@ -78,6 +84,7 @@
'commands/command_instance_unittest.cc',
'commands/command_manager_unittest.cc',
'commands/command_queue_unittest.cc',
+ 'commands/dbus_command_proxy_unittest.cc',
'commands/object_schema_unittest.cc',
'commands/schema_utils_unittest.cc',
'commands/unittest_utils.cc',
diff --git a/buffet/commands/dbus_command_proxy.cc b/buffet/commands/dbus_command_proxy.cc
new file mode 100644
index 0000000..9204710
--- /dev/null
+++ b/buffet/commands/dbus_command_proxy.cc
@@ -0,0 +1,102 @@
+// 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_proxy.h"
+
+#include <chromeos/async_event_sequencer.h>
+#include <chromeos/dbus_utils.h>
+#include <chromeos/exported_object_manager.h>
+
+#include "buffet/commands/command_instance.h"
+#include "buffet/commands/prop_constraints.h"
+#include "buffet/commands/prop_types.h"
+#include "buffet/dbus_constants.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace buffet {
+
+DBusCommandProxy::DBusCommandProxy(ExportedObjectManager* object_manager,
+ const scoped_refptr<dbus::Bus>& bus,
+ CommandInstance* command_instance)
+ : object_path_(dbus_constants::kCommandServicePathPrefix +
+ command_instance->GetID()),
+ command_instance_(command_instance),
+ dbus_object_(object_manager, bus, object_path_) {
+}
+
+void DBusCommandProxy::RegisterAsync(
+ const AsyncEventSequencer::CompletionAction& completion_callback) {
+ chromeos::dbus_utils::DBusInterface* itf =
+ dbus_object_.AddOrGetInterface(dbus_constants::kCommandInterface);
+
+ // DBus methods.
+ itf->AddMethodHandler(dbus_constants::kCommandSetProgress,
+ base::Unretained(this),
+ &DBusCommandProxy::HandleSetProgress);
+ itf->AddMethodHandler(dbus_constants::kCommandAbort,
+ base::Unretained(this),
+ &DBusCommandProxy::HandleAbort);
+ itf->AddMethodHandler(dbus_constants::kCommandCancel,
+ base::Unretained(this),
+ &DBusCommandProxy::HandleCancel);
+ itf->AddMethodHandler(dbus_constants::kCommandDone,
+ base::Unretained(this),
+ &DBusCommandProxy::HandleDone);
+
+ // DBus properties.
+ itf->AddProperty(dbus_constants::kCommandName, &name_);
+ itf->AddProperty(dbus_constants::kCommandCategory, &category_);
+ itf->AddProperty(dbus_constants::kCommandId, &id_);
+ itf->AddProperty(dbus_constants::kCommandStatus, &status_);
+ itf->AddProperty(dbus_constants::kCommandProgress, &progress_);
+
+ // Set the initial property values before registering the DBus object.
+ name_.SetValue(command_instance_->GetName());
+ category_.SetValue(command_instance_->GetCategory());
+ id_.SetValue(command_instance_->GetID());
+ status_.SetValue(dbus_constants::kCommandStatusQueued);
+ progress_.SetValue(0);
+
+ // Register the command DBus object and expose its methods and properties.
+ dbus_object_.RegisterAsync(completion_callback);
+}
+
+void DBusCommandProxy::HandleSetProgress(chromeos::ErrorPtr* error,
+ int32_t progress) {
+ LOG(INFO) << "Received call to Command<"
+ << command_instance_->GetName() << ">::SetProgress("
+ << progress << ")";
+
+ // Validate |progress| parameter. Its value must be between 0 and 100.
+ IntPropType progress_type;
+ progress_type.AddMinMaxConstraint(0, 100);
+ if (progress_type.ValidateValue(progress, error)) {
+ status_.SetValue(dbus_constants::kCommandStatusInProgress);
+ progress_.SetValue(progress);
+ }
+}
+
+void DBusCommandProxy::HandleAbort(chromeos::ErrorPtr* error) {
+ LOG(INFO) << "Received call to Command<"
+ << command_instance_->GetName() << ">::Abort()";
+ status_.SetValue(dbus_constants::kCommandStatusAborted);
+}
+
+void DBusCommandProxy::HandleCancel(chromeos::ErrorPtr* error) {
+ LOG(INFO) << "Received call to Command<"
+ << command_instance_->GetName() << ">::Cancel()";
+ status_.SetValue(dbus_constants::kCommandStatusCanceled);
+}
+
+void DBusCommandProxy::HandleDone(chromeos::ErrorPtr* error) {
+ LOG(INFO) << "Received call to Command<"
+ << command_instance_->GetName() << ">::Done()";
+ status_.SetValue(dbus_constants::kCommandStatusDone);
+ progress_.SetValue(100);
+}
+
+
+} // namespace buffet
diff --git a/buffet/commands/dbus_command_proxy.h b/buffet/commands/dbus_command_proxy.h
new file mode 100644
index 0000000..a4b2a53
--- /dev/null
+++ b/buffet/commands/dbus_command_proxy.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef BUFFET_COMMANDS_DBUS_COMMAND_PROXY_H_
+#define BUFFET_COMMANDS_DBUS_COMMAND_PROXY_H_
+
+#include <memory>
+#include <string>
+
+#include <base/basictypes.h>
+#include <chromeos/dbus/dbus_object.h>
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+} // namespace dbus_utils
+} // namespace chromeos
+
+namespace buffet {
+
+class CommandInstance;
+
+class DBusCommandProxy {
+ public:
+ DBusCommandProxy(chromeos::dbus_utils::ExportedObjectManager* object_manager,
+ const scoped_refptr<dbus::Bus>& bus,
+ CommandInstance* command_instance);
+ virtual ~DBusCommandProxy() = default;
+
+ void RegisterAsync(
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction&
+ completion_callback);
+
+ private:
+ // DBus properties for org.chromium.Buffet.Command interface.
+ chromeos::dbus_utils::ExportedProperty<std::string> name_;
+ chromeos::dbus_utils::ExportedProperty<std::string> category_;
+ chromeos::dbus_utils::ExportedProperty<std::string> id_;
+ chromeos::dbus_utils::ExportedProperty<std::string> status_;
+ chromeos::dbus_utils::ExportedProperty<int32_t> progress_;
+
+ // Handles calls to org.chromium.Buffet.Command.SetProgress(progress).
+ void HandleSetProgress(chromeos::ErrorPtr* error, int32_t progress);
+ // Handles calls to org.chromium.Buffet.Command.Abort().
+ void HandleAbort(chromeos::ErrorPtr* error);
+ // Handles calls to org.chromium.Buffet.Command.Cancel().
+ void HandleCancel(chromeos::ErrorPtr* error);
+ // Handles calls to org.chromium.Buffet.Command.Done().
+ void HandleDone(chromeos::ErrorPtr* error);
+
+ dbus::ObjectPath object_path_;
+ CommandInstance* command_instance_;
+
+ chromeos::dbus_utils::DBusObject dbus_object_;
+
+ friend class DBusCommandProxyTest;
+ DISALLOW_COPY_AND_ASSIGN(DBusCommandProxy);
+};
+
+} // namespace buffet
+
+#endif // BUFFET_COMMANDS_DBUS_COMMAND_PROXY_H_
diff --git a/buffet/commands/dbus_command_proxy_unittest.cc b/buffet/commands/dbus_command_proxy_unittest.cc
new file mode 100644
index 0000000..8dd11a6
--- /dev/null
+++ b/buffet/commands/dbus_command_proxy_unittest.cc
@@ -0,0 +1,230 @@
+// 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 <functional>
+#include <memory>
+
+#include <dbus/mock_bus.h>
+#include <dbus/mock_exported_object.h>
+#include <dbus/property.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <gtest/gtest.h>
+
+#include "buffet/commands/command_dictionary.h"
+#include "buffet/commands/command_instance.h"
+#include "buffet/commands/dbus_command_proxy.h"
+#include "buffet/commands/unittest_utils.h"
+#include "buffet/dbus_constants.h"
+
+using ::testing::AnyNumber;
+using ::testing::Return;
+using ::testing::Invoke;
+using ::testing::_;
+
+using chromeos::dbus_utils::ExportedObjectManager;
+using buffet::unittests::CreateDictionaryValue;
+
+namespace buffet {
+
+namespace {
+
+const char kTestCommandCategoty[] = "test_command_category";
+const char kTestCommandId[] = "cmd_1";
+
+void NoAction(bool all_succeeded) {}
+
+} // namespace
+
+class DBusCommandProxyTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // Set up a mock DBus bus object.
+ 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());
+
+ // Command instance.
+ auto json = CreateDictionaryValue(R"({
+ 'robot': {
+ 'jump': {
+ 'parameters': {
+ 'height': {
+ 'type': 'integer',
+ 'minimum': 0,
+ 'maximum': 100
+ },
+ '_jumpType': {
+ 'type': 'string',
+ 'enum': ['_withAirFlip', '_withSpin', '_withKick']
+ }
+ }
+ }
+ }
+ })");
+ CHECK(dict_.LoadCommands(*json, kTestCommandCategoty, nullptr, nullptr))
+ << "Failed to parse test command dictionary";
+
+ json = CreateDictionaryValue(R"({
+ 'name': 'robot.jump',
+ 'parameters': {
+ 'height': 53,
+ '_jumpType': '_withKick'
+ }
+ })");
+ command_instance_ = CommandInstance::FromJson(json.get(), dict_, nullptr);
+ command_instance_->SetID(kTestCommandId);
+
+ // Set up a mock ExportedObject to be used with the DBus command proxy.
+ std::string cmd_path = dbus_constants::kCommandServicePathPrefix;
+ cmd_path += kTestCommandId;
+ const dbus::ObjectPath kCmdObjPath(cmd_path);
+ // Use a mock exported object for the exported object manager.
+ mock_exported_object_command_ =
+ new dbus::MockExportedObject(bus_.get(), kCmdObjPath);
+ EXPECT_CALL(*bus_, GetExportedObject(kCmdObjPath)).Times(AnyNumber())
+ .WillRepeatedly(Return(mock_exported_object_command_.get()));
+ EXPECT_CALL(*mock_exported_object_command_,
+ ExportMethod(_, _, _, _)).Times(AnyNumber());
+
+ command_proxy_.reset(new DBusCommandProxy(nullptr, bus_,
+ command_instance_.get()));
+ command_proxy_->RegisterAsync(base::Bind(NoAction));
+ }
+
+ void TearDown() override {
+ EXPECT_CALL(*mock_exported_object_command_, Unregister()).Times(1);
+ command_proxy_.reset();
+ command_instance_.reset();
+ dict_.Clear();
+ bus_ = nullptr;
+ }
+
+ chromeos::dbus_utils::DBusObject* GetProxyDBusObject() {
+ return &command_proxy_->dbus_object_;
+ }
+
+ std::string GetStatus() const {
+ return command_proxy_->status_.value();
+ }
+
+ int32_t GetProgress() const {
+ return command_proxy_->progress_.value();
+ }
+
+ std::unique_ptr<dbus::Response> CallMethod(
+ const std::string& method_name,
+ const std::function<void(dbus::MessageWriter*)>& param_callback) {
+ dbus::MethodCall method_call(dbus_constants::kCommandInterface,
+ method_name);
+ method_call.SetSerial(1234);
+ dbus::MessageWriter writer(&method_call);
+ if (param_callback)
+ param_callback(&writer);
+ return chromeos::dbus_utils::CallMethod(*GetProxyDBusObject(),
+ &method_call);
+ }
+
+ static bool IsResponseError(const std::unique_ptr<dbus::Response>& response) {
+ return (response->GetMessageType() == dbus::Message::MESSAGE_ERROR);
+ }
+
+ static void VerifyResponse(
+ const std::unique_ptr<dbus::Response>& response,
+ const std::function<void(dbus::MessageReader*)>& result_callback) {
+ EXPECT_FALSE(IsResponseError(response));
+ dbus::MessageReader reader(response.get());
+ if (result_callback)
+ result_callback(&reader);
+ EXPECT_FALSE(reader.HasMoreData());
+ }
+
+ template<typename T>
+ T GetPropertyValue(const std::string& property_name) {
+ dbus::MethodCall method_call(dbus::kPropertiesInterface,
+ dbus::kPropertiesGet);
+ method_call.SetSerial(1234);
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(dbus_constants::kCommandInterface);
+ writer.AppendString(property_name);
+ auto response = chromeos::dbus_utils::CallMethod(*GetProxyDBusObject(),
+ &method_call);
+ T value{};
+ VerifyResponse(response, [&value](dbus::MessageReader* reader) {
+ EXPECT_TRUE(chromeos::dbus_utils::PopValueFromReader(reader, &value));
+ });
+ return value;
+ }
+
+ std::unique_ptr<DBusCommandProxy> command_proxy_;
+ std::unique_ptr<CommandInstance> command_instance_;
+ CommandDictionary dict_;
+
+ scoped_refptr<dbus::MockExportedObject> mock_exported_object_command_;
+ scoped_refptr<dbus::MockBus> bus_;
+};
+
+TEST_F(DBusCommandProxyTest, Init) {
+ EXPECT_EQ(dbus_constants::kCommandStatusQueued, GetStatus());
+ EXPECT_EQ(0, GetProgress());
+ EXPECT_EQ("robot.jump",
+ GetPropertyValue<std::string>(dbus_constants::kCommandName));
+ EXPECT_EQ(kTestCommandCategoty,
+ GetPropertyValue<std::string>(dbus_constants::kCommandCategory));
+ EXPECT_EQ(kTestCommandId,
+ GetPropertyValue<std::string>(dbus_constants::kCommandId));
+ EXPECT_EQ(dbus_constants::kCommandStatusQueued,
+ GetPropertyValue<std::string>(dbus_constants::kCommandStatus));
+ EXPECT_EQ(0, GetPropertyValue<int32_t>(dbus_constants::kCommandProgress));
+}
+
+TEST_F(DBusCommandProxyTest, SetProgress) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(2);
+ auto response = CallMethod(dbus_constants::kCommandSetProgress,
+ [](dbus::MessageWriter* writer) {
+ writer->AppendInt32(10);
+ });
+ VerifyResponse(response, {});
+ EXPECT_EQ(dbus_constants::kCommandStatusInProgress, GetStatus());
+ EXPECT_EQ(10, GetProgress());
+ EXPECT_EQ(dbus_constants::kCommandStatusInProgress,
+ GetPropertyValue<std::string>(dbus_constants::kCommandStatus));
+ EXPECT_EQ(10, GetPropertyValue<int32_t>(dbus_constants::kCommandProgress));
+}
+
+TEST_F(DBusCommandProxyTest, SetProgress_OutOfRange) {
+ auto response = CallMethod(dbus_constants::kCommandSetProgress,
+ [](dbus::MessageWriter* writer) {
+ writer->AppendInt32(110);
+ });
+ EXPECT_TRUE(IsResponseError(response));
+ EXPECT_EQ(dbus_constants::kCommandStatusQueued, GetStatus());
+ EXPECT_EQ(0, GetProgress());
+}
+
+TEST_F(DBusCommandProxyTest, Abort) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
+ auto response = CallMethod(dbus_constants::kCommandAbort, {});
+ VerifyResponse(response, {});
+ EXPECT_EQ(dbus_constants::kCommandStatusAborted, GetStatus());
+}
+
+TEST_F(DBusCommandProxyTest, Cancel) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
+ auto response = CallMethod(dbus_constants::kCommandCancel, {});
+ VerifyResponse(response, {});
+ EXPECT_EQ(dbus_constants::kCommandStatusCanceled, GetStatus());
+}
+
+TEST_F(DBusCommandProxyTest, Done) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(2);
+ auto response = CallMethod(dbus_constants::kCommandDone, {});
+ VerifyResponse(response, {});
+ EXPECT_EQ(dbus_constants::kCommandStatusDone, GetStatus());
+ EXPECT_EQ(100, GetProgress());
+}
+
+} // namespace buffet
diff --git a/buffet/commands/prop_types.cc b/buffet/commands/prop_types.cc
index 505dd5d..694d74e 100644
--- a/buffet/commands/prop_types.cc
+++ b/buffet/commands/prop_types.cc
@@ -149,6 +149,13 @@
return val->FromJson(value, error) && ValidateConstraints(*val, error);
}
+bool PropType::ValidateValue(const Any& value,
+ chromeos::ErrorPtr* error) const {
+ std::shared_ptr<PropValue> val = CreateValue(value);
+ CHECK(val) << "Failed to create value object";
+ return ValidateConstraints(*val, error);
+}
+
bool PropType::ValidateConstraints(const PropValue& value,
chromeos::ErrorPtr* error) const {
for (const auto& pair : constraints_) {
diff --git a/buffet/commands/prop_types.h b/buffet/commands/prop_types.h
index 7f2b2e6..506c822 100644
--- a/buffet/commands/prop_types.h
+++ b/buffet/commands/prop_types.h
@@ -121,6 +121,9 @@
// the |error| parameter.
bool ValidateValue(const base::Value* value, chromeos::ErrorPtr* error) const;
+ // Similar to the above method, but uses Any as the value container.
+ bool ValidateValue(const Any& value, chromeos::ErrorPtr* error) const;
+
// Additional helper static methods to help with converting a type enum
// value into a string and back.
using TypeMap = std::vector<std::pair<ValueType, std::string>>;
diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc
index d8c70ba..7fc0acf 100644
--- a/buffet/dbus_constants.cc
+++ b/buffet/dbus_constants.cc
@@ -22,6 +22,29 @@
const char kManagerUpdateStateMethod[] = "UpdateState";
const char kManagerTestMethod[] = "TestMethod";
+const char kCommandInterface[] = "org.chromium.Buffet.Command";
+const char kCommandServicePathPrefix[] = "/org/chromium/Buffet/commands/";
+
+const char kCommandSetProgress[] = "SetProgress";
+const char kCommandAbort[] = "Abort";
+const char kCommandCancel[] = "Cancel";
+const char kCommandDone[] = "Done";
+
+const char kCommandName[] = "Name";
+const char kCommandCategory[] = "Category";
+const char kCommandId[] = "Id";
+const char kCommandStatus[] = "Status";
+const char kCommandProgress[] = "Progress";
+
+const char kCommandStatusQueued[] = "queued";
+const char kCommandStatusInProgress[] = "inProgress";
+const char kCommandStatusPaused[] = "paused";
+const char kCommandStatusError[] = "error";
+const char kCommandStatusDone[] = "done";
+const char kCommandStatusCanceled[] = "canceled";
+const char kCommandStatusAborted[] = "aborted";
+const char kCommandStatusExpired[] = "expired";
+
} // namespace dbus_constants
} // namespace buffet
diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h
index b7e9239..aa6cf9f 100644
--- a/buffet/dbus_constants.h
+++ b/buffet/dbus_constants.h
@@ -27,6 +27,33 @@
extern const char kManagerUpdateStateMethod[];
extern const char kManagerTestMethod[];
+// Interface implemented by the command instance objects.
+extern const char kCommandInterface[];
+extern const char kCommandServicePathPrefix[];
+
+// Methods exposed as part of kCommandInterface.
+extern const char kCommandSetProgress[];
+extern const char kCommandAbort[];
+extern const char kCommandCancel[];
+extern const char kCommandDone[];
+
+// Properties exposed as part of kCommandInterface.
+extern const char kCommandName[];
+extern const char kCommandCategory[];
+extern const char kCommandId[];
+extern const char kCommandStatus[];
+extern const char kCommandProgress[];
+
+// Values for command execution status.
+extern const char kCommandStatusQueued[];
+extern const char kCommandStatusInProgress[];
+extern const char kCommandStatusPaused[];
+extern const char kCommandStatusError[];
+extern const char kCommandStatusDone[];
+extern const char kCommandStatusCanceled[];
+extern const char kCommandStatusAborted[];
+extern const char kCommandStatusExpired[];
+
} // namespace dbus_constants
} // namespace buffet