buffet: Add support for 'default' properties in CDD The internals of supporting optional command parameters/state properties was built into buffet earlier, but this was not exposed in JSON reading/writing routines, so it was impossible to use this feature. Added JSON serialization/deserialization code and unit tests to verify the operation. BUG=brillo:357 TEST=`FEATURES=test emerge-link buffet` Change-Id: I29c8d3d7c0894a9c837e73d0fdb16bafdfadfeca Reviewed-on: https://chromium-review.googlesource.com/253070 Trybot-Ready: Alex Vakulenko <avakulenko@chromium.org> Tested-by: Alex Vakulenko <avakulenko@chromium.org> Reviewed-by: Christopher Wiley <wiley@chromium.org> Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/object_schema.cc b/buffet/commands/object_schema.cc index 41e7639..6437262 100644 --- a/buffet/commands/object_schema.cc +++ b/buffet/commands/object_schema.cc
@@ -236,6 +236,18 @@ commands::attributes::kOneOf_Enum, &list)) return DetectArrayType(list, base_schema); + // If we have "default", try to use it for type detection. + if (dict->Get(commands::attributes::kDefault, &value)) { + if (value->IsType(base::Value::TYPE_DOUBLE)) + return PropType::GetTypeStringFromType(ValueType::Double); + if (value->IsType(base::Value::TYPE_INTEGER)) + return PropType::GetTypeStringFromType(ValueType::Int); + if (value->IsType(base::Value::TYPE_BOOLEAN)) + return PropType::GetTypeStringFromType(ValueType::Boolean); + if (value->IsType(base::Value::TYPE_STRING)) + return PropType::GetTypeStringFromType(ValueType::String); + } + return std::string(); }
diff --git a/buffet/commands/object_schema_unittest.cc b/buffet/commands/object_schema_unittest.cc index 505d6a6..0f429a1 100644 --- a/buffet/commands/object_schema_unittest.cc +++ b/buffet/commands/object_schema_unittest.cc
@@ -27,6 +27,7 @@ EXPECT_TRUE(prop.GetConstraints().empty()); EXPECT_FALSE(prop.HasOverriddenAttributes()); EXPECT_FALSE(prop.IsBasedOnSchema()); + EXPECT_EQ(nullptr, prop.GetDefaultValue()); } TEST(CommandSchema, IntPropType_Types) { @@ -62,6 +63,10 @@ nullptr); EXPECT_EQ("[1,2,3]", ValueToString(param2.ToJson(false, nullptr).get())); + param2.FromJson(CreateDictionaryValue("{'default':123}").get(), + &prop, nullptr); + EXPECT_EQ("{'default':123}", + ValueToString(param2.ToJson(false, nullptr).get())); } TEST(CommandSchema, IntPropType_FromJson) { @@ -86,12 +91,12 @@ EXPECT_TRUE(param2.IsBasedOnSchema()); EXPECT_EQ(-2, param2.GetMinValue()); EXPECT_EQ(17, param2.GetMaxValue()); - param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':6}").get(), - &prop, nullptr); + + ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':3}").get(), + &prop, nullptr)); EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(0, param2.GetMinValue()); - EXPECT_EQ(6, param2.GetMaxValue()); + ASSERT_NE(nullptr, param2.GetDefaultValue()); + EXPECT_EQ(3, param2.GetDefaultValue()->GetInt()->GetValue()); } TEST(CommandSchema, IntPropType_Validate) { @@ -147,6 +152,7 @@ EXPECT_TRUE(prop.GetConstraints().empty()); EXPECT_FALSE(prop.HasOverriddenAttributes()); EXPECT_FALSE(prop.IsBasedOnSchema()); + EXPECT_EQ(nullptr, prop.GetDefaultValue()); } TEST(CommandSchema, BoolPropType_Types) { @@ -171,6 +177,10 @@ EXPECT_EQ("[true,false]", ValueToString(param2.ToJson(false, nullptr).get())); EXPECT_EQ("{'enum':[true,false],'type':'boolean'}", ValueToString(param2.ToJson(true, nullptr).get())); + param2.FromJson(CreateDictionaryValue("{'default':true}").get(), + &prop, nullptr); + EXPECT_EQ("{'default':true}", + ValueToString(param2.ToJson(false, nullptr).get())); } TEST(CommandSchema, BoolPropType_FromJson) { @@ -182,6 +192,14 @@ EXPECT_FALSE(param2.HasOverriddenAttributes()); EXPECT_TRUE(param2.IsBasedOnSchema()); EXPECT_EQ(std::vector<bool>{true}, prop.GetOneOfValues()); + + buffet::BooleanPropType prop_base; + buffet::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) { @@ -226,6 +244,7 @@ EXPECT_DOUBLE_EQ((std::numeric_limits<double>::max)(), prop.GetMaxValue()); EXPECT_FALSE(prop.HasOverriddenAttributes()); EXPECT_FALSE(prop.IsBasedOnSchema()); + EXPECT_EQ(nullptr, prop.GetDefaultValue()); } TEST(CommandSchema, DoublePropType_Types) { @@ -257,6 +276,10 @@ &prop, nullptr); EXPECT_EQ("{'maximum':5.0,'minimum':0.0}", ValueToString(param2.ToJson(false, nullptr).get())); + param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(), + &prop, nullptr); + EXPECT_EQ("{'default':12.3}", + ValueToString(param2.ToJson(false, nullptr).get())); } TEST(CommandSchema, DoublePropType_FromJson) { @@ -287,6 +310,12 @@ 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) { @@ -337,6 +366,7 @@ EXPECT_EQ((std::numeric_limits<int>::max)(), prop.GetMaxLength()); EXPECT_FALSE(prop.HasOverriddenAttributes()); EXPECT_FALSE(prop.IsBasedOnSchema()); + EXPECT_EQ(nullptr, prop.GetDefaultValue()); } TEST(CommandSchema, StringPropType_Types) { @@ -368,6 +398,10 @@ &prop, nullptr); EXPECT_EQ("{'maxLength':5,'minLength':0}", ValueToString(param2.ToJson(false, nullptr).get())); + param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(), + &prop, nullptr); + EXPECT_EQ("{'default':'abcd'}", + ValueToString(param2.ToJson(false, nullptr).get())); } TEST(CommandSchema, StringPropType_FromJson) { @@ -398,6 +432,12 @@ 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) { @@ -452,6 +492,7 @@ buffet::ObjectPropType prop; EXPECT_TRUE(prop.HasOverriddenAttributes()); EXPECT_FALSE(prop.IsBasedOnSchema()); + EXPECT_EQ(nullptr, prop.GetDefaultValue()); } TEST(CommandSchema, ObjectPropType_Types) { @@ -488,6 +529,18 @@ "'password':{'maxLength':100,'minLength':6,'type':'string'}}," "'type':'object'}", ValueToString(prop2.ToJson(true, nullptr).get())); + + buffet::ObjectPropType prop3; + ASSERT_TRUE(prop3.FromJson(CreateDictionaryValue( + "{'default':{'expires':3,'password':'abracadabra'}}").get(), &prop2, + nullptr)); + EXPECT_EQ("{'default':{'expires':3,'password':'abracadabra'}}", + ValueToString(prop3.ToJson(false, nullptr).get())); + EXPECT_EQ("{'default':{'expires':3,'password':'abracadabra'}," + "'properties':{'expires':{'type':'integer'}," + "'password':{'maxLength':100,'minLength':6,'type':'string'}}," + "'type':'object'}", + ValueToString(prop3.ToJson(true, nullptr).get())); } TEST(CommandSchema, ObjectPropType_FromJson) { @@ -500,6 +553,17 @@ EXPECT_EQ(buffet::ValueType::String, prop->GetType()); prop = schema->GetProp("age"); EXPECT_EQ(buffet::ValueType::Int, prop->GetType()); + + buffet::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 buffet::ObjectValue* defval = prop2.GetDefaultValue()->GetObject(); + ASSERT_NE(nullptr, defval); + buffet::native_types::Object objval = defval->GetValue(); + EXPECT_EQ("Bob", objval["name"]->GetString()->GetValue()); + EXPECT_EQ(33, objval["age"]->GetInt()->GetValue()); } TEST(CommandSchema, ObjectPropType_Validate) { @@ -588,10 +652,10 @@ TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) { buffet::ObjectSchema schema; const char* schema_str = "{" - "'param1':'integer'," - "'param2':'number'," - "'param3':'string'" - "}"; + "'param1':'integer'," + "'param2':'number'," + "'param3':'string'" + "}"; EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); EXPECT_EQ(buffet::ValueType::Int, schema.GetProp("param1")->GetType()); @@ -617,10 +681,10 @@ TEST(CommandSchema, ObjectSchema_FromJson_Full_TypeName) { buffet::ObjectSchema schema; const char* schema_str = "{" - "'param1':{'type':'integer'}," - "'param2':{'type':'number'}," - "'param3':{'type':'string'}" - "}"; + "'param1':{'type':'integer'}," + "'param2':{'type':'number'}," + "'param3':{'type':'string'}" + "}"; EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); EXPECT_EQ(buffet::ValueType::Int, schema.GetProp("param1")->GetType()); @@ -646,18 +710,23 @@ TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Scalar) { buffet::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}" - "}"; + "'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'}" + "}"; EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); @@ -671,6 +740,11 @@ 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()); int min_int = (std::numeric_limits<int>::min)(); int max_int = (std::numeric_limits<int>::max)(); @@ -700,22 +774,32 @@ 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 buffet::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()); } TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Array) { buffet::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':[]}" - "}"; + "'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':[]}" + "}"; EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); @@ -761,51 +845,57 @@ 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}" - "}"; + "'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'" + "}"; buffet::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':{}" - "}"; + "'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}" + "}"; buffet::ObjectSchema schema; EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), &base_schema, nullptr)); @@ -868,64 +958,140 @@ 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) { + buffet::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'}}" + "}}"; + ASSERT_TRUE(prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, + nullptr)); + + // Omit all. + auto value = prop.CreateValue(); + ASSERT_TRUE(value->FromJson(CreateDictionaryValue("{}").get(), nullptr)); + buffet::native_types::Object 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()); + buffet::native_types::Object param5 = obj["param5"]->GetObject()->GetValue(); + EXPECT_EQ(5, param5["x"]->GetInt()->GetValue()); + EXPECT_EQ(6, param5["y"]->GetInt()->GetValue()); + + // Specify some. + value = prop.CreateValue(); + const char* val_json = "{" + "'param1':false," + "'param3':33.3," + "'param5':{'x':-5,'y':-6}" + "}"; + ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr)); + 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()); + + // Specify all. + value = prop.CreateValue(); + val_json = "{" + "'param1':false," + "'param2':22," + "'param3':333.3," + "'param4':'FOUR'," + "'param5':{'x':-55,'y':66}" + "}"; + ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr)); + 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()); } TEST(CommandSchema, ObjectSchema_FromJson_BaseSchema_Failures) { buffet::ObjectSchema schema; chromeos::ErrorPtr error; const char* schema_str = "{" - "'param1':{}" - "}"; + "'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'}" - "}"; + "'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':[]" - "}"; + "'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'}" - "}"; + "'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]" - "}"; + "'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. - "}"; + "'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. - "}"; + "'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(); }
diff --git a/buffet/commands/prop_types.cc b/buffet/commands/prop_types.cc index 5a8b10f..37a6dc8 100644 --- a/buffet/commands/prop_types.cc +++ b/buffet/commands/prop_types.cc
@@ -32,6 +32,9 @@ } bool PropType::HasOverriddenAttributes() const { + if (default_.value && !default_.is_inherited) + return true; + for (const auto& pair : constraints_) { if (pair.second->HasOverriddenAttributes()) return true; @@ -79,6 +82,14 @@ if (!pair.second->AddToJsonDict(dict.get(), !full_schema, error)) return std::unique_ptr<base::Value>(); } + + if (default_.value && (full_schema || !default_.is_inherited)) { + auto defval = default_.value->ToJson(error); + if (!defval) + return std::unique_ptr<base::Value>(); + dict->Set(commands::attributes::kDefault, defval.release()); + } + return std::unique_ptr<base::Value>(dict.release()); } @@ -95,7 +106,14 @@ } based_on_schema_ = (base_schema != nullptr); constraints_.clear(); - std::set<std::string> processed_keys{commands::attributes::kType}; + // Add the well-known object properties first (like "type", "displayName", + // "default") to the list of "processed" keys so we do not complain about them + // when we check for unknown/unexpected keys below. + std::set<std::string> processed_keys{ + commands::attributes::kType, + commands::attributes::kDisplayName, + commands::attributes::kDefault, + }; if (!ObjectSchemaFromJson(value, base_schema, &processed_keys, error)) return false; if (base_schema) { @@ -121,6 +139,31 @@ iter.Advance(); } + // Read the default value, if specified. + // We need to do this last since the current type definition must be complete, + // so we can parse and validate the value of the default. + const base::Value* defval = nullptr; // Owned by value + if (value->GetWithoutPathExpansion(commands::attributes::kDefault, &defval)) { + std::shared_ptr<PropValue> prop_value = CreateValue(); + if (!prop_value->FromJson(defval, error) || + !ValidateValue(prop_value->GetValueAsAny(), error)) { + chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, + errors::commands::kInvalidPropValue, + "Invalid value for property '%s'", + commands::attributes::kDefault); + return false; + } + default_.value = prop_value; + default_.is_inherited = false; + } else if (base_schema) { + // If we have the base schema, inherit the type's default value from it. + // It doesn't matter if the base schema actually has a default value + // specified or not. If it doesn't, then the current type definition will + // have no default value set either (|default_.value| is a shared_ptr to + // PropValue, which can be set to nullptr). + default_.value = base_schema->default_.value; + default_.is_inherited = true; + } return true; }
diff --git a/buffet/commands/schema_constants.cc b/buffet/commands/schema_constants.cc index a26c355..22a4766 100644 --- a/buffet/commands/schema_constants.cc +++ b/buffet/commands/schema_constants.cc
@@ -30,6 +30,7 @@ namespace attributes { const char kType[] = "type"; const char kDisplayName[] = "displayName"; +const char kDefault[] = "default"; const char kNumeric_Min[] = "minimum"; const char kNumeric_Max[] = "maximum";
diff --git a/buffet/commands/schema_constants.h b/buffet/commands/schema_constants.h index 6b06e2f..1cfbc75 100644 --- a/buffet/commands/schema_constants.h +++ b/buffet/commands/schema_constants.h
@@ -34,6 +34,7 @@ // Command description JSON schema attributes. extern const char kType[]; extern const char kDisplayName[]; +extern const char kDefault[]; extern const char kNumeric_Min[]; extern const char kNumeric_Max[];
diff --git a/buffet/commands/unittest_utils.cc b/buffet/commands/unittest_utils.cc index 8d4c369..db8aa4a 100644 --- a/buffet/commands/unittest_utils.cc +++ b/buffet/commands/unittest_utils.cc
@@ -21,7 +21,8 @@ std::string json2(json); std::replace(json2.begin(), json2.end(), '\'', '"'); base::Value* value = base::JSONReader::Read(json2); - base::DictionaryValue* dict; + CHECK(value) << "Failed to load JSON: " << json2; + base::DictionaryValue* dict = nullptr; value->GetAsDictionary(&dict); return std::unique_ptr<base::DictionaryValue>(dict); }