You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by ko...@apache.org on 2019/11/28 17:35:38 UTC
[avro] branch master updated: AVRO-748: Protocol Error Schema
Implicit String (#717)
This is an automated email from the ASF dual-hosted git repository.
kojiromike pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/master by this push:
new d28bf07 AVRO-748: Protocol Error Schema Implicit String (#717)
d28bf07 is described below
commit d28bf07d2da8dbddb561630ca69c0cee2d3f5b52
Author: Michael A. Smith <mi...@smith-li.com>
AuthorDate: Thu Nov 28 12:35:28 2019 -0500
AVRO-748: Protocol Error Schema Implicit String (#717)
Account for the implicit string schema as one of the possible protocol error schema.
Similar to the changes in AVRO-2580, the protocol tests could hide errors when the overloaded exception handling catches the assertion error from the test itself. This corrects that.
---
lang/py/src/avro/protocol.py | 3 +-
lang/py/test/test_protocol.py | 653 ++++++++++++++++++------------------------
lang/py/test/test_schema.py | 4 +-
3 files changed, 284 insertions(+), 376 deletions(-)
diff --git a/lang/py/src/avro/protocol.py b/lang/py/src/avro/protocol.py
index 117401c..fef2633 100644
--- a/lang/py/src/avro/protocol.py
+++ b/lang/py/src/avro/protocol.py
@@ -169,8 +169,7 @@ class Message(object):
self._props = {}
self.set_prop('request', self._parse_request(request, names))
self.set_prop('response', self._parse_response(response, names))
- if errors is not None:
- self.set_prop('errors', self._parse_errors(errors, names))
+ self.set_prop('errors', self._parse_errors(errors or [], names))
# read-only properties
name = property(lambda self: self._name)
diff --git a/lang/py/test/test_protocol.py b/lang/py/test/test_protocol.py
index 4d8e783..28e03b6 100644
--- a/lang/py/test/test_protocol.py
+++ b/lang/py/test/test_protocol.py
@@ -21,42 +21,49 @@
from __future__ import absolute_import, division, print_function
+import json
import unittest
-from avro import protocol
+import avro.protocol
+import avro.schema
-class ExampleProtocol(object):
- def __init__(self, protocol_string, valid, name='', comment=''):
- self._protocol_string = protocol_string
- self._valid = valid
- self._name = name or protocol_string # default to schema_string for name
- self._comment = comment
+class TestProtocol(object):
+ """A proxy for a protocol string that provides useful test metadata."""
- # read-only properties
- protocol_string = property(lambda self: self._protocol_string)
- valid = property(lambda self: self._valid)
- name = property(lambda self: self._name)
+ def __init__(self, data, name='', comment=''):
+ if not isinstance(data, basestring):
+ data = json.dumps(data)
+ self.data = data
+ self.name = name or data
+ self.comment = comment
- # read/write properties
- def set_comment(self, new_comment): self._comment = new_comment
- comment = property(lambda self: self._comment, set_comment)
+ def parse(self):
+ return avro.protocol.parse(str(self))
-#
-# Example Protocols
-#
-HELLO_WORLD = ExampleProtocol("""\
-{
+ def __str__(self):
+ return str(self.data)
+
+
+class ValidTestProtocol(TestProtocol):
+ """A proxy for a valid protocol string that provides useful test metadata."""
+ valid = True
+
+
+class InvalidTestProtocol(TestProtocol):
+ """A proxy for an invalid protocol string that provides useful test metadata."""
+ valid = False
+
+
+HELLO_WORLD = ValidTestProtocol({
"namespace": "com.acme",
"protocol": "HelloWorld",
-
"types": [
{"name": "Greeting", "type": "record", "fields": [
{"name": "message", "type": "string"}]},
{"name": "Curse", "type": "error", "fields": [
{"name": "message", "type": "string"}]}
],
-
"messages": {
"hello": {
"request": [{"name": "greeting", "type": "Greeting" }],
@@ -64,382 +71,284 @@ HELLO_WORLD = ExampleProtocol("""\
"errors": ["Curse"]
}
}
-}
- """, True)
-EXAMPLES = [
- HELLO_WORLD,
- ExampleProtocol("""\
-{"namespace": "org.apache.avro.test",
- "protocol": "Simple",
-
- "types": [
- {"name": "Kind", "type": "enum", "symbols": ["FOO","BAR","BAZ"]},
-
- {"name": "MD5", "type": "fixed", "size": 16},
-
- {"name": "TestRecord", "type": "record",
- "fields": [
- {"name": "name", "type": "string", "order": "ignore"},
- {"name": "kind", "type": "Kind", "order": "descending"},
- {"name": "hash", "type": "MD5"}
- ]
- },
-
- {"name": "TestError", "type": "error", "fields": [
- {"name": "message", "type": "string"}
- ]
- }
-
- ],
-
- "messages": {
-
- "hello": {
- "request": [{"name": "greeting", "type": "string"}],
- "response": "string"
- },
-
- "echo": {
- "request": [{"name": "record", "type": "TestRecord"}],
- "response": "TestRecord"
- },
-
- "add": {
- "request": [{"name": "arg1", "type": "int"}, {"name": "arg2", "type": "int"}],
- "response": "int"
- },
-
- "echoBytes": {
- "request": [{"name": "data", "type": "bytes"}],
- "response": "bytes"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["TestError"]
- }
- }
-
-}
- """, True),
- ExampleProtocol("""\
-{"namespace": "org.apache.avro.test.namespace",
- "protocol": "TestNamespace",
-
- "types": [
- {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
- {"name": "TestRecord", "type": "record",
- "fields": [ {"name": "hash", "type": "org.apache.avro.test.util.MD5"} ]
- },
- {"name": "TestError", "namespace": "org.apache.avro.test.errors",
- "type": "error", "fields": [ {"name": "message", "type": "string"} ]
- }
- ],
-
- "messages": {
- "echo": {
- "request": [{"name": "record", "type": "TestRecord"}],
- "response": "TestRecord"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["org.apache.avro.test.errors.TestError"]
- }
-
- }
-
-}
- """, True),
-ExampleProtocol("""\
-{"namespace": "org.apache.avro.test.namespace",
- "protocol": "TestImplicitNamespace",
-
- "types": [
- {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
- {"name": "ReferencedRecord", "type": "record",
- "fields": [ {"name": "foo", "type": "string"} ] },
- {"name": "TestRecord", "type": "record",
- "fields": [ {"name": "hash", "type": "org.apache.avro.test.util.MD5"},
- {"name": "unqalified", "type": "ReferencedRecord"} ]
- },
- {"name": "TestError",
- "type": "error", "fields": [ {"name": "message", "type": "string"} ]
- }
- ],
-
- "messages": {
- "echo": {
- "request": [{"name": "qualified",
- "type": "org.apache.avro.test.namespace.TestRecord"}],
- "response": "TestRecord"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["org.apache.avro.test.namespace.TestError"]
- }
-
- }
-
-}
- """, True),
-ExampleProtocol("""\
-{"namespace": "org.apache.avro.test.namespace",
- "protocol": "TestNamespaceTwo",
-
- "types": [
- {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
- {"name": "ReferencedRecord", "type": "record",
+})
+EXAMPLES = [HELLO_WORLD, ValidTestProtocol({
+ "namespace": "org.apache.avro.test",
+ "protocol": "Simple",
+ "types": [
+ {"name": "Kind", "type": "enum", "symbols": ["FOO","BAR","BAZ"]},
+ {"name": "MD5", "type": "fixed", "size": 16},
+ {"name": "TestRecord", "type": "record", "fields": [
+ {"name": "name", "type": "string", "order": "ignore"},
+ {"name": "kind", "type": "Kind", "order": "descending"},
+ {"name": "hash", "type": "MD5"}
+ ]},
+ {"name": "TestError", "type": "error", "fields": [{"name": "message", "type": "string"}]}
+ ],
+ "messages": {
+ "hello": {
+ "request": [{"name": "greeting", "type": "string"}],
+ "response": "string"
+ }, "echo": {
+ "request": [{"name": "record", "type": "TestRecord"}],
+ "response": "TestRecord"
+ }, "add": {
+ "request": [{"name": "arg1", "type": "int"}, {"name": "arg2", "type": "int"}],
+ "response": "int"
+ }, "echoBytes": {
+ "request": [{"name": "data", "type": "bytes"}],
+ "response": "bytes"
+ }, "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["TestError"]
+ }
+ }
+ }), ValidTestProtocol({
+ "namespace": "org.apache.avro.test.namespace",
+ "protocol": "TestNamespace",
+ "types": [
+ {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
+ {"name": "TestRecord", "type": "record", "fields": [
+ {"name": "hash", "type": "org.apache.avro.test.util.MD5"}
+ ]},
+ {"name": "TestError", "namespace": "org.apache.avro.test.errors", "type": "error",
+ "fields": [ {"name": "message", "type": "string"}]}
+ ],
+ "messages": {
+ "echo": {
+ "request": [{"name": "record", "type": "TestRecord"}],
+ "response": "TestRecord"
+ }, "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["org.apache.avro.test.errors.TestError"]
+ }
+ }
+ }), ValidTestProtocol({
+ "namespace": "org.apache.avro.test.namespace",
+ "protocol": "TestImplicitNamespace",
+ "types": [
+ {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
+ {"name": "ReferencedRecord", "type": "record",
+ "fields": [ {"name": "foo", "type": "string"}]},
+ {"name": "TestRecord", "type": "record",
+ "fields": [{"name": "hash", "type": "org.apache.avro.test.util.MD5"},
+ {"name": "unqualified", "type": "ReferencedRecord"}]
+ },
+ {"name": "TestError", "type": "error", "fields": [{"name": "message", "type": "string"}]}
+ ],
+ "messages": {
+ "echo": {
+ "request": [{"name": "qualified", "type": "org.apache.avro.test.namespace.TestRecord"}],
+ "response": "TestRecord"
+ }, "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["org.apache.avro.test.namespace.TestError"]
+ }
+ }
+ }), ValidTestProtocol({
+ "namespace": "org.apache.avro.test.namespace",
+ "protocol": "TestNamespaceTwo",
+ "types": [
+ {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
+ {"name": "ReferencedRecord", "type": "record",
"namespace": "org.apache.avro.other.namespace",
- "fields": [ {"name": "foo", "type": "string"} ] },
- {"name": "TestRecord", "type": "record",
- "fields": [ {"name": "hash", "type": "org.apache.avro.test.util.MD5"},
+ "fields": [{"name": "foo", "type": "string"}]},
+ {"name": "TestRecord", "type": "record",
+ "fields": [{"name": "hash", "type": "org.apache.avro.test.util.MD5"},
{"name": "qualified",
- "type": "org.apache.avro.other.namespace.ReferencedRecord"}
- ]
- },
- {"name": "TestError",
- "type": "error", "fields": [ {"name": "message", "type": "string"} ]
- }
- ],
-
- "messages": {
- "echo": {
- "request": [{"name": "qualified",
- "type": "org.apache.avro.test.namespace.TestRecord"}],
- "response": "TestRecord"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["org.apache.avro.test.namespace.TestError"]
- }
-
- }
-
-}
- """, True),
-ExampleProtocol("""\
-{"namespace": "org.apache.avro.test.namespace",
- "protocol": "TestValidRepeatedName",
-
- "types": [
- {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
- {"name": "ReferencedRecord", "type": "record",
- "namespace": "org.apache.avro.other.namespace",
- "fields": [ {"name": "foo", "type": "string"} ] },
- {"name": "ReferencedRecord", "type": "record",
- "fields": [ {"name": "bar", "type": "double"} ] },
- {"name": "TestError",
- "type": "error", "fields": [ {"name": "message", "type": "string"} ]
- }
- ],
-
- "messages": {
- "echo": {
- "request": [{"name": "qualified",
- "type": "ReferencedRecord"}],
- "response": "org.apache.avro.other.namespace.ReferencedRecord"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["org.apache.avro.test.namespace.TestError"]
- }
-
- }
-
-}
- """, True),
-ExampleProtocol("""\
-{"namespace": "org.apache.avro.test.namespace",
- "protocol": "TestInvalidRepeatedName",
-
- "types": [
- {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
- {"name": "ReferencedRecord", "type": "record",
- "fields": [ {"name": "foo", "type": "string"} ] },
- {"name": "ReferencedRecord", "type": "record",
- "fields": [ {"name": "bar", "type": "double"} ] },
- {"name": "TestError",
- "type": "error", "fields": [ {"name": "message", "type": "string"} ]
- }
- ],
-
- "messages": {
- "echo": {
- "request": [{"name": "qualified",
- "type": "ReferencedRecord"}],
- "response": "org.apache.avro.other.namespace.ReferencedRecord"
- },
-
- "error": {
- "request": [],
- "response": "null",
- "errors": ["org.apache.avro.test.namespace.TestError"]
- }
-
- }
-
-}
- """, False),
- ExampleProtocol("""\
-{"namespace": "org.apache.avro.test",
- "protocol": "BulkData",
-
- "types": [],
-
- "messages": {
-
- "read": {
- "request": [],
- "response": "bytes"
- },
-
- "write": {
- "request": [ {"name": "data", "type": "bytes"} ],
- "response": "null"
- }
-
- }
-
-}
- """, True),
- ExampleProtocol("""\
-{
- "protocol" : "API",
- "namespace" : "xyz.api",
- "types" : [ {
- "type" : "enum",
- "name" : "Symbology",
- "namespace" : "xyz.api.product",
- "symbols" : [ "OPRA", "CUSIP", "ISIN", "SEDOL" ]
- }, {
- "type" : "record",
- "name" : "Symbol",
- "namespace" : "xyz.api.product",
- "fields" : [ {
- "name" : "symbology",
- "type" : "xyz.api.product.Symbology"
- }, {
- "name" : "symbol",
- "type" : "string"
- } ]
- }, {
- "type" : "record",
- "name" : "MultiSymbol",
- "namespace" : "xyz.api.product",
- "fields" : [ {
- "name" : "symbols",
- "type" : {
- "type" : "map",
- "values" : "xyz.api.product.Symbol"
+ "type": "org.apache.avro.other.namespace.ReferencedRecord"}]
+ },
+ {"name": "TestError",
+ "type": "error", "fields": [{"name": "message", "type": "string"}]}],
+ "messages": {
+ "echo": {
+ "request": [{"name": "qualified", "type": "org.apache.avro.test.namespace.TestRecord"}],
+ "response": "TestRecord"
+ }, "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["org.apache.avro.test.namespace.TestError"]
}
- } ]
- } ],
- "messages" : {
- }
-}
- """, True),
+ }
+ }), ValidTestProtocol({
+ "namespace": "org.apache.avro.test.namespace",
+ "protocol": "TestValidRepeatedName",
+ "types": [
+ {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
+ {"name": "ReferencedRecord", "type": "record",
+ "namespace": "org.apache.avro.other.namespace",
+ "fields": [{"name": "foo", "type": "string"}]},
+ {"name": "ReferencedRecord", "type": "record",
+ "fields": [{"name": "bar", "type": "double"}]},
+ {"name": "TestError",
+ "type": "error", "fields": [{"name": "message", "type": "string"}]}],
+ "messages": {
+ "echo": {
+ "request": [{"name": "qualified", "type": "ReferencedRecord"}],
+ "response": "org.apache.avro.other.namespace.ReferencedRecord"},
+ "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["org.apache.avro.test.namespace.TestError"]}
+ }
+ }), InvalidTestProtocol({
+ "namespace": "org.apache.avro.test.namespace",
+ "protocol": "TestInvalidRepeatedName",
+ "types": [
+ {"name": "org.apache.avro.test.util.MD5", "type": "fixed", "size": 16},
+ {"name": "ReferencedRecord", "type": "record",
+ "fields": [ {"name": "foo", "type": "string"}]},
+ {"name": "ReferencedRecord", "type": "record",
+ "fields": [ {"name": "bar", "type": "double"}]},
+ {"name": "TestError",
+ "type": "error", "fields": [{"name": "message", "type": "string"}]}],
+ "messages": {
+ "echo": {
+ "request": [{"name": "qualified", "type": "ReferencedRecord"}],
+ "response": "org.apache.avro.other.namespace.ReferencedRecord"
+ }, "error": {
+ "request": [],
+ "response": "null",
+ "errors": ["org.apache.avro.test.namespace.TestError"]
+ }
+ }
+ }),
+ ValidTestProtocol({
+ "namespace": "org.apache.avro.test",
+ "protocol": "BulkData",
+ "types": [],
+ "messages": {
+ "read": {
+ "request": [],
+ "response": "bytes"
+ }, "write": {
+ "request": [ {"name": "data", "type": "bytes"} ],
+ "response": "null"
+ }
+ }
+ }), ValidTestProtocol({
+ "protocol": "API",
+ "namespace": "xyz.api",
+ "types": [{
+ "type": "enum",
+ "name": "Symbology",
+ "namespace": "xyz.api.product",
+ "symbols": ["OPRA", "CUSIP", "ISIN", "SEDOL"]
+ }, {
+ "type": "record",
+ "name": "Symbol",
+ "namespace": "xyz.api.product",
+ "fields": [{"name": "symbology", "type": "xyz.api.product.Symbology"},
+ {"name": "symbol", "type": "string"}]
+ }, {
+ "type": "record",
+ "name": "MultiSymbol",
+ "namespace": "xyz.api.product",
+ "fields": [{"name": "symbols",
+ "type": {"type": "map", "values": "xyz.api.product.Symbol"}}]
+ }],
+ "messages": {}
+ }),
]
VALID_EXAMPLES = [e for e in EXAMPLES if e.valid]
-class TestProtocol(unittest.TestCase):
- def test_parse(self):
- num_correct = 0
- for example in EXAMPLES:
- try:
- protocol.parse(example.protocol_string)
- if example.valid:
- num_correct += 1
- else:
- self.fail("Parsed invalid protocol: %s" % (example.name,))
- except Exception as e:
- if not example.valid:
- num_correct += 1
- else:
- self.fail("Coudl not parse valid protocol: %s" % (example.name,))
-
- fail_msg = "Parse behavior correct on %d out of %d protocols." % \
- (num_correct, len(EXAMPLES))
- self.assertEqual(num_correct, len(EXAMPLES), fail_msg)
-
+class TestMisc(unittest.TestCase):
def test_inner_namespace_set(self):
print('')
print('TEST INNER NAMESPACE')
print('===================')
print('')
- proto = protocol.parse(HELLO_WORLD.protocol_string)
+ proto = HELLO_WORLD.parse()
self.assertEqual(proto.namespace, "com.acme")
greeting_type = proto.types_dict['Greeting']
self.assertEqual(greeting_type.namespace, 'com.acme')
def test_inner_namespace_not_rendered(self):
- proto = protocol.parse(HELLO_WORLD.protocol_string)
+ proto = HELLO_WORLD.parse()
self.assertEqual('com.acme.Greeting', proto.types[0].fullname)
self.assertEqual('Greeting', proto.types[0].name)
# but there shouldn't be 'namespace' rendered to json on the inner type
self.assertFalse('namespace' in proto.to_json()['types'][0])
- def test_valid_cast_to_string_after_parse(self):
- """
- Test that the string generated by an Avro Protocol object
- is, in fact, a valid Avro protocol.
- """
- print('')
- print('TEST CAST TO STRING')
- print('===================')
- print('')
- num_correct = 0
- for example in VALID_EXAMPLES:
- protocol_data = protocol.parse(example.protocol_string)
- try:
- protocol.parse(str(protocol_data))
- except (ValueError, ProtocolParseException):
- debug_msg = "%s: STRING CAST FAILURE" % example.name
- else:
- debug_msg = "%s: STRING CAST SUCCESS" % example.name
- num_correct += 1
- print(debug_msg)
-
- fail_msg = "Cast to string success on %d out of %d protocols" % \
- (num_correct, len(VALID_EXAMPLES))
- self.assertEqual(num_correct, len(VALID_EXAMPLES), fail_msg)
-
- def test_equivalence_after_round_trip(self):
+class ProtocolParseTestCase(unittest.TestCase):
+ """Enable generating parse test cases over all the valid and invalid example protocols."""
+
+ def __init__(self, test_proto):
+ """Ignore the normal signature for unittest.TestCase because we are generating
+ many test cases from this one class. This is safe as long as the autoloader
+ ignores this class. The autoloader will ignore this class as long as it has
+ no methods starting with `test_`.
"""
- 1. Given a string, parse it to get Avro protocol "original".
- 2. Serialize "original" to a string and parse that string
- to generate Avro protocol "round trip".
- 3. Ensure "original" and "round trip" protocols are equivalent.
+ super(ProtocolParseTestCase, self).__init__(
+ 'parse_valid' if test_proto.valid else 'parse_invalid')
+ self.test_proto = test_proto
+
+ def parse_valid(self):
+ """Parsing a valid protocol should not error."""
+ try:
+ self.test_proto.parse()
+ except avro.protocol.ProtocolParseException:
+ self.fail("Valid protocol failed to parse: {!s}".format(self.test_proto))
+
+ def parse_invalid(self):
+ """Parsing an invalid schema should error."""
+ try:
+ self.test_proto.parse()
+ except (avro.protocol.ProtocolParseException, avro.schema.SchemaParseException):
+ pass
+ else:
+ self.fail("Invalid protocol should not have parsed: {!s}".format(self.test_proto))
+
+class ErrorSchemaTestCase(unittest.TestCase):
+ """Enable generating error schema test cases across all the valid test protocols."""
+
+ def __init__(self, test_proto):
+ """Ignore the normal signature for unittest.TestCase because we are generating
+ many test cases from this one class. This is safe as long as the autoloader
+ ignores this class. The autoloader will ignore this class as long as it has
+ no methods starting with `test_`.
"""
- print('')
- print('TEST ROUND TRIP')
- print('===============')
- print('')
-
- num_correct = 0
- for example in VALID_EXAMPLES:
- original_protocol = protocol.parse(example.protocol_string)
- round_trip_protocol = protocol.parse(str(original_protocol))
-
- if original_protocol == round_trip_protocol:
- num_correct += 1
- debug_msg = "%s: ROUND TRIP SUCCESS" % example.name
- else:
- self.fail("Round trip failure: %s %s %s", (example.name, example.protocol_string, str(original_protocol)))
-
- fail_msg = "Round trip success on %d out of %d protocols" % \
- (num_correct, len(VALID_EXAMPLES))
- self.assertEqual(num_correct, len(VALID_EXAMPLES), fail_msg)
+ super(ErrorSchemaTestCase, self).__init__('check_error_schema_exists')
+ self.test_proto = test_proto
+
+ def check_error_schema_exists(self):
+ """Protocol messages should always have at least a string error schema."""
+ p = self.test_proto.parse()
+ for k, m in p.messages.items():
+ self.assertIsNotNone(m.errors, "Message {} did not have the expected implicit "
+ "string error schema.".format(k))
+
+class RoundTripParseTestCase(unittest.TestCase):
+ """Enable generating round-trip parse test cases over all the valid test protocols."""
+
+ def __init__(self, test_proto):
+ """Ignore the normal signature for unittest.TestCase because we are generating
+ many test cases from this one class. This is safe as long as the autoloader
+ ignores this class. The autoloader will ignore this class as long as it has
+ no methods starting with `test_`.
+ """
+ super(RoundTripParseTestCase, self).__init__('parse_round_trip')
+ self.test_proto = test_proto
+
+ def parse_round_trip(self):
+ """The string of a Schema should be parseable to the same Schema."""
+ parsed = self.test_proto.parse()
+ round_trip = avro.protocol.parse(str(parsed))
+ self.assertEqual(parsed, round_trip)
+
+
+def load_tests(loader, default_tests, pattern):
+ """Generate test cases across many test schema."""
+ suite = unittest.TestSuite()
+ suite.addTests(loader.loadTestsFromTestCase(TestMisc))
+ suite.addTests(ProtocolParseTestCase(ex) for ex in EXAMPLES)
+ suite.addTests(RoundTripParseTestCase(ex) for ex in VALID_EXAMPLES)
+ return suite
if __name__ == '__main__':
unittest.main()
diff --git a/lang/py/test/test_schema.py b/lang/py/test/test_schema.py
index 63b0705..3b0b4e2 100644
--- a/lang/py/test/test_schema.py
+++ b/lang/py/test/test_schema.py
@@ -306,7 +306,7 @@ EXAMPLES += IGNORED_LOGICAL_TYPE
VALID_EXAMPLES = [e for e in EXAMPLES if e.valid]
INVALID_EXAMPLES = [e for e in EXAMPLES if not e.valid]
-class TestSchema(unittest.TestCase):
+class TestMisc(unittest.TestCase):
"""Miscellaneous tests for schema"""
def test_correct_recursive_extraction(self):
@@ -557,7 +557,7 @@ class OtherAttributesTestCase(unittest.TestCase):
def load_tests(loader, default_tests, pattern):
"""Generate test cases across many test schema."""
suite = unittest.TestSuite()
- suite.addTests(loader.loadTestsFromTestCase(TestSchema))
+ suite.addTests(loader.loadTestsFromTestCase(TestMisc))
suite.addTests(SchemaParseTestCase(ex) for ex in EXAMPLES)
suite.addTests(RoundTripParseTestCase(ex) for ex in VALID_EXAMPLES)
suite.addTests(DocAttributesTestCase(ex) for ex in DOC_EXAMPLES)