|  | // 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/states/state_manager.h" | 
|  |  | 
|  | #include <cstdlib>  // for abs(). | 
|  | #include <vector> | 
|  |  | 
|  | #include <base/values.h> | 
|  | #include <gmock/gmock.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "buffet/commands/schema_constants.h" | 
|  | #include "buffet/commands/unittest_utils.h" | 
|  | #include "buffet/states/error_codes.h" | 
|  | #include "buffet/states/mock_state_change_queue_interface.h" | 
|  |  | 
|  | namespace buffet { | 
|  |  | 
|  | using testing::_; | 
|  | using testing::Return; | 
|  | using unittests::CreateDictionaryValue; | 
|  |  | 
|  | namespace { | 
|  | std::unique_ptr<base::DictionaryValue> GetTestSchema() { | 
|  | return CreateDictionaryValue(R"({ | 
|  | 'base': { | 
|  | 'manufacturer':'string', | 
|  | 'serialNumber':'string' | 
|  | }, | 
|  | 'terminator': { | 
|  | 'target':'string' | 
|  | } | 
|  | })"); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> GetTestValues() { | 
|  | return CreateDictionaryValue(R"({ | 
|  | 'base': { | 
|  | 'manufacturer':'Skynet', | 
|  | 'serialNumber':'T1000' | 
|  | } | 
|  | })"); | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | class StateManagerTest : public ::testing::Test { | 
|  | public: | 
|  | void SetUp() override { | 
|  | // Initial expectations. | 
|  | EXPECT_CALL(mock_state_change_queue_, IsEmpty()).Times(0); | 
|  | EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_, _)) | 
|  | .Times(0); | 
|  | EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges()) | 
|  | .Times(0); | 
|  | mgr_.reset(new StateManager(&mock_state_change_queue_)); | 
|  | LoadStateDefinition(GetTestSchema().get(), "default", nullptr); | 
|  | ASSERT_TRUE(mgr_->LoadStateDefaults(*GetTestValues().get(), nullptr)); | 
|  | } | 
|  | void TearDown() override { mgr_.reset(); } | 
|  |  | 
|  | void LoadStateDefinition(const base::DictionaryValue* json, | 
|  | const std::string& category, | 
|  | chromeos::ErrorPtr* error) { | 
|  | ASSERT_TRUE(mgr_->LoadStateDefinition(*json, category, error)); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<StateManager> mgr_; | 
|  | MockStateChangeQueueInterface mock_state_change_queue_; | 
|  | }; | 
|  |  | 
|  | TEST(StateManager, Empty) { | 
|  | MockStateChangeQueueInterface mock_state_change_queue; | 
|  | StateManager manager(&mock_state_change_queue); | 
|  | EXPECT_TRUE(manager.GetCategories().empty()); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, Initialized) { | 
|  | EXPECT_EQ(std::set<std::string>{"default"}, mgr_->GetCategories()); | 
|  | auto expected = R"({ | 
|  | 'base': { | 
|  | 'manufacturer': 'Skynet', | 
|  | 'serialNumber': 'T1000' | 
|  | }, | 
|  | 'terminator': { | 
|  | 'target': '' | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr)); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, LoadStateDefinition) { | 
|  | auto dict = CreateDictionaryValue(R"({ | 
|  | 'power': { | 
|  | 'battery_level':'integer' | 
|  | } | 
|  | })"); | 
|  | LoadStateDefinition(dict.get(), "powerd", nullptr); | 
|  | EXPECT_EQ((std::set<std::string>{"default", "powerd"}), | 
|  | mgr_->GetCategories()); | 
|  |  | 
|  | auto expected = R"({ | 
|  | 'base': { | 
|  | 'manufacturer': 'Skynet', | 
|  | 'serialNumber': 'T1000' | 
|  | }, | 
|  | 'power': { | 
|  | 'battery_level': 0 | 
|  | }, | 
|  | 'terminator': { | 
|  | 'target': '' | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr)); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, SetPropertyValue) { | 
|  | native_types::Object expected_prop_set{ | 
|  | {"terminator.target", unittests::make_string_prop_value("John Connor")}, | 
|  | }; | 
|  | base::Time timestamp = base::Time::Now(); | 
|  | EXPECT_CALL(mock_state_change_queue_, | 
|  | NotifyPropertiesUpdated(timestamp, expected_prop_set)) | 
|  | .WillOnce(Return(true)); | 
|  | ASSERT_TRUE(mgr_->SetPropertyValue( | 
|  | "terminator.target", std::string{"John Connor"}, timestamp, nullptr)); | 
|  | auto expected = R"({ | 
|  | 'base': { | 
|  | 'manufacturer': 'Skynet', | 
|  | 'serialNumber': 'T1000' | 
|  | }, | 
|  | 'terminator': { | 
|  | 'target': 'John Connor' | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr)); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, SetPropertyValue_Error_NoName) { | 
|  | chromeos::ErrorPtr error; | 
|  | ASSERT_FALSE(mgr_->SetPropertyValue("", int{0}, base::Time::Now(), &error)); | 
|  | EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | 
|  | EXPECT_EQ(errors::state::kPropertyNameMissing, error->GetCode()); | 
|  | EXPECT_EQ("Property name is missing", error->GetMessage()); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, SetPropertyValue_Error_NoPackage) { | 
|  | chromeos::ErrorPtr error; | 
|  | ASSERT_FALSE( | 
|  | mgr_->SetPropertyValue("target", int{0}, base::Time::Now(), &error)); | 
|  | EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | 
|  | EXPECT_EQ(errors::state::kPackageNameMissing, error->GetCode()); | 
|  | EXPECT_EQ("Package name is missing in the property name", | 
|  | error->GetMessage()); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownPackage) { | 
|  | chromeos::ErrorPtr error; | 
|  | ASSERT_FALSE( | 
|  | mgr_->SetPropertyValue("power.level", int{0}, base::Time::Now(), &error)); | 
|  | EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | 
|  | EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | 
|  | EXPECT_EQ("Unknown state property package 'power'", error->GetMessage()); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownProperty) { | 
|  | chromeos::ErrorPtr error; | 
|  | ASSERT_FALSE( | 
|  | mgr_->SetPropertyValue("base.level", int{0}, base::Time::Now(), &error)); | 
|  | EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | 
|  | EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | 
|  | EXPECT_EQ("State property 'base.level' is not defined", error->GetMessage()); | 
|  | } | 
|  |  | 
|  | TEST_F(StateManagerTest, GetAndClearRecordedStateChanges) { | 
|  | base::Time timestamp = base::Time::Now(); | 
|  | EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(timestamp, _)) | 
|  | .WillOnce(Return(true)); | 
|  | ASSERT_TRUE(mgr_->SetPropertyValue( | 
|  | "terminator.target", std::string{"John Connor"}, timestamp, nullptr)); | 
|  | std::vector<StateChange> expected_val; | 
|  | expected_val.emplace_back( | 
|  | timestamp, | 
|  | native_types::Object{{"terminator.target", | 
|  | unittests::make_string_prop_value("John Connor")}}); | 
|  | EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges()) | 
|  | .WillOnce(Return(expected_val)); | 
|  | auto changes = mgr_->GetAndClearRecordedStateChanges(); | 
|  | ASSERT_EQ(1, changes.size()); | 
|  | EXPECT_EQ(expected_val.back().timestamp, changes.back().timestamp); | 
|  | EXPECT_EQ(expected_val.back().changed_properties, | 
|  | changes.back().changed_properties); | 
|  | } | 
|  |  | 
|  | }  // namespace buffet |