|  | // 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/commands/object_schema.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <limits> | 
|  | #include <memory> | 
|  | #include <vector> | 
|  |  | 
|  | #include <base/json/json_reader.h> | 
|  | #include <base/json/json_writer.h> | 
|  | #include <base/values.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "src/commands/prop_constraints.h" | 
|  | #include "src/commands/prop_types.h" | 
|  | #include "src/commands/schema_constants.h" | 
|  | #include "src/commands/unittest_utils.h" | 
|  |  | 
|  | namespace weave { | 
|  |  | 
|  | using test::CreateValue; | 
|  | using test::CreateDictionaryValue; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<T> GetArrayValues(const ValueVector& arr) { | 
|  | std::vector<T> values; | 
|  | values.reserve(arr.size()); | 
|  | for (const auto& prop_value : arr) { | 
|  | const auto& value = static_cast<const TypedValueBase<T>&>(*prop_value); | 
|  | values.push_back(value.GetValue()); | 
|  | } | 
|  | return values; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<T> GetOneOfValues(const PropType* prop_type) { | 
|  | auto one_of = static_cast<const ConstraintOneOf*>( | 
|  | prop_type->GetConstraint(ConstraintType::OneOf)); | 
|  | if (!one_of) | 
|  | return {}; | 
|  |  | 
|  | return GetArrayValues<T>(one_of->set_.value); | 
|  | } | 
|  |  | 
|  | bool ValidateValue(const PropType& type, | 
|  | const base::Value& value, | 
|  | ErrorPtr* error) { | 
|  | std::unique_ptr<PropValue> val = type.CreatePropValue(value, error); | 
|  | return val != nullptr; | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_Empty) { | 
|  | IntPropType prop; | 
|  | EXPECT_TRUE(prop.GetConstraints().empty()); | 
|  | EXPECT_FALSE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_Types) { | 
|  | IntPropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetBoolean()); | 
|  | EXPECT_EQ(&prop, prop.GetInt()); | 
|  | EXPECT_EQ(nullptr, prop.GetDouble()); | 
|  | EXPECT_EQ(nullptr, prop.GetString()); | 
|  | EXPECT_EQ(nullptr, prop.GetObject()); | 
|  | EXPECT_EQ(nullptr, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_ToJson) { | 
|  | IntPropType prop; | 
|  | EXPECT_JSON_EQ("'integer'", *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); | 
|  | IntPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{'minimum':3}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'maximum':-7}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(), | 
|  | &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{'maximum':5,'minimum':0}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'enum':[1,2,3]}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("[1,2,3]", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'default':123}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'default':123}", *param2.ToJson(false, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_FromJson) { | 
|  | IntPropType prop; | 
|  | prop.AddMinMaxConstraint(2, 8); | 
|  | IntPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_FALSE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(2, prop.GetMinValue()); | 
|  | EXPECT_EQ(8, prop.GetMaxValue()); | 
|  | prop.AddMinMaxConstraint(-2, 30); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(7, param2.GetMinValue()); | 
|  | EXPECT_EQ(30, param2.GetMaxValue()); | 
|  | param2.FromJson(CreateDictionaryValue("{'maximum':17}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(-2, param2.GetMinValue()); | 
|  | EXPECT_EQ(17, param2.GetMaxValue()); | 
|  |  | 
|  | ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':3}").get(), | 
|  | &prop, nullptr)); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | ASSERT_NE(nullptr, param2.GetDefaultValue()); | 
|  | EXPECT_EQ(3, param2.GetDefaultValue()->GetInt()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_Validate) { | 
|  | IntPropType prop; | 
|  | prop.AddMinMaxConstraint(2, 4); | 
|  | ErrorPtr error; | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("0"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("2"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("3"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("4"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("5"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, IntPropType_CreateValue) { | 
|  | IntPropType prop; | 
|  | ErrorPtr error; | 
|  | auto val = prop.CreateValue(base::FundamentalValue{2}, &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_EQ(2, val->GetValue()); | 
|  |  | 
|  | val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_Empty) { | 
|  | BooleanPropType prop; | 
|  | EXPECT_TRUE(prop.GetConstraints().empty()); | 
|  | EXPECT_FALSE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | EXPECT_FALSE(prop.IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_Types) { | 
|  | BooleanPropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetInt()); | 
|  | EXPECT_EQ(&prop, prop.GetBoolean()); | 
|  | EXPECT_EQ(nullptr, prop.GetDouble()); | 
|  | EXPECT_EQ(nullptr, prop.GetString()); | 
|  | EXPECT_EQ(nullptr, prop.GetObject()); | 
|  | EXPECT_EQ(nullptr, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_ToJson) { | 
|  | BooleanPropType prop; | 
|  | EXPECT_JSON_EQ("'boolean'", *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'type':'boolean'}", *prop.ToJson(true, false)); | 
|  | BooleanPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'enum':[true,false]}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("[true,false]", *param2.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'enum':[true,false],'type':'boolean'}", | 
|  | *param2.ToJson(true, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'default':true}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'default':true}", *param2.ToJson(false, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_FromJson) { | 
|  | BooleanPropType prop; | 
|  | prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr); | 
|  | BooleanPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_FALSE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(std::vector<bool>{true}, GetOneOfValues<bool>(&prop)); | 
|  |  | 
|  | BooleanPropType prop_base; | 
|  | BooleanPropType param3; | 
|  | ASSERT_TRUE(param3.FromJson(CreateDictionaryValue("{'default':false}").get(), | 
|  | &prop_base, nullptr)); | 
|  | EXPECT_TRUE(param3.HasOverriddenAttributes()); | 
|  | ASSERT_NE(nullptr, param3.GetDefaultValue()); | 
|  | EXPECT_FALSE(param3.GetDefaultValue()->GetBoolean()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_Validate) { | 
|  | BooleanPropType prop; | 
|  | prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr); | 
|  | ErrorPtr error; | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("false"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("true"), &error)); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, BoolPropType_CreateValue) { | 
|  | BooleanPropType prop; | 
|  | ErrorPtr error; | 
|  | auto val = prop.CreateValue(base::FundamentalValue{true}, &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(val->GetValue()); | 
|  |  | 
|  | val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_Empty) { | 
|  | DoublePropType prop; | 
|  | EXPECT_DOUBLE_EQ(std::numeric_limits<double>::lowest(), prop.GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ((std::numeric_limits<double>::max)(), prop.GetMaxValue()); | 
|  | EXPECT_FALSE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | EXPECT_FALSE(prop.IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_Types) { | 
|  | DoublePropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetInt()); | 
|  | EXPECT_EQ(nullptr, prop.GetBoolean()); | 
|  | EXPECT_EQ(&prop, prop.GetDouble()); | 
|  | EXPECT_EQ(nullptr, prop.GetString()); | 
|  | EXPECT_EQ(nullptr, prop.GetObject()); | 
|  | EXPECT_EQ(nullptr, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_ToJson) { | 
|  | DoublePropType prop; | 
|  | EXPECT_JSON_EQ("'number'", *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'type':'number'}", *prop.ToJson(true, false)); | 
|  | DoublePropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{'minimum':3.0}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'maximum':-7.0}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(), | 
|  | &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{'maximum':5.0,'minimum':0.0}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'default':12.3}", *param2.ToJson(false, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_FromJson) { | 
|  | DoublePropType prop; | 
|  | prop.AddMinMaxConstraint(2.5, 8.7); | 
|  | DoublePropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_FALSE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_DOUBLE_EQ(2.5, prop.GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(8.7, prop.GetMaxValue()); | 
|  | prop.AddMinMaxConstraint(-2.2, 30.4); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_DOUBLE_EQ(7.0, param2.GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(30.4, param2.GetMaxValue()); | 
|  | param2.FromJson(CreateDictionaryValue("{'maximum':17.2}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_DOUBLE_EQ(-2.2, param2.GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(17.2, param2.GetMaxValue()); | 
|  | param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':6.1}").get(), | 
|  | &prop, nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_DOUBLE_EQ(0.0, param2.GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(6.1, param2.GetMaxValue()); | 
|  |  | 
|  | ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':-1.234}").get(), | 
|  | &prop, nullptr)); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | ASSERT_NE(nullptr, param2.GetDefaultValue()); | 
|  | EXPECT_DOUBLE_EQ(-1.234, param2.GetDefaultValue()->GetDouble()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_Validate) { | 
|  | DoublePropType prop; | 
|  | prop.AddMinMaxConstraint(-1.2, 1.3); | 
|  | ErrorPtr error; | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("-2"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1.3"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("-1.2"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("0.0"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("1.3"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("1.31"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'0.0'"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, DoublePropType_CreateValue) { | 
|  | DoublePropType prop; | 
|  | ErrorPtr error; | 
|  | auto val = prop.CreateValue(base::FundamentalValue{2.0}, &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_DOUBLE_EQ(2.0, val->GetValue()); | 
|  |  | 
|  | val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_Empty) { | 
|  | StringPropType prop; | 
|  | EXPECT_EQ(0, prop.GetMinLength()); | 
|  | EXPECT_EQ((std::numeric_limits<int>::max)(), prop.GetMaxLength()); | 
|  | EXPECT_FALSE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | EXPECT_FALSE(prop.IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_Types) { | 
|  | StringPropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetInt()); | 
|  | EXPECT_EQ(nullptr, prop.GetBoolean()); | 
|  | EXPECT_EQ(nullptr, prop.GetDouble()); | 
|  | EXPECT_EQ(&prop, prop.GetString()); | 
|  | EXPECT_EQ(nullptr, prop.GetObject()); | 
|  | EXPECT_EQ(nullptr, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_ToJson) { | 
|  | StringPropType prop; | 
|  | EXPECT_JSON_EQ("'string'", *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'type':'string'}", *prop.ToJson(true, false)); | 
|  | StringPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minLength':3}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'minLength':3}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'maxLength':7}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'maxLength':7}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'minLength':0,'maxLength':5}").get(), | 
|  | &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{'maxLength':5,'minLength':0}", *param2.ToJson(false, false)); | 
|  | param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'default':'abcd'}", *param2.ToJson(false, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_FromJson) { | 
|  | StringPropType prop; | 
|  | prop.AddLengthConstraint(2, 8); | 
|  | StringPropType param2; | 
|  | param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_FALSE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(2, prop.GetMinLength()); | 
|  | EXPECT_EQ(8, prop.GetMaxLength()); | 
|  | prop.AddLengthConstraint(3, 5); | 
|  | param2.FromJson(CreateDictionaryValue("{'minLength':4}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(4, param2.GetMinLength()); | 
|  | EXPECT_EQ(5, param2.GetMaxLength()); | 
|  | param2.FromJson(CreateDictionaryValue("{'maxLength':8}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(3, param2.GetMinLength()); | 
|  | EXPECT_EQ(8, param2.GetMaxLength()); | 
|  | param2.FromJson(CreateDictionaryValue("{'minLength':1,'maxLength':7}").get(), | 
|  | &prop, nullptr); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | EXPECT_TRUE(param2.IsBasedOnSchema()); | 
|  | EXPECT_EQ(1, param2.GetMinLength()); | 
|  | EXPECT_EQ(7, param2.GetMaxLength()); | 
|  |  | 
|  | ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':'foo'}").get(), | 
|  | &prop, nullptr)); | 
|  | EXPECT_TRUE(param2.HasOverriddenAttributes()); | 
|  | ASSERT_NE(nullptr, param2.GetDefaultValue()); | 
|  | EXPECT_EQ("foo", param2.GetDefaultValue()->GetString()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_Validate) { | 
|  | StringPropType prop; | 
|  | prop.AddLengthConstraint(1, 3); | 
|  | ErrorPtr error; | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | prop.AddLengthConstraint(2, 3); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'a'"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("'ab'"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error)); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'abcd'"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | prop.FromJson(CreateDictionaryValue("{'enum':['abc','def','xyz!!']}").get(), | 
|  | nullptr, &error); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error)); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("'def'"), &error)); | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("'xyz!!'"), &error)); | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("'xyz'"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, StringPropType_CreateValue) { | 
|  | StringPropType prop; | 
|  | ErrorPtr error; | 
|  | auto val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_EQ("blah", val->GetValue()); | 
|  |  | 
|  | val = prop.CreateValue(base::FundamentalValue{4}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_Empty) { | 
|  | ObjectPropType prop; | 
|  | EXPECT_TRUE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | EXPECT_FALSE(prop.IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_Types) { | 
|  | ObjectPropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetInt()); | 
|  | EXPECT_EQ(nullptr, prop.GetBoolean()); | 
|  | EXPECT_EQ(nullptr, prop.GetDouble()); | 
|  | EXPECT_EQ(nullptr, prop.GetString()); | 
|  | EXPECT_EQ(&prop, prop.GetObject()); | 
|  | EXPECT_EQ(nullptr, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_ToJson) { | 
|  | ObjectPropType prop; | 
|  | EXPECT_JSON_EQ("{'additionalProperties':false,'properties':{}}", | 
|  | *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ( | 
|  | "{'additionalProperties':false,'properties':{},'type':'object'}", | 
|  | *prop.ToJson(true, false)); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | ObjectPropType prop2; | 
|  | prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false)); | 
|  | EXPECT_TRUE(prop2.IsBasedOnSchema()); | 
|  |  | 
|  | auto schema = ObjectSchema::Create(); | 
|  | schema->AddProp("expires", PropType::Create(ValueType::Int)); | 
|  | auto pw = PropType::Create(ValueType::String); | 
|  | pw->GetString()->AddLengthConstraint(6, 100); | 
|  | schema->AddProp("password", std::move(pw)); | 
|  | prop2.SetObjectSchema(std::move(schema)); | 
|  | auto expected = R"({ | 
|  | 'additionalProperties': false, | 
|  | 'properties': { | 
|  | 'expires': 'integer', | 
|  | 'password': { | 
|  | 'maxLength': 100, | 
|  | 'minLength': 6 | 
|  | } | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop2.ToJson(false, false)); | 
|  |  | 
|  | expected = R"({ | 
|  | 'additionalProperties': false, | 
|  | 'properties': { | 
|  | 'expires': { | 
|  | 'type': 'integer' | 
|  | }, | 
|  | 'password': { | 
|  | 'maxLength': 100, | 
|  | 'minLength': 6, | 
|  | 'type': 'string' | 
|  | } | 
|  | }, | 
|  | 'type': 'object' | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop2.ToJson(true, false)); | 
|  |  | 
|  | ObjectPropType prop3; | 
|  | ASSERT_TRUE( | 
|  | prop3.FromJson(CreateDictionaryValue( | 
|  | "{'default':{'expires':3,'password':'abracadabra'}}") | 
|  | .get(), | 
|  | &prop2, nullptr)); | 
|  | expected = R"({ | 
|  | 'default': { | 
|  | 'expires': 3, | 
|  | 'password': 'abracadabra' | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop3.ToJson(false, false)); | 
|  |  | 
|  | expected = R"({ | 
|  | 'additionalProperties': false, | 
|  | 'default': { | 
|  | 'expires': 3, | 
|  | 'password': 'abracadabra' | 
|  | }, | 
|  | 'properties': { | 
|  | 'expires': { | 
|  | 'type': 'integer' | 
|  | }, | 
|  | 'password': { | 
|  | 'maxLength': 100, | 
|  | 'minLength': 6, | 
|  | 'type': 'string' | 
|  | } | 
|  | }, | 
|  | 'type': 'object' | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop3.ToJson(true, false)); | 
|  |  | 
|  | ObjectPropType prop4; | 
|  | ASSERT_TRUE(prop4.FromJson( | 
|  | CreateDictionaryValue("{'additionalProperties':true," | 
|  | "'default':{'expires':3,'password':'abracadabra'}}") | 
|  | .get(), | 
|  | &prop2, nullptr)); | 
|  | expected = R"({ | 
|  | 'additionalProperties': true, | 
|  | 'default': { | 
|  | 'expires': 3, | 
|  | 'password': 'abracadabra' | 
|  | }, | 
|  | 'properties': { | 
|  | 'expires': 'integer', | 
|  | 'password': { | 
|  | 'maxLength': 100, | 
|  | 'minLength': 6 | 
|  | } | 
|  | } | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop4.ToJson(false, false)); | 
|  |  | 
|  | expected = R"({ | 
|  | 'additionalProperties': true, | 
|  | 'default': { | 
|  | 'expires': 3, | 
|  | 'password': 'abracadabra' | 
|  | }, | 
|  | 'properties': { | 
|  | 'expires': { | 
|  | 'type': 'integer' | 
|  | }, | 
|  | 'password': { | 
|  | 'maxLength': 100, | 
|  | 'minLength': 6, | 
|  | 'type': 'string' | 
|  | } | 
|  | }, | 
|  | 'type': 'object' | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *prop4.ToJson(true, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_FromJson) { | 
|  | ObjectPropType base_prop; | 
|  | EXPECT_TRUE(base_prop.FromJson( | 
|  | CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  | auto schema = base_prop.GetObject()->GetObjectSchemaPtr(); | 
|  | const PropType* prop = schema->GetProp("name"); | 
|  | EXPECT_EQ(ValueType::String, prop->GetType()); | 
|  | prop = schema->GetProp("age"); | 
|  | EXPECT_EQ(ValueType::Int, prop->GetType()); | 
|  |  | 
|  | ObjectPropType prop2; | 
|  | ASSERT_TRUE(prop2.FromJson( | 
|  | CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}," | 
|  | "'default':{'name':'Bob','age':33}}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  | ASSERT_NE(nullptr, prop2.GetDefaultValue()); | 
|  | const ObjectValue* defval = prop2.GetDefaultValue()->GetObject(); | 
|  | ASSERT_NE(nullptr, defval); | 
|  | ValueMap objval = defval->GetValue(); | 
|  | EXPECT_EQ("Bob", objval["name"]->GetString()->GetValue()); | 
|  | EXPECT_EQ(33, objval["age"]->GetInt()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_Validate) { | 
|  | ObjectPropType prop; | 
|  | prop.FromJson( | 
|  | CreateDictionaryValue("{'properties':{'expires':'integer'," | 
|  | "'password':{'maxLength':100,'minLength':6}}," | 
|  | "'required':['expires','password']}") | 
|  | .get(), | 
|  | nullptr, nullptr); | 
|  | ErrorPtr error; | 
|  | EXPECT_TRUE(ValidateValue( | 
|  | prop, *CreateValue("{'expires':10,'password':'abcdef'}"), &error)); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("{'expires':10}"), &error)); | 
|  | EXPECT_EQ("parameter_missing", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE( | 
|  | ValidateValue(prop, *CreateValue("{'password':'abcdef'}"), &error)); | 
|  | EXPECT_EQ("parameter_missing", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue( | 
|  | prop, *CreateValue("{'expires':10,'password':'abcde'}"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("2"), &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue( | 
|  | prop, *CreateValue("{'expires':10,'password':'abcdef','retry':true}"), | 
|  | &error)); | 
|  | EXPECT_EQ("unexpected_parameter", error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_Validate_Enum) { | 
|  | ObjectPropType prop; | 
|  | EXPECT_TRUE(prop.FromJson( | 
|  | CreateDictionaryValue( | 
|  | "{'properties':{'width':'integer','height':'integer'}," | 
|  | "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  | ErrorPtr error; | 
|  | EXPECT_TRUE( | 
|  | ValidateValue(prop, *CreateValue("{'height':20,'width':10}"), &error)); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | ValidateValue(prop, *CreateValue("{'height':200,'width':100}"), &error)); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE( | 
|  | ValidateValue(prop, *CreateValue("{'height':12,'width':10}"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectPropType_CreateValue) { | 
|  | ObjectPropType prop; | 
|  | IntPropType int_type; | 
|  | ASSERT_TRUE(prop.FromJson( | 
|  | CreateDictionaryValue( | 
|  | "{'properties':{'width':'integer','height':'integer'}," | 
|  | "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  | ValueMap obj{ | 
|  | {"width", int_type.CreateValue(base::FundamentalValue{10}, nullptr)}, | 
|  | {"height", int_type.CreateValue(base::FundamentalValue{20}, nullptr)}, | 
|  | }; | 
|  |  | 
|  | ErrorPtr error; | 
|  | auto val = prop.CreateValue( | 
|  | *CreateDictionaryValue("{'width': 10, 'height': 20}"), &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_EQ(obj, val->GetValue()); | 
|  |  | 
|  | val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_Empty) { | 
|  | ArrayPropType prop; | 
|  | EXPECT_FALSE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_EQ(nullptr, prop.GetDefaultValue()); | 
|  | EXPECT_EQ(nullptr, prop.GetItemTypePtr()); | 
|  | prop.SetItemType(PropType::Create(ValueType::Int)); | 
|  | EXPECT_TRUE(prop.HasOverriddenAttributes()); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | EXPECT_NE(nullptr, prop.GetItemTypePtr()); | 
|  | EXPECT_FALSE(prop.IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_Types) { | 
|  | ArrayPropType prop; | 
|  | EXPECT_EQ(nullptr, prop.GetInt()); | 
|  | EXPECT_EQ(nullptr, prop.GetBoolean()); | 
|  | EXPECT_EQ(nullptr, prop.GetDouble()); | 
|  | EXPECT_EQ(nullptr, prop.GetString()); | 
|  | EXPECT_EQ(nullptr, prop.GetObject()); | 
|  | EXPECT_EQ(&prop, prop.GetArray()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_ToJson) { | 
|  | ArrayPropType prop; | 
|  | prop.SetItemType(PropType::Create(ValueType::Int)); | 
|  | EXPECT_JSON_EQ("{'items':'integer'}", *prop.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ("{'items':{'type':'integer'},'type':'array'}", | 
|  | *prop.ToJson(true, false)); | 
|  | EXPECT_FALSE(prop.IsBasedOnSchema()); | 
|  | ArrayPropType prop2; | 
|  | prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); | 
|  | EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false)); | 
|  | EXPECT_TRUE(prop2.IsBasedOnSchema()); | 
|  | prop2.FromJson(CreateDictionaryValue("{'default':[1,2,3]}").get(), &prop, | 
|  | nullptr); | 
|  | EXPECT_JSON_EQ("{'default':[1,2,3]}", *prop2.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ( | 
|  | "{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}", | 
|  | *prop2.ToJson(true, false)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_FromJson) { | 
|  | ArrayPropType prop; | 
|  | EXPECT_TRUE(prop.FromJson(CreateDictionaryValue("{'items':'integer'}").get(), | 
|  | nullptr, nullptr)); | 
|  | EXPECT_EQ(ValueType::Int, prop.GetItemTypePtr()->GetType()); | 
|  |  | 
|  | ArrayPropType prop2; | 
|  | ASSERT_TRUE( | 
|  | prop2.FromJson(CreateDictionaryValue( | 
|  | "{'items':'string','default':['foo', 'bar', 'baz']}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  | ASSERT_NE(nullptr, prop2.GetDefaultValue()); | 
|  | const ArrayValue* defval = prop2.GetDefaultValue()->GetArray(); | 
|  | ASSERT_NE(nullptr, defval); | 
|  | EXPECT_EQ((std::vector<std::string>{"foo", "bar", "baz"}), | 
|  | GetArrayValues<std::string>(defval->GetValue())); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_Validate) { | 
|  | ArrayPropType prop; | 
|  | prop.FromJson( | 
|  | CreateDictionaryValue("{'items':{'minimum':2.3, 'maximum':10.5}}").get(), | 
|  | nullptr, nullptr); | 
|  |  | 
|  | ErrorPtr error; | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("[3,4,10.5]"), &error)); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("[4, 5, 20]"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_Validate_Enum) { | 
|  | ArrayPropType prop; | 
|  | prop.FromJson( | 
|  | CreateDictionaryValue("{'items':'integer', 'enum':[[1], [2,3], [4,5,6]]}") | 
|  | .get(), | 
|  | nullptr, nullptr); | 
|  |  | 
|  | ErrorPtr error; | 
|  | EXPECT_TRUE(ValidateValue(prop, *CreateValue("[2,3]"), &error)); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2,3,4]"), &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_CreateValue) { | 
|  | ArrayPropType prop; | 
|  | ASSERT_TRUE(prop.FromJson( | 
|  | CreateDictionaryValue( | 
|  | "{'items':{'properties':{'width':'integer','height':'integer'}}}") | 
|  | .get(), | 
|  | nullptr, nullptr)); | 
|  |  | 
|  | ErrorPtr error; | 
|  | ValueVector arr; | 
|  |  | 
|  | auto val = prop.CreateValue(base::ListValue{}, &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_EQ(arr, val->GetValue()); | 
|  | EXPECT_JSON_EQ("[]", *val->ToJson()); | 
|  |  | 
|  | val = prop.CreateValue( | 
|  | *CreateValue("[{'height':20,'width':10},{'width':17, 'height':18}]"), | 
|  | &error); | 
|  | ASSERT_NE(nullptr, val.get()); | 
|  | EXPECT_EQ(nullptr, error.get()); | 
|  | EXPECT_JSON_EQ("[{'height':20,'width':10},{'height':18,'width':17}]", | 
|  | *val->ToJson()); | 
|  |  | 
|  | val = prop.CreateValue(base::StringValue{"blah"}, &error); | 
|  | EXPECT_EQ(nullptr, val.get()); | 
|  | ASSERT_NE(nullptr, error.get()); | 
|  | EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ArrayPropType_NestedArrays_NotSupported) { | 
|  | ArrayPropType prop; | 
|  | ErrorPtr error; | 
|  | EXPECT_FALSE(prop.FromJson( | 
|  | CreateDictionaryValue("{'items':{'items':'integer'}}").get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) { | 
|  | ObjectSchema schema; | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1':'integer'," | 
|  | "'param2':'number'," | 
|  | "'param3':'string'" | 
|  | "}"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType()); | 
|  | EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType()); | 
|  | EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); | 
|  | EXPECT_EQ(nullptr, schema.GetProp("param4")); | 
|  |  | 
|  | int min_int = (std::numeric_limits<int>::min)(); | 
|  | int max_int = (std::numeric_limits<int>::max)(); | 
|  | double min_dbl = (std::numeric_limits<double>::lowest)(); | 
|  | double max_dbl = (std::numeric_limits<double>::max)(); | 
|  | EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_Full_TypeName) { | 
|  | ObjectSchema schema; | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1':{'type':'integer'}," | 
|  | "'param2':{'type':'number'}," | 
|  | "'param3':{'type':'string'}," | 
|  | "'param4':{'type':'array', 'items':'integer'}," | 
|  | "'param5':{'type':'object', 'properties':{'p1':'integer'}}" | 
|  | "}"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType()); | 
|  | EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType()); | 
|  | EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType()); | 
|  | EXPECT_EQ(ValueType::Array, schema.GetProp("param4")->GetType()); | 
|  | EXPECT_EQ(ValueType::Object, schema.GetProp("param5")->GetType()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); | 
|  | EXPECT_EQ("array", schema.GetProp("param4")->GetTypeAsString()); | 
|  | EXPECT_EQ("object", schema.GetProp("param5")->GetTypeAsString()); | 
|  | EXPECT_EQ(nullptr, schema.GetProp("param77")); | 
|  |  | 
|  | int min_int = (std::numeric_limits<int>::min)(); | 
|  | int max_int = (std::numeric_limits<int>::max)(); | 
|  | double min_dbl = (std::numeric_limits<double>::lowest)(); | 
|  | double max_dbl = (std::numeric_limits<double>::max)(); | 
|  | EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Scalar) { | 
|  | ObjectSchema schema; | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1' :{'minimum':2}," | 
|  | "'param2' :{'maximum':10}," | 
|  | "'param3' :{'maximum':8, 'minimum':2}," | 
|  | "'param4' :{'minimum':2.1}," | 
|  | "'param5' :{'maximum':10.1}," | 
|  | "'param6' :{'maximum':8.1, 'minimum':3.1}," | 
|  | "'param7' :{'maximum':8, 'minimum':3.1}," | 
|  | "'param8' :{'maximum':8.1, 'minimum':3}," | 
|  | "'param9' :{'minLength':2}," | 
|  | "'param10':{'maxLength':10}," | 
|  | "'param11':{'maxLength':8, 'minLength':3}," | 
|  | "'param12':{'default':12}," | 
|  | "'param13':{'default':13.5}," | 
|  | "'param14':{'default':true}," | 
|  | "'param15':{'default':false}," | 
|  | "'param16':{'default':'foobar'}," | 
|  | "'param17':{'default':[1,2,3]}," | 
|  | "'param18':{'items':'number', 'default':[]}" | 
|  | "}"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param4")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param12")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param13")->GetTypeAsString()); | 
|  | EXPECT_EQ("boolean", schema.GetProp("param14")->GetTypeAsString()); | 
|  | EXPECT_EQ("boolean", schema.GetProp("param15")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param16")->GetTypeAsString()); | 
|  | EXPECT_EQ("array", schema.GetProp("param17")->GetTypeAsString()); | 
|  | auto prop17 = schema.GetProp("param17"); | 
|  | EXPECT_EQ("integer", prop17->GetArray()->GetItemTypePtr()->GetTypeAsString()); | 
|  | EXPECT_EQ("array", schema.GetProp("param18")->GetTypeAsString()); | 
|  | auto prop18 = schema.GetProp("param18"); | 
|  | EXPECT_EQ("number", prop18->GetArray()->GetItemTypePtr()->GetTypeAsString()); | 
|  |  | 
|  | int min_int = (std::numeric_limits<int>::min)(); | 
|  | int max_int = (std::numeric_limits<int>::max)(); | 
|  | double min_dbl = (std::numeric_limits<double>::lowest)(); | 
|  | double max_dbl = (std::numeric_limits<double>::max)(); | 
|  | EXPECT_EQ(2, schema.GetProp("param1")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ(min_int, schema.GetProp("param2")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(10, schema.GetProp("param2")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ(2, schema.GetProp("param3")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(8, schema.GetProp("param3")->GetInt()->GetMaxValue()); | 
|  | EXPECT_DOUBLE_EQ(2.1, schema.GetProp("param4")->GetDouble()->GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(max_dbl, | 
|  | schema.GetProp("param4")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_DOUBLE_EQ(min_dbl, | 
|  | schema.GetProp("param5")->GetDouble()->GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(10.1, schema.GetProp("param5")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param6")->GetDouble()->GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param6")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param7")->GetDouble()->GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(8.0, schema.GetProp("param7")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_DOUBLE_EQ(3.0, schema.GetProp("param8")->GetDouble()->GetMinValue()); | 
|  | EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param8")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ(2, schema.GetProp("param9")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(max_int, schema.GetProp("param9")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ(0, schema.GetProp("param10")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(10, schema.GetProp("param10")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ(3, schema.GetProp("param11")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength()); | 
|  | const PropValue* val = schema.GetProp("param12")->GetDefaultValue(); | 
|  | EXPECT_EQ(12, val->GetInt()->GetValue()); | 
|  | val = schema.GetProp("param13")->GetDefaultValue(); | 
|  | EXPECT_DOUBLE_EQ(13.5, val->GetDouble()->GetValue()); | 
|  | val = schema.GetProp("param14")->GetDefaultValue(); | 
|  | EXPECT_TRUE(val->GetBoolean()->GetValue()); | 
|  | val = schema.GetProp("param15")->GetDefaultValue(); | 
|  | EXPECT_FALSE(val->GetBoolean()->GetValue()); | 
|  | val = schema.GetProp("param16")->GetDefaultValue(); | 
|  | EXPECT_EQ("foobar", val->GetString()->GetValue()); | 
|  | val = schema.GetProp("param17")->GetDefaultValue(); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), | 
|  | GetArrayValues<int>(val->GetArray()->GetValue())); | 
|  | val = schema.GetProp("param18")->GetDefaultValue(); | 
|  | EXPECT_TRUE(val->GetArray()->GetValue().empty()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Array) { | 
|  | ObjectSchema schema; | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1' :[0,1,2,3]," | 
|  | "'param2' :[0.0,1.1,2.2]," | 
|  | "'param3' :['id1', 'id2']," | 
|  | "'param4' :{'enum':[1,2,3]}," | 
|  | "'param5' :{'enum':[-1.1,2.2,3]}," | 
|  | "'param6' :{'enum':['id0', 'id1']}," | 
|  | "'param7' :{'type':'integer', 'enum':[1,2,3]}," | 
|  | "'param8' :{'type':'number',  'enum':[1,2,3]}," | 
|  | "'param9' :{'type':'number',  'enum':[]}," | 
|  | "'param10':{'type':'integer', 'enum':[]}," | 
|  | "'param11':[[0,1],[2,3]]," | 
|  | "'param12':[['foo','bar']]," | 
|  | "'param13':{'enum':[['id0', 'id1']]}" | 
|  | "}"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); | 
|  | EXPECT_EQ("string", schema.GetProp("param6")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param7")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); | 
|  | EXPECT_EQ("number", schema.GetProp("param9")->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param10")->GetTypeAsString()); | 
|  |  | 
|  | auto prop_type11 = schema.GetProp("param11"); | 
|  | EXPECT_EQ("array", prop_type11->GetTypeAsString()); | 
|  | EXPECT_EQ("integer", | 
|  | prop_type11->GetArray()->GetItemTypePtr()->GetTypeAsString()); | 
|  |  | 
|  | auto prop_type12 = schema.GetProp("param12"); | 
|  | EXPECT_EQ("array", prop_type12->GetTypeAsString()); | 
|  | EXPECT_EQ("string", | 
|  | prop_type12->GetArray()->GetItemTypePtr()->GetTypeAsString()); | 
|  |  | 
|  | auto prop_type13 = schema.GetProp("param13"); | 
|  | EXPECT_EQ("array", prop_type12->GetTypeAsString()); | 
|  | EXPECT_EQ("string", | 
|  | prop_type13->GetArray()->GetItemTypePtr()->GetTypeAsString()); | 
|  |  | 
|  | EXPECT_EQ((std::vector<int>{0, 1, 2, 3}), | 
|  | GetOneOfValues<int>(schema.GetProp("param1"))); | 
|  | EXPECT_EQ((std::vector<double>{0.0, 1.1, 2.2}), | 
|  | GetOneOfValues<double>(schema.GetProp("param2"))); | 
|  | EXPECT_EQ((std::vector<std::string>{"id1", "id2"}), | 
|  | GetOneOfValues<std::string>(schema.GetProp("param3"))); | 
|  |  | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), | 
|  | GetOneOfValues<int>(schema.GetProp("param4"))); | 
|  | EXPECT_EQ((std::vector<double>{-1.1, 2.2, 3.0}), | 
|  | GetOneOfValues<double>(schema.GetProp("param5"))); | 
|  | EXPECT_EQ((std::vector<std::string>{"id0", "id1"}), | 
|  | GetOneOfValues<std::string>(schema.GetProp("param6"))); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), | 
|  | GetOneOfValues<int>(schema.GetProp("param7"))); | 
|  | EXPECT_EQ((std::vector<double>{1.0, 2.0, 3.0}), | 
|  | GetOneOfValues<double>(schema.GetProp("param8"))); | 
|  | EXPECT_TRUE(GetOneOfValues<double>(schema.GetProp("param9")).empty()); | 
|  | EXPECT_TRUE(GetOneOfValues<int>(schema.GetProp("param10")).empty()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_Inheritance) { | 
|  | const char* base_schema_str = | 
|  | "{" | 
|  | "'param0' :{'minimum':1, 'maximum':5}," | 
|  | "'param1' :{'minimum':1, 'maximum':5}," | 
|  | "'param2' :{'minimum':1, 'maximum':5}," | 
|  | "'param3' :{'minimum':1, 'maximum':5}," | 
|  | "'param4' :{'minimum':1, 'maximum':5}," | 
|  | "'param5' :{'minimum':1.1, 'maximum':5.5}," | 
|  | "'param6' :{'minimum':1.1, 'maximum':5.5}," | 
|  | "'param7' :{'minimum':1.1, 'maximum':5.5}," | 
|  | "'param8' :{'minimum':1.1, 'maximum':5.5}," | 
|  | "'param9' :{'minLength':1, 'maxLength':5}," | 
|  | "'param10':{'minLength':1, 'maxLength':5}," | 
|  | "'param11':{'minLength':1, 'maxLength':5}," | 
|  | "'param12':{'minLength':1, 'maxLength':5}," | 
|  | "'param13':[1,2,3]," | 
|  | "'param14':[1,2,3]," | 
|  | "'param15':[1.1,2.2,3.3]," | 
|  | "'param16':[1.1,2.2,3.3]," | 
|  | "'param17':['id1', 'id2']," | 
|  | "'param18':['id1', 'id2']," | 
|  | "'param19':{'minimum':1, 'maximum':5}," | 
|  | "'param20':{'default':49}," | 
|  | "'param21':{'default':49}," | 
|  | "'param22':'integer'" | 
|  | "}"; | 
|  | ObjectSchema base_schema; | 
|  | EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(), | 
|  | nullptr, nullptr)); | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1' :{}," | 
|  | "'param2' :{'minimum':2}," | 
|  | "'param3' :{'maximum':9}," | 
|  | "'param4' :{'minimum':2, 'maximum':9}," | 
|  | "'param5' :{}," | 
|  | "'param6' :{'minimum':2.2}," | 
|  | "'param7' :{'maximum':9.9}," | 
|  | "'param8' :{'minimum':2.2, 'maximum':9.9}," | 
|  | "'param9' :{}," | 
|  | "'param10':{'minLength':3}," | 
|  | "'param11':{'maxLength':8}," | 
|  | "'param12':{'minLength':3, 'maxLength':8}," | 
|  | "'param13':{}," | 
|  | "'param14':[1,2,3,4]," | 
|  | "'param15':{}," | 
|  | "'param16':[1.1,2.2,3.3,4.4]," | 
|  | "'param17':{}," | 
|  | "'param18':['id1', 'id3']," | 
|  | "'param19':{}," | 
|  | "'param20':{}," | 
|  | "'param21':{'default':8}," | 
|  | "'param22':{'default':123}" | 
|  | "}"; | 
|  | ObjectSchema schema; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), | 
|  | &base_schema, nullptr)); | 
|  | EXPECT_EQ(nullptr, schema.GetProp("param0")); | 
|  | EXPECT_NE(nullptr, schema.GetProp("param1")); | 
|  | EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); | 
|  | EXPECT_EQ(1, schema.GetProp("param1")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(5, schema.GetProp("param1")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString()); | 
|  | EXPECT_EQ(2, schema.GetProp("param2")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(5, schema.GetProp("param2")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString()); | 
|  | EXPECT_EQ(1, schema.GetProp("param3")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(9, schema.GetProp("param3")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString()); | 
|  | EXPECT_EQ(2, schema.GetProp("param4")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(9, schema.GetProp("param4")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); | 
|  | EXPECT_EQ(1.1, schema.GetProp("param5")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(5.5, schema.GetProp("param5")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString()); | 
|  | EXPECT_EQ(2.2, schema.GetProp("param6")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(5.5, schema.GetProp("param6")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString()); | 
|  | EXPECT_EQ(1.1, schema.GetProp("param7")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(9.9, schema.GetProp("param7")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); | 
|  | EXPECT_EQ(2.2, schema.GetProp("param8")->GetDouble()->GetMinValue()); | 
|  | EXPECT_EQ(9.9, schema.GetProp("param8")->GetDouble()->GetMaxValue()); | 
|  | EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString()); | 
|  | EXPECT_EQ(1, schema.GetProp("param9")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(5, schema.GetProp("param9")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString()); | 
|  | EXPECT_EQ(3, schema.GetProp("param10")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(5, schema.GetProp("param10")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString()); | 
|  | EXPECT_EQ(1, schema.GetProp("param11")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ("string", schema.GetProp("param12")->GetTypeAsString()); | 
|  | EXPECT_EQ(3, schema.GetProp("param12")->GetString()->GetMinLength()); | 
|  | EXPECT_EQ(8, schema.GetProp("param12")->GetString()->GetMaxLength()); | 
|  | EXPECT_EQ("integer", schema.GetProp("param13")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), | 
|  | GetOneOfValues<int>(schema.GetProp("param13"))); | 
|  | EXPECT_EQ("integer", schema.GetProp("param14")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3, 4}), | 
|  | GetOneOfValues<int>(schema.GetProp("param14"))); | 
|  | EXPECT_EQ("number", schema.GetProp("param15")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3}), | 
|  | GetOneOfValues<double>(schema.GetProp("param15"))); | 
|  | EXPECT_EQ("number", schema.GetProp("param16")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3, 4.4}), | 
|  | GetOneOfValues<double>(schema.GetProp("param16"))); | 
|  | EXPECT_EQ("string", schema.GetProp("param17")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<std::string>{"id1", "id2"}), | 
|  | GetOneOfValues<std::string>(schema.GetProp("param17"))); | 
|  | EXPECT_EQ("string", schema.GetProp("param18")->GetTypeAsString()); | 
|  | EXPECT_EQ((std::vector<std::string>{"id1", "id3"}), | 
|  | GetOneOfValues<std::string>(schema.GetProp("param18"))); | 
|  | EXPECT_EQ("integer", schema.GetProp("param19")->GetTypeAsString()); | 
|  | EXPECT_EQ(1, schema.GetProp("param19")->GetInt()->GetMinValue()); | 
|  | EXPECT_EQ(5, schema.GetProp("param19")->GetInt()->GetMaxValue()); | 
|  | EXPECT_EQ(49, | 
|  | schema.GetProp("param20")->GetDefaultValue()->GetInt()->GetValue()); | 
|  | EXPECT_EQ(8, | 
|  | schema.GetProp("param21")->GetDefaultValue()->GetInt()->GetValue()); | 
|  | EXPECT_EQ(123, | 
|  | schema.GetProp("param22")->GetDefaultValue()->GetInt()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_UseDefaults) { | 
|  | ObjectPropType prop; | 
|  | const char* schema_str = | 
|  | "{'properties':{" | 
|  | "'param1':{'default':true}," | 
|  | "'param2':{'default':2}," | 
|  | "'param3':{'default':3.3}," | 
|  | "'param4':{'default':'four'}," | 
|  | "'param5':{'default':{'x':5,'y':6}," | 
|  | "'properties':{'x':'integer','y':'integer'}}," | 
|  | "'param6':{'default':[1,2,3]}" | 
|  | "}}"; | 
|  | ASSERT_TRUE( | 
|  | prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); | 
|  |  | 
|  | // Omit all. | 
|  | auto value = | 
|  | prop.CreatePropValue(*CreateDictionaryValue("{}").get(), nullptr); | 
|  | ASSERT_NE(nullptr, value); | 
|  | ValueMap obj = value->GetObject()->GetValue(); | 
|  | EXPECT_TRUE(obj["param1"]->GetBoolean()->GetValue()); | 
|  | EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue()); | 
|  | EXPECT_DOUBLE_EQ(3.3, obj["param3"]->GetDouble()->GetValue()); | 
|  | EXPECT_EQ("four", obj["param4"]->GetString()->GetValue()); | 
|  | ValueMap param5 = obj["param5"]->GetObject()->GetValue(); | 
|  | EXPECT_EQ(5, param5["x"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(6, param5["y"]->GetInt()->GetValue()); | 
|  | ValueVector param6 = obj["param6"]->GetArray()->GetValue(); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6)); | 
|  |  | 
|  | // Specify some. | 
|  | const char* val_json = | 
|  | "{" | 
|  | "'param1':false," | 
|  | "'param3':33.3," | 
|  | "'param5':{'x':-5,'y':-6}" | 
|  | "}"; | 
|  | value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); | 
|  | ASSERT_NE(nullptr, value); | 
|  | obj = value->GetObject()->GetValue(); | 
|  | EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue()); | 
|  | EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue()); | 
|  | EXPECT_DOUBLE_EQ(33.3, obj["param3"]->GetDouble()->GetValue()); | 
|  | EXPECT_EQ("four", obj["param4"]->GetString()->GetValue()); | 
|  | param5 = obj["param5"]->GetObject()->GetValue(); | 
|  | EXPECT_EQ(-5, param5["x"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(-6, param5["y"]->GetInt()->GetValue()); | 
|  | param6 = obj["param6"]->GetArray()->GetValue(); | 
|  | EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6)); | 
|  |  | 
|  | // Specify all. | 
|  | val_json = | 
|  | "{" | 
|  | "'param1':false," | 
|  | "'param2':22," | 
|  | "'param3':333.3," | 
|  | "'param4':'FOUR'," | 
|  | "'param5':{'x':-55,'y':66}," | 
|  | "'param6':[-1, 0]" | 
|  | "}"; | 
|  | value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); | 
|  | ASSERT_NE(nullptr, value); | 
|  | obj = value->GetObject()->GetValue(); | 
|  | EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue()); | 
|  | EXPECT_EQ(22, obj["param2"]->GetInt()->GetValue()); | 
|  | EXPECT_DOUBLE_EQ(333.3, obj["param3"]->GetDouble()->GetValue()); | 
|  | EXPECT_EQ("FOUR", obj["param4"]->GetString()->GetValue()); | 
|  | param5 = obj["param5"]->GetObject()->GetValue(); | 
|  | EXPECT_EQ(-55, param5["x"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(66, param5["y"]->GetInt()->GetValue()); | 
|  | param6 = obj["param6"]->GetArray()->GetValue(); | 
|  | EXPECT_EQ((std::vector<int>{-1, 0}), GetArrayValues<int>(param6)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_FromJson_BaseSchema_Failures) { | 
|  | ObjectSchema schema; | 
|  | ErrorPtr error; | 
|  | const char* schema_str = | 
|  | "{" | 
|  | "'param1':{}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'type':'foo'}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("unknown_type", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':[]" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'minimum':'foo'}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':[1,2.2]" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'minimum':1, 'enum':[1,2,3]}"  // can't have min/max & enum. | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'maximum':1, 'blah':2}"  // 'blah' is unexpected. | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'enum':[1,2,3],'default':5}"  // 'default' must be 1, 2, or 3. | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':[[1,2.3]]" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':[[1,2],[3,4],['blah']]" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'default':[]}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':[[[1]],[[2]]]" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'enum':[[['foo']]]}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | schema_str = | 
|  | "{" | 
|  | "'param1':{'default':[[1],[2]]}" | 
|  | "}"; | 
|  | EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_Integral) { | 
|  | IntPropType prop; | 
|  |  | 
|  | prop.MakeRequired(false); | 
|  | EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); | 
|  | EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}", | 
|  | *prop.ToJson(true, true)); | 
|  |  | 
|  | prop.MakeRequired(true); | 
|  | EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); | 
|  | EXPECT_JSON_EQ("{'isRequired':true,'type':'integer'}", | 
|  | *prop.ToJson(true, true)); | 
|  |  | 
|  | IntPropType prop2; | 
|  | EXPECT_TRUE( | 
|  | prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr)); | 
|  | EXPECT_TRUE(prop2.IsRequired()); | 
|  |  | 
|  | EXPECT_TRUE(prop2.FromJson( | 
|  | CreateDictionaryValue("{'isRequired': false}").get(), &prop, nullptr)); | 
|  | EXPECT_FALSE(prop2.IsRequired()); | 
|  |  | 
|  | EXPECT_JSON_EQ("{'type':'integer'}", *prop2.ToJson(true, false)); | 
|  | EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}", | 
|  | *prop2.ToJson(true, true)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_Object) { | 
|  | ObjectPropType obj_type; | 
|  | auto schema = ObjectSchema::Create(); | 
|  | auto type = PropType::Create(ValueType::Int); | 
|  | type->MakeRequired(true); | 
|  | schema->AddProp("prop1", std::move(type)); | 
|  | type = PropType::Create(ValueType::String); | 
|  | type->MakeRequired(false); | 
|  | schema->AddProp("prop2", std::move(type)); | 
|  | type = PropType::Create(ValueType::Boolean); | 
|  | type->MakeRequired(true); | 
|  | schema->AddProp("prop3", std::move(type)); | 
|  | type = PropType::Create(ValueType::Array); | 
|  | type->GetArray()->SetItemType(PropType::Create(ValueType::String)); | 
|  | schema->AddProp("prop4", std::move(type)); | 
|  | auto expected1 = R"({ | 
|  | 'prop1': 'integer', | 
|  | 'prop2': 'string', | 
|  | 'prop3': 'boolean', | 
|  | 'prop4': {'items': 'string'} | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected1, *schema->ToJson(false, false)); | 
|  | auto expected2 = R"({ | 
|  | 'prop1': {'type':'integer','isRequired': true}, | 
|  | 'prop2': {'type':'string','isRequired': false}, | 
|  | 'prop3': {'type':'boolean','isRequired': true}, | 
|  | 'prop4': {'items': 'string'} | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected2, *schema->ToJson(false, true)); | 
|  |  | 
|  | obj_type.SetObjectSchema(std::move(schema)); | 
|  | auto expected3 = R"({ | 
|  | 'additionalProperties': false, | 
|  | 'properties': { | 
|  | 'prop1': 'integer', | 
|  | 'prop2': 'string', | 
|  | 'prop3': 'boolean', | 
|  | 'prop4': {'items': 'string'} | 
|  | }, | 
|  | 'required': ['prop1','prop3'] | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, false)); | 
|  | EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, true)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_Schema_FromJson) { | 
|  | ObjectSchema schema; | 
|  | auto schema_str = R"({ | 
|  | 'prop1': {'type':'integer','isRequired': true}, | 
|  | 'prop2': {'type':'string','isRequired': false}, | 
|  | 'prop3': 'boolean' | 
|  | })"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_TRUE(schema.GetProp("prop1")->IsRequired()); | 
|  | EXPECT_FALSE(schema.GetProp("prop2")->IsRequired()); | 
|  | EXPECT_FALSE(schema.GetProp("prop3")->IsRequired()); | 
|  | EXPECT_JSON_EQ(schema_str, *schema.ToJson(false, true)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_Schema_FromJson_Inherit) { | 
|  | ObjectSchema base_schema; | 
|  | auto base_schema_str = R"({ | 
|  | 'prop1': {'type':'integer','isRequired': true}, | 
|  | 'prop2': {'type':'integer','isRequired': false}, | 
|  | 'prop3': {'type':'integer','isRequired': true}, | 
|  | 'prop4': {'type':'integer','isRequired': false} | 
|  | })"; | 
|  | EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(), | 
|  | nullptr, nullptr)); | 
|  | ObjectSchema schema; | 
|  | auto schema_str = R"({ | 
|  | 'prop1': {'isRequired': false}, | 
|  | 'prop2': {'isRequired': true}, | 
|  | 'prop3': {}, | 
|  | 'prop4': 'integer' | 
|  | })"; | 
|  | EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), | 
|  | &base_schema, nullptr)); | 
|  | EXPECT_FALSE(schema.GetProp("prop1")->IsRequired()); | 
|  | EXPECT_TRUE(schema.GetProp("prop2")->IsRequired()); | 
|  | EXPECT_TRUE(schema.GetProp("prop3")->IsRequired()); | 
|  | EXPECT_FALSE(schema.GetProp("prop4")->IsRequired()); | 
|  | auto expected = R"({ | 
|  | 'prop1': {'type':'integer','isRequired': false}, | 
|  | 'prop2': {'type':'integer','isRequired': true}, | 
|  | 'prop3': {}, | 
|  | 'prop4': {} | 
|  | })"; | 
|  | EXPECT_JSON_EQ(expected, *schema.ToJson(false, true)); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_ObjectPropType_FromJson) { | 
|  | ObjectPropType obj_type; | 
|  | auto type_str = R"({ | 
|  | 'properties': { | 
|  | 'prop1': 'integer', | 
|  | 'prop2': 'string', | 
|  | 'prop3': {'type':'boolean','isRequired':true}, | 
|  | 'prop4': {'items': 'string','isRequired':false}, | 
|  | 'prop5': {'type':'number','isRequired':true} | 
|  | }, | 
|  | 'required': ['prop1','prop3','prop4','prop5'] | 
|  | })"; | 
|  | EXPECT_TRUE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, | 
|  | nullptr)); | 
|  | EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop1")->IsRequired()); | 
|  | EXPECT_FALSE(obj_type.GetObjectSchemaPtr()->GetProp("prop2")->IsRequired()); | 
|  | EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop3")->IsRequired()); | 
|  | // 'required' takes precedence over 'isRequired'. | 
|  | EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop4")->IsRequired()); | 
|  | EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop5")->IsRequired()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, RequiredProperties_Failures) { | 
|  | ObjectPropType obj_type; | 
|  | ErrorPtr error; | 
|  |  | 
|  | auto type_str = R"({ | 
|  | 'properties': { | 
|  | 'prop1': 'integer', | 
|  | 'prop2': 'string' | 
|  | }, | 
|  | 'required': ['prop1','prop3','prop4'] | 
|  | })"; | 
|  | EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode()); | 
|  | error.reset(); | 
|  |  | 
|  | type_str = R"({ | 
|  | 'properties': { | 
|  | 'prop1': 'integer', | 
|  | 'prop2': 'string' | 
|  | }, | 
|  | 'required': 'prop1' | 
|  | })"; | 
|  | EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, | 
|  | &error)); | 
|  | EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode()); | 
|  | error.reset(); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_UseRequired) { | 
|  | ObjectPropType prop; | 
|  | auto schema_str = R"({ | 
|  | 'properties':{ | 
|  | 'param1':'integer', | 
|  | 'param2':'integer', | 
|  | 'param3':{'default':3}, | 
|  | 'param4':{'default':4} | 
|  | }, | 
|  | 'required':['param1','param3'] | 
|  | })"; | 
|  | ASSERT_TRUE( | 
|  | prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); | 
|  |  | 
|  | auto val_json = R"({ | 
|  | 'param1':10, | 
|  | 'param2':20, | 
|  | 'param3':30, | 
|  | 'param4':40 | 
|  | })"; | 
|  | auto value = | 
|  | prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); | 
|  | ASSERT_NE(nullptr, value); | 
|  | ValueMap obj = value->GetObject()->GetValue(); | 
|  | EXPECT_EQ(10, obj["param1"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(20, obj["param2"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(30, obj["param3"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(40, obj["param4"]->GetInt()->GetValue()); | 
|  |  | 
|  | val_json = "{'param1':100}"; | 
|  | value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); | 
|  | ASSERT_NE(nullptr, value); | 
|  | obj = value->GetObject()->GetValue(); | 
|  | EXPECT_EQ(3u, obj.size()); | 
|  |  | 
|  | EXPECT_EQ(100, obj["param1"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(obj.end(), obj.find("param2")); | 
|  | EXPECT_EQ(3, obj["param3"]->GetInt()->GetValue()); | 
|  | EXPECT_EQ(4, obj["param4"]->GetInt()->GetValue()); | 
|  | } | 
|  |  | 
|  | TEST(CommandSchema, ObjectSchema_UseRequired_Failure) { | 
|  | ObjectPropType prop; | 
|  | auto schema_str = R"({ | 
|  | 'properties':{ | 
|  | 'param1':'integer', | 
|  | 'param2':'integer', | 
|  | 'param3':{'default':3}, | 
|  | 'param4':{'default':4} | 
|  | }, | 
|  | 'required':['param1','param3'] | 
|  | })"; | 
|  | ASSERT_TRUE( | 
|  | prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); | 
|  |  | 
|  | auto val_json = "{'param2':20}"; | 
|  | ErrorPtr error; | 
|  | auto value = | 
|  | prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), &error); | 
|  | ASSERT_EQ(nullptr, value); | 
|  | EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode()); | 
|  | } | 
|  |  | 
|  | }  // namespace weave |