| // Copyright 2015 The Weave 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 "src/states/state_manager.h" | |
| #include <cstdlib> // for abs(). | |
| #include <vector> | |
| #include <base/bind.h> | |
| #include <base/values.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include <weave/provider/test/mock_config_store.h> | |
| #include "src/commands/schema_constants.h" | |
| #include "src/commands/unittest_utils.h" | |
| #include "src/states/error_codes.h" | |
| #include "src/states/mock_state_change_queue_interface.h" | |
| namespace weave { | |
| using testing::_; | |
| using testing::Return; | |
| using test::CreateDictionaryValue; | |
| namespace { | |
| const char kBaseDefinition[] = R"({ | |
| "base": { | |
| "manufacturer":"string", | |
| "serialNumber":"string" | |
| }, | |
| "device": { | |
| "state_property":"string" | |
| } | |
| })"; | |
| std::unique_ptr<base::DictionaryValue> GetTestSchema() { | |
| return CreateDictionaryValue(kBaseDefinition); | |
| } | |
| const char kBaseDefaults[] = R"({ | |
| "base": { | |
| "manufacturer":"Test Factory", | |
| "serialNumber":"Test Model" | |
| } | |
| })"; | |
| std::unique_ptr<base::DictionaryValue> GetTestValues() { | |
| return CreateDictionaryValue(kBaseDefaults); | |
| } | |
| } // 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(_, _)) | |
| .WillRepeatedly(Return(true)); | |
| EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges()) | |
| .Times(0); | |
| mgr_.reset(new StateManager(&mock_state_change_queue_)); | |
| EXPECT_CALL(*this, OnStateChanged()).Times(2); | |
| mgr_->AddChangedCallback( | |
| base::Bind(&StateManagerTest::OnStateChanged, base::Unretained(this))); | |
| LoadStateDefinition(GetTestSchema().get(), nullptr); | |
| ASSERT_TRUE(mgr_->SetProperties(*GetTestValues().get(), nullptr)); | |
| } | |
| void TearDown() override { mgr_.reset(); } | |
| void LoadStateDefinition(const base::DictionaryValue* json, | |
| ErrorPtr* error) { | |
| ASSERT_TRUE(mgr_->LoadStateDefinition(*json, error)); | |
| } | |
| bool SetPropertyValue(const std::string& name, | |
| const base::Value& value, | |
| ErrorPtr* error) { | |
| return mgr_->SetPropertyValue(name, value, timestamp_, error); | |
| } | |
| MOCK_CONST_METHOD0(OnStateChanged, void()); | |
| base::Time timestamp_{base::Time::Now()}; | |
| std::unique_ptr<StateManager> mgr_; | |
| testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_; | |
| }; | |
| TEST(StateManager, Empty) { | |
| testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue; | |
| StateManager manager(&mock_state_change_queue); | |
| } | |
| TEST_F(StateManagerTest, Initialized) { | |
| auto expected = R"({ | |
| 'base': { | |
| 'manufacturer': 'Test Factory', | |
| 'serialNumber': 'Test Model' | |
| }, | |
| 'device': { | |
| 'state_property': '' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *mgr_->GetState()); | |
| } | |
| TEST_F(StateManagerTest, LoadStateDefinition) { | |
| auto dict = CreateDictionaryValue(R"({ | |
| 'power': { | |
| 'battery_level':'integer' | |
| } | |
| })"); | |
| LoadStateDefinition(dict.get(), nullptr); | |
| auto expected = R"({ | |
| 'base': { | |
| 'manufacturer': 'Test Factory', | |
| 'serialNumber': 'Test Model' | |
| }, | |
| 'power': { | |
| 'battery_level': 0 | |
| }, | |
| 'device': { | |
| 'state_property': '' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *mgr_->GetState()); | |
| } | |
| TEST_F(StateManagerTest, Startup) { | |
| StateManager manager(&mock_state_change_queue_); | |
| auto state_definition = R"({ | |
| "base": { | |
| "firmwareVersion": "string", | |
| "localDiscoveryEnabled": "boolean", | |
| "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ], | |
| "localPairingEnabled": "boolean" | |
| }, | |
| "power": {"battery_level":"integer"} | |
| })"; | |
| ASSERT_TRUE(manager.LoadStateDefinitionFromJson(state_definition, nullptr)); | |
| auto state_values = R"({ | |
| "base": { | |
| "firmwareVersion": "unknown", | |
| "localDiscoveryEnabled": false, | |
| "localAnonymousAccessMaxRole": "none", | |
| "localPairingEnabled": false | |
| }, | |
| "power": {"battery_level":44} | |
| })"; | |
| ASSERT_TRUE(manager.SetPropertiesFromJson(state_values, nullptr)); | |
| auto expected = R"({ | |
| 'base': { | |
| 'firmwareVersion': 'unknown', | |
| 'localAnonymousAccessMaxRole': 'none', | |
| 'localDiscoveryEnabled': false, | |
| 'localPairingEnabled': false | |
| }, | |
| 'power': { | |
| 'battery_level': 44 | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *manager.GetState()); | |
| } | |
| TEST_F(StateManagerTest, SetPropertyValue) { | |
| ValueMap expected_prop_set{ | |
| {"device.state_property", test::make_string_prop_value("Test Value")}, | |
| }; | |
| EXPECT_CALL(mock_state_change_queue_, | |
| NotifyPropertiesUpdated(timestamp_, expected_prop_set)) | |
| .WillOnce(Return(true)); | |
| ASSERT_TRUE(SetPropertyValue("device.state_property", | |
| base::StringValue{"Test Value"}, nullptr)); | |
| auto expected = R"({ | |
| 'base': { | |
| 'manufacturer': 'Test Factory', | |
| 'serialNumber': 'Test Model' | |
| }, | |
| 'device': { | |
| 'state_property': 'Test Value' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *mgr_->GetState()); | |
| } | |
| TEST_F(StateManagerTest, SetPropertyValue_Error_NoName) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(SetPropertyValue("", base::FundamentalValue{0}, &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNameMissing, error->GetCode()); | |
| } | |
| TEST_F(StateManagerTest, SetPropertyValue_Error_NoPackage) { | |
| ErrorPtr error; | |
| ASSERT_FALSE( | |
| SetPropertyValue("state_property", base::FundamentalValue{0}, &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPackageNameMissing, error->GetCode()); | |
| } | |
| TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownPackage) { | |
| ErrorPtr error; | |
| ASSERT_FALSE( | |
| SetPropertyValue("power.level", base::FundamentalValue{0}, &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | |
| } | |
| TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownProperty) { | |
| ErrorPtr error; | |
| ASSERT_FALSE( | |
| SetPropertyValue("base.level", base::FundamentalValue{0}, &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | |
| } | |
| TEST_F(StateManagerTest, GetAndClearRecordedStateChanges) { | |
| EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(timestamp_, _)) | |
| .WillOnce(Return(true)); | |
| ASSERT_TRUE(SetPropertyValue("device.state_property", | |
| base::StringValue{"Test Value"}, nullptr)); | |
| std::vector<StateChange> expected_val; | |
| expected_val.emplace_back( | |
| timestamp_, ValueMap{{"device.state_property", | |
| test::make_string_prop_value("Test Value")}}); | |
| EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges()) | |
| .WillOnce(Return(expected_val)); | |
| EXPECT_CALL(mock_state_change_queue_, GetLastStateChangeId()) | |
| .WillOnce(Return(0)); | |
| auto changes = mgr_->GetAndClearRecordedStateChanges(); | |
| ASSERT_EQ(1, changes.second.size()); | |
| EXPECT_EQ(expected_val.back().timestamp, changes.second.back().timestamp); | |
| EXPECT_EQ(expected_val.back().changed_properties, | |
| changes.second.back().changed_properties); | |
| } | |
| TEST_F(StateManagerTest, SetProperties) { | |
| ValueMap expected_prop_set{ | |
| {"base.manufacturer", test::make_string_prop_value("No Name")}, | |
| }; | |
| EXPECT_CALL(mock_state_change_queue_, | |
| NotifyPropertiesUpdated(_, expected_prop_set)) | |
| .WillOnce(Return(true)); | |
| EXPECT_CALL(*this, OnStateChanged()).Times(1); | |
| ASSERT_TRUE(mgr_->SetProperties( | |
| *CreateDictionaryValue("{'base':{'manufacturer':'No Name'}}"), nullptr)); | |
| auto expected = R"({ | |
| 'base': { | |
| 'manufacturer': 'No Name', | |
| 'serialNumber': 'Test Model' | |
| }, | |
| 'device': { | |
| 'state_property': '' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *mgr_->GetState()); | |
| } | |
| TEST_F(StateManagerTest, GetProperty) { | |
| EXPECT_JSON_EQ("'Test Model'", *mgr_->GetProperty("base.serialNumber")); | |
| EXPECT_JSON_EQ("''", *mgr_->GetProperty("device.state_property")); | |
| EXPECT_EQ(nullptr, mgr_->GetProperty("device.unknown")); | |
| EXPECT_EQ(nullptr, mgr_->GetProperty("unknown.state_property")); | |
| } | |
| } // namespace weave |