buffet: Allow tests to pass in a path to search for command definitions
This allows us to dynamically generate commands to test Buffet with at
runtime. The read only nature of the file system makes this difficult
with just the statically configured files.
BUG=brillo:172
TEST=unittests, buffet_Registration passes with this change.
Change-Id: I54a92edadbb5c8a5ebad63b8464d87bb21ba5aa2
Reviewed-on: https://chromium-review.googlesource.com/249440
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
diff --git a/buffet/commands/command_manager.cc b/buffet/commands/command_manager.cc
index 1676cdc..78e0dee 100644
--- a/buffet/commands/command_manager.cc
+++ b/buffet/commands/command_manager.cc
@@ -60,26 +60,32 @@
return LoadCommands(*json, category, error);
}
-void CommandManager::Startup() {
+void CommandManager::Startup(const base::FilePath& definitions_path,
+ const base::FilePath& test_definitions_path) {
LOG(INFO) << "Initializing CommandManager.";
// Load global standard GCD command dictionary.
- base::FilePath base_command_file("/etc/buffet/gcd.json");
+ base::FilePath base_command_file{definitions_path.Append("gcd.json")};
LOG(INFO) << "Loading standard commands from " << base_command_file.value();
CHECK(LoadBaseCommands(base_command_file, nullptr))
<< "Failed to load the standard command definitions.";
- // Load static device command definitions.
- base::FilePath device_command_dir("/etc/buffet/commands");
- base::FileEnumerator enumerator(device_command_dir, false,
- base::FileEnumerator::FILES,
- FILE_PATH_LITERAL("*.json"));
- base::FilePath json_file_path = enumerator.Next();
- while (!json_file_path.empty()) {
- LOG(INFO) << "Loading command schema from " << json_file_path.value();
- CHECK(LoadCommands(json_file_path, nullptr))
- << "Failed to load the command definition file.";
- json_file_path = enumerator.Next();
- }
+ auto LoadPackages = [this](const base::FilePath& root,
+ const base::FilePath::StringType& pattern) {
+ base::FilePath device_command_dir{root.Append("commands")};
+ VLOG(2) << "Looking for commands in " << root.value();
+ base::FileEnumerator enumerator(device_command_dir, false,
+ base::FileEnumerator::FILES,
+ pattern);
+ base::FilePath json_file_path = enumerator.Next();
+ while (!json_file_path.empty()) {
+ LOG(INFO) << "Loading command schema from " << json_file_path.value();
+ CHECK(this->LoadCommands(json_file_path, nullptr))
+ << "Failed to load the command definition file.";
+ json_file_path = enumerator.Next();
+ }
+ };
+ LoadPackages(definitions_path, FILE_PATH_LITERAL("*.json"));
+ LoadPackages(test_definitions_path, FILE_PATH_LITERAL("*test.json"));
}
void CommandManager::AddCommand(
diff --git a/buffet/commands/command_manager.h b/buffet/commands/command_manager.h
index 88b551a..f176cad 100644
--- a/buffet/commands/command_manager.h
+++ b/buffet/commands/command_manager.h
@@ -70,10 +70,13 @@
chromeos::ErrorPtr* error);
// Startup method to be called by buffet daemon at startup.
- // Initializes the object and loads the standard GCD command
- // dictionary as well as static vendor-provided command definitions for
- // the current device.
- void Startup();
+ // Initializes the object and reads files in |definitions_path| to load
+ // 1) the standard GCD command dictionary
+ // 2) static vendor-provided command definitions
+ // If |test_definitions_path| is not empty, we'll also look there for
+ // additional commands.
+ void Startup(const base::FilePath& definitions_path,
+ const base::FilePath& test_definitions_path);
// Adds a new command to the command queue.
void AddCommand(std::unique_ptr<CommandInstance> command_instance);
diff --git a/buffet/commands/command_manager_unittest.cc b/buffet/commands/command_manager_unittest.cc
index c40f2dc..2d4fed8 100644
--- a/buffet/commands/command_manager_unittest.cc
+++ b/buffet/commands/command_manager_unittest.cc
@@ -5,6 +5,7 @@
#include "buffet/commands/command_manager.h"
#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
#include <base/json/json_writer.h>
#include <gtest/gtest.h>
@@ -12,15 +13,60 @@
using buffet::unittests::CreateDictionaryValue;
-static base::FilePath SaveJsonToTempFile(const base::DictionaryValue& dict) {
+namespace {
+
+const char kTestBaseCommands[] = R"({
+ 'base': {
+ 'reboot': {
+ 'parameters': {'delay': 'integer'},
+ 'results': {}
+ },
+ 'shutdown': {
+ 'parameters': {},
+ 'results': {}
+ }
+ }
+})";
+
+const char kTestVendorCommands[] = R"({
+ 'robot': {
+ '_jump': {
+ 'parameters': {'height': 'integer'},
+ 'results': {}
+ },
+ '_speak': {
+ 'parameters': {'phrase': 'string'},
+ 'results': {}
+ }
+ }
+})";
+
+const char kTestTestCommands[] = R"({
+ 'test': {
+ '_yo': {
+ 'parameters': {'name': 'string'},
+ 'results': {}
+ }
+ }
+})";
+
+static void SaveJsonToFile(const base::DictionaryValue& dict,
+ const base::FilePath& file_path) {
std::string json;
base::JSONWriter::Write(&dict, &json);
+ const int bytes_to_write = static_cast<int>(json.size());
+ CHECK_EQ(bytes_to_write, WriteFile(file_path, json.data(), bytes_to_write));
+}
+
+static base::FilePath SaveJsonToTempFile(const base::DictionaryValue& dict) {
base::FilePath temp_file;
base::CreateTemporaryFile(&temp_file);
- base::WriteFile(temp_file, json.data(), static_cast<int>(json.size()));
+ SaveJsonToFile(dict, temp_file);
return temp_file;
}
+} // namespace
+
TEST(CommandManager, Empty) {
buffet::CommandManager manager;
EXPECT_TRUE(manager.GetCommandDictionary().IsEmpty());
@@ -28,35 +74,13 @@
TEST(CommandManager, LoadBaseCommandsJSON) {
buffet::CommandManager manager;
- auto json = CreateDictionaryValue(R"({
- 'base': {
- 'reboot': {
- 'parameters': {'delay': 'integer'},
- 'results': {}
- },
- 'shutdown': {
- 'parameters': {},
- 'results': {}
- }
- }
- })");
+ auto json = CreateDictionaryValue(kTestBaseCommands);
EXPECT_TRUE(manager.LoadBaseCommands(*json, nullptr));
}
TEST(CommandManager, LoadBaseCommandsFile) {
buffet::CommandManager manager;
- auto json = CreateDictionaryValue(R"({
- 'base': {
- 'reboot': {
- 'parameters': {'delay': 'integer'},
- 'results': {}
- },
- 'shutdown': {
- 'parameters': {},
- 'results': {}
- }
- }
- })");
+ auto json = CreateDictionaryValue(kTestBaseCommands);
base::FilePath temp_file = SaveJsonToTempFile(*json);
EXPECT_TRUE(manager.LoadBaseCommands(temp_file, nullptr));
base::DeleteFile(temp_file, false);
@@ -64,18 +88,7 @@
TEST(CommandManager, LoadCommandsJSON) {
buffet::CommandManager manager;
- auto json = CreateDictionaryValue(R"({
- 'robot': {
- '_jump': {
- 'parameters': {'height': 'integer'},
- 'results': {}
- },
- '_speak': {
- 'parameters': {'phrase': 'string'},
- 'results': {}
- }
- }
- })");
+ auto json = CreateDictionaryValue(kTestVendorCommands);
EXPECT_TRUE(manager.LoadCommands(*json, "category", nullptr));
}
@@ -117,3 +130,29 @@
EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("base.reboot"));
EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("robot._jump"));
}
+
+TEST(CommandManager, ShouldLoadStandardAndTestDefinitions) {
+ buffet::CommandManager manager;
+ base::ScopedTempDir temp;
+ CHECK(temp.CreateUniqueTempDir());
+ base::FilePath base_path{temp.path().Append("base_defs")};
+ base::FilePath test_path{temp.path().Append("test_defs")};
+ base::FilePath base_commands_path{base_path.Append("commands")};
+ base::FilePath test_commands_path{test_path.Append("commands")};
+ CHECK(CreateDirectory(base_commands_path));
+ CHECK(CreateDirectory(test_commands_path));
+ SaveJsonToFile(*CreateDictionaryValue(kTestBaseCommands),
+ base_path.Append("gcd.json"));
+ SaveJsonToFile(*CreateDictionaryValue(kTestVendorCommands),
+ base_commands_path.Append("category.json"));
+ SaveJsonToFile(*CreateDictionaryValue(kTestTestCommands),
+ test_commands_path.Append("test.json"));
+ manager.Startup(base_path, test_path);
+ EXPECT_EQ(3, manager.GetCommandDictionary().GetSize());
+ EXPECT_NE(nullptr,
+ manager.GetCommandDictionary().FindCommand("robot._jump"));
+ EXPECT_NE(nullptr,
+ manager.GetCommandDictionary().FindCommand("robot._speak"));
+ EXPECT_NE(nullptr,
+ manager.GetCommandDictionary().FindCommand("test._yo"));
+}
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index fb530b9..31fb7b6 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -34,17 +34,16 @@
namespace buffet {
namespace storage_keys {
-// Statically configured fields
-const char kApiKey[] = "api_key";
+// Persistent keys
const char kClientId[] = "client_id";
const char kClientSecret[] = "client_secret";
+const char kApiKey[] = "api_key";
+const char kRefreshToken[] = "refresh_token";
+const char kDeviceId[] = "device_id";
const char kOAuthURL[] = "oauth_url";
const char kServiceURL[] = "service_url";
-// Credentials related to a particular registration.
-const char kDeviceId[] = "device_id";
-const char kRefreshToken[] = "refresh_token";
const char kRobotAccount[] = "robot_account";
-// Fields in our device metadata.
+// Transient keys
const char kDeviceKind[] = "device_kind";
const char kName[] = "name";
const char kDisplayName[] = "display_name";
diff --git a/buffet/etc/init/buffet.conf b/buffet/etc/init/buffet.conf
index 0747ef5..1e27a53 100644
--- a/buffet/etc/init/buffet.conf
+++ b/buffet/etc/init/buffet.conf
@@ -12,6 +12,7 @@
env BUFFET_LOG_LEVEL=0
env BUFFET_STATE_PATH=
env BUFFET_CONFIG_PATH=
+env BUFFET_TEST_DEFINITIONS_PATH=
pre-start script
mkdir -m 0755 -p /var/lib/buffet
@@ -24,4 +25,5 @@
exec minijail0 -i -g buffet -u buffet /usr/bin/buffet \
--v="${BUFFET_LOG_LEVEL}" \
--config_path="${BUFFET_CONFIG_PATH}" \
- --state_path="${BUFFET_STATE_PATH}"
+ --state_path="${BUFFET_STATE_PATH}" \
+ --test_definitions_path="${BUFFET_TEST_DEFINITIONS_PATH}"
diff --git a/buffet/main.cc b/buffet/main.cc
index ab3798a..a4156c4 100644
--- a/buffet/main.cc
+++ b/buffet/main.cc
@@ -24,10 +24,12 @@
class Daemon : public DBusServiceDaemon {
public:
Daemon(const base::FilePath& config_path,
- const base::FilePath& state_path)
+ const base::FilePath& state_path,
+ const base::FilePath& test_definitions_path)
: DBusServiceDaemon(kServiceName, kRootServicePath),
config_path_{config_path},
- state_path_{state_path} {}
+ state_path_{state_path},
+ test_definitions_path_{test_definitions_path} {}
protected:
void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override {
@@ -35,6 +37,7 @@
manager_->RegisterAsync(
config_path_,
state_path_,
+ test_definitions_path_,
sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
}
@@ -42,6 +45,7 @@
std::unique_ptr<buffet::Manager> manager_;
const base::FilePath config_path_;
const base::FilePath state_path_;
+ const base::FilePath test_definitions_path_;
DISALLOW_COPY_AND_ASSIGN(Daemon);
};
@@ -60,15 +64,17 @@
"Path to file containing config information.");
DEFINE_string(state_path, kDefaultStateFilePath,
"Path to file containing state information.");
+ DEFINE_string(test_definitions_path, "",
+ "Path to directory containing additional command "
+ "and state definitions. For use in test only.");
chromeos::FlagHelper::Init(argc, argv, "Privet protocol handler daemon");
-
if (FLAGS_config_path.empty())
FLAGS_config_path = kDefaultConfigFilePath;
if (FLAGS_state_path.empty())
FLAGS_state_path = kDefaultStateFilePath;
-
chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader);
buffet::Daemon daemon{base::FilePath{FLAGS_config_path},
- base::FilePath{FLAGS_state_path}};
+ base::FilePath{FLAGS_state_path},
+ base::FilePath{FLAGS_test_definitions_path}};
return daemon.Run();
}
diff --git a/buffet/manager.cc b/buffet/manager.cc
index da90b00..cbb2fad 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -45,10 +45,12 @@
void Manager::RegisterAsync(
const base::FilePath& config_path,
const base::FilePath& state_path,
+ const base::FilePath& test_definitions_path,
const AsyncEventSequencer::CompletionAction& cb) {
command_manager_ =
std::make_shared<CommandManager>(dbus_object_.GetObjectManager());
- command_manager_->Startup();
+ command_manager_->Startup(base::FilePath{"/etc/buffet"},
+ test_definitions_path);
state_change_queue_ = std::unique_ptr<StateChangeQueue>(
new StateChangeQueue(kMaxStateChangeQueueSize));
state_manager_ = std::make_shared<StateManager>(state_change_queue_.get());
diff --git a/buffet/manager.h b/buffet/manager.h
index c0086ed..d7d71bf 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -50,6 +50,7 @@
void RegisterAsync(
const base::FilePath& config_path,
const base::FilePath& state_path,
+ const base::FilePath& test_definitions_path,
const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
private: