| // 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_package.h" | |
| #include <memory> | |
| #include <string> | |
| #include <base/values.h> | |
| #include <gtest/gtest.h> | |
| #include "src/commands/schema_constants.h" | |
| #include "src/commands/unittest_utils.h" | |
| #include "src/states/error_codes.h" | |
| namespace weave { | |
| using test::CreateDictionaryValue; | |
| class StatePackageTestHelper { | |
| public: | |
| // Returns the state property definitions (types/constraints/etc). | |
| static const ObjectSchema& GetTypes(const StatePackage& package) { | |
| return package.types_; | |
| } | |
| // Returns the all state property values in this package. | |
| static const ValueMap& GetValues(const StatePackage& package) { | |
| return package.values_; | |
| } | |
| }; | |
| namespace { | |
| std::unique_ptr<base::DictionaryValue> GetTestSchema() { | |
| return CreateDictionaryValue(R"({ | |
| 'light': 'boolean', | |
| 'color': 'string', | |
| 'direction':{ | |
| 'properties':{ | |
| 'azimuth': {'type': 'number', 'isRequired': true}, | |
| 'altitude': {'maximum': 90.0} | |
| } | |
| }, | |
| 'iso': [50, 100, 200, 400] | |
| })"); | |
| } | |
| std::unique_ptr<base::DictionaryValue> GetTestValues() { | |
| return CreateDictionaryValue(R"({ | |
| 'light': true, | |
| 'color': 'white', | |
| 'direction': {'azimuth':57.2957795, 'altitude':89.9}, | |
| 'iso': 200 | |
| })"); | |
| } | |
| inline const ObjectSchema& GetTypes(const StatePackage& package) { | |
| return StatePackageTestHelper::GetTypes(package); | |
| } | |
| // Returns the all state property values in this package. | |
| inline const ValueMap& GetValues(const StatePackage& package) { | |
| return StatePackageTestHelper::GetValues(package); | |
| } | |
| } // anonymous namespace | |
| class StatePackageTest : public ::testing::Test { | |
| public: | |
| void SetUp() override { | |
| package_.reset(new StatePackage("test")); | |
| ASSERT_TRUE(package_->AddSchemaFromJson(GetTestSchema().get(), nullptr)); | |
| ASSERT_TRUE(package_->AddValuesFromJson(GetTestValues().get(), nullptr)); | |
| } | |
| void TearDown() override { package_.reset(); } | |
| std::unique_ptr<StatePackage> package_; | |
| }; | |
| TEST(StatePackage, Empty) { | |
| StatePackage package("test"); | |
| EXPECT_EQ("test", package.GetName()); | |
| EXPECT_TRUE(GetTypes(package).GetProps().empty()); | |
| EXPECT_TRUE(GetValues(package).empty()); | |
| } | |
| TEST(StatePackage, AddSchemaFromJson_OnEmpty) { | |
| StatePackage package("test"); | |
| ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr)); | |
| EXPECT_EQ(4, GetTypes(package).GetProps().size()); | |
| EXPECT_EQ(4, GetValues(package).size()); | |
| auto expected = R"({ | |
| 'color': { | |
| 'type': 'string' | |
| }, | |
| 'direction': { | |
| 'additionalProperties': false, | |
| 'properties': { | |
| 'altitude': { | |
| 'maximum': 90.0, | |
| 'type': 'number' | |
| }, | |
| 'azimuth': { | |
| 'type': 'number' | |
| } | |
| }, | |
| 'type': 'object', | |
| 'required': [ 'azimuth' ] | |
| }, | |
| 'iso': { | |
| 'enum': [50, 100, 200, 400], | |
| 'type': 'integer' | |
| }, | |
| 'light': { | |
| 'type': 'boolean' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *GetTypes(package).ToJson(true, false)); | |
| expected = R"({ | |
| 'color': '', | |
| 'direction': {}, | |
| 'iso': 0, | |
| 'light': false | |
| })"; | |
| EXPECT_JSON_EQ(expected, *package.GetValuesAsJson()); | |
| } | |
| TEST(StatePackage, AddValuesFromJson_OnEmpty) { | |
| StatePackage package("test"); | |
| ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr)); | |
| ASSERT_TRUE(package.AddValuesFromJson(GetTestValues().get(), nullptr)); | |
| EXPECT_EQ(4, GetValues(package).size()); | |
| auto expected = R"({ | |
| 'color': 'white', | |
| 'direction': { | |
| 'altitude': 89.9, | |
| 'azimuth': 57.2957795 | |
| }, | |
| 'iso': 200, | |
| 'light': true | |
| })"; | |
| EXPECT_JSON_EQ(expected, *package.GetValuesAsJson()); | |
| } | |
| TEST_F(StatePackageTest, AddSchemaFromJson_AddMore) { | |
| auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}"); | |
| ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr)); | |
| EXPECT_EQ(5, GetTypes(*package_).GetProps().size()); | |
| EXPECT_EQ(5, GetValues(*package_).size()); | |
| auto expected = R"({ | |
| 'brightness': { | |
| 'enum': ['low', 'medium', 'high'], | |
| 'type': 'string' | |
| }, | |
| 'color': { | |
| 'type': 'string' | |
| }, | |
| 'direction': { | |
| 'additionalProperties': false, | |
| 'properties': { | |
| 'altitude': { | |
| 'maximum': 90.0, | |
| 'type': 'number' | |
| }, | |
| 'azimuth': { | |
| 'type': 'number' | |
| } | |
| }, | |
| 'type': 'object', | |
| 'required': [ 'azimuth' ] | |
| }, | |
| 'iso': { | |
| 'enum': [50, 100, 200, 400], | |
| 'type': 'integer' | |
| }, | |
| 'light': { | |
| 'type': 'boolean' | |
| } | |
| })"; | |
| EXPECT_JSON_EQ(expected, *GetTypes(*package_).ToJson(true, false)); | |
| expected = R"({ | |
| 'brightness': '', | |
| 'color': 'white', | |
| 'direction': { | |
| 'altitude': 89.9, | |
| 'azimuth': 57.2957795 | |
| }, | |
| 'iso': 200, | |
| 'light': true | |
| })"; | |
| EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson()); | |
| } | |
| TEST_F(StatePackageTest, AddValuesFromJson_AddMore) { | |
| auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}"); | |
| ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr)); | |
| dict = CreateDictionaryValue("{'brightness':'medium'}"); | |
| ASSERT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr)); | |
| EXPECT_EQ(5, GetValues(*package_).size()); | |
| auto expected = R"({ | |
| 'brightness': 'medium', | |
| 'color': 'white', | |
| 'direction': { | |
| 'altitude': 89.9, | |
| 'azimuth': 57.2957795 | |
| }, | |
| 'iso': 200, | |
| 'light': true | |
| })"; | |
| EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson()); | |
| } | |
| TEST_F(StatePackageTest, AddSchemaFromJson_Error_Redefined) { | |
| auto dict = CreateDictionaryValue("{'color':['white', 'blue', 'red']}"); | |
| ErrorPtr error; | |
| EXPECT_FALSE(package_->AddSchemaFromJson(dict.get(), &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyRedefinition, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, AddValuesFromJson_Error_Undefined) { | |
| auto dict = CreateDictionaryValue("{'brightness':'medium'}"); | |
| ErrorPtr error; | |
| EXPECT_FALSE(package_->AddValuesFromJson(dict.get(), &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, GetPropertyValue) { | |
| EXPECT_JSON_EQ("'white'", *package_->GetPropertyValue("color", nullptr)); | |
| EXPECT_JSON_EQ("true", *package_->GetPropertyValue("light", nullptr)); | |
| EXPECT_JSON_EQ("200", *package_->GetPropertyValue("iso", nullptr)); | |
| EXPECT_JSON_EQ("{'altitude': 89.9, 'azimuth': 57.2957795}", | |
| *package_->GetPropertyValue("direction", nullptr)); | |
| } | |
| TEST_F(StatePackageTest, GetPropertyValue_Unknown) { | |
| ErrorPtr error; | |
| EXPECT_EQ(nullptr, package_->GetPropertyValue("volume", &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Simple) { | |
| EXPECT_TRUE( | |
| package_->SetPropertyValue("color", base::StringValue{"blue"}, nullptr)); | |
| EXPECT_JSON_EQ("'blue'", *package_->GetPropertyValue("color", nullptr)); | |
| EXPECT_TRUE(package_->SetPropertyValue("light", base::FundamentalValue{false}, | |
| nullptr)); | |
| bool light = false; | |
| ASSERT_TRUE( | |
| package_->GetPropertyValue("light", nullptr)->GetAsBoolean(&light)); | |
| EXPECT_FALSE(light); | |
| EXPECT_TRUE( | |
| package_->SetPropertyValue("iso", base::FundamentalValue{400}, nullptr)); | |
| EXPECT_JSON_EQ("400", *package_->GetPropertyValue("iso", nullptr)); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Object) { | |
| EXPECT_TRUE(package_->SetPropertyValue( | |
| "direction", | |
| *CreateDictionaryValue("{'altitude': 45.0, 'azimuth': 15.0}"), nullptr)); | |
| auto expected = R"({ | |
| 'color': 'white', | |
| 'direction': { | |
| 'altitude': 45.0, | |
| 'azimuth': 15.0 | |
| }, | |
| 'iso': 200, | |
| 'light': true | |
| })"; | |
| EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_TypeMismatch) { | |
| ErrorPtr error; | |
| ASSERT_FALSE( | |
| package_->SetPropertyValue("color", base::FundamentalValue{12}, &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | |
| error.reset(); | |
| ASSERT_FALSE( | |
| package_->SetPropertyValue("iso", base::FundamentalValue{false}, &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_OutOfRange) { | |
| ErrorPtr error; | |
| ASSERT_FALSE( | |
| package_->SetPropertyValue("iso", base::FundamentalValue{150}, &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_Object_TypeMismatch) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(package_->SetPropertyValue( | |
| "direction", | |
| *CreateDictionaryValue("{'altitude': 45.0, 'azimuth': '15'}"), &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode()); | |
| const Error* inner = error->GetInnerError(); | |
| EXPECT_EQ(errors::commands::kDomain, inner->GetDomain()); | |
| EXPECT_EQ(errors::commands::kTypeMismatch, inner->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_Object_OutOfRange) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(package_->SetPropertyValue( | |
| "direction", | |
| *CreateDictionaryValue("{'altitude': 100.0, 'azimuth': 290.0}"), &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode()); | |
| const Error* inner = error->GetInnerError(); | |
| EXPECT_EQ(errors::commands::kDomain, inner->GetDomain()); | |
| EXPECT_EQ(errors::commands::kOutOfRange, inner->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_Object_UnknownProperty) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(package_->SetPropertyValue( | |
| "direction", *CreateDictionaryValue( | |
| "{'altitude': 10.0, 'azimuth': 20.0, 'spin': 30.0}"), | |
| &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Object_OptionalProperty) { | |
| EXPECT_JSON_EQ("{'altitude': 89.9, 'azimuth': 57.2957795}", | |
| *package_->GetProperty("direction")->ToJson()); | |
| ASSERT_TRUE(package_->SetPropertyValue( | |
| "direction", *CreateDictionaryValue("{'azimuth': 10.0}"), nullptr)); | |
| EXPECT_JSON_EQ("{'azimuth': 10.0}", | |
| *package_->GetProperty("direction")->ToJson()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_Object_MissingProperty) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(package_->SetPropertyValue( | |
| "direction", *CreateDictionaryValue("{'altitude': 10.0}"), &error)); | |
| EXPECT_EQ(errors::commands::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode()); | |
| } | |
| TEST_F(StatePackageTest, SetPropertyValue_Error_Unknown) { | |
| ErrorPtr error; | |
| ASSERT_FALSE(package_->SetPropertyValue("volume", base::FundamentalValue{100}, | |
| &error)); | |
| EXPECT_EQ(errors::state::kDomain, error->GetDomain()); | |
| EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode()); | |
| } | |
| } // namespace weave |