|  | // 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/command_manager.h" | 
|  |  | 
|  | #include <base/files/file_util.h> | 
|  | #include <base/files/scoped_temp_dir.h> | 
|  | #include <base/json/json_writer.h> | 
|  | #include <chromeos/bind_lambda.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "buffet/commands/unittest_utils.h" | 
|  |  | 
|  | namespace buffet { | 
|  |  | 
|  | using unittests::CreateDictionaryValue; | 
|  |  | 
|  | 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); | 
|  | SaveJsonToFile(dict, temp_file); | 
|  | return temp_file; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(CommandManager, Empty) { | 
|  | CommandManager manager; | 
|  | EXPECT_TRUE(manager.GetCommandDictionary().IsEmpty()); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, LoadBaseCommandsJSON) { | 
|  | CommandManager manager; | 
|  | auto json = CreateDictionaryValue(kTestBaseCommands); | 
|  | EXPECT_TRUE(manager.LoadBaseCommands(*json, nullptr)); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, LoadBaseCommandsFile) { | 
|  | CommandManager manager; | 
|  | auto json = CreateDictionaryValue(kTestBaseCommands); | 
|  | base::FilePath temp_file = SaveJsonToTempFile(*json); | 
|  | EXPECT_TRUE(manager.LoadBaseCommands(temp_file, nullptr)); | 
|  | base::DeleteFile(temp_file, false); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, LoadCommandsJSON) { | 
|  | CommandManager manager; | 
|  | auto json = CreateDictionaryValue(kTestVendorCommands); | 
|  | EXPECT_TRUE(manager.LoadCommands(*json, "category", nullptr)); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, LoadCommandsFile) { | 
|  | CommandManager manager; | 
|  | // Load some standard command definitions first. | 
|  | auto json = CreateDictionaryValue(R"({ | 
|  | 'base': { | 
|  | 'reboot': { | 
|  | 'parameters': {'delay': 'integer'}, | 
|  | 'results': {} | 
|  | }, | 
|  | 'shutdown': { | 
|  | 'parameters': {}, | 
|  | 'results': {} | 
|  | } | 
|  | } | 
|  | })"); | 
|  | manager.LoadBaseCommands(*json, nullptr); | 
|  | // Load device-supported commands. | 
|  | json = CreateDictionaryValue(R"({ | 
|  | 'base': { | 
|  | 'reboot': { | 
|  | 'parameters': {'delay': 'integer'}, | 
|  | 'results': {} | 
|  | } | 
|  | }, | 
|  | 'robot': { | 
|  | '_jump': { | 
|  | 'parameters': {'height': 'integer'}, | 
|  | 'results': {} | 
|  | } | 
|  | } | 
|  | })"); | 
|  | base::FilePath temp_file = SaveJsonToTempFile(*json); | 
|  | EXPECT_TRUE(manager.LoadCommands(temp_file, nullptr)); | 
|  | base::DeleteFile(temp_file, false); | 
|  | EXPECT_EQ(2, manager.GetCommandDictionary().GetSize()); | 
|  | EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("base.reboot")); | 
|  | EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("robot._jump")); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, ShouldLoadStandardAndTestDefinitions) { | 
|  | 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")); | 
|  | } | 
|  |  | 
|  | TEST(CommandManager, UpdateCommandVisibility) { | 
|  | CommandManager manager; | 
|  | int update_count = 0; | 
|  | auto on_command_change = [&update_count]() { update_count++; }; | 
|  | manager.AddOnCommandDefChanged(base::Bind(on_command_change)); | 
|  |  | 
|  | auto json = CreateDictionaryValue(R"({ | 
|  | 'foo': { | 
|  | '_baz': { | 
|  | 'parameters': {}, | 
|  | 'results': {} | 
|  | }, | 
|  | '_bar': { | 
|  | 'parameters': {}, | 
|  | 'results': {} | 
|  | } | 
|  | }, | 
|  | 'bar': { | 
|  | '_quux': { | 
|  | 'parameters': {}, | 
|  | 'results': {}, | 
|  | 'visibility': 'none' | 
|  | } | 
|  | } | 
|  | })"); | 
|  | ASSERT_TRUE(manager.LoadCommands(*json, "test", nullptr)); | 
|  | EXPECT_EQ(1, update_count); | 
|  | const CommandDictionary& dict = manager.GetCommandDictionary(); | 
|  | EXPECT_TRUE(manager.SetCommandVisibility( | 
|  | {"foo._baz"}, CommandDefinition::Visibility::GetLocal(), nullptr)); | 
|  | EXPECT_EQ(2, update_count); | 
|  | EXPECT_EQ("local", dict.FindCommand("foo._baz")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("all", dict.FindCommand("foo._bar")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("none", dict.FindCommand("bar._quux")->GetVisibility().ToString()); | 
|  |  | 
|  | chromeos::ErrorPtr error; | 
|  | ASSERT_FALSE(manager.SetCommandVisibility( | 
|  | {"foo._baz", "foo._bar", "test.cmd"}, | 
|  | CommandDefinition::Visibility::GetLocal(), &error)); | 
|  | EXPECT_EQ(errors::commands::kInvalidCommandName, error->GetCode()); | 
|  | EXPECT_EQ("Command 'test.cmd' is unknown", error->GetMessage()); | 
|  | // The visibility state of commands shouldn't have changed. | 
|  | EXPECT_EQ(2, update_count); | 
|  | EXPECT_EQ("local", dict.FindCommand("foo._baz")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("all", dict.FindCommand("foo._bar")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("none", dict.FindCommand("bar._quux")->GetVisibility().ToString()); | 
|  |  | 
|  | EXPECT_TRUE(manager.SetCommandVisibility( | 
|  | {"foo._baz", "bar._quux"}, CommandDefinition::Visibility::GetCloud(), | 
|  | nullptr)); | 
|  | EXPECT_EQ(3, update_count); | 
|  | EXPECT_EQ("cloud", dict.FindCommand("foo._baz")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("all", dict.FindCommand("foo._bar")->GetVisibility().ToString()); | 
|  | EXPECT_EQ("cloud", dict.FindCommand("bar._quux")->GetVisibility().ToString()); | 
|  | } | 
|  |  | 
|  | }  // namespace buffet |