blob: f958bf5380c2f2cfc0076937ba076abe92282fea [file] [log] [blame]
// 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