summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/json_schema_compiler/cc_generator.py82
-rw-r--r--tools/json_schema_compiler/h_generator.py5
-rw-r--r--tools/json_schema_compiler/test/error_generation.json34
-rw-r--r--tools/json_schema_compiler/test/error_generation_unittest.cc106
4 files changed, 201 insertions, 26 deletions
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index 04d951b..2c47b5e 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -49,6 +49,8 @@ class _Generator(object):
(self._namespace.source_file_dir, self._namespace.short_filename))
.Cblock(self._type_helper.GenerateIncludes(include_soft=True))
.Append()
+ .Append('using base::UTF8ToUTF16;')
+ .Append()
.Concat(cpp_util.OpenNamespace(self._cpp_namespace))
.Cblock(self._type_helper.GetNamespaceStart())
)
@@ -529,7 +531,8 @@ class _Generator(object):
.Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams(
['const base::ListValue& args']))
.Concat(self._GenerateParamsCheck(function, 'args'))
- .Append('scoped_ptr<Params> params(new Params());'))
+ .Append('scoped_ptr<Params> params(new Params());')
+ )
for param in function.params:
c.Concat(self._InitializePropertyToDefault(param, 'params'))
@@ -603,10 +606,13 @@ class _Generator(object):
'"\'%%(key)s\': expected ' + '%s, got " + %s' % (
type_.name,
self._util_cc_helper.GetValueTypeString(
- '%%(src_var)s', True))))
- .Append('return %(failure_value)s;')
- .Eblock('}')
- .Append('%(dst_var)s.reset(new %(cpp_type)s(temp));')
+ '%%(src_var)s', True)))))
+ c.Append('%(dst_var)s.reset();')
+ if not self._generate_error_messages:
+ c.Append('return %(failure_value)s;')
+ (c.Eblock('}')
+ .Append('else')
+ .Append(' %(dst_var)s.reset(new %(cpp_type)s(temp));')
)
else:
(c.Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue(
@@ -627,15 +633,23 @@ class _Generator(object):
.Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {')
.Concat(self._GenerateError(
'"\'%%(key)s\': expected dictionary, got " + ' +
- self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
- .Append('return %(failure_value)s;')
- .Eblock('}')
+ self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))))
+ # If an optional property fails to populate, the population can still
+ # succeed with a warning. If no error messages are generated, this
+ # warning is not set and we fail out instead.
+ if not self._generate_error_messages:
+ c.Append('return %(failure_value)s;')
+ (c.Eblock('}')
+ .Sblock('else {')
.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());')
.Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs(
('*dictionary', 'temp.get()')))
.Append(' return %(failure_value)s;')
- .Append('}')
- .Append('%(dst_var)s = temp.Pass();')
+ )
+ (c.Append('}')
+ .Append('else')
+ .Append(' %(dst_var)s = temp.Pass();')
+ .Eblock('}')
)
else:
(c.Append('const base::DictionaryValue* dictionary = NULL;')
@@ -662,8 +676,13 @@ class _Generator(object):
.Concat(self._GenerateError(
'"\'%%(key)s\': expected list, got " + ' +
self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
- .Append('return %(failure_value)s;')
- .Eblock('}'))
+ )
+ if is_ptr and self._generate_error_messages:
+ c.Append('%(dst_var)s.reset();')
+ else:
+ c.Append('return %(failure_value)s;')
+ c.Eblock('}')
+ c.Sblock('else {')
item_type = self._type_helper.FollowRef(underlying_type.item_type)
if item_type.property_type == PropertyType.ENUM:
c.Concat(self._GenerateListValueToEnumArrayConversion(
@@ -677,12 +696,15 @@ class _Generator(object):
underlying_type,
'list',
dst_var,
- is_ptr))
- .Concat(self._GenerateError(
+ is_ptr)))
+ c.Concat(self._GenerateError(
'"unable to populate array \'%%(parent_key)s\'"'))
- .Append('return %(failure_value)s;')
- .Eblock('}')
- )
+ if is_ptr and self._generate_error_messages:
+ c.Append('%(dst_var)s.reset();')
+ else:
+ c.Append('return %(failure_value)s;')
+ c.Eblock('}')
+ c.Eblock('}')
elif underlying_type.property_type == PropertyType.CHOICES:
if is_ptr:
(c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());')
@@ -701,14 +723,18 @@ class _Generator(object):
dst_var,
failure_value))
elif underlying_type.property_type == PropertyType.BINARY:
- (c.Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {')
+ (c.Append('const base::BinaryValue* binary_value = NULL;')
+ .Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {')
.Concat(self._GenerateError(
'"\'%%(key)s\': expected binary, got " + ' +
self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
- .Append('return %(failure_value)s;')
- .Eblock('}')
- .Append('const base::BinaryValue* binary_value =')
- .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);')
+ )
+ if not self._generate_error_messages:
+ c.Append('return %(failure_value)s;')
+ (c.Eblock('}')
+ .Sblock('else {')
+ .Append(' binary_value =')
+ .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);')
)
if is_ptr:
(c.Append('%(dst_var)s.reset(')
@@ -719,6 +745,7 @@ class _Generator(object):
(c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),')
.Append(' binary_value->GetSize());')
)
+ c.Eblock('}')
else:
raise NotImplementedError(type_)
if c.IsEmpty():
@@ -729,7 +756,7 @@ class _Generator(object):
'dst_var': dst_var,
'failure_value': failure_value,
'key': type_.name,
- 'parent_key': type_.parent.name
+ 'parent_key': type_.parent.name,
})).Eblock('}')
def _GenerateListValueToEnumArrayConversion(self,
@@ -927,8 +954,13 @@ class _Generator(object):
c = Code()
if not self._generate_error_messages:
return c
- (c.Append('if (error)')
- .Append(' *error = base::UTF8ToUTF16(' + body + ');'))
+ (c.Append('if (error) {')
+ .Append(' if (error->length())')
+ .Append(' error->append(UTF8ToUTF16("; "));')
+ .Append(' error->append(UTF8ToUTF16(%s));' % body)
+ .Append('}')
+ .Append('else')
+ .Append(' *error = UTF8ToUTF16(%s);' % body))
return c
def _GenerateParams(self, params):
diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py
index bdbd8a2..237a028 100644
--- a/tools/json_schema_compiler/h_generator.py
+++ b/tools/json_schema_compiler/h_generator.py
@@ -392,6 +392,11 @@ class _Generator(object):
def _GenerateParams(self, params):
"""Builds the parameter list for a function, given an array of parameters.
"""
+ # |error| is populated with warnings and/or errors found during parsing.
+ # |error| being set does not necessarily imply failure and may be
+ # recoverable.
+ # For example, optional properties may have failed to parse, but the
+ # parser was able to continue.
if self._generate_error_messages:
params += ('base::string16* error = NULL',)
return ', '.join(str(p) for p in params)
diff --git a/tools/json_schema_compiler/test/error_generation.json b/tools/json_schema_compiler/test/error_generation.json
index b0478a9..f90ef6e 100644
--- a/tools/json_schema_compiler/test/error_generation.json
+++ b/tools/json_schema_compiler/test/error_generation.json
@@ -17,6 +17,17 @@
}
},
{
+ "id": "OptionalTestType",
+ "type": "object",
+ "properties": {
+ "string": {
+ "type": "string",
+ "description": "Some string.",
+ "optional": true
+ }
+ }
+ },
+ {
"id": "ChoiceType",
"type": "object",
"properties": {
@@ -29,6 +40,19 @@
}
},
{
+ "id": "OptionalChoiceType",
+ "type": "object",
+ "properties": {
+ "integers": {
+ "choices": [
+ {"type": "array", "items": {"type": "integer", "minimum": 0}},
+ {"type": "integer"}
+ ],
+ "optional": true
+ }
+ }
+ },
+ {
"id": "ObjectType",
"type": "object",
"properties": {
@@ -62,6 +86,16 @@
}
},
{
+ "id": "OptionalBinaryData",
+ "type": "object",
+ "properties": {
+ "data": {
+ "type" : "binary",
+ "optional": true
+ }
+ }
+ },
+ {
"id": "ArrayObject",
"type": "object",
"properties": {
diff --git a/tools/json_schema_compiler/test/error_generation_unittest.cc b/tools/json_schema_compiler/test/error_generation_unittest.cc
index 9515f07..5e227fd 100644
--- a/tools/json_schema_compiler/test/error_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/error_generation_unittest.cc
@@ -147,8 +147,12 @@ TEST(JsonSchemaCompilerErrorTest, WrongTypeValueType) {
{
scoped_ptr<base::DictionaryValue> value = Dictionary(
"otherType", new FundamentalValue(1.1));
+ ObjectType out;
+ base::string16 error;
+ EXPECT_TRUE(ObjectType::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'otherType': expected dictionary, got number",
- GetPopulateError<ObjectType>(*value)));
+ error));
+ EXPECT_EQ(NULL, out.other_type.get());
}
}
@@ -212,3 +216,103 @@ TEST(JsonSchemaCompilerErrorTest, BadEnumValue) {
GetPopulateError<HasEnumeration>(*value)));
}
}
+
+// Warn but don't fail out errors
+
+TEST(JsonSchemaCompilerErrorTest, WarnOnOptionalFailure) {
+ {
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "string", new base::StringValue("bling"));
+ EXPECT_TRUE(EqualsUtf16("", GetPopulateError<OptionalTestType>(*value)));
+ }
+ {
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "string", new base::FundamentalValue(1));
+
+ OptionalTestType out;
+ base::string16 error;
+ EXPECT_TRUE(OptionalTestType::Populate(*value, &out, &error));
+ EXPECT_TRUE(EqualsUtf16("'string': expected string, got integer",
+ error));
+ EXPECT_EQ(NULL, out.string.get());
+ }
+}
+
+TEST(JsonSchemaCompilerErrorTest, OptionalBinaryTypeFailure) {
+ {
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "data", new base::BinaryValue());
+ EXPECT_TRUE(EqualsUtf16("", GetPopulateError<OptionalBinaryData>(*value)));
+ }
+ {
+ // There's a bug with silent failures if the key doesn't exist.
+ scoped_ptr<base::DictionaryValue> value = Dictionary("data",
+ new base::FundamentalValue(1));
+
+ OptionalBinaryData out;
+ base::string16 error;
+ EXPECT_TRUE(OptionalBinaryData::Populate(*value, &out, &error));
+ EXPECT_TRUE(EqualsUtf16("'data': expected binary, got integer",
+ error));
+ EXPECT_EQ(NULL, out.data.get());
+ }
+}
+
+TEST(JsonSchemaCompilerErrorTest, OptionalArrayTypeFailure) {
+ {
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "TheArray", new base::ListValue());
+ EXPECT_TRUE(EqualsUtf16("", GetPopulateError<ArrayObject>(*value)));
+ }
+ {
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "TheArray", new FundamentalValue(5));
+ ArrayObject out;
+ base::string16 error;
+ EXPECT_TRUE(ArrayObject::Populate(*value, &out, &error));
+ EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer",
+ error));
+ EXPECT_EQ(NULL, out.the_array.get());
+ }
+}
+
+TEST(JsonSchemaCompilerErrorTest, OptionalUnableToPopulateArray) {
+ {
+ scoped_ptr<base::ListValue> params_value = List(
+ new FundamentalValue(5));
+ EXPECT_TRUE(EqualsUtf16("",
+ GetPopulateError<OptionalChoiceType::Integers>(*params_value)));
+ }
+ {
+ scoped_ptr<base::ListValue> params_value = List(
+ new FundamentalValue(5),
+ new FundamentalValue(false));
+ OptionalChoiceType::Integers out;
+ base::string16 error;
+ EXPECT_TRUE(OptionalChoiceType::Integers::Populate(*params_value, &out,
+ &error));
+ EXPECT_TRUE(EqualsUtf16("unable to populate array 'integers'",
+ error));
+ EXPECT_EQ(NULL, out.as_integer.get());
+ }
+}
+
+TEST(JsonSchemaCompilerErrorTest, MultiplePopulationErrors) {
+ {
+
+ scoped_ptr<base::DictionaryValue> value = Dictionary(
+ "TheArray", new FundamentalValue(5));
+ ArrayObject out;
+ base::string16 error;
+ EXPECT_TRUE(ArrayObject::Populate(*value, &out, &error));
+ EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer",
+ error));
+ EXPECT_EQ(NULL, out.the_array.get());
+
+ EXPECT_TRUE(ArrayObject::Populate(*value, &out, &error));
+ EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer; "
+ "'TheArray': expected list, got integer",
+ error));
+ EXPECT_EQ(NULL, out.the_array.get());
+ }
+}