blob: a35e69f132f983c4158e6f8aea134fde6b50e52d [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.
*/
/* Test cases for the new avro_value_t interface */
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "avro.h"
#include "avro_private.h"
typedef int (*avro_test) (void);
#ifndef SHOW_ALLOCATIONS
#define SHOW_ALLOCATIONS 0
#endif
/*
* 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 SHOW_ALLOCATIONS
fprintf(stderr, "alloc(%p, %" PRIsz ", %" PRIsz ") => ", ptr, osize, nsize);
#endif
if (nsize == 0) {
size_t *size = ((size_t *) ptr) - 1;
if (osize != *size) {
fprintf(stderr,
#if SHOW_ALLOCATIONS
"ERROR!\n"
#endif
"Error freeing %p:\n"
"Size passed to avro_free (%" PRIsz ") "
"doesn't match size passed to "
"avro_malloc (%" PRIsz ")\n",
ptr, osize, *size);
exit(EXIT_FAILURE);
}
free(size);
#if SHOW_ALLOCATIONS
fprintf(stderr, "NULL\n");
#endif
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;
#if SHOW_ALLOCATIONS
fprintf(stderr, "%p\n", (size+1));
#endif
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);
}
size_t
rand_count(void)
{
return (size_t) rand_number(0, 100);
}
#define check_(call) \
do { \
int _rval = call; \
if (_rval) { return _rval; } \
} while (0)
/*
* Verify that we can't call any of the getters and setters that don't
* apply to the given value.
*/
static int
_check_invalid_methods(const char *name, avro_value_t *val)
{
avro_type_t type = avro_value_get_type(val);
/* For a description on GCC vs Visual Studio 2008 usage of variadic
* macros see:
* http://stackoverflow.com/questions/2575864/the-problem-about-different
* -treatment-to-va-args-when-using-vs-2008-and-gcc
*/
#define expand_args(...) __VA_ARGS__
#define check_bad(method, ...) \
do { \
if (!expand_args(avro_value_##method(__VA_ARGS__))) { \
fprintf(stderr, \
"Shouldn't be able to " #method " a %s\n", \
name); \
return EXIT_FAILURE; \
} \
} while (0)
if (type != AVRO_BOOLEAN) {
int dummy = 0;
check_bad(get_boolean, val, &dummy);
check_bad(set_boolean, val, dummy);
}
if (type != AVRO_BYTES) {
const void *cbuf = NULL;
void *buf = NULL;
size_t size = 0;
check_bad(get_bytes, val, &cbuf, &size);
check_bad(set_bytes, val, buf, size);
}
if (type != AVRO_DOUBLE) {
double dummy = 0;
check_bad(get_double, val, &dummy);
check_bad(set_double, val, dummy);
}
if (type != AVRO_FLOAT) {
float dummy = 0;
check_bad(get_float, val, &dummy);
check_bad(set_float, val, dummy);
}
if (type != AVRO_INT32) {
int32_t dummy = 0;
check_bad(get_int, val, &dummy);
check_bad(set_int, val, dummy);
}
if (type != AVRO_INT64) {
int64_t dummy = 0;
check_bad(get_long, val, &dummy);
check_bad(set_long, val, dummy);
}
if (type != AVRO_NULL) {
check_bad(get_null, val);
check_bad(set_null, val);
}
if (type != AVRO_STRING) {
const char *cstr = NULL;
char *str = NULL;
size_t size = 0;
check_bad(get_string, val, &cstr, &size);
check_bad(set_string, val, str);
check_bad(set_string_len, val, str, size);
}
if (type != AVRO_ENUM) {
int dummy = 0;
check_bad(get_enum, val, &dummy);
check_bad(set_enum, val, dummy);
}
if (type != AVRO_FIXED) {
const void *cbuf = NULL;
void *buf = NULL;
size_t size = 0;
check_bad(get_fixed, val, &cbuf, &size);
check_bad(set_fixed, val, buf, size);
}
if (type != AVRO_ARRAY && type != AVRO_MAP && type != AVRO_RECORD) {
size_t size = 0;
check_bad(get_size, val, &size);
size_t index = 0;
avro_value_t child;
const char *key = NULL;
check_bad(get_by_index, val, index, &child, &key);
}
if (type != AVRO_MAP && type != AVRO_RECORD) {
const char *key = NULL;
avro_value_t child;
size_t index = 0;
check_bad(get_by_name, val, key, &child, &index);
}
if (type != AVRO_ARRAY) {
avro_value_t child;
size_t index;
check_bad(append, val, &child, &index);
}
if (type != AVRO_MAP) {
const char *key = NULL;
avro_value_t child;
size_t index = 0;
int is_new = 0;
check_bad(add, val, key, &child, &index, &is_new);
}
if (type != AVRO_UNION) {
int discriminant = 0;
avro_value_t branch;
check_bad(get_discriminant, val, &discriminant);
check_bad(get_current_branch, val, &branch);
check_bad(set_branch, val, discriminant, &branch);
}
#undef check_bad
return EXIT_SUCCESS;
}
#define check_invalid_methods(name, val) \
check_(_check_invalid_methods(name, val))
/*
* Verify that we get the expected type code and schema for a value.
*/
static int
check_type_and_schema(const char *name,
avro_value_t *val,
avro_type_t expected_type,
avro_schema_t expected_schema)
{
if (avro_value_get_type(val) != expected_type) {
avro_schema_decref(expected_schema);
fprintf(stderr, "Unexpected type for %s\n", name);
return EXIT_FAILURE;
}
if (!avro_schema_equal(avro_value_get_schema(val),
expected_schema)) {
avro_schema_decref(expected_schema);
fprintf(stderr, "Unexpected schema for %s\n", name);
return EXIT_FAILURE;
}
avro_schema_decref(expected_schema);
return EXIT_SUCCESS;
}
#define try(call, msg) \
do { \
if (call) { \
fprintf(stderr, msg ":\n %s\n", avro_strerror()); \
return EXIT_FAILURE; \
} \
} while (0)
static int
_check_write_read(avro_value_t *val)
{
static char buf[4096];
avro_reader_t reader = avro_reader_memory(buf, sizeof(buf));
avro_writer_t writer = avro_writer_memory(buf, sizeof(buf));
if (avro_value_write(writer, val)) {
fprintf(stderr, "Unable to write value:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
avro_writer_dump(writer, stderr);
size_t size;
if (avro_value_sizeof(val, &size)) {
fprintf(stderr, "Unable to determine size of value:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
if (size != (size_t) avro_writer_tell(writer)) {
fprintf(stderr, "Unexpected size of encoded value\n");
return EXIT_FAILURE;
}
avro_value_t val_in;
if (avro_generic_value_new(val->iface, &val_in)) {
fprintf(stderr, "Cannot allocate new value instance:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
if (avro_value_read(reader, &val_in)) {
fprintf(stderr, "Unable to read value:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
if (!avro_value_equal(val, &val_in)) {
fprintf(stderr, "Round-trip values not equal\n");
exit(EXIT_FAILURE);
}
avro_value_decref(&val_in);
avro_reader_free(reader);
avro_writer_free(writer);
return EXIT_SUCCESS;
}
#define check_write_read(val) \
check_(_check_write_read(val))
static int
_check_hash(avro_value_t *val1, avro_value_t *val2)
{
uint32_t hash1 = avro_value_hash(val1);
uint32_t hash2 = avro_value_hash(val2);
if (hash1 != hash2) {
fprintf(stderr, "Copied hashed not equal\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#define check_hash(val1, val2) \
check_(_check_hash(val1, val2))
static int
_check_copy(avro_value_t *val)
{
avro_value_t copied_val;
if (avro_generic_value_new(val->iface, &copied_val)) {
fprintf(stderr, "Cannot allocate new value instance:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
if (avro_value_copy_fast(&copied_val, val)) {
fprintf(stderr, "Cannot copy value:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
if (!avro_value_equal(val, &copied_val)) {
fprintf(stderr, "Copied values not equal\n");
return EXIT_FAILURE;
}
check_hash(val, &copied_val);
avro_value_decref(&copied_val);
return EXIT_SUCCESS;
}
#define check_copy(val) \
check_(_check_copy(val))
static int
test_boolean(void)
{
int rval;
int i;
for (i = 0; i <= 1; i++) {
avro_value_t val;
try(avro_generic_boolean_new(&val, i),
"Cannot create boolean");
check(rval, check_type_and_schema
("boolean", &val,
AVRO_BOOLEAN, avro_schema_boolean()));
try(avro_value_reset(&val),
"Cannot reset boolean");
try(avro_value_set_boolean(&val, i),
"Cannot set boolean");
/* Start with the wrong value to make sure _get does
* something. */
int actual = (int) 23;
try(avro_value_get_boolean(&val, &actual),
"Cannot get boolean value");
if (actual != i) {
fprintf(stderr, "Unexpected boolean value\n");
return EXIT_FAILURE;
}
check_invalid_methods("boolean", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
avro_value_t val1;
avro_value_t val2;
try(avro_generic_boolean_new(&val1, 0),
"Cannot create boolean");
try(avro_generic_boolean_new(&val2, 1),
"Cannot create boolean");
if (avro_value_cmp_fast(&val1, &val2) >= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val2, &val1) <= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val1, &val1) != 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
avro_value_decref(&val1);
avro_value_decref(&val2);
return 0;
}
static int
test_bytes(void)
{
int rval;
char bytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
avro_value_t val;
try(avro_generic_bytes_new(&val, bytes, sizeof(bytes)),
"Cannot create bytes");
check(rval, check_type_and_schema
("bytes", &val,
AVRO_BYTES, avro_schema_bytes()));
try(avro_value_reset(&val),
"Cannot reset bytes");
try(avro_value_set_bytes(&val, bytes, sizeof(bytes)),
"Cannot set bytes");
const void *actual_buf = NULL;
size_t actual_size = 0;
try(avro_value_get_bytes(&val, &actual_buf, &actual_size),
"Cannot get bytes value");
if (actual_size != sizeof(bytes)) {
fprintf(stderr, "Unexpected bytes size\n");
return EXIT_FAILURE;
}
if (memcmp(actual_buf, bytes, sizeof(bytes)) != 0) {
fprintf(stderr, "Unexpected bytes contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_t wbuf;
try(avro_value_grab_bytes(&val, &wbuf),
"Cannot grab bytes value");
if (wbuf.size != sizeof(bytes)) {
fprintf(stderr, "Unexpected grabbed bytes size\n");
return EXIT_FAILURE;
}
if (memcmp(wbuf.buf, bytes, sizeof(bytes)) != 0) {
fprintf(stderr, "Unexpected grabbed bytes contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_free(&wbuf);
check_invalid_methods("bytes", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
avro_value_t val1;
avro_value_t val2;
avro_value_t val3;
try(avro_generic_bytes_new(&val1, "abcd", 4),
"Cannot create bytes");
try(avro_generic_bytes_new(&val2, "abcde", 5),
"Cannot create bytes");
try(avro_generic_bytes_new(&val3, "abce", 4),
"Cannot create bytes");
if (avro_value_cmp_fast(&val1, &val2) >= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val2, &val1) <= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val1, &val3) >= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val1, &val1) != 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
avro_value_decref(&val1);
avro_value_decref(&val2);
avro_value_decref(&val3);
return 0;
}
static int
test_double(void)
{
int rval;
int i;
for (i = 0; i < 100; i++) {
double expected = rand_number(-1e10, 1e10);
avro_value_t val;
try(avro_generic_double_new(&val, expected),
"Cannot create double");
check(rval, check_type_and_schema
("double", &val,
AVRO_DOUBLE, avro_schema_double()));
try(avro_value_reset(&val),
"Cannot reset double");
try(avro_value_set_double(&val, expected),
"Cannot set double");
double actual = 0.0;
try(avro_value_get_double(&val, &actual),
"Cannot get double value");
if (actual != expected) {
fprintf(stderr, "Unexpected double value\n");
return EXIT_FAILURE;
}
check_invalid_methods("double", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
return 0;
}
static int
test_float(void)
{
int rval;
int i;
for (i = 0; i < 100; i++) {
float expected = rand_number(-1e10, 1e10);
avro_value_t val;
try(avro_generic_float_new(&val, expected),
"Cannot create float");
check(rval, check_type_and_schema
("float", &val,
AVRO_FLOAT, avro_schema_float()));
try(avro_value_reset(&val),
"Cannot reset float");
try(avro_value_set_float(&val, expected),
"Cannot set float");
float actual = 0.0f;
try(avro_value_get_float(&val, &actual),
"Cannot get float value");
if (actual != expected) {
fprintf(stderr, "Unexpected float value\n");
return EXIT_FAILURE;
}
check_invalid_methods("float", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
return 0;
}
static int
test_int(void)
{
int rval;
int i;
for (i = 0; i < 100; i++) {
int32_t expected = rand_int32();
avro_value_t val;
try(avro_generic_int_new(&val, expected),
"Cannot create int");
check(rval, check_type_and_schema
("int", &val,
AVRO_INT32, avro_schema_int()));
try(avro_value_reset(&val),
"Cannot reset int");
try(avro_value_set_int(&val, expected),
"Cannot set int");
int32_t actual = 0;
try(avro_value_get_int(&val, &actual),
"Cannot get int value");
if (actual != expected) {
fprintf(stderr, "Unexpected int value\n");
return EXIT_FAILURE;
}
check_invalid_methods("int", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
avro_value_t val1;
avro_value_t val2;
try(avro_generic_int_new(&val1, -10),
"Cannot create int");
try(avro_generic_int_new(&val2, 42),
"Cannot create int");
if (avro_value_cmp_fast(&val1, &val2) >= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val2, &val1) <= 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
if (avro_value_cmp_fast(&val1, &val1) != 0) {
fprintf(stderr, "Incorrect sort order\n");
return EXIT_FAILURE;
}
avro_value_decref(&val1);
avro_value_decref(&val2);
return 0;
}
static int
test_long(void)
{
int rval;
int i;
for (i = 0; i < 100; i++) {
int64_t expected = rand_int64();
avro_value_t val;
try(avro_generic_long_new(&val, expected),
"Cannot create long");
check(rval, check_type_and_schema
("long", &val,
AVRO_INT64, avro_schema_long()));
try(avro_value_reset(&val),
"Cannot reset long");
try(avro_value_set_long(&val, expected),
"Cannot set long");
int64_t actual = 0;
try(avro_value_get_long(&val, &actual),
"Cannot get long value");
if (actual != expected) {
fprintf(stderr, "Unexpected long value\n");
return EXIT_FAILURE;
}
check_invalid_methods("long", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
return 0;
}
static int
test_null(void)
{
int rval;
avro_value_t val;
try(avro_generic_null_new(&val),
"Cannot create null");
check(rval, check_type_and_schema
("null", &val,
AVRO_NULL, avro_schema_null()));
try(avro_value_reset(&val),
"Cannot reset null");
try(avro_value_set_null(&val),
"Cannot set null");
try(avro_value_get_null(&val),
"Cannot get null");
check_invalid_methods("null", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
return 0;
}
static int
test_string(void)
{
int rval;
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."
};
unsigned int i;
for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i++) {
avro_value_t val;
try(avro_generic_string_new(&val, strings[i]),
"Cannot create string");
check(rval, check_type_and_schema
("string", &val,
AVRO_STRING, avro_schema_string()));
try(avro_value_reset(&val),
"Cannot reset string");
try(avro_value_set_string_len(&val, "", 0),
"Cannot set_len dummy string");
/* First try a round-trip using set_string */
try(avro_value_set_string(&val, strings[i]),
"Cannot set string");
const char *actual_str = NULL;
size_t actual_size = 0;
try(avro_value_get_string(&val, &actual_str, &actual_size),
"Cannot get string value");
if (actual_size != strlen(strings[i])+1) {
fprintf(stderr, "Unexpected string size\n");
return EXIT_FAILURE;
}
if (strcmp(actual_str, strings[i]) != 0) {
fprintf(stderr, "Unexpected string contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_t wbuf;
try(avro_value_grab_string(&val, &wbuf),
"Cannot grab string value");
if (wbuf.size != strlen(strings[i])+1) {
fprintf(stderr, "Unexpected grabbed string size\n");
return EXIT_FAILURE;
}
if (strcmp((const char *) wbuf.buf, strings[i]) != 0) {
fprintf(stderr, "Unexpected grabbed string contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_free(&wbuf);
/* and then again using set_string_len */
size_t str_length = strlen(strings[i])+1;
try(avro_value_set_string_len(&val, strings[i], str_length),
"Cannot set_len string");
actual_str = NULL;
actual_size = 0;
try(avro_value_get_string(&val, &actual_str, &actual_size),
"Cannot get string value");
if (actual_size != strlen(strings[i])+1) {
fprintf(stderr, "Unexpected string size\n");
return EXIT_FAILURE;
}
if (strcmp(actual_str, strings[i]) != 0) {
fprintf(stderr, "Unexpected string contents\n");
return EXIT_FAILURE;
}
try(avro_value_grab_string(&val, &wbuf),
"Cannot grab string value");
if (wbuf.size != strlen(strings[i])+1) {
fprintf(stderr, "Unexpected grabbed string size\n");
return EXIT_FAILURE;
}
if (strcmp((const char *) wbuf.buf, strings[i]) != 0) {
fprintf(stderr, "Unexpected grabbed string contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_free(&wbuf);
check_invalid_methods("string", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
return 0;
}
static int
test_array(void)
{
avro_schema_t double_schema = avro_schema_double();
avro_schema_t array_schema = avro_schema_array(double_schema);
avro_value_iface_t *array_class =
avro_generic_class_from_schema(array_schema);
int rval;
int i;
for (i = 0; i < 100; i++) {
size_t count = rand_count();
avro_value_t val;
try(avro_generic_value_new(array_class, &val),
"Cannot create array");
check(rval, check_type_and_schema
("array", &val, AVRO_ARRAY,
avro_schema_incref(array_schema)));
size_t j;
for (j = 0; j < count; j++) {
avro_value_t element;
size_t new_index;
try(avro_value_append(&val, &element, &new_index),
"Cannot append to array");
if (new_index != j) {
fprintf(stderr, "Unexpected index\n");
return EXIT_FAILURE;
}
double expected = rand_number(-1e10, 1e10);
try(avro_value_set_double(&element, expected),
"Cannot set double");
try(avro_value_get_by_index(&val, j, &element, NULL),
"Cannot get from array");
double actual = 0.0;
try(avro_value_get_double(&element, &actual),
"Cannot get double value");
if (actual != expected) {
fprintf(stderr, "Unexpected double value\n");
return EXIT_FAILURE;
}
}
size_t actual_size = 0;
try(avro_value_get_size(&val, &actual_size),
"Cannot get_size array");
if (actual_size != count) {
fprintf(stderr, "Unexpected size\n");
return EXIT_FAILURE;
}
check_write_read(&val);
check_copy(&val);
try(avro_value_reset(&val),
"Cannot reset array");
try(avro_value_get_size(&val, &actual_size),
"Cannot get_size empty array");
if (actual_size != 0) {
fprintf(stderr, "Unexpected empty size\n");
return EXIT_FAILURE;
}
check_invalid_methods("array", &val);
avro_value_decref(&val);
}
avro_schema_decref(double_schema);
avro_schema_decref(array_schema);
avro_value_iface_decref(array_class);
return 0;
}
static int
test_enum(void)
{
static const char SCHEMA_JSON[] =
"{"
" \"type\": \"enum\","
" \"name\": \"suits\","
" \"symbols\": [\"CLUBS\",\"DIAMONDS\",\"HEARTS\",\"SPADES\"]"
"}";
avro_schema_t enum_schema = NULL;
if (avro_schema_from_json_literal(SCHEMA_JSON, &enum_schema)) {
fprintf(stderr, "Error parsing schema:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
avro_value_iface_t *enum_class =
avro_generic_class_from_schema(enum_schema);
int rval;
int i;
for (i = 0; i < 4; i++) {
int expected = i;
avro_value_t val;
try(avro_generic_value_new(enum_class, &val),
"Cannot create enum");
check(rval, check_type_and_schema
("enum", &val, AVRO_ENUM,
avro_schema_incref(enum_schema)));
try(avro_value_reset(&val),
"Cannot reset enum");
try(avro_value_set_enum(&val, expected),
"Cannot set enum");
int actual = -1;
try(avro_value_get_enum(&val, &actual),
"Cannot get enum value");
if (actual != expected) {
fprintf(stderr, "Unexpected enum value\n");
return EXIT_FAILURE;
}
check_invalid_methods("enum", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
}
avro_schema_decref(enum_schema);
avro_value_iface_decref(enum_class);
return 0;
}
static int
test_fixed(void)
{
static const char SCHEMA_JSON[] =
"{"
" \"type\": \"fixed\","
" \"name\": \"ipv4\","
" \"size\": 4"
"}";
avro_schema_t fixed_schema = NULL;
if (avro_schema_from_json_literal(SCHEMA_JSON, &fixed_schema)) {
fprintf(stderr, "Error parsing schema:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
avro_value_iface_t *fixed_class =
avro_generic_class_from_schema(fixed_schema);
int rval;
char fixed[] = { 0xDE, 0xAD, 0xBE, 0xEF };
avro_value_t val;
try(avro_generic_value_new(fixed_class, &val),
"Cannot create fixed");
check(rval, check_type_and_schema
("fixed", &val, AVRO_FIXED,
avro_schema_incref(fixed_schema)));
try(avro_value_reset(&val),
"Cannot reset fixed");
/* verify an error on invalid size */
try(!avro_value_set_fixed(&val, fixed, 0),
"Expected error with invalid size");
try(avro_value_set_fixed(&val, fixed, sizeof(fixed)),
"Cannot set fixed");
const void *actual_buf = NULL;
size_t actual_size = 0;
try(avro_value_get_fixed(&val, &actual_buf, &actual_size),
"Cannot get fixed value");
if (actual_size != sizeof(fixed)) {
fprintf(stderr, "Unexpected fixed size\n");
return EXIT_FAILURE;
}
if (memcmp(actual_buf, fixed, sizeof(fixed)) != 0) {
fprintf(stderr, "Unexpected fixed contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_t wbuf;
try(avro_value_grab_fixed(&val, &wbuf),
"Cannot grab fixed value");
if (wbuf.size != sizeof(fixed)) {
fprintf(stderr, "Unexpected grabbed fixed size\n");
return EXIT_FAILURE;
}
if (memcmp(wbuf.buf, fixed, sizeof(fixed)) != 0) {
fprintf(stderr, "Unexpected grabbed fixed contents\n");
return EXIT_FAILURE;
}
avro_wrapped_buffer_free(&wbuf);
check_invalid_methods("fixed", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
avro_schema_decref(fixed_schema);
avro_value_iface_decref(fixed_class);
return 0;
}
static int
test_map(void)
{
avro_schema_t double_schema = avro_schema_double();
avro_schema_t map_schema = avro_schema_map(double_schema);
avro_value_iface_t *map_class =
avro_generic_class_from_schema(map_schema);
int rval;
int i;
for (i = 0; i < 100; i++) {
size_t count = rand_count();
avro_value_t val;
try(avro_generic_value_new(map_class, &val),
"Cannot create map");
check(rval, check_type_and_schema
("map", &val, AVRO_MAP,
avro_schema_incref(map_schema)));
size_t j;
for (j = 0; j < count; j++) {
avro_value_t element;
size_t new_index;
int is_new = 0;
char key[64];
snprintf(key, 64, "%" PRIsz, j);
try(avro_value_add(&val, key,
&element, &new_index, &is_new),
"Cannot add to map");
if (new_index != j) {
fprintf(stderr, "Unexpected index\n");
return EXIT_FAILURE;
}
if (!is_new) {
fprintf(stderr, "Expected new element\n");
return EXIT_FAILURE;
}
double expected = rand_number(-1e10, 1e10);
try(avro_value_set_double(&element, expected),
"Cannot set double");
try(avro_value_add(&val, key,
&element, &new_index, &is_new),
"Cannot re-add to map");
if (is_new) {
fprintf(stderr, "Expected non-new element\n");
return EXIT_FAILURE;
}
const char *actual_key = NULL;
try(avro_value_get_by_index(&val, j, &element,
&actual_key),
"Cannot get from map");
if (strcmp(actual_key, key) != 0) {
fprintf(stderr, "Unexpected key\n");
return EXIT_FAILURE;
}
double actual = 0.0;
try(avro_value_get_double(&element, &actual),
"Cannot get double value");
if (actual != expected) {
fprintf(stderr, "Unexpected double value\n");
return EXIT_FAILURE;
}
}
size_t actual_size = 0;
try(avro_value_get_size(&val, &actual_size),
"Cannot get_size map");
if (actual_size != count) {
fprintf(stderr, "Unexpected size\n");
return EXIT_FAILURE;
}
/*
* Create a reversed copy of the map to ensure that the
* element ordering doesn't affect the hash value.
*/
avro_value_t reversed;
try(avro_generic_value_new(map_class, &reversed),
"Cannot create map");
for (j = count; j-- > 0; ) {
avro_value_t element;
const char *key = NULL;
double element_value = 0.0;
try(avro_value_get_by_index(&val, j, &element, &key),
"Cannot get from map");
try(avro_value_get_double(&element, &element_value),
"Cannot get double value");
try(avro_value_add(&reversed, key, &element, NULL, NULL),
"Cannot add to map");
try(avro_value_set_double(&element, element_value),
"Cannot set double");
}
check_hash(&val, &reversed);
if (!avro_value_equal(&val, &reversed)) {
fprintf(stderr, "Reversed values not equal\n");
return EXIT_FAILURE;
}
/* Final tests and cleanup */
check_write_read(&val);
check_copy(&val);
try(avro_value_reset(&val),
"Cannot reset map");
try(avro_value_get_size(&val, &actual_size),
"Cannot get_size empty map");
if (actual_size != 0) {
fprintf(stderr, "Unexpected empty size\n");
return EXIT_FAILURE;
}
check_invalid_methods("map", &val);
avro_value_decref(&val);
avro_value_decref(&reversed);
}
avro_schema_decref(double_schema);
avro_schema_decref(map_schema);
avro_value_iface_decref(map_class);
return 0;
}
static int
test_record(void)
{
static const char SCHEMA_JSON[] =
"{"
" \"type\": \"record\","
" \"name\": \"test\","
" \"fields\": ["
" { \"name\": \"b\", \"type\": \"boolean\" },"
" { \"name\": \"i\", \"type\": \"int\" },"
" { \"name\": \"s\", \"type\": \"string\" },"
" { \"name\": \"ds\", \"type\": "
" { \"type\": \"array\", \"items\": \"double\" } },"
" { \"name\": \"sub\", \"type\": "
" {"
" \"type\": \"record\","
" \"name\": \"subtest\","
" \"fields\": ["
" { \"name\": \"f\", \"type\": \"float\" },"
" { \"name\": \"l\", \"type\": \"long\" }"
" ]"
" }"
" },"
" { \"name\": \"nested\", \"type\": [\"null\", \"test\"] }"
" ]"
"}";
avro_schema_t record_schema = NULL;
if (avro_schema_from_json_literal(SCHEMA_JSON, &record_schema)) {
fprintf(stderr, "Error parsing schema:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
avro_value_iface_t *record_class =
avro_generic_class_from_schema(record_schema);
int rval;
avro_value_t val;
try(avro_generic_value_new(record_class, &val),
"Cannot create record");
check(rval, check_type_and_schema
("record", &val, AVRO_RECORD,
avro_schema_incref(record_schema)));
size_t field_count;
try(avro_value_get_size(&val, &field_count),
"Cannot get field count");
if (field_count != 6) {
fprintf(stderr, "Unexpected field count\n");
return EXIT_FAILURE;
}
/* Assign to each field */
avro_value_t field;
avro_value_t element;
avro_value_t subfield;
avro_value_t branch;
const char *name;
size_t index;
try(avro_value_get_by_index(&val, 0, &field, NULL),
"Cannot get field 0");
try(avro_value_set_boolean(&field, 1),
"Cannot set field 0");
try(avro_value_get_by_index(&val, 1, &field, &name),
"Cannot get field 1");
try(avro_value_set_int(&field, 42),
"Cannot set field 1");
if (strcmp(name, "i") != 0) {
fprintf(stderr, "Unexpected name for field 1: %s\n", name);
return EXIT_FAILURE;
}
try(avro_value_get_by_index(&val, 2, &field, NULL),
"Cannot get field 2");
try(avro_value_set_string(&field, "Hello world!"),
"Cannot set field 2");
try(avro_value_get_by_name(&val, "i", &field, &index),
"Cannot get \"i\" field");
if (index != 1) {
fprintf(stderr, "Unexpected index for \"i\" field: %" PRIsz "\n", index);
return EXIT_FAILURE;
}
try(avro_value_get_by_index(&val, 3, &field, NULL),
"Cannot get field 3");
try(avro_value_append(&field, &element, NULL),
"Cannot append to field 3");
try(avro_value_set_double(&element, 10.0),
"Cannot set field 3, element 0");
try(avro_value_get_by_index(&val, 4, &field, NULL),
"Cannot get field 4");
try(avro_value_get_by_index(&field, 0, &subfield, NULL),
"Cannot get field 4, subfield 0");
try(avro_value_set_float(&subfield, 5.0f),
"Cannot set field 4, subfield 0");
try(avro_value_get_by_index(&field, 1, &subfield, NULL),
"Cannot get field 4, subfield 1");
try(avro_value_set_long(&subfield, 10000),
"Cannot set field 4, subfield 1");
try(avro_value_get_by_index(&val, 5, &field, NULL),
"Cannot get field 5");
try(avro_value_set_branch(&field, 0, &branch),
"Cannot select null branch");
check_write_read(&val);
check_copy(&val);
/* Reset and verify that the fields are empty again */
try(avro_value_reset(&val),
"Cannot reset record");
int bval;
try(avro_value_get_by_index(&val, 0, &field, NULL),
"Cannot get field 0");
try(avro_value_get_boolean(&field, &bval),
"Cannot get field 0 value");
if (bval) {
fprintf(stderr, "Unexpected value for field 0\n");
return EXIT_FAILURE;
}
size_t count;
try(avro_value_get_by_index(&val, 3, &field, NULL),
"Cannot get field 3");
try(avro_value_get_size(&field, &count),
"Cannot get field 3 size");
if (count != 0) {
fprintf(stderr, "Unexpected size for field 3\n");
return EXIT_FAILURE;
}
check_invalid_methods("record", &val);
avro_value_decref(&val);
avro_value_iface_decref(record_class);
avro_schema_decref(record_schema);
return EXIT_SUCCESS;
}
static int
test_union(void)
{
static const char SCHEMA_JSON[] =
"["
" \"null\","
" \"int\","
" \"double\","
" \"bytes\""
"]";
avro_schema_t union_schema = NULL;
if (avro_schema_from_json_literal(SCHEMA_JSON, &union_schema)) {
fprintf(stderr, "Error parsing schema:\n %s\n",
avro_strerror());
return EXIT_FAILURE;
}
avro_value_iface_t *union_class =
avro_generic_class_from_schema(union_schema);
int rval;
avro_value_t val;
try(avro_generic_value_new(union_class, &val),
"Cannot create union");
check(rval, check_type_and_schema
("union", &val, AVRO_UNION,
avro_schema_incref(union_schema)));
int discriminant = 0;
try(avro_value_get_discriminant(&val, &discriminant),
"Cannot get union discriminant");
if (discriminant != -1) {
fprintf(stderr, "Unexpected union discriminant\n");
return EXIT_FAILURE;
}
avro_value_t branch;
try(!avro_value_get_current_branch(&val, &branch),
"Expected error getting empty current branch");
try(avro_value_set_branch(&val, 0, &branch),
"Cannot select null branch");
try(avro_value_set_null(&branch),
"Cannot set null branch value");
try(avro_value_set_branch(&val, 1, &branch),
"Cannot select int branch");
try(avro_value_set_int(&branch, 42),
"Cannot set int branch value");
try(avro_value_set_branch(&val, 1, &branch),
"Cannot select int branch");
try(avro_value_set_int(&branch, 10),
"Cannot set int branch value");
try(avro_value_set_branch(&val, 2, &branch),
"Cannot select double branch");
try(avro_value_set_double(&branch, 10.0),
"Cannot set double branch value");
char bytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
try(avro_value_set_branch(&val, 3, &branch),
"Cannot select bytes branch");
try(avro_value_set_bytes(&branch, bytes, sizeof(bytes)),
"Cannot set bytes branch value");
check_invalid_methods("union", &val);
check_write_read(&val);
check_copy(&val);
avro_value_decref(&val);
avro_schema_decref(union_schema);
avro_value_iface_decref(union_class);
return 0;
}
int main(void)
{
avro_set_allocator(test_allocator, NULL);
unsigned int i;
struct avro_tests {
char *name;
avro_test func;
} tests[] = {
{ "boolean", test_boolean },
{ "bytes", test_bytes },
{ "double", test_double },
{ "float", test_float },
{ "int", test_int },
{ "long", test_long },
{ "null", test_null },
{ "string", test_string },
{ "array", test_array },
{ "enum", test_enum },
{ "fixed", test_fixed },
{ "map", test_map },
{ "record", test_record },
{ "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;
}