diff options
-rw-r--r-- | tools/json_schema_compiler/cc_generator.py | 82 | ||||
-rw-r--r-- | tools/json_schema_compiler/h_generator.py | 5 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/error_generation.json | 34 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/error_generation_unittest.cc | 106 |
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()); + } +} |