blob: 0b82c2711d6c47c7ee82097a625b610ff9b1b2f1 [file] [log] [blame]
/*
* 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
*
* http://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 "avro.h"
#include "avro_private.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
char buf[4096];
avro_reader_t reader;
avro_writer_t writer;
typedef int (*avro_test) (void);
/*
* Use a custom allocator that verifies that the size that we use to
* free an object matches the size that we use to allocate it.
*/
static void *
test_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
AVRO_UNUSED(ud);
AVRO_UNUSED(osize);
if (nsize == 0) {
size_t *size = ((size_t *) ptr) - 1;
if (osize != *size) {
fprintf(stderr,
"Error freeing %p:\n"
"Size passed to avro_free (%" PRIsz ") "
"doesn't match size passed to "
"avro_malloc (%" PRIsz ")\n",
ptr, osize, *size);
abort();
//exit(EXIT_FAILURE);
}
free(size);
return NULL;
} else {
size_t real_size = nsize + sizeof(size_t);
size_t *old_size = ptr? ((size_t *) ptr)-1: NULL;
size_t *size = (size_t *) realloc(old_size, real_size);
*size = nsize;
return (size + 1);
}
}
void init_rand(void)
{
srand(time(NULL));
}
double rand_number(double from, double to)
{
double range = to - from;
return from + ((double)rand() / (RAND_MAX + 1.0)) * range;
}
int64_t rand_int64(void)
{
return (int64_t) rand_number(LONG_MIN, LONG_MAX);
}
int32_t rand_int32(void)
{
return (int32_t) rand_number(INT_MIN, INT_MAX);
}
void
write_read_check(avro_schema_t writers_schema, avro_datum_t datum,
avro_schema_t readers_schema, avro_datum_t expected, char *type)
{
avro_datum_t datum_out;
int validate;
for (validate = 0; validate <= 1; validate++) {
reader = avro_reader_memory(buf, sizeof(buf));
writer = avro_writer_memory(buf, sizeof(buf));
if (!expected) {
expected = datum;
}
/* Validating read/write */
if (avro_write_data
(writer, validate ? writers_schema : NULL, datum)) {
fprintf(stderr, "Unable to write %s validate=%d\n %s\n",
type, validate, avro_strerror());
exit(EXIT_FAILURE);
}
int64_t size =
avro_size_data(writer, validate ? writers_schema : NULL,
datum);
if (size != avro_writer_tell(writer)) {
fprintf(stderr,
"Unable to calculate size %s validate=%d "
"(%"PRId64" != %"PRId64")\n %s\n",
type, validate, size, avro_writer_tell(writer),
avro_strerror());
exit(EXIT_FAILURE);
}
if (avro_read_data
(reader, writers_schema, readers_schema, &datum_out)) {
fprintf(stderr, "Unable to read %s validate=%d\n %s\n",
type, validate, avro_strerror());
fprintf(stderr, " %s\n", avro_strerror());
exit(EXIT_FAILURE);
}
if (!avro_datum_equal(expected, datum_out)) {
fprintf(stderr,
"Unable to encode/decode %s validate=%d\n %s\n",
type, validate, avro_strerror());
exit(EXIT_FAILURE);
}
avro_reader_dump(reader, stderr);
avro_datum_decref(datum_out);
avro_reader_free(reader);
avro_writer_free(writer);
}
}
static void test_json(avro_datum_t datum, const char *expected)
{
char *json = NULL;
avro_datum_to_json(datum, 1, &json);
if (strcasecmp(json, expected) != 0) {
fprintf(stderr, "Unexpected JSON encoding: %s\n", json);
exit(EXIT_FAILURE);
}
free(json);
}
static int test_string(void)
{
unsigned int i;
const char *strings[] = { "Four score and seven years ago",
"our father brought forth on this continent",
"a new nation", "conceived in Liberty",
"and dedicated to the proposition that all men are created equal."
};
avro_schema_t writer_schema = avro_schema_string();
for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i++) {
avro_datum_t datum = avro_givestring(strings[i], NULL);
write_read_check(writer_schema, datum, NULL, NULL, "string");
avro_datum_decref(datum);
}
avro_datum_t datum = avro_givestring(strings[0], NULL);
test_json(datum, "\"Four score and seven years ago\"");
avro_datum_decref(datum);
// The following should bork if we don't copy the string value
// correctly (since we'll try to free a static string).
datum = avro_string("this should be copied");
avro_string_set(datum, "also this");
avro_datum_decref(datum);
avro_schema_decref(writer_schema);
return 0;
}
static int test_bytes(void)
{
char bytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
avro_schema_t writer_schema = avro_schema_bytes();
avro_datum_t datum;
avro_datum_t expected_datum;
datum = avro_givebytes(bytes, sizeof(bytes), NULL);
write_read_check(writer_schema, datum, NULL, NULL, "bytes");
test_json(datum, "\"\\u00de\\u00ad\\u00be\\u00ef\"");
avro_datum_decref(datum);
avro_schema_decref(writer_schema);
datum = avro_givebytes(NULL, 0, NULL);
avro_givebytes_set(datum, bytes, sizeof(bytes), NULL);
expected_datum = avro_givebytes(bytes, sizeof(bytes), NULL);
if (!avro_datum_equal(datum, expected_datum)) {
fprintf(stderr,
"Expected equal bytes instances.\n");
exit(EXIT_FAILURE);
}
avro_datum_decref(datum);
avro_datum_decref(expected_datum);
// The following should bork if we don't copy the bytes value
// correctly (since we'll try to free a static string).
datum = avro_bytes("original", 8);
avro_bytes_set(datum, "alsothis", 8);
avro_datum_decref(datum);
avro_schema_decref(writer_schema);
return 0;
}
static int test_int32(void)
{
int i;
avro_schema_t writer_schema = avro_schema_int();
avro_schema_t long_schema = avro_schema_long();
avro_schema_t float_schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
int32_t value = rand_int32();
avro_datum_t datum = avro_int32(value);
avro_datum_t long_datum = avro_int64(value);
avro_datum_t float_datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(writer_schema, datum, NULL, NULL, "int");
write_read_check(writer_schema, datum,
long_schema, long_datum, "int->long");
write_read_check(writer_schema, datum,
float_schema, float_datum, "int->float");
write_read_check(writer_schema, datum,
double_schema, double_datum, "int->double");
avro_datum_decref(datum);
avro_datum_decref(long_datum);
avro_datum_decref(float_datum);
avro_datum_decref(double_datum);
}
avro_datum_t datum = avro_int32(10000);
test_json(datum, "10000");
avro_datum_decref(datum);
avro_schema_decref(writer_schema);
avro_schema_decref(long_schema);
avro_schema_decref(float_schema);
avro_schema_decref(double_schema);
return 0;
}
static int test_int64(void)
{
int i;
avro_schema_t writer_schema = avro_schema_long();
avro_schema_t float_schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
int64_t value = rand_int64();
avro_datum_t datum = avro_int64(value);
avro_datum_t float_datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(writer_schema, datum, NULL, NULL, "long");
write_read_check(writer_schema, datum,
float_schema, float_datum, "long->float");
write_read_check(writer_schema, datum,
double_schema, double_datum, "long->double");
avro_datum_decref(datum);
avro_datum_decref(float_datum);
avro_datum_decref(double_datum);
}
avro_datum_t datum = avro_int64(10000);
test_json(datum, "10000");
avro_datum_decref(datum);
avro_schema_decref(writer_schema);
avro_schema_decref(float_schema);
avro_schema_decref(double_schema);
return 0;
}
static int test_double(void)
{
int i;
avro_schema_t schema = avro_schema_double();
for (i = 0; i < 100; i++) {
avro_datum_t datum = avro_double(rand_number(-1.0E10, 1.0E10));
write_read_check(schema, datum, NULL, NULL, "double");
avro_datum_decref(datum);
}
avro_datum_t datum = avro_double(2000.0);
test_json(datum, "2000.0");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
static int test_float(void)
{
int i;
avro_schema_t schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
float value = rand_number(-1.0E10, 1.0E10);
avro_datum_t datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(schema, datum, NULL, NULL, "float");
write_read_check(schema, datum,
double_schema, double_datum, "float->double");
avro_datum_decref(datum);
avro_datum_decref(double_datum);
}
avro_datum_t datum = avro_float(2000.0);
test_json(datum, "2000.0");
avro_datum_decref(datum);
avro_schema_decref(schema);
avro_schema_decref(double_schema);
return 0;
}
static int test_boolean(void)
{
int i;
const char *expected_json[] = { "false", "true" };
avro_schema_t schema = avro_schema_boolean();
for (i = 0; i <= 1; i++) {
avro_datum_t datum = avro_boolean(i);
write_read_check(schema, datum, NULL, NULL, "boolean");
test_json(datum, expected_json[i]);
avro_datum_decref(datum);
}
avro_schema_decref(schema);
return 0;
}
static int test_null(void)
{
avro_schema_t schema = avro_schema_null();
avro_datum_t datum = avro_null();
write_read_check(schema, datum, NULL, NULL, "null");
test_json(datum, "null");
avro_datum_decref(datum);
return 0;
}
static int test_record(void)
{
avro_schema_t schema = avro_schema_record("person", NULL);
avro_schema_record_field_append(schema, "name", avro_schema_string());
avro_schema_record_field_append(schema, "age", avro_schema_int());
avro_datum_t datum = avro_record(schema);
avro_datum_t name_datum, age_datum;
name_datum = avro_givestring("Joseph Campbell", NULL);
age_datum = avro_int32(83);
avro_record_set(datum, "name", name_datum);
avro_record_set(datum, "age", age_datum);
write_read_check(schema, datum, NULL, NULL, "record");
test_json(datum, "{\"name\": \"Joseph Campbell\", \"age\": 83}");
int rc;
avro_record_set_field_value(rc, datum, int32, "age", 104);
int32_t age = 0;
avro_record_get_field_value(rc, datum, int32, "age", &age);
if (age != 104) {
fprintf(stderr, "Incorrect age value\n");
exit(EXIT_FAILURE);
}
avro_datum_decref(name_datum);
avro_datum_decref(age_datum);
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
static int test_nested_record(void)
{
const char *json =
"{"
" \"type\": \"record\","
" \"name\": \"list\","
" \"fields\": ["
" { \"name\": \"x\", \"type\": \"int\" },"
" { \"name\": \"y\", \"type\": \"int\" },"
" { \"name\": \"next\", \"type\": [\"null\",\"list\"]}"
" ]"
"}";
int rval;
avro_schema_t schema = NULL;
avro_schema_error_t error;
avro_schema_from_json(json, strlen(json), &schema, &error);
avro_datum_t head = avro_datum_from_schema(schema);
avro_record_set_field_value(rval, head, int32, "x", 10);
avro_record_set_field_value(rval, head, int32, "y", 10);
avro_datum_t next = NULL;
avro_datum_t tail = NULL;
avro_record_get(head, "next", &next);
avro_union_set_discriminant(next, 1, &tail);
avro_record_set_field_value(rval, tail, int32, "x", 20);
avro_record_set_field_value(rval, tail, int32, "y", 20);
avro_record_get(tail, "next", &next);
avro_union_set_discriminant(next, 0, NULL);
write_read_check(schema, head, NULL, NULL, "nested record");
avro_schema_decref(schema);
avro_datum_decref(head);
return 0;
}
static int test_enum(void)
{
enum avro_languages {
AVRO_C,
AVRO_CPP,
AVRO_PYTHON,
AVRO_RUBY,
AVRO_JAVA
};
avro_schema_t schema = avro_schema_enum("language");
avro_datum_t datum = avro_enum(schema, AVRO_C);
avro_schema_enum_symbol_append(schema, "C");
avro_schema_enum_symbol_append(schema, "C++");
avro_schema_enum_symbol_append(schema, "Python");
avro_schema_enum_symbol_append(schema, "Ruby");
avro_schema_enum_symbol_append(schema, "Java");
if (avro_enum_get(datum) != AVRO_C) {
fprintf(stderr, "Unexpected enum value AVRO_C\n");
exit(EXIT_FAILURE);
}
if (strcmp(avro_enum_get_name(datum), "C") != 0) {
fprintf(stderr, "Unexpected enum value name C\n");
exit(EXIT_FAILURE);
}
write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"C\"");
avro_enum_set(datum, AVRO_CPP);
if (strcmp(avro_enum_get_name(datum), "C++") != 0) {
fprintf(stderr, "Unexpected enum value name C++\n");
exit(EXIT_FAILURE);
}
write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"C++\"");
avro_enum_set_name(datum, "Python");
if (avro_enum_get(datum) != AVRO_PYTHON) {
fprintf(stderr, "Unexpected enum value AVRO_PYTHON\n");
exit(EXIT_FAILURE);
}
write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"Python\"");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
static int test_array(void)
{
int i, rval;
avro_schema_t schema = avro_schema_array(avro_schema_int());
avro_datum_t datum = avro_array(schema);
for (i = 0; i < 10; i++) {
avro_datum_t i32_datum = avro_int32(i);
rval = avro_array_append_datum(datum, i32_datum);
avro_datum_decref(i32_datum);
if (rval) {
exit(EXIT_FAILURE);
}
}
if (avro_array_size(datum) != 10) {
fprintf(stderr, "Unexpected array size");
exit(EXIT_FAILURE);
}
write_read_check(schema, datum, NULL, NULL, "array");
test_json(datum, "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
static int test_map(void)
{
avro_schema_t schema = avro_schema_map(avro_schema_long());
avro_datum_t datum = avro_map(schema);
int64_t i = 0;
char *nums[] =
{ "zero", "one", "two", "three", "four", "five", "six", NULL };
while (nums[i]) {
avro_datum_t i_datum = avro_int64(i);
avro_map_set(datum, nums[i], i_datum);
avro_datum_decref(i_datum);
i++;
}
if (avro_array_size(datum) != 7) {
fprintf(stderr, "Unexpected map size\n");
exit(EXIT_FAILURE);
}
avro_datum_t value;
const char *key;
avro_map_get_key(datum, 2, &key);
avro_map_get(datum, key, &value);
int64_t val;
avro_int64_get(value, &val);
if (val != 2) {
fprintf(stderr, "Unexpected map value 2\n");
exit(EXIT_FAILURE);
}
int index;
if (avro_map_get_index(datum, "two", &index)) {
fprintf(stderr, "Can't get index for key \"two\": %s\n",
avro_strerror());
exit(EXIT_FAILURE);
}
if (index != 2) {
fprintf(stderr, "Unexpected index for key \"two\"\n");
exit(EXIT_FAILURE);
}
if (!avro_map_get_index(datum, "foobar", &index)) {
fprintf(stderr, "Unexpected index for key \"foobar\"\n");
exit(EXIT_FAILURE);
}
write_read_check(schema, datum, NULL, NULL, "map");
test_json(datum,
"{\"zero\": 0, \"one\": 1, \"two\": 2, \"three\": 3, "
"\"four\": 4, \"five\": 5, \"six\": 6}");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
static int test_union(void)
{
avro_schema_t schema = avro_schema_union();
avro_datum_t union_datum;
avro_datum_t datum;
avro_datum_t union_datum1;
avro_datum_t datum1;
avro_schema_union_append(schema, avro_schema_string());
avro_schema_union_append(schema, avro_schema_int());
avro_schema_union_append(schema, avro_schema_null());
datum = avro_givestring("Follow your bliss.", NULL);
union_datum = avro_union(schema, 0, datum);
if (avro_union_discriminant(union_datum) != 0) {
fprintf(stderr, "Unexpected union discriminant\n");
exit(EXIT_FAILURE);
}
if (avro_union_current_branch(union_datum) != datum) {
fprintf(stderr, "Unexpected union branch datum\n");
exit(EXIT_FAILURE);
}
union_datum1 = avro_datum_from_schema(schema);
avro_union_set_discriminant(union_datum1, 0, &datum1);
avro_givestring_set(datum1, "Follow your bliss.", NULL);
if (!avro_datum_equal(datum, datum1)) {
fprintf(stderr, "Union values should be equal\n");
exit(EXIT_FAILURE);
}
write_read_check(schema, union_datum, NULL, NULL, "union");
test_json(union_datum, "{\"string\": \"Follow your bliss.\"}");
avro_datum_decref(datum);
avro_union_set_discriminant(union_datum, 2, &datum);
test_json(union_datum, "null");
avro_datum_decref(union_datum);
avro_datum_decref(datum);
avro_datum_decref(union_datum1);
avro_schema_decref(schema);
return 0;
}
static int test_fixed(void)
{
char bytes[] = { 0xD, 0xA, 0xD, 0xA, 0xB, 0xA, 0xB, 0xA };
avro_schema_t schema = avro_schema_fixed("msg", sizeof(bytes));
avro_datum_t datum;
avro_datum_t expected_datum;
datum = avro_givefixed(schema, bytes, sizeof(bytes), NULL);
write_read_check(schema, datum, NULL, NULL, "fixed");
test_json(datum, "\"\\r\\n\\r\\n\\u000b\\n\\u000b\\n\"");
avro_datum_decref(datum);
datum = avro_givefixed(schema, NULL, sizeof(bytes), NULL);
avro_givefixed_set(datum, bytes, sizeof(bytes), NULL);
expected_datum = avro_givefixed(schema, bytes, sizeof(bytes), NULL);
if (!avro_datum_equal(datum, expected_datum)) {
fprintf(stderr,
"Expected equal fixed instances.\n");
exit(EXIT_FAILURE);
}
avro_datum_decref(datum);
avro_datum_decref(expected_datum);
// The following should bork if we don't copy the fixed value
// correctly (since we'll try to free a static string).
datum = avro_fixed(schema, "original", 8);
avro_fixed_set(datum, "alsothis", 8);
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}
int main(void)
{
avro_set_allocator(test_allocator, NULL);
unsigned int i;
struct avro_tests {
char *name;
avro_test func;
} tests[] = {
{
"string", test_string}, {
"bytes", test_bytes}, {
"int", test_int32}, {
"long", test_int64}, {
"float", test_float}, {
"double", test_double}, {
"boolean", test_boolean}, {
"null", test_null}, {
"record", test_record}, {
"nested_record", test_nested_record}, {
"enum", test_enum}, {
"array", test_array}, {
"map", test_map}, {
"fixed", test_fixed}, {
"union", test_union}
};
init_rand();
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
struct avro_tests *test = tests + i;
fprintf(stderr, "**** Running %s tests ****\n", test->name);
if (test->func() != 0) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}