| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Compiler.hh" |
| #include "GenericDatum.hh" |
| #include "ValidSchema.hh" |
| |
| #include <boost/algorithm/string/replace.hpp> |
| #include <boost/test/included/unit_test.hpp> |
| #include <boost/test/parameterized_test.hpp> |
| #include <boost/test/unit_test.hpp> |
| |
| namespace avro { |
| namespace schema { |
| |
| const char *basicSchemas[] = { |
| R"("null")", |
| R"("boolean")", |
| R"("int")", |
| R"("long")", |
| R"("float")", |
| R"("double")", |
| R"("bytes")", |
| R"("string")", |
| |
| // Primitive types - longer |
| R"({ "type": "null" })", |
| R"({ "type": "boolean" })", |
| R"({ "type": "int" })", |
| R"({ "type": "long" })", |
| R"({ "type": "float" })", |
| R"({ "type": "double" })", |
| R"({ "type": "bytes" })", |
| R"({ "type": "string" })", |
| |
| // Record |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "doc":"Doc_string", |
| "fields":[] |
| })", |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields": [ |
| {"name":"f","type":"long"} |
| ] |
| })", |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields":[ |
| {"name":"f1","type":"long","doc":"field_doc"}, |
| {"name":"f2","type":"int"} |
| ] |
| })", |
| R"({ |
| "type":"error", |
| "name":"Test", |
| "fields":[ |
| {"name":"f1","type":"long"}, |
| {"name":"f2","type":"int"} |
| ] |
| })", |
| // Recursive. |
| R"({ |
| "type":"record", |
| "name":"LongList", |
| "fields":[ |
| {"name":"value","type":"long","doc":"recursive_doc"}, |
| {"name":"next","type":["LongList","null"]} |
| ] |
| })", |
| |
| // Enum |
| R"({ |
| "type":"enum", |
| "doc":"enum_doc", |
| "name":"Test", |
| "symbols":["A","B"] |
| })", |
| |
| // Array |
| R"({ |
| "type":"array", |
| "doc":"array_doc", |
| "items":"long" |
| })", |
| R"({ |
| "type":"array", |
| "items":{ |
| "type":"enum", |
| "name":"Test", |
| "symbols":["A","B"] |
| } |
| })", |
| |
| // Map |
| R"({"type":"map","doc":"map_doc","values":"long"})", |
| R"({ |
| "type":"map", |
| "values":{ |
| "type":"enum", |
| "name":"Test", |
| "symbols":["A","B"] |
| } |
| })", |
| |
| // Union |
| R"(["string","null","long"])", |
| |
| // Fixed |
| R"({"type":"fixed","doc":"fixed_doc","name":"Test","size":1})", |
| R"({"type":"fixed","name":"MyFixed","namespace":"org.apache.hadoop.avro","size":1})", |
| R"({"type":"fixed","name":"Test","size":1})", |
| R"({"type":"fixed","name":"Test","size":1})", |
| |
| // Extra attributes (should be ignored) |
| R"({"type": "null", "extra attribute": "should be ignored"})", |
| R"({"type": "boolean", "extra1": 1, "extra2": 2, "extra3": 3})", |
| R"({ |
| "type": "record", |
| "name": "Test", |
| "fields":[ |
| {"name": "f","type":"long"} |
| ], |
| "extra attribute": 1 |
| })", |
| R"({"type": "enum", "name": "Test", "symbols": ["A", "B"],"extra attribute": 1})", |
| R"({"type": "array", "items": "long", "extra attribute": "1"})", |
| R"({"type": "map", "values": "long", "extra attribute": 1})", |
| R"({"type": "fixed", "name": "Test", "size": 1, "extra attribute": 1})", |
| |
| // defaults |
| // default double - long |
| R"({ "name":"test", "type": "record", "fields": [ {"name": "double","type": "double","default" : 2 }]})", |
| // default double - double |
| R"({ "name":"test", "type": "record", "fields": [ {"name": "double","type": "double","default" : 1.2 }]})", |
| |
| // namespace with '$' in it. |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "namespace":"a.b$", |
| "fields":[ |
| {"name":"f","type":"long"} |
| ] |
| })", |
| |
| // Custom attribute(s) for field in record |
| R"({ |
| "type": "record", |
| "name": "Test", |
| "fields":[ |
| {"name": "f1","type": "long","extra field": "1"} |
| ] |
| })", |
| R"({ |
| "type": "record", |
| "name": "Test", |
| "fields":[ |
| {"name": "f1","type": "long","extra field1": "1","extra field2": "2"} |
| ] |
| })" |
| }; |
| |
| const char *basicSchemaErrors[] = { |
| // Record |
| // No fields |
| R"({"type":"record","name":"LongList"})", |
| // Fields not an array |
| R"({"type":"record","name":"LongList", "fields": "hi"})", |
| |
| // Undefined name |
| R"({ |
| "type":"record", |
| "name":"LongList", |
| "fields":[ |
| {"name":"value","type":"long"}, |
| {"name":"next","type":["LongListA","null"]} |
| ] |
| })", |
| |
| // Enum |
| // Symbols not an array |
| R"({"type": "enum", "name": "Status", "symbols":"Normal Caution Critical"})", |
| // Name not a string |
| R"({"type": "enum", "name": [ 0, 1, 1, 2, 3, 5, 8 ], "symbols": ["Golden", "Mean"]})", |
| // No name |
| R"({"type": "enum", "symbols" : ["I", "will", "fail", "no", "name"]})", |
| // Duplicate symbol |
| R"({"type": "enum", "name": "Test", "symbols" : ["AA", "AA"]})", |
| |
| // Union |
| // Duplicate type |
| R"(["string", "long", "long"])", |
| // Duplicate type |
| R"([ |
| {"type": "array", "items": "long"}, |
| {"type": "array", "items": "string"} |
| ])", |
| |
| // Fixed |
| // No size |
| R"({"type": "fixed", "name": "Missing size"})", |
| // No name |
| R"({"type": "fixed", "size": 314})", |
| |
| // defaults |
| // default double - null |
| R"({ "name":"test", "type": "record", "fields": [ {"name": "double","type": "double","default" : null }]})", |
| // default double - string |
| R"({ "name":"test", "type": "record", "fields": [ {"name": "double","type": "double","default" : "string" }]})" |
| |
| }; |
| |
| const char *roundTripSchemas[] = { |
| R"("null")", |
| R"("boolean")", |
| R"("int")", |
| R"("long")", |
| R"("float")", |
| R"("double")", |
| R"("bytes")", |
| R"("string")", |
| |
| // Record |
| R"({"type":"record","name":"Test","fields":[]})", |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields":[ |
| {"name":"f","type":"long"} |
| ] |
| })", |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields":[ |
| {"name":"f1","type":"long"}, |
| {"name":"f2","type":"int"} |
| ] |
| })", |
| |
| /* Avro-C++ cannot do a round-trip on error schemas. |
| * R"({ |
| * "type":"error", |
| * "name":"Test", |
| * "fields":[ |
| * {"name":"f1","type":"long"}, |
| * {"name":"f2","type":"int"} |
| * ] |
| * })", |
| */ |
| |
| // Recursive. |
| R"({ |
| "type":"record", |
| "name":"LongList", |
| "fields":[ |
| {"name":"value","type":"long"}, |
| {"name":"next","type":["LongList","null"]} |
| ] |
| })", |
| |
| // Enum |
| R"({"type":"enum","name":"Test","symbols":["A","B"]})", |
| |
| // Array |
| R"({"type":"array","items":"long"})", |
| R"({ |
| "type":"array", |
| "items":{ |
| "type":"enum", |
| "name":"Test", |
| "symbols":["A","B"] |
| } |
| })", |
| |
| // Map |
| R"({"type":"map","values":"long"})", |
| R"({ |
| "type":"map", |
| "values":{ |
| "type":"enum", |
| "name":"Test", |
| "symbols":["A","B"] |
| } |
| })", |
| |
| // Union |
| R"(["string","null","long"])", |
| |
| // Fixed |
| R"({"type":"fixed","name":"Test","size":1})", |
| R"({"type":"fixed","namespace":"org.apache.hadoop.avro","name":"MyFixed","size":1})", |
| R"({"type":"fixed","name":"Test","size":1})", |
| R"({"type":"fixed","name":"Test","size":1})", |
| |
| // Logical types |
| R"({"type":"bytes","logicalType":"big-decimal"})", |
| R"({"type":"bytes","logicalType":"decimal","precision":12,"scale":6})", |
| R"({"type":"fixed","name":"test","size":16,"logicalType":"decimal","precision":38,"scale":9})", |
| R"({"type":"fixed","name":"test","size":129,"logicalType":"decimal","precision":310,"scale":155})", |
| R"({"type":"int","logicalType":"date"})", |
| R"({"type":"int","logicalType":"time-millis"})", |
| R"({"type":"long","logicalType":"time-micros"})", |
| R"({"type":"long","logicalType":"timestamp-millis"})", |
| R"({"type":"long","logicalType":"timestamp-micros"})", |
| R"({"type":"long","logicalType":"timestamp-nanos"})", |
| R"({"type":"long","logicalType":"local-timestamp-millis"})", |
| R"({"type":"long","logicalType":"local-timestamp-micros"})", |
| R"({"type":"long","logicalType":"local-timestamp-nanos"})", |
| R"({"type":"fixed","name":"test","size":12,"logicalType":"duration"})", |
| R"({"type":"string","logicalType":"uuid"})", |
| |
| // namespace with '$' in it. |
| R"({ |
| "type":"record", |
| "namespace":"a.b$", |
| "name":"Test", |
| "fields":[ |
| {"name":"f","type":"long"} |
| ] |
| })", |
| |
| // Custom fields |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields":[ |
| {"name":"f1","type":"long","extra_field":"1"}, |
| {"name":"f2","type":"int"} |
| ] |
| })", |
| R"({ |
| "type":"record", |
| "name":"Test", |
| "fields":[ |
| {"name":"f1","type":"long","extra_field":"1"}, |
| {"name":"f2","type":"int","extra_field1":"21","extra_field2":"22"} |
| ] |
| })", |
| R"({"type":"array","items":"long","extra":"1"})" |
| }; |
| |
| const char *malformedLogicalTypes[] = { |
| // Wrong base type. |
| R"({"type":"long","logicalType": "big-decimal"})", |
| R"({"type":"long","logicalType": "decimal","precision": 10})", |
| R"({"type":"string","logicalType":"date"})", |
| R"({"type":"string","logicalType":"time-millis"})", |
| R"({"type":"string","logicalType":"time-micros"})", |
| R"({"type":"string","logicalType":"timestamp-millis"})", |
| R"({"type":"string","logicalType":"timestamp-micros"})", |
| R"({"type":"string","logicalType":"timestamp-nanos"})", |
| R"({"type":"string","logicalType":"local-timestamp-millis"})", |
| R"({"type":"string","logicalType":"local-timestamp-micros"})", |
| R"({"type":"string","logicalType":"local-timestamp-nanos"})", |
| R"({"type":"string","logicalType":"duration"})", |
| R"({"type":"long","logicalType":"uuid"})", |
| // Missing the required field 'precision'. |
| R"({"type":"bytes","logicalType":"decimal"})", |
| // The claimed precision is not supported by the size of the fixed type. |
| R"({"type":"fixed","logicalType":"decimal","size":4,"name":"a","precision":20})", |
| R"({"type":"fixed","logicalType":"decimal","size":129,"name":"a","precision":311})", |
| // Scale is larger than precision. |
| R"({"type":"bytes","logicalType":"decimal","precision":5,"scale":10})", |
| // Precision is not supported by the big-decimal logical type |
| // and scale is integrated in bytes. |
| R"({"type":"bytes","logicalType": "big-decimal","precision": 9})", |
| R"({"type":"bytes","logicalType": "big-decimal","scale": 2})", |
| R"({"type":"bytes","logicalType": "big-decimal","precision": 9,"scale": 2})"}; |
| const char *schemasToCompact[] = { |
| // Schema without any whitespace |
| R"({"type":"record","name":"Test","fields":[]})", |
| |
| // Schema with whitespaces outside of field names/values only. |
| "{\"type\": \"record\",\n \n\"name\":\"Test\", \t\t\"fields\":[]}\n \n", |
| |
| // Schema with whitespaces both inside and outside of field names/values. |
| "{\"type\": \"record\", \"name\": \"ComplexInteger\"\n, " |
| "\"doc\": \"record_doc °C \u00f8 \x1f \\n \n \t\", " |
| "\"fields\": [" |
| "{\"name\": \"re1\", \"type\": \"long\", " |
| "\"doc\": \"A \\\"quoted doc\\\"\" }, " |
| "{\"name\": \"re2\", \"type\": \"long\", \n\t" |
| "\"doc\": \"extra slashes\\\\\\\\\"}" |
| "]}"}; |
| |
| const char *compactSchemas[] = { |
| R"({"type":"record","name":"Test","fields":[]})", |
| R"({"type":"record","name":"Test","fields":[]})", |
| "{\"type\":\"record\",\"name\":\"ComplexInteger\"," |
| "\"doc\":\"record_doc °C \u00f8 \\u001f \\n \\n \\t\"," |
| "\"fields\":[" |
| "{\"name\":\"re1\",\"type\":\"long\",\"doc\":\"A \\\"quoted doc\\\"\"}," |
| "{\"name\":\"re2\",\"type\":\"long\",\"doc\":\"extra slashes\\\\\\\\\"}" |
| "]}" |
| }; |
| |
| static const std::vector<char> whitespaces = {' ', '\f', '\n', '\r', '\t', '\v'}; |
| |
| static std::string removeWhitespaceFromSchema(const std::string& schema){ |
| std::string trimmedSchema = schema; |
| for (char toReplace : whitespaces){ |
| boost::algorithm::replace_all(trimmedSchema, std::string{toReplace}, ""); |
| } |
| return trimmedSchema; |
| } |
| |
| void testTypes() { |
| BOOST_CHECK_EQUAL(isAvroType(AVRO_BOOL), true); |
| } |
| |
| static void testBasic(const char *schema) { |
| BOOST_TEST_CHECKPOINT(schema); |
| compileJsonSchemaFromString(schema); |
| } |
| |
| static void testBasic_fail(const char *schema) { |
| BOOST_TEST_CHECKPOINT(schema); |
| BOOST_CHECK_THROW(compileJsonSchemaFromString(schema), Exception); |
| } |
| |
| static void testCompile(const char *schema) { |
| BOOST_TEST_CHECKPOINT(schema); |
| compileJsonSchemaFromString(std::string(schema)); |
| } |
| |
| // Test that the JSON output from a valid schema matches the JSON that was |
| // used to construct it, apart from whitespace changes. |
| static void testRoundTrip(const char *schema) { |
| BOOST_TEST_CHECKPOINT(schema); |
| avro::ValidSchema compiledSchema = |
| compileJsonSchemaFromString(std::string(schema)); |
| std::ostringstream os; |
| compiledSchema.toJson(os); |
| std::string result = removeWhitespaceFromSchema(os.str()); |
| std::string trimmedSchema = removeWhitespaceFromSchema(schema); |
| BOOST_CHECK_EQUAL(result, trimmedSchema); |
| // Verify that the compact schema from toJson has the same content as the |
| // schema. |
| std::string result2 = compiledSchema.toJson(false); |
| BOOST_CHECK_EQUAL(result2, trimmedSchema); |
| } |
| |
| static void testCompactSchemas() { |
| for (size_t i = 0; i < sizeof(schemasToCompact) / sizeof(schemasToCompact[0]); i++) { |
| const char *schema = schemasToCompact[i]; |
| BOOST_TEST_CHECKPOINT(schema); |
| avro::ValidSchema compiledSchema = |
| compileJsonSchemaFromString(std::string(schema)); |
| |
| std::string result = compiledSchema.toJson(false); |
| BOOST_CHECK_EQUAL(result, compactSchemas[i]); |
| } |
| } |
| |
| static void testLogicalTypes() { |
| const char *bytesBigDecimalType = R"({ |
| "type": "bytes", |
| "logicalType": "big-decimal" |
| })"; |
| const char *bytesDecimalType = R"({ |
| "type": "bytes", |
| "logicalType": "decimal", |
| "precision": 10, |
| "scale": 2 |
| })"; |
| const char *fixedDecimalType = R"({ |
| "type": "fixed", |
| "size": 16, |
| "name": "fixedDecimalType", |
| "logicalType": "decimal", |
| "precision": 12, |
| "scale": 6 |
| })"; |
| const char *dateType = R"({"type": "int", "logicalType": "date"})"; |
| const char *timeMillisType = R"({"type": "int", "logicalType": "time-millis"})"; |
| const char *timeMicrosType = R"({"type": "long", "logicalType": "time-micros"})"; |
| const char *timestampMillisType = R"({"type": "long", "logicalType": "timestamp-millis"})"; |
| const char *timestampMicrosType = R"({"type": "long", "logicalType": "timestamp-micros"})"; |
| const char *timestampNanosType = R"({"type": "long", "logicalType": "timestamp-nanos"})"; |
| const char *localTimestampMillisType = R"({"type": "long", "logicalType": "local-timestamp-millis"})"; |
| const char *localTimestampMicrosType = R"({"type": "long", "logicalType": "local-timestamp-micros"})"; |
| const char *localTimestampNanosType = R"({"type": "long", "logicalType": "local-timestamp-nanos"})"; |
| const char *durationType = R"({"type": "fixed","size": 12,"name": "durationType","logicalType": "duration"})"; |
| const char *uuidType = R"({"type": "string","logicalType": "uuid"})"; |
| // AVRO-2923 Union with LogicalType |
| const char *unionType = R"([{"type":"string", "logicalType":"uuid"},"null"]})"; |
| { |
| BOOST_TEST_CHECKPOINT(bytesBigDecimalType); |
| ValidSchema schema = compileJsonSchemaFromString(bytesBigDecimalType); |
| BOOST_CHECK(schema.root()->type() == AVRO_BYTES); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::BIG_DECIMAL); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(bytesDecimalType); |
| ValidSchema schema1 = compileJsonSchemaFromString(bytesDecimalType); |
| BOOST_CHECK(schema1.root()->type() == AVRO_BYTES); |
| LogicalType logicalType = schema1.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::DECIMAL); |
| BOOST_CHECK(logicalType.precision() == 10); |
| BOOST_CHECK(logicalType.scale() == 2); |
| |
| BOOST_TEST_CHECKPOINT(fixedDecimalType); |
| ValidSchema schema2 = compileJsonSchemaFromString(fixedDecimalType); |
| BOOST_CHECK(schema2.root()->type() == AVRO_FIXED); |
| logicalType = schema2.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::DECIMAL); |
| BOOST_CHECK(logicalType.precision() == 12); |
| BOOST_CHECK(logicalType.scale() == 6); |
| |
| GenericDatum bytesDatum(schema1); |
| BOOST_CHECK(bytesDatum.logicalType().type() == LogicalType::DECIMAL); |
| GenericDatum fixedDatum(schema2); |
| BOOST_CHECK(fixedDatum.logicalType().type() == LogicalType::DECIMAL); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(dateType); |
| ValidSchema schema = compileJsonSchemaFromString(dateType); |
| BOOST_CHECK(schema.root()->type() == AVRO_INT); |
| BOOST_CHECK(schema.root()->logicalType().type() == LogicalType::DATE); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::DATE); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(timeMillisType); |
| ValidSchema schema = compileJsonSchemaFromString(timeMillisType); |
| BOOST_CHECK(schema.root()->type() == AVRO_INT); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::TIME_MILLIS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::TIME_MILLIS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(timeMicrosType); |
| ValidSchema schema = compileJsonSchemaFromString(timeMicrosType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::TIME_MICROS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::TIME_MICROS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(timestampMillisType); |
| ValidSchema schema = compileJsonSchemaFromString(timestampMillisType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::TIMESTAMP_MILLIS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::TIMESTAMP_MILLIS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(timestampMicrosType); |
| ValidSchema schema = compileJsonSchemaFromString(timestampMicrosType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::TIMESTAMP_MICROS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::TIMESTAMP_MICROS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(timestampNanosType); |
| ValidSchema schema = compileJsonSchemaFromString(timestampNanosType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::TIMESTAMP_NANOS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::TIMESTAMP_NANOS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(localTimestampMillisType); |
| ValidSchema schema = compileJsonSchemaFromString(localTimestampMillisType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::LOCAL_TIMESTAMP_MILLIS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::LOCAL_TIMESTAMP_MILLIS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(localTimestampMicrosType); |
| ValidSchema schema = compileJsonSchemaFromString(localTimestampMicrosType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::LOCAL_TIMESTAMP_MICROS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::LOCAL_TIMESTAMP_MICROS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(localTimestampNanosType); |
| ValidSchema schema = compileJsonSchemaFromString(localTimestampNanosType); |
| BOOST_CHECK(schema.root()->type() == AVRO_LONG); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::LOCAL_TIMESTAMP_NANOS); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::LOCAL_TIMESTAMP_NANOS); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(durationType); |
| ValidSchema schema = compileJsonSchemaFromString(durationType); |
| BOOST_CHECK(schema.root()->type() == AVRO_FIXED); |
| BOOST_CHECK(schema.root()->fixedSize() == 12); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::DURATION); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::DURATION); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(uuidType); |
| ValidSchema schema = compileJsonSchemaFromString(uuidType); |
| BOOST_CHECK(schema.root()->type() == AVRO_STRING); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::UUID); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::UUID); |
| } |
| { |
| BOOST_TEST_CHECKPOINT(unionType); |
| ValidSchema schema = compileJsonSchemaFromString(unionType); |
| BOOST_CHECK(schema.root()->type() == AVRO_UNION); |
| LogicalType logicalType = schema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::NONE); |
| GenericDatum datum(schema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::UUID); |
| } |
| } |
| |
| static void testMalformedLogicalTypes(const char *schema) { |
| BOOST_TEST_CHECKPOINT(schema); |
| ValidSchema parsedSchema = compileJsonSchemaFromString(schema); |
| LogicalType logicalType = parsedSchema.root()->logicalType(); |
| BOOST_CHECK(logicalType.type() == LogicalType::NONE); |
| GenericDatum datum(parsedSchema); |
| BOOST_CHECK(datum.logicalType().type() == LogicalType::NONE); |
| } |
| |
| } // namespace schema |
| } // namespace avro |
| |
| #define ENDOF(x) (x + sizeof(x) / sizeof(x[0])) |
| |
| #define ADD_PARAM_TEST(ts, func, data) \ |
| ts->add(BOOST_PARAM_TEST_CASE(&func, data, ENDOF(data))) |
| |
| boost::unit_test::test_suite * |
| init_unit_test_suite(int /*argc*/, char * /*argv*/[]) { |
| using namespace boost::unit_test; |
| |
| auto *ts = BOOST_TEST_SUITE("Avro C++ unit tests for schemas"); |
| ts->add(BOOST_TEST_CASE(&avro::schema::testTypes)); |
| ADD_PARAM_TEST(ts, avro::schema::testBasic, avro::schema::basicSchemas); |
| ADD_PARAM_TEST(ts, avro::schema::testBasic_fail, |
| avro::schema::basicSchemaErrors); |
| ADD_PARAM_TEST(ts, avro::schema::testCompile, avro::schema::basicSchemas); |
| ADD_PARAM_TEST(ts, avro::schema::testRoundTrip, avro::schema::roundTripSchemas); |
| ts->add(BOOST_TEST_CASE(&avro::schema::testLogicalTypes)); |
| ADD_PARAM_TEST(ts, avro::schema::testMalformedLogicalTypes, |
| avro::schema::malformedLogicalTypes); |
| ts->add(BOOST_TEST_CASE(&avro::schema::testCompactSchemas)); |
| return ts; |
| } |