buffet: Move commands/dbus_* our of libweave
This code is owned by buffet now.
BUG=brillo:1245
TEST='FEATURES=test emerge-gizmo buffet'
Change-Id: I148336aba06a89167539fdd8d987080ec4cda82d
Reviewed-on: https://chromium-review.googlesource.com/287126
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index d6fb0ad..8d22323 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -27,6 +27,9 @@
'sources': [
'dbus_bindings/org.chromium.Buffet.Command.xml',
'dbus_bindings/org.chromium.Buffet.Manager.xml',
+ 'dbus_command_dispatcher.cc',
+ 'dbus_command_proxy.cc',
+ 'dbus_conversion.cc',
'dbus_constants.cc',
'manager.cc',
'../libweave/src/base_api_handler.cc',
@@ -37,9 +40,6 @@
'../libweave/src/commands/command_instance.cc',
'../libweave/src/commands/command_manager.cc',
'../libweave/src/commands/command_queue.cc',
- '../libweave/src/commands/dbus_command_dispatcher.cc',
- '../libweave/src/commands/dbus_command_proxy.cc',
- '../libweave/src/commands/dbus_conversion.cc',
'../libweave/src/commands/object_schema.cc',
'../libweave/src/commands/prop_constraints.cc',
'../libweave/src/commands/prop_types.cc',
@@ -184,6 +184,8 @@
},
'includes': ['../common-mk/common_test.gypi'],
'sources': [
+ 'dbus_command_proxy_unittest.cc',
+ 'dbus_conversion_unittest.cc',
'../libweave/src/base_api_handler_unittest.cc',
'../libweave/src/buffet_config_unittest.cc',
'../libweave/src/buffet_testrunner.cc',
@@ -193,8 +195,6 @@
'../libweave/src/commands/command_instance_unittest.cc',
'../libweave/src/commands/command_manager_unittest.cc',
'../libweave/src/commands/command_queue_unittest.cc',
- '../libweave/src/commands/dbus_command_proxy_unittest.cc',
- '../libweave/src/commands/dbus_conversion_unittest.cc',
'../libweave/src/commands/mock_command.cc',
'../libweave/src/commands/object_schema_unittest.cc',
'../libweave/src/commands/schema_utils_unittest.cc',
diff --git a/buffet/dbus_command_dispatcher.cc b/buffet/dbus_command_dispatcher.cc
new file mode 100644
index 0000000..39e106b
--- /dev/null
+++ b/buffet/dbus_command_dispatcher.cc
@@ -0,0 +1,36 @@
+// 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/dbus_command_dispatcher.h"
+
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "buffet/dbus_command_proxy.h"
+#include "buffet/dbus_constants.h"
+#include "weave/command.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace buffet {
+
+DBusCommandDispacher::DBusCommandDispacher(
+ const base::WeakPtr<ExportedObjectManager>& object_manager,
+ weave::Commands* command_manager)
+ : object_manager_{object_manager} {
+ command_manager->AddOnCommandAddedCallback(base::Bind(
+ &DBusCommandDispacher::OnCommandAdded, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DBusCommandDispacher::OnCommandAdded(weave::Command* command) {
+ if (!object_manager_)
+ return;
+ std::unique_ptr<DBusCommandProxy> proxy{new DBusCommandProxy(
+ object_manager_.get(), object_manager_->GetBus(), command,
+ buffet::kCommandServicePathPrefix + std::to_string(++next_id_))};
+ proxy->RegisterAsync(AsyncEventSequencer::GetDefaultCompletionAction());
+ command->AddObserver(proxy.release());
+}
+
+} // namespace buffet
diff --git a/buffet/dbus_command_dispatcher.h b/buffet/dbus_command_dispatcher.h
new file mode 100644
index 0000000..ae8e499
--- /dev/null
+++ b/buffet/dbus_command_dispatcher.h
@@ -0,0 +1,54 @@
+// 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_DBUS_COMMAND_DISPATCHER_H_
+#define BUFFET_DBUS_COMMAND_DISPATCHER_H_
+
+#include <map>
+#include <string>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+
+#include "weave/commands.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+} // namespace dbus_utils
+} // namespace chromeos
+
+namespace buffet {
+
+// Implements D-Bus dispatch of commands. When OnCommandAdded is called,
+// DBusCommandDispacher creates an instance of DBusCommandProxy object and
+// advertises it through ExportedObjectManager on D-Bus. Command handling
+// processes can watch the new D-Bus object appear and communicate with it to
+// update the command handling progress. Once command is handled,
+// DBusCommandProxy::Done() is called and the command is removed from the
+// command queue and D-Bus ExportedObjectManager.
+class DBusCommandDispacher final {
+ public:
+ explicit DBusCommandDispacher(
+ const base::WeakPtr<chromeos::dbus_utils::ExportedObjectManager>&
+ object_manager,
+ weave::Commands* command_manager);
+
+ private:
+ void OnCommandAdded(weave::Command* command);
+
+ base::WeakPtr<chromeos::dbus_utils::ExportedObjectManager> object_manager_;
+ int next_id_{0};
+
+ // Default constructor is used in special circumstances such as for testing.
+ DBusCommandDispacher() = default;
+
+ base::WeakPtrFactory<DBusCommandDispacher> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(DBusCommandDispacher);
+};
+
+} // namespace buffet
+
+#endif // BUFFET_DBUS_COMMAND_DISPATCHER_H_
diff --git a/buffet/dbus_command_proxy.cc b/buffet/dbus_command_proxy.cc
new file mode 100644
index 0000000..d53355a
--- /dev/null
+++ b/buffet/dbus_command_proxy.cc
@@ -0,0 +1,103 @@
+// 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/dbus_command_proxy.h"
+
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "buffet/dbus_conversion.h"
+#include "weave/enum_to_string.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace buffet {
+
+DBusCommandProxy::DBusCommandProxy(ExportedObjectManager* object_manager,
+ const scoped_refptr<dbus::Bus>& bus,
+ weave::Command* command,
+ std::string object_path)
+ : command_{command},
+ dbus_object_{object_manager, bus, dbus::ObjectPath{object_path}} {}
+
+void DBusCommandProxy::RegisterAsync(
+ const AsyncEventSequencer::CompletionAction& completion_callback) {
+ dbus_adaptor_.RegisterWithDBusObject(&dbus_object_);
+
+ // Set the initial property values before registering the DBus object.
+ dbus_adaptor_.SetName(command_->GetName());
+ dbus_adaptor_.SetCategory(command_->GetCategory());
+ dbus_adaptor_.SetId(command_->GetID());
+ dbus_adaptor_.SetStatus(EnumToString(command_->GetStatus()));
+ dbus_adaptor_.SetProgress(
+ DictionaryToDBusVariantDictionary(*command_->GetProgress()));
+ dbus_adaptor_.SetOrigin(EnumToString(command_->GetOrigin()));
+ dbus_adaptor_.SetParameters(
+ DictionaryToDBusVariantDictionary(*command_->GetParameters()));
+ dbus_adaptor_.SetResults(
+ DictionaryToDBusVariantDictionary(*command_->GetResults()));
+
+ // Register the command DBus object and expose its methods and properties.
+ dbus_object_.RegisterAsync(completion_callback);
+}
+
+void DBusCommandProxy::OnResultsChanged() {
+ dbus_adaptor_.SetResults(
+ DictionaryToDBusVariantDictionary(*command_->GetResults()));
+}
+
+void DBusCommandProxy::OnStatusChanged() {
+ dbus_adaptor_.SetStatus(EnumToString(command_->GetStatus()));
+}
+
+void DBusCommandProxy::OnProgressChanged() {
+ dbus_adaptor_.SetProgress(
+ DictionaryToDBusVariantDictionary(*command_->GetProgress()));
+}
+
+void DBusCommandProxy::OnCommandDestroyed() {
+ delete this;
+}
+
+bool DBusCommandProxy::SetProgress(
+ chromeos::ErrorPtr* error,
+ const chromeos::VariantDictionary& progress) {
+ LOG(INFO) << "Received call to Command<" << command_->GetName()
+ << ">::SetProgress()";
+ auto dictionary = DictionaryFromDBusVariantDictionary(progress, error);
+ if (!dictionary)
+ return false;
+ return command_->SetProgress(*dictionary, error);
+}
+
+bool DBusCommandProxy::SetResults(chromeos::ErrorPtr* error,
+ const chromeos::VariantDictionary& results) {
+ LOG(INFO) << "Received call to Command<" << command_->GetName()
+ << ">::SetResults()";
+ auto dictionary = DictionaryFromDBusVariantDictionary(results, error);
+ if (!dictionary)
+ return false;
+ return command_->SetResults(*dictionary, error);
+}
+
+void DBusCommandProxy::Abort() {
+ LOG(INFO) << "Received call to Command<" << command_->GetName()
+ << ">::Abort()";
+ command_->Abort();
+}
+
+void DBusCommandProxy::Cancel() {
+ LOG(INFO) << "Received call to Command<" << command_->GetName()
+ << ">::Cancel()";
+ command_->Cancel();
+}
+
+void DBusCommandProxy::Done() {
+ LOG(INFO) << "Received call to Command<" << command_->GetName()
+ << ">::Done()";
+ command_->Done();
+}
+
+} // namespace buffet
diff --git a/buffet/dbus_command_proxy.h b/buffet/dbus_command_proxy.h
new file mode 100644
index 0000000..86d9c6e
--- /dev/null
+++ b/buffet/dbus_command_proxy.h
@@ -0,0 +1,69 @@
+// 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_DBUS_COMMAND_PROXY_H_
+#define BUFFET_DBUS_COMMAND_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <chromeos/dbus/data_serialization.h>
+#include <chromeos/dbus/dbus_object.h>
+
+#include "buffet/org.chromium.Buffet.Command.h"
+#include "weave/command.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+} // namespace dbus_utils
+} // namespace chromeos
+
+namespace buffet {
+
+class DBusCommandProxy : public weave::Command::Observer,
+ public org::chromium::Buffet::CommandInterface {
+ public:
+ DBusCommandProxy(chromeos::dbus_utils::ExportedObjectManager* object_manager,
+ const scoped_refptr<dbus::Bus>& bus,
+ weave::Command* command,
+ std::string object_path);
+ ~DBusCommandProxy() override = default;
+
+ void RegisterAsync(
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction&
+ completion_callback);
+
+ // CommandProxyInterface implementation/overloads.
+ void OnResultsChanged() override;
+ void OnStatusChanged() override;
+ void OnProgressChanged() override;
+ void OnCommandDestroyed() override;
+
+ private:
+ // Handles calls to org.chromium.Buffet.Command.SetProgress(progress).
+ bool SetProgress(chromeos::ErrorPtr* error,
+ const chromeos::VariantDictionary& progress) override;
+ // Handles calls to org.chromium.Buffet.Command.SetResults(results).
+ bool SetResults(chromeos::ErrorPtr* error,
+ const chromeos::VariantDictionary& results) override;
+ // Handles calls to org.chromium.Buffet.Command.Abort().
+ void Abort() override;
+ // Handles calls to org.chromium.Buffet.Command.Cancel().
+ void Cancel() override;
+ // Handles calls to org.chromium.Buffet.Command.Done().
+ void Done() override;
+
+ weave::Command* command_;
+ org::chromium::Buffet::CommandAdaptor dbus_adaptor_{this};
+ chromeos::dbus_utils::DBusObject dbus_object_;
+
+ friend class DBusCommandProxyTest;
+ friend class DBusCommandDispacherTest;
+ DISALLOW_COPY_AND_ASSIGN(DBusCommandProxy);
+};
+
+} // namespace buffet
+
+#endif // BUFFET_DBUS_COMMAND_PROXY_H_
diff --git a/buffet/dbus_command_proxy_unittest.cc b/buffet/dbus_command_proxy_unittest.cc
new file mode 100644
index 0000000..93f5d01
--- /dev/null
+++ b/buffet/dbus_command_proxy_unittest.cc
@@ -0,0 +1,206 @@
+// 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/dbus_command_proxy.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <dbus/mock_bus.h>
+#include <dbus/mock_exported_object.h>
+#include <dbus/property.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <chromeos/dbus/dbus_object_test_helpers.h>
+#include <gtest/gtest.h>
+
+#include "buffet/dbus_constants.h"
+#include "weave/command.h"
+#include "weave/enum_to_string.h"
+#include "weave/mock_command.h"
+#include "weave/mock_commands.h"
+#include "weave/unittest_utils.h"
+
+namespace buffet {
+
+using ::testing::AnyNumber;
+using ::testing::Return;
+using ::testing::ReturnRefOfCopy;
+using ::testing::_;
+
+using chromeos::VariantDictionary;
+using chromeos::dbus_utils::AsyncEventSequencer;
+using weave::unittests::CreateDictionaryValue;
+using weave::unittests::IsEqualValue;
+
+namespace {
+
+const char kTestCommandCategoty[] = "test_command_category";
+const char kTestCommandId[] = "cmd_1";
+
+MATCHER_P(EqualToJson, json, "") {
+ auto json_value = CreateDictionaryValue(json);
+ return IsEqualValue(*json_value, arg);
+}
+
+} // 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());
+
+ EXPECT_CALL(command_, GetID())
+ .WillOnce(ReturnRefOfCopy<std::string>(kTestCommandId));
+ // Use WillRepeatedly becase GetName is used for logging.
+ EXPECT_CALL(command_, GetName())
+ .WillRepeatedly(ReturnRefOfCopy<std::string>("robot.jump"));
+ EXPECT_CALL(command_, GetCategory())
+ .WillOnce(ReturnRefOfCopy<std::string>(kTestCommandCategoty));
+ EXPECT_CALL(command_, GetStatus())
+ .WillOnce(Return(weave::CommandStatus::kQueued));
+ EXPECT_CALL(command_, GetOrigin())
+ .WillOnce(Return(weave::CommandOrigin::kLocal));
+ EXPECT_CALL(command_, MockGetParameters())
+ .WillOnce(ReturnRefOfCopy<std::string>(R"({
+ 'height': 53,
+ '_jumpType': '_withKick'
+ })"));
+ EXPECT_CALL(command_, MockGetProgress())
+ .WillOnce(ReturnRefOfCopy<std::string>("{}"));
+ EXPECT_CALL(command_, MockGetResults())
+ .WillOnce(ReturnRefOfCopy<std::string>("{}"));
+
+ // Set up a mock ExportedObject to be used with the DBus command proxy.
+ std::string cmd_path = buffet::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());
+
+ proxy_.reset(new DBusCommandProxy(nullptr, bus_, &command_, cmd_path));
+ GetCommandProxy()->RegisterAsync(
+ AsyncEventSequencer::GetDefaultCompletionAction());
+ }
+
+ void TearDown() override {
+ EXPECT_CALL(*mock_exported_object_command_, Unregister()).Times(1);
+ bus_ = nullptr;
+ }
+
+ DBusCommandProxy* GetCommandProxy() const { return proxy_.get(); }
+
+ org::chromium::Buffet::CommandAdaptor* GetCommandAdaptor() const {
+ return &GetCommandProxy()->dbus_adaptor_;
+ }
+
+ org::chromium::Buffet::CommandInterface* GetCommandInterface() const {
+ // DBusCommandProxy also implements CommandInterface.
+ return GetCommandProxy();
+ }
+
+ weave::CommandStatus GetCommandStatus() const {
+ weave::CommandStatus status;
+ EXPECT_TRUE(StringToEnum(GetCommandAdaptor()->GetStatus(), &status));
+ return status;
+ }
+
+ scoped_refptr<dbus::MockExportedObject> mock_exported_object_command_;
+ scoped_refptr<dbus::MockBus> bus_;
+
+ weave::unittests::MockCommand command_;
+ std::unique_ptr<DBusCommandProxy> proxy_;
+};
+
+TEST_F(DBusCommandProxyTest, Init) {
+ VariantDictionary params = {
+ {"height", int32_t{53}}, {"_jumpType", std::string{"_withKick"}},
+ };
+ EXPECT_EQ(weave::CommandStatus::kQueued, GetCommandStatus());
+ EXPECT_EQ(params, GetCommandAdaptor()->GetParameters());
+ EXPECT_EQ(VariantDictionary{}, GetCommandAdaptor()->GetProgress());
+ EXPECT_EQ(VariantDictionary{}, GetCommandAdaptor()->GetResults());
+ EXPECT_EQ("robot.jump", GetCommandAdaptor()->GetName());
+ EXPECT_EQ(kTestCommandCategoty, GetCommandAdaptor()->GetCategory());
+ EXPECT_EQ(kTestCommandId, GetCommandAdaptor()->GetId());
+}
+
+TEST_F(DBusCommandProxyTest, OnProgressChanged) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
+ EXPECT_CALL(command_, MockGetProgress())
+ .WillOnce(ReturnRefOfCopy<std::string>("{'progress': 10}"));
+ proxy_->OnProgressChanged();
+ EXPECT_EQ((VariantDictionary{{"progress", int32_t{10}}}),
+ GetCommandAdaptor()->GetProgress());
+}
+
+TEST_F(DBusCommandProxyTest, OnResultsChanged) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
+ EXPECT_CALL(command_, MockGetResults())
+ .WillOnce(ReturnRefOfCopy<std::string>(
+ "{'foo': 42, 'bar': 'foobar', 'resultList': [1, 2, 3]}"));
+ proxy_->OnResultsChanged();
+
+ EXPECT_EQ((VariantDictionary{{"foo", int32_t{42}},
+ {"bar", std::string{"foobar"}},
+ {"resultList", std::vector<int>{1, 2, 3}}}),
+ GetCommandAdaptor()->GetResults());
+}
+
+TEST_F(DBusCommandProxyTest, OnStatusChanged) {
+ EXPECT_CALL(*mock_exported_object_command_, SendSignal(_)).Times(1);
+ EXPECT_CALL(command_, GetStatus())
+ .WillOnce(Return(weave::CommandStatus::kInProgress));
+ proxy_->OnStatusChanged();
+ EXPECT_EQ(weave::CommandStatus::kInProgress, GetCommandStatus());
+}
+
+TEST_F(DBusCommandProxyTest, SetProgress) {
+ EXPECT_CALL(command_, SetProgress(EqualToJson("{'progress': 10}"), _))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(
+ GetCommandInterface()->SetProgress(nullptr, {{"progress", int32_t{10}}}));
+}
+
+TEST_F(DBusCommandProxyTest, SetResults) {
+ EXPECT_CALL(
+ command_,
+ SetResults(
+ EqualToJson("{'foo': 42, 'bar': 'foobar', 'resultList': [1, 2, 3]}"),
+ _))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(GetCommandInterface()->SetResults(
+ nullptr, VariantDictionary{{"foo", int32_t{42}},
+ {"bar", std::string{"foobar"}},
+ {"resultList", std::vector<int>{1, 2, 3}}}));
+}
+
+TEST_F(DBusCommandProxyTest, Abort) {
+ EXPECT_CALL(command_, Abort());
+ GetCommandInterface()->Abort();
+}
+
+TEST_F(DBusCommandProxyTest, Cancel) {
+ EXPECT_CALL(command_, Cancel());
+ GetCommandInterface()->Cancel();
+}
+
+TEST_F(DBusCommandProxyTest, Done) {
+ EXPECT_CALL(command_, Done());
+ GetCommandInterface()->Done();
+}
+
+} // namespace buffet
diff --git a/buffet/dbus_conversion.cc b/buffet/dbus_conversion.cc
new file mode 100644
index 0000000..51d35f1
--- /dev/null
+++ b/buffet/dbus_conversion.cc
@@ -0,0 +1,246 @@
+// Copyright 2015 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/dbus_conversion.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <chromeos/type_name_undecorate.h>
+
+namespace buffet {
+
+namespace {
+
+// Helpers for JsonToAny().
+template <typename T>
+chromeos::Any ValueToAny(const base::Value& json,
+ bool (base::Value::*fnc)(T*) const) {
+ T val;
+ CHECK((json.*fnc)(&val));
+ return val;
+}
+
+chromeos::Any ValueToAny(const base::Value& json);
+
+template <typename T>
+chromeos::Any ListToAny(const base::ListValue& list,
+ bool (base::Value::*fnc)(T*) const) {
+ std::vector<T> result;
+ result.reserve(list.GetSize());
+ for (const base::Value* v : list) {
+ T val;
+ CHECK((v->*fnc)(&val));
+ result.push_back(val);
+ }
+ return result;
+}
+
+chromeos::Any DictListToAny(const base::ListValue& list) {
+ std::vector<chromeos::VariantDictionary> result;
+ result.reserve(list.GetSize());
+ for (const base::Value* v : list) {
+ const base::DictionaryValue* dict = nullptr;
+ CHECK(v->GetAsDictionary(&dict));
+ result.push_back(DictionaryToDBusVariantDictionary(*dict));
+ }
+ return result;
+}
+
+chromeos::Any ListListToAny(const base::ListValue& list) {
+ std::vector<chromeos::Any> result;
+ result.reserve(list.GetSize());
+ for (const base::Value* v : list)
+ result.push_back(ValueToAny(*v));
+ return result;
+}
+
+// Converts a JSON value into an Any so it can be sent over D-Bus using
+// UpdateState D-Bus method from Buffet.
+chromeos::Any ValueToAny(const base::Value& json) {
+ chromeos::Any prop_value;
+ switch (json.GetType()) {
+ case base::Value::TYPE_BOOLEAN:
+ prop_value = ValueToAny<bool>(json, &base::Value::GetAsBoolean);
+ break;
+ case base::Value::TYPE_INTEGER:
+ prop_value = ValueToAny<int>(json, &base::Value::GetAsInteger);
+ break;
+ case base::Value::TYPE_DOUBLE:
+ prop_value = ValueToAny<double>(json, &base::Value::GetAsDouble);
+ break;
+ case base::Value::TYPE_STRING:
+ prop_value = ValueToAny<std::string>(json, &base::Value::GetAsString);
+ break;
+ case base::Value::TYPE_DICTIONARY: {
+ const base::DictionaryValue* dict = nullptr;
+ CHECK(json.GetAsDictionary(&dict));
+ prop_value = DictionaryToDBusVariantDictionary(*dict);
+ break;
+ }
+ case base::Value::TYPE_LIST: {
+ const base::ListValue* list = nullptr;
+ CHECK(json.GetAsList(&list));
+ if (list->empty()) {
+ // We don't know type of objects this list intended for, so we just use
+ // vector<chromeos::Any>.
+ prop_value = ListListToAny(*list);
+ break;
+ }
+ auto type = (*list->begin())->GetType();
+ for (const base::Value* v : *list)
+ CHECK_EQ(v->GetType(), type) << "Unsupported different type elements";
+
+ switch (type) {
+ case base::Value::TYPE_BOOLEAN:
+ prop_value = ListToAny<bool>(*list, &base::Value::GetAsBoolean);
+ break;
+ case base::Value::TYPE_INTEGER:
+ prop_value = ListToAny<int>(*list, &base::Value::GetAsInteger);
+ break;
+ case base::Value::TYPE_DOUBLE:
+ prop_value = ListToAny<double>(*list, &base::Value::GetAsDouble);
+ break;
+ case base::Value::TYPE_STRING:
+ prop_value = ListToAny<std::string>(*list, &base::Value::GetAsString);
+ break;
+ case base::Value::TYPE_DICTIONARY:
+ prop_value = DictListToAny(*list);
+ break;
+ case base::Value::TYPE_LIST:
+ // We can't support Any{vector<vector<>>} as the type is only known
+ // in runtime when we need to instantiate templates in compile time.
+ // We can use Any{vector<Any>} instead.
+ prop_value = ListListToAny(*list);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported JSON value type for list element: "
+ << (*list->begin())->GetType();
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected JSON value type: " << json.GetType();
+ break;
+ }
+ return prop_value;
+}
+
+template <typename T>
+std::unique_ptr<base::Value> CreateValue(const T& value,
+ chromeos::ErrorPtr* error) {
+ return std::unique_ptr<base::Value>{new base::FundamentalValue{value}};
+}
+
+template <>
+std::unique_ptr<base::Value> CreateValue<std::string>(
+ const std::string& value,
+ chromeos::ErrorPtr* error) {
+ return std::unique_ptr<base::Value>{new base::StringValue{value}};
+}
+
+template <>
+std::unique_ptr<base::Value> CreateValue<chromeos::VariantDictionary>(
+ const chromeos::VariantDictionary& value,
+ chromeos::ErrorPtr* error) {
+ return DictionaryFromDBusVariantDictionary(value, error);
+}
+
+template <typename T>
+std::unique_ptr<base::ListValue> CreateListValue(const std::vector<T>& value,
+ chromeos::ErrorPtr* error) {
+ std::unique_ptr<base::ListValue> list{new base::ListValue};
+
+ for (const T& i : value) {
+ auto item = CreateValue(i, error);
+ if (!item)
+ return nullptr;
+ list->Append(item.release());
+ }
+
+ return list;
+}
+
+// Returns false only in case of error. True can be returned if type is not
+// matched.
+template <typename T>
+bool TryCreateValue(const chromeos::Any& any,
+ std::unique_ptr<base::Value>* value,
+ chromeos::ErrorPtr* error) {
+ if (any.IsTypeCompatible<T>()) {
+ *value = CreateValue(any.Get<T>(), error);
+ return *value != nullptr;
+ }
+
+ if (any.IsTypeCompatible<std::vector<T>>()) {
+ *value = CreateListValue(any.Get<std::vector<T>>(), error);
+ return *value != nullptr;
+ }
+
+ return true; // Not an error, we will try different type.
+}
+
+template <>
+std::unique_ptr<base::Value> CreateValue<chromeos::Any>(
+ const chromeos::Any& any,
+ chromeos::ErrorPtr* error) {
+ std::unique_ptr<base::Value> result;
+ if (!TryCreateValue<bool>(any, &result, error) || result)
+ return result;
+
+ if (!TryCreateValue<int>(any, &result, error) || result)
+ return result;
+
+ if (!TryCreateValue<double>(any, &result, error) || result)
+ return result;
+
+ if (!TryCreateValue<std::string>(any, &result, error) || result)
+ return result;
+
+ if (!TryCreateValue<chromeos::VariantDictionary>(any, &result, error) ||
+ result) {
+ return result;
+ }
+
+ // This will collapse Any{Any{T}} and vector{Any{T}}.
+ if (!TryCreateValue<chromeos::Any>(any, &result, error) || result)
+ return result;
+
+ chromeos::Error::AddToPrintf(
+ error, FROM_HERE, "buffet", "unknown_type", "Type '%s' is not supported.",
+ chromeos::UndecorateTypeName(any.GetType().name()).c_str());
+
+ return nullptr;
+}
+
+} // namespace
+
+// TODO(vitalybuka): Use in buffet_client.
+chromeos::VariantDictionary DictionaryToDBusVariantDictionary(
+ const base::DictionaryValue& object) {
+ chromeos::VariantDictionary result;
+
+ for (base::DictionaryValue::Iterator it(object); !it.IsAtEnd(); it.Advance())
+ result.emplace(it.key(), ValueToAny(it.value()));
+
+ return result;
+}
+
+std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary(
+ const chromeos::VariantDictionary& object,
+ chromeos::ErrorPtr* error) {
+ std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue};
+
+ for (const auto& pair : object) {
+ auto value = CreateValue(pair.second, error);
+ if (!value)
+ return nullptr;
+ result->SetWithoutPathExpansion(pair.first, value.release());
+ }
+
+ return result;
+}
+
+} // namespace buffet
diff --git a/buffet/dbus_conversion.h b/buffet/dbus_conversion.h
new file mode 100644
index 0000000..20c3f58
--- /dev/null
+++ b/buffet/dbus_conversion.h
@@ -0,0 +1,26 @@
+// Copyright 2015 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_DBUS_CONVERSION_H_
+#define BUFFET_DBUS_CONVERSION_H_
+
+#include <base/values.h>
+#include <chromeos/any.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+
+namespace buffet {
+
+// Converts DictionaryValue to D-Bus variant dictionary.
+chromeos::VariantDictionary DictionaryToDBusVariantDictionary(
+ const base::DictionaryValue& object);
+
+// Converts D-Bus variant dictionary to DictionaryValue.
+std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary(
+ const chromeos::VariantDictionary& object,
+ chromeos::ErrorPtr* error);
+
+} // namespace buffet
+
+#endif // BUFFET_DBUS_CONVERSION_H_
diff --git a/buffet/dbus_conversion_unittest.cc b/buffet/dbus_conversion_unittest.cc
new file mode 100644
index 0000000..7a00c35
--- /dev/null
+++ b/buffet/dbus_conversion_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2015 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/dbus_conversion.h"
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/guid.h>
+#include <base/rand_util.h>
+#include <base/values.h>
+#include <chromeos/variant_dictionary.h>
+#include <gtest/gtest.h>
+
+#include "weave/unittest_utils.h"
+
+namespace buffet {
+
+namespace {
+
+using chromeos::Any;
+using chromeos::VariantDictionary;
+using weave::unittests::CreateDictionaryValue;
+using weave::unittests::IsEqualValue;
+
+chromeos::VariantDictionary ToDBus(const base::DictionaryValue& object) {
+ return DictionaryToDBusVariantDictionary(object);
+}
+
+std::unique_ptr<base::DictionaryValue> FromDBus(
+ const chromeos::VariantDictionary& object) {
+ chromeos::ErrorPtr error;
+ auto result = DictionaryFromDBusVariantDictionary(object, &error);
+ EXPECT_TRUE(result || error);
+ return result;
+}
+
+std::unique_ptr<base::Value> CreateRandomValue(int children);
+std::unique_ptr<base::Value> CreateRandomValue(int children,
+ base::Value::Type type);
+
+const base::Value::Type kRandomTypes[] = {
+ base::Value::TYPE_BOOLEAN, base::Value::TYPE_INTEGER,
+ base::Value::TYPE_DOUBLE, base::Value::TYPE_STRING,
+ base::Value::TYPE_DICTIONARY, base::Value::TYPE_LIST,
+};
+
+const base::Value::Type kRandomTypesWithChildren[] = {
+ base::Value::TYPE_DICTIONARY, base::Value::TYPE_LIST,
+};
+
+base::Value::Type CreateRandomValueType(bool with_children) {
+ if (with_children) {
+ return kRandomTypesWithChildren[base::RandInt(
+ 0, arraysize(kRandomTypesWithChildren) - 1)];
+ }
+ return kRandomTypes[base::RandInt(0, arraysize(kRandomTypes) - 1)];
+}
+
+std::unique_ptr<base::DictionaryValue> CreateRandomDictionary(int children) {
+ std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue};
+
+ while (children > 0) {
+ int sub_children = base::RandInt(1, children);
+ children -= sub_children;
+ result->Set(base::GenerateGUID(),
+ CreateRandomValue(sub_children).release());
+ }
+
+ return result;
+}
+
+std::unique_ptr<base::ListValue> CreateRandomList(int children) {
+ std::unique_ptr<base::ListValue> result{new base::ListValue};
+
+ base::Value::Type type = CreateRandomValueType(children > 0);
+ while (children > 0) {
+ size_t max_children =
+ (type != base::Value::TYPE_DICTIONARY && type != base::Value::TYPE_LIST)
+ ? 1
+ : children;
+ size_t sub_children = base::RandInt(1, max_children);
+ children -= sub_children;
+ result->Append(CreateRandomValue(sub_children, type).release());
+ }
+
+ return result;
+}
+
+std::unique_ptr<base::Value> CreateRandomValue(int children,
+ base::Value::Type type) {
+ CHECK_GE(children, 1);
+ switch (type) {
+ case base::Value::TYPE_INTEGER:
+ return std::unique_ptr<base::Value>{new base::FundamentalValue{
+ base::RandInt(std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max())}};
+ case base::Value::TYPE_DOUBLE:
+ return std::unique_ptr<base::Value>{
+ new base::FundamentalValue{base::RandDouble()}};
+ case base::Value::TYPE_STRING:
+ return std::unique_ptr<base::Value>{
+ new base::StringValue{base::GenerateGUID()}};
+ case base::Value::TYPE_DICTIONARY:
+ CHECK_GE(children, 1);
+ return CreateRandomDictionary(children - 1);
+ case base::Value::TYPE_LIST:
+ CHECK_GE(children, 1);
+ return CreateRandomList(children - 1);
+ default:
+ return std::unique_ptr<base::Value>{
+ new base::FundamentalValue{base::RandInt(0, 1) != 0}};
+ }
+}
+
+std::unique_ptr<base::Value> CreateRandomValue(int children) {
+ return CreateRandomValue(children, CreateRandomValueType(children > 0));
+}
+
+} // namespace
+
+TEST(DBusConversionTest, DictionaryToDBusVariantDictionary) {
+ EXPECT_EQ((VariantDictionary{{"bool", true}}),
+ ToDBus(*CreateDictionaryValue("{'bool': true}")));
+ EXPECT_EQ((VariantDictionary{{"int", 5}}),
+ ToDBus(*CreateDictionaryValue("{'int': 5}")));
+ EXPECT_EQ((VariantDictionary{{"double", 6.7}}),
+ ToDBus(*CreateDictionaryValue("{'double': 6.7}")));
+ EXPECT_EQ((VariantDictionary{{"string", std::string{"abc"}}}),
+ ToDBus(*CreateDictionaryValue("{'string': 'abc'}")));
+ EXPECT_EQ((VariantDictionary{{"object", VariantDictionary{{"bool", true}}}}),
+ ToDBus(*CreateDictionaryValue("{'object': {'bool': true}}")));
+ EXPECT_EQ((VariantDictionary{{"emptyList", std::vector<Any>{}}}),
+ ToDBus(*CreateDictionaryValue("{'emptyList': []}")));
+ EXPECT_EQ((VariantDictionary{{"intList", std::vector<int>{5}}}),
+ ToDBus(*CreateDictionaryValue("{'intList': [5]}")));
+ EXPECT_EQ((VariantDictionary{
+ {"intListList", std::vector<Any>{std::vector<int>{5},
+ std::vector<int>{6, 7}}}}),
+ ToDBus(*CreateDictionaryValue("{'intListList': [[5], [6, 7]]}")));
+ EXPECT_EQ((VariantDictionary{{"objList",
+ std::vector<VariantDictionary>{
+ {{"string", std::string{"abc"}}}}}}),
+ ToDBus(*CreateDictionaryValue("{'objList': [{'string': 'abc'}]}")));
+}
+
+TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary) {
+ EXPECT_JSON_EQ("{'bool': true}", *FromDBus({{"bool", true}}));
+ EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", 5}}));
+ EXPECT_JSON_EQ("{'double': 6.7}", *FromDBus({{"double", 6.7}}));
+ EXPECT_JSON_EQ("{'string': 'abc'}",
+ *FromDBus({{"string", std::string{"abc"}}}));
+ EXPECT_JSON_EQ("{'object': {'bool': true}}",
+ *FromDBus({{"object", VariantDictionary{{"bool", true}}}}));
+ EXPECT_JSON_EQ("{'emptyList': []}",
+ *FromDBus({{"emptyList", std::vector<bool>{}}}));
+ EXPECT_JSON_EQ("{'intList': [5]}",
+ *FromDBus({{"intList", std::vector<int>{5}}}));
+ EXPECT_JSON_EQ(
+ "{'intListList': [[5], [6, 7]]}",
+ *FromDBus({{"intListList", std::vector<Any>{std::vector<int>{5},
+ std::vector<int>{6, 7}}}}));
+ EXPECT_JSON_EQ(
+ "{'objList': [{'string': 'abc'}]}",
+ *FromDBus({{"objList", std::vector<VariantDictionary>{
+ {{"string", std::string{"abc"}}}}}}));
+ EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", Any{Any{5}}}}));
+}
+
+TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary_Errors) {
+ EXPECT_FALSE(FromDBus({{"cString", "abc"}}));
+ EXPECT_FALSE(FromDBus({{"float", 1.0f}}));
+ EXPECT_FALSE(FromDBus({{"listList", std::vector<std::vector<int>>{}}}));
+ EXPECT_FALSE(FromDBus({{"any", Any{}}}));
+ EXPECT_FALSE(FromDBus({{"null", nullptr}}));
+}
+
+TEST(DBusConversionTest, DBusRandomDictionaryConversion) {
+ auto dict = CreateRandomDictionary(10000);
+ auto varian_dict = ToDBus(*dict);
+ auto dict_restored = FromDBus(varian_dict);
+ EXPECT_PRED2(IsEqualValue, *dict, *dict_restored);
+}
+
+} // namespace buffet
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 0b64d5b..85c61c1 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -23,9 +23,8 @@
#include <dbus/object_path.h>
#include <dbus/values_util.h>
-// TODO(vitalybuka): Will be moved into buffet soon.
-#include "libweave/src/commands/dbus_command_dispatcher.h"
-#include "libweave/src/commands/dbus_conversion.h"
+#include "buffet/dbus_command_dispatcher.h"
+#include "buffet/dbus_conversion.h"
#include "weave/enum_to_string.h"
using chromeos::dbus_utils::AsyncEventSequencer;
@@ -58,7 +57,7 @@
device_ = weave::Device::Create();
device_->Start(options, &dbus_object_, sequencer);
- command_dispatcher_.reset(new weave::DBusCommandDispacher{
+ command_dispatcher_.reset(new DBusCommandDispacher{
dbus_object_.GetObjectManager(), device_->GetCommands()});
device_->GetState()->AddOnChangedCallback(
@@ -143,8 +142,7 @@
void Manager::UpdateState(DBusMethodResponsePtr<> response,
const chromeos::VariantDictionary& property_set) {
chromeos::ErrorPtr error;
- auto properties =
- weave::DictionaryFromDBusVariantDictionary(property_set, &error);
+ auto properties = DictionaryFromDBusVariantDictionary(property_set, &error);
if (!properties)
response->ReplyWithError(error.get());
diff --git a/buffet/manager.h b/buffet/manager.h
index ef433e2..9dc84d8 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -22,10 +22,6 @@
#include "buffet/org.chromium.Buffet.Manager.h"
#include "weave/device.h"
-namespace weave {
-class DBusCommandDispacher;
-}
-
namespace chromeos {
namespace dbus_utils {
class ExportedObjectManager;
@@ -34,6 +30,8 @@
namespace buffet {
+class DBusCommandDispacher;
+
template<typename... Types>
using DBusMethodResponsePtr =
std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<Types...>>;
@@ -117,7 +115,7 @@
chromeos::dbus_utils::DBusObject dbus_object_;
std::unique_ptr<weave::Device> device_;
- std::unique_ptr<weave::DBusCommandDispacher> command_dispatcher_;
+ std::unique_ptr<DBusCommandDispacher> command_dispatcher_;
base::WeakPtrFactory<Manager> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(Manager);