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);
}