src.dualinventive.com/dinet/sec-multi-proxy/libdi/3rdparty/mpack/test/test-file.c

742 lines
26 KiB
C

#include "test-file.h"
#include "test-write.h"
#include "test-reader.h"
#include "test-node.h"
#if MPACK_STDIO
// the file tests currently all require the writer, since it
// is used to write the test data that is read back.
#if MPACK_WRITER
#ifdef WIN32
#define TEST_PATH "..\\..\\test\\"
#else
#include <unistd.h>
#define TEST_PATH "test/"
#endif
static const char* test_blank_filename = "mpack-test-blank-file";
static const char* test_filename = "mpack-test-file";
static const char* test_dir = "mpack-test-dir";
static const int nesting_depth = 150;
static const char* lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec justo purus. Nunc finibus dolor id lorem sagittis, euismod efficitur arcu aliquam. Nullam a ante eget mi porttitor dignissim vitae at libero. Maecenas in justo massa. Mauris ultricies leo nisl, at ullamcorper erat maximus sit amet. Quisque pharetra sed ligula nec tristique. Mauris consectetur sapien lacus, et pharetra turpis rhoncus a. Sed in eleifend eros. Donec in libero lacus. Sed et finibus ipsum. Etiam eros leo, mollis eget molestie quis, rhoncus ac magna. Donec dolor risus, bibendum et scelerisque at, faucibus in mi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum convallis accumsan mollis.";
static const char* quick_brown_fox = "The quick brown fox jumps over a lazy dog.";
static char* test_file_fetch(const char* filename, size_t* out_size) {
*out_size = 0;
// open the file
FILE* file = fopen(filename, "rb");
if (!file) {
TEST_TRUE(false, "missing file %s", filename);
return NULL;
}
// get the file size
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
if (size < 0) {
TEST_TRUE(false, "invalid file size %i for %s", (int)size, filename);
fclose(file);
return NULL;
}
// allocate the data
if (size == 0) {
fclose(file);
return (char*)MPACK_MALLOC(1);
}
char* data = (char*)MPACK_MALLOC((size_t)size);
// read the file
long total = 0;
while (total < size) {
size_t count = fread(data + total, 1, (size_t)(size - total), file);
if (count <= 0) {
TEST_TRUE(false, "failed to read from file %s", filename);
fclose(file);
MPACK_FREE(data);
return NULL;
}
total += (long)count;
}
fclose(file);
*out_size = (size_t)size;
return data;
}
static void test_file_write_bytes(mpack_writer_t* writer, mpack_tag_t tag) {
mpack_write_tag(writer, tag);
char buf[1024];
memset(buf, 0, sizeof(buf));
for (; tag.v.l > sizeof(buf); tag.v.l -= (uint32_t)sizeof(buf))
mpack_write_bytes(writer, buf, sizeof(buf));
mpack_write_bytes(writer, buf, tag.v.l);
mpack_finish_type(writer, tag.type);
}
static void test_file_write_elements(mpack_writer_t* writer, mpack_tag_t tag) {
mpack_write_tag(writer, tag);
for (size_t i = 0; i < tag.v.n; ++i) {
if (tag.type == mpack_type_map)
mpack_write_nil(writer);
mpack_write_nil(writer);
}
mpack_finish_type(writer, tag.type);
}
static void test_file_write(void) {
mpack_writer_t writer;
mpack_writer_init_file(&writer, test_filename);
TEST_TRUE(mpack_writer_error(&writer) == mpack_ok, "file open failed with %s",
mpack_error_to_string(mpack_writer_error(&writer)));
mpack_start_array(&writer, 7);
// write lipsum to test a large fill/seek
mpack_write_cstr(&writer, lipsum);
// test compound types of various sizes
mpack_start_array(&writer, 5);
test_file_write_bytes(&writer, mpack_tag_str(0));
test_file_write_bytes(&writer, mpack_tag_str(INT8_MAX));
test_file_write_bytes(&writer, mpack_tag_str(UINT8_MAX));
test_file_write_bytes(&writer, mpack_tag_str(UINT8_MAX + 1));
test_file_write_bytes(&writer, mpack_tag_str(UINT16_MAX + 1));
mpack_finish_array(&writer);
mpack_start_array(&writer, 5);
test_file_write_bytes(&writer, mpack_tag_bin(0));
test_file_write_bytes(&writer, mpack_tag_bin(INT8_MAX));
test_file_write_bytes(&writer, mpack_tag_bin(UINT8_MAX));
test_file_write_bytes(&writer, mpack_tag_bin(UINT8_MAX + 1));
test_file_write_bytes(&writer, mpack_tag_bin(UINT16_MAX + 1));
mpack_finish_array(&writer);
mpack_start_array(&writer, 10);
test_file_write_bytes(&writer, mpack_tag_ext(1, 0));
test_file_write_bytes(&writer, mpack_tag_ext(1, 1));
test_file_write_bytes(&writer, mpack_tag_ext(1, 2));
test_file_write_bytes(&writer, mpack_tag_ext(1, 4));
test_file_write_bytes(&writer, mpack_tag_ext(1, 8));
test_file_write_bytes(&writer, mpack_tag_ext(1, 16));
test_file_write_bytes(&writer, mpack_tag_ext(2, INT8_MAX));
test_file_write_bytes(&writer, mpack_tag_ext(3, UINT8_MAX));
test_file_write_bytes(&writer, mpack_tag_ext(4, UINT8_MAX + 1));
test_file_write_bytes(&writer, mpack_tag_ext(5, UINT16_MAX + 1));
mpack_finish_array(&writer);
mpack_start_array(&writer, 5);
test_file_write_elements(&writer, mpack_tag_array(0));
test_file_write_elements(&writer, mpack_tag_array(INT8_MAX));
test_file_write_elements(&writer, mpack_tag_array(UINT8_MAX));
test_file_write_elements(&writer, mpack_tag_array(UINT8_MAX + 1));
test_file_write_elements(&writer, mpack_tag_array(UINT16_MAX + 1));
mpack_finish_array(&writer);
mpack_start_array(&writer, 5);
test_file_write_elements(&writer, mpack_tag_map(0));
test_file_write_elements(&writer, mpack_tag_map(INT8_MAX));
test_file_write_elements(&writer, mpack_tag_map(UINT8_MAX));
test_file_write_elements(&writer, mpack_tag_map(UINT8_MAX + 1));
test_file_write_elements(&writer, mpack_tag_map(UINT16_MAX + 1));
mpack_finish_array(&writer);
// test deep nesting
for (int i = 0; i < nesting_depth; ++i)
mpack_start_array(&writer, 1);
mpack_write_nil(&writer);
for (int i = 0; i < nesting_depth; ++i)
mpack_finish_array(&writer);
mpack_finish_array(&writer);
mpack_error_t error = mpack_writer_destroy(&writer);
TEST_TRUE(error == mpack_ok, "write failed with %s", mpack_error_to_string(error));
// test invalid filename
(void)mkdir(test_dir, 0700);
mpack_writer_init_file(&writer, test_dir);
TEST_WRITER_DESTROY_ERROR(&writer, mpack_error_io);
// test close and flush failure
// (if we write more than libc's internal FILE buffer size, fwrite()
// fails, otherwise fclose() fails. we test both here.)
mpack_writer_init_file(&writer, "/dev/full");
mpack_write_cstr(&writer, quick_brown_fox);
TEST_WRITER_DESTROY_ERROR(&writer, mpack_error_io);
int count = UINT16_MAX / 20;
mpack_writer_init_file(&writer, "/dev/full");
mpack_start_array(&writer, (uint32_t)count);
for (int i = 0; i < count; ++i)
mpack_write_cstr(&writer, quick_brown_fox);
mpack_finish_array(&writer);
TEST_WRITER_DESTROY_ERROR(&writer, mpack_error_io);
}
static bool test_file_write_failure(void) {
// The write failure test may fail with either
// mpack_error_memory or mpack_error_io. We write a
// bunch of strs and bins to test the various expect
// allocator modes.
mpack_writer_t writer;
mpack_writer_init_file(&writer, test_filename);
mpack_start_array(&writer, 2);
mpack_start_array(&writer, 6);
// write a large string near the start to cause a
// more than double buffer size growth
mpack_write_cstr(&writer, quick_brown_fox);
mpack_write_cstr(&writer, "one");
mpack_write_cstr(&writer, "two");
mpack_write_cstr(&writer, "three");
mpack_write_cstr(&writer, "four");
mpack_write_cstr(&writer, "five");
mpack_finish_array(&writer);
// test deep nesting
for (int i = 0; i < nesting_depth; ++i)
mpack_start_array(&writer, 1);
mpack_write_nil(&writer);
for (int i = 0; i < nesting_depth; ++i)
mpack_finish_array(&writer);
mpack_finish_array(&writer);
mpack_error_t error = mpack_writer_destroy(&writer);
if (error == mpack_error_io || error == mpack_error_memory)
return false;
TEST_TRUE(error == mpack_ok, "unexpected error state %i (%s)", (int)error,
mpack_error_to_string(error));
return true;
}
// compares the test filename to the expected debug output
static void test_compare_print() {
size_t expected_size;
char* expected_data = test_file_fetch(TEST_PATH "test-file.debug", &expected_size);
size_t actual_size;
char* actual_data = test_file_fetch(test_filename, &actual_size);
TEST_TRUE(actual_size == expected_size, "print length %i does not match expected length %i",
(int)actual_size, (int)expected_size);
TEST_TRUE(0 == memcmp(actual_data, expected_data, actual_size), "print does not match expected");
MPACK_FREE(expected_data);
MPACK_FREE(actual_data);
}
#if MPACK_READER
static void test_print(void) {
// miscellaneous print tests
// (we're not actually checking the output; we just want to make
// sure it doesn't crash under the below errors.)
FILE* out = fopen(test_filename, "wb");
mpack_print_file("\x91", 1, out); // truncated file
mpack_print_file("\xa1", 1, out); // truncated str
mpack_print_file("\x92\x00", 2, out); // truncated array
mpack_print_file("\x81", 1, out); // truncated map key
mpack_print_file("\x81\x00", 2, out); // truncated map value
mpack_print_file("\x90\xc0", 2, out); // extra bytes
mpack_print_file("\xca\x00\x00\x00\x00", 5, out); // float
fclose(out);
// dump MessagePack to debug file
size_t input_size;
char* input_data = test_file_fetch(TEST_PATH "test-file.mp", &input_size);
out = fopen(test_filename, "wb");
mpack_print_file(input_data, input_size, out);
fclose(out);
MPACK_FREE(input_data);
test_compare_print();
}
#endif
#if MPACK_NODE
static void test_node_print(void) {
mpack_tree_t tree;
// miscellaneous node print tests
FILE* out = fopen(test_filename, "wb");
mpack_tree_init(&tree, "\xca\x00\x00\x00\x00", 5); // float
mpack_node_print_file(mpack_tree_root(&tree), out);
mpack_tree_destroy(&tree);
fclose(out);
// dump MessagePack to debug file
mpack_tree_init_file(&tree, TEST_PATH "test-file.mp", 0);
out = fopen(test_filename, "wb");
mpack_node_print_file(mpack_tree_root(&tree), out);
fclose(out);
TEST_TRUE(mpack_ok == mpack_tree_destroy(&tree));
test_compare_print();
}
#endif
#if MPACK_READER
static void test_file_discard(void) {
mpack_reader_t reader;
mpack_reader_init_file(&reader, test_filename);
mpack_discard(&reader);
TEST_READER_DESTROY_NOERROR(&reader);
mpack_reader_init_file(&reader, test_filename);
reader.skip = NULL; // disable the skip callback to test skipping without it
mpack_discard(&reader);
TEST_READER_DESTROY_NOERROR(&reader);
}
#endif
#if MPACK_EXPECT
static void test_file_expect_bytes(mpack_reader_t* reader, mpack_tag_t tag) {
mpack_expect_tag(reader, tag);
TEST_TRUE(mpack_reader_error(reader) == mpack_ok, "got error %i (%s)", (int)mpack_reader_error(reader), mpack_error_to_string(mpack_reader_error(reader)));
char expected[1024];
memset(expected, 0, sizeof(expected));
char buf[sizeof(expected)];
while (tag.v.l > 0) {
uint32_t count = (tag.v.l < (uint32_t)sizeof(buf)) ? tag.v.l : (uint32_t)sizeof(buf);
mpack_read_bytes(reader, buf, count);
TEST_TRUE(mpack_reader_error(reader) == mpack_ok, "got error %i (%s)", (int)mpack_reader_error(reader), mpack_error_to_string(mpack_reader_error(reader)));
TEST_TRUE(memcmp(buf, expected, count) == 0, "data does not match!");
tag.v.l -= count;
}
mpack_done_type(reader, tag.type);
}
static void test_file_expect_elements(mpack_reader_t* reader, mpack_tag_t tag) {
mpack_expect_tag(reader, tag);
for (size_t i = 0; i < tag.v.l; ++i) {
if (tag.type == mpack_type_map)
mpack_expect_nil(reader);
mpack_expect_nil(reader);
}
mpack_done_type(reader, tag.type);
}
static void test_file_read(void) {
mpack_reader_t reader;
mpack_reader_init_file(&reader, test_filename);
TEST_TRUE(mpack_reader_error(&reader) == mpack_ok, "file open failed with %s",
mpack_error_to_string(mpack_reader_error(&reader)));
TEST_TRUE(7 == mpack_expect_array(&reader));
// test matching a cstr larger than the buffer size
mpack_expect_cstr_match(&reader, lipsum);
TEST_TRUE(5 == mpack_expect_array(&reader));
test_file_expect_bytes(&reader, mpack_tag_str(0));
test_file_expect_bytes(&reader, mpack_tag_str(INT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_str(UINT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_str(UINT8_MAX + 1));
test_file_expect_bytes(&reader, mpack_tag_str(UINT16_MAX + 1));
mpack_done_array(&reader);
TEST_TRUE(5 == mpack_expect_array(&reader));
test_file_expect_bytes(&reader, mpack_tag_bin(0));
test_file_expect_bytes(&reader, mpack_tag_bin(INT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_bin(UINT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_bin(UINT8_MAX + 1));
test_file_expect_bytes(&reader, mpack_tag_bin(UINT16_MAX + 1));
mpack_done_array(&reader);
TEST_TRUE(10 == mpack_expect_array(&reader));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 0));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 1));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 2));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 4));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 8));
test_file_expect_bytes(&reader, mpack_tag_ext(1, 16));
test_file_expect_bytes(&reader, mpack_tag_ext(2, INT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_ext(3, UINT8_MAX));
test_file_expect_bytes(&reader, mpack_tag_ext(4, UINT8_MAX + 1));
test_file_expect_bytes(&reader, mpack_tag_ext(5, UINT16_MAX + 1));
mpack_done_array(&reader);
TEST_TRUE(5 == mpack_expect_array(&reader));
test_file_expect_elements(&reader, mpack_tag_array(0));
test_file_expect_elements(&reader, mpack_tag_array(INT8_MAX));
test_file_expect_elements(&reader, mpack_tag_array(UINT8_MAX));
test_file_expect_elements(&reader, mpack_tag_array(UINT8_MAX + 1));
test_file_expect_elements(&reader, mpack_tag_array(UINT16_MAX + 1));
mpack_done_array(&reader);
TEST_TRUE(5 == mpack_expect_array(&reader));
test_file_expect_elements(&reader, mpack_tag_map(0));
test_file_expect_elements(&reader, mpack_tag_map(INT8_MAX));
test_file_expect_elements(&reader, mpack_tag_map(UINT8_MAX));
test_file_expect_elements(&reader, mpack_tag_map(UINT8_MAX + 1));
test_file_expect_elements(&reader, mpack_tag_map(UINT16_MAX + 1));
mpack_done_array(&reader);
for (int i = 0; i < nesting_depth; ++i)
mpack_expect_array_match(&reader, 1);
mpack_expect_nil(&reader);
for (int i = 0; i < nesting_depth; ++i)
mpack_done_array(&reader);
mpack_done_array(&reader);
mpack_error_t error = mpack_reader_destroy(&reader);
TEST_TRUE(error == mpack_ok, "read failed with %s", mpack_error_to_string(error));
// test missing file
mpack_reader_init_file(&reader, "invalid-filename");
TEST_READER_DESTROY_ERROR(&reader, mpack_error_io);
}
static bool test_file_expect_failure(void) {
// The expect failure test may fail with either
// mpack_error_memory or mpack_error_io.
mpack_reader_t reader;
#define TEST_POSSIBLE_FAILURE() do { \
mpack_error_t error = mpack_reader_error(&reader); \
if (error == mpack_error_memory || error == mpack_error_io) { \
mpack_reader_destroy(&reader); \
return false; \
} \
} while (0)
mpack_reader_init_file(&reader, test_filename);
mpack_expect_array_match(&reader, 2);
uint32_t count;
char** strings = mpack_expect_array_alloc(&reader, char*, 50, &count);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(strings != NULL);
TEST_TRUE(count == 6);
MPACK_FREE(strings);
char* str = mpack_expect_cstr_alloc(&reader, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
if (str) {
TEST_TRUE(strcmp(str, quick_brown_fox) == 0);
MPACK_FREE(str);
}
str = mpack_expect_utf8_cstr_alloc(&reader, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
if (str) {
TEST_TRUE(strcmp(str, "one") == 0);
MPACK_FREE(str);
}
str = mpack_expect_cstr_alloc(&reader, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
if (str) {
TEST_TRUE(strcmp(str, "two") == 0);
MPACK_FREE(str);
}
str = mpack_expect_utf8_cstr_alloc(&reader, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
if (str) {
TEST_TRUE(strcmp(str, "three") == 0);
MPACK_FREE(str);
}
mpack_discard(&reader);
mpack_discard(&reader);
mpack_done_array(&reader);
mpack_discard(&reader); // discard the deep nested arrays
mpack_done_array(&reader);
#undef TEST_POSSIBLE_FAILURE
mpack_error_t error = mpack_reader_destroy(&reader);
if (error == mpack_error_io || error == mpack_error_memory)
return false;
TEST_TRUE(error == mpack_ok, "unexpected error state %i (%s)", (int)error,
mpack_error_to_string(error));
return true;
}
#endif
#if MPACK_NODE
static void test_file_node_bytes(mpack_node_t node, mpack_tag_t tag) {
TEST_TRUE(mpack_tag_equal(tag, mpack_node_tag(node)));
const char* data = mpack_node_data(node);
uint32_t length = mpack_node_data_len(node);
TEST_TRUE(mpack_node_error(node) == mpack_ok);
char expected[1024];
memset(expected, 0, sizeof(expected));
while (length > 0) {
uint32_t count = (length < (uint32_t)sizeof(expected)) ? length : (uint32_t)sizeof(expected);
TEST_TRUE(memcmp(data, expected, count) == 0);
length -= count;
data += count;
}
}
static void test_file_node_elements(mpack_node_t node, mpack_tag_t tag) {
TEST_TRUE(mpack_tag_equal(tag, mpack_node_tag(node)));
for (size_t i = 0; i < tag.v.n; ++i) {
if (tag.type == mpack_type_map) {
mpack_node_nil(mpack_node_map_key_at(node, i));
mpack_node_nil(mpack_node_map_value_at(node, i));
} else {
mpack_node_nil(mpack_node_array_at(node, i));
}
}
}
static void test_file_node(void) {
mpack_tree_t tree;
// test maximum size
mpack_tree_init_file(&tree, test_filename, 100);
TEST_TREE_DESTROY_ERROR(&tree, mpack_error_too_big);
// test blank file
mpack_tree_init_file(&tree, test_blank_filename, 0);
TEST_TREE_DESTROY_ERROR(&tree, mpack_error_invalid);
// test successful parse
mpack_tree_init_file(&tree, test_filename, 0);
TEST_TRUE(mpack_tree_error(&tree) == mpack_ok, "file tree parsing failed: %s",
mpack_error_to_string(mpack_tree_error(&tree)));
mpack_node_t root = mpack_tree_root(&tree);
TEST_TRUE(mpack_node_array_length(root) == 7);
mpack_node_t lipsum_node = mpack_node_array_at(root, 0);
const char* lipsum_str = mpack_node_str(lipsum_node);
TEST_TRUE(lipsum_str != NULL);
if (lipsum_str) {
TEST_TRUE(mpack_node_strlen(lipsum_node) == strlen(lipsum));
TEST_TRUE(memcmp(lipsum_str, lipsum, strlen(lipsum)) == 0);
}
mpack_node_t node = mpack_node_array_at(root, 1);
TEST_TRUE(mpack_node_array_length(node) == 5);
test_file_node_bytes(mpack_node_array_at(node, 0), mpack_tag_str(0));
test_file_node_bytes(mpack_node_array_at(node, 1), mpack_tag_str(INT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 2), mpack_tag_str(UINT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 3), mpack_tag_str(UINT8_MAX + 1));
test_file_node_bytes(mpack_node_array_at(node, 4), mpack_tag_str(UINT16_MAX + 1));
node = mpack_node_array_at(root, 2);
TEST_TRUE(5 == mpack_node_array_length(node));
test_file_node_bytes(mpack_node_array_at(node, 0), mpack_tag_bin(0));
test_file_node_bytes(mpack_node_array_at(node, 1), mpack_tag_bin(INT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 2), mpack_tag_bin(UINT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 3), mpack_tag_bin(UINT8_MAX + 1));
test_file_node_bytes(mpack_node_array_at(node, 4), mpack_tag_bin(UINT16_MAX + 1));
node = mpack_node_array_at(root, 3);
TEST_TRUE(10 == mpack_node_array_length(node));
test_file_node_bytes(mpack_node_array_at(node, 0), mpack_tag_ext(1, 0));
test_file_node_bytes(mpack_node_array_at(node, 1), mpack_tag_ext(1, 1));
test_file_node_bytes(mpack_node_array_at(node, 2), mpack_tag_ext(1, 2));
test_file_node_bytes(mpack_node_array_at(node, 3), mpack_tag_ext(1, 4));
test_file_node_bytes(mpack_node_array_at(node, 4), mpack_tag_ext(1, 8));
test_file_node_bytes(mpack_node_array_at(node, 5), mpack_tag_ext(1, 16));
test_file_node_bytes(mpack_node_array_at(node, 6), mpack_tag_ext(2, INT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 7), mpack_tag_ext(3, UINT8_MAX));
test_file_node_bytes(mpack_node_array_at(node, 8), mpack_tag_ext(4, UINT8_MAX + 1));
test_file_node_bytes(mpack_node_array_at(node, 9), mpack_tag_ext(5, UINT16_MAX + 1));
node = mpack_node_array_at(root, 4);
TEST_TRUE(5 == mpack_node_array_length(node));
test_file_node_elements(mpack_node_array_at(node, 0), mpack_tag_array(0));
test_file_node_elements(mpack_node_array_at(node, 1), mpack_tag_array(INT8_MAX));
test_file_node_elements(mpack_node_array_at(node, 2), mpack_tag_array(UINT8_MAX));
test_file_node_elements(mpack_node_array_at(node, 3), mpack_tag_array(UINT8_MAX + 1));
test_file_node_elements(mpack_node_array_at(node, 4), mpack_tag_array(UINT16_MAX + 1));
node = mpack_node_array_at(root, 5);
TEST_TRUE(5 == mpack_node_array_length(node));
test_file_node_elements(mpack_node_array_at(node, 0), mpack_tag_map(0));
test_file_node_elements(mpack_node_array_at(node, 1), mpack_tag_map(INT8_MAX));
test_file_node_elements(mpack_node_array_at(node, 2), mpack_tag_map(UINT8_MAX));
test_file_node_elements(mpack_node_array_at(node, 3), mpack_tag_map(UINT8_MAX + 1));
test_file_node_elements(mpack_node_array_at(node, 4), mpack_tag_map(UINT16_MAX + 1));
node = mpack_node_array_at(root, 6);
for (int i = 0; i < nesting_depth; ++i)
node = mpack_node_array_at(node, 0);
TEST_TRUE(mpack_ok == mpack_node_error(node));
mpack_node_nil(node);
mpack_error_t error = mpack_tree_destroy(&tree);
TEST_TRUE(error == mpack_ok, "file tree failed with error %s", mpack_error_to_string(error));
// test file size out of bounds
#if MPACK_DEBUG
if (sizeof(size_t) >= sizeof(long)) {
TEST_BREAK((mpack_tree_init_file(&tree, "invalid-filename", ((size_t)LONG_MAX) + 1), true));
TEST_TREE_DESTROY_ERROR(&tree, mpack_error_bug);
}
#endif
// test missing file
mpack_tree_init_file(&tree, "invalid-filename", 0);
TEST_TREE_DESTROY_ERROR(&tree, mpack_error_io);
}
static bool test_file_node_failure(void) {
// The node failure test may fail with either
// mpack_error_memory or mpack_error_io.
mpack_tree_t tree;
#define TEST_POSSIBLE_FAILURE() do { \
mpack_error_t error = mpack_tree_error(&tree); \
TEST_TRUE(test_tree_error == error); \
if (error == mpack_error_memory || error == mpack_error_io) { \
test_tree_error = mpack_ok; \
mpack_tree_destroy(&tree); \
return false; \
} \
} while (0)
mpack_tree_init_file(&tree, test_filename, 0);
if (mpack_tree_error(&tree) == mpack_error_memory || mpack_tree_error(&tree) == mpack_error_io) {
mpack_tree_destroy(&tree);
return false;
}
mpack_tree_set_error_handler(&tree, test_tree_error_handler);
mpack_node_t root = mpack_tree_root(&tree);
mpack_node_t strings = mpack_node_array_at(root, 0);
size_t length = mpack_node_array_length(strings);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(6 == length);
mpack_node_t node = mpack_node_array_at(strings, 0);
char* str = mpack_node_data_alloc(node, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
const char* expected = "The quick brown fox jumps over a lazy dog.";
TEST_TRUE(mpack_node_strlen(node) == strlen(expected));
if (str) {
TEST_TRUE(memcmp(str, expected, mpack_node_strlen(node)) == 0);
MPACK_FREE(str);
}
node = mpack_node_array_at(strings, 1);
str = mpack_node_cstr_alloc(node, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
expected = "one";
if (str) {
TEST_TRUE(strlen(str) == strlen(expected));
TEST_TRUE(strcmp(str, expected) == 0);
MPACK_FREE(str);
}
str = mpack_node_utf8_cstr_alloc(node, 100);
TEST_POSSIBLE_FAILURE();
TEST_TRUE(str != NULL);
if (str) {
TEST_TRUE(strlen(str) == strlen(expected));
TEST_TRUE(strcmp(str, expected) == 0);
MPACK_FREE(str);
}
node = mpack_node_array_at(root, 1);
for (int i = 0; i < nesting_depth; ++i)
node = mpack_node_array_at(node, 0);
TEST_TRUE(mpack_ok == mpack_node_error(node));
mpack_node_nil(node);
#undef TEST_POSSIBLE_FAILURE
mpack_error_t error = mpack_tree_destroy(&tree);
if (error == mpack_error_io || error == mpack_error_memory)
return false;
TEST_TRUE(error == mpack_ok, "unexpected error state %i (%s)", (int)error,
mpack_error_to_string(error));
return true;
}
#endif
void test_file(void) {
// write a blank file for test purposes
FILE* blank = fopen(test_blank_filename, "wb");
fclose(blank);
#if MPACK_READER
test_print();
#endif
#if MPACK_NODE
test_node_print();
#endif
test_file_write();
#if MPACK_READER
test_file_discard();
#endif
#if MPACK_EXPECT
test_file_read();
#endif
#if MPACK_NODE
test_file_node();
#endif
test_system_fail_until_ok(&test_file_write_failure);
#if MPACK_EXPECT
test_system_fail_until_ok(&test_file_expect_failure);
#endif
#if MPACK_NODE
test_system_fail_until_ok(&test_file_node_failure);
#endif
TEST_TRUE(remove(test_filename) == 0, "failed to delete %s", test_filename);
TEST_TRUE(remove(test_blank_filename) == 0, "failed to delete %s", test_blank_filename);
TEST_TRUE(rmdir(test_dir) == 0, "failed to delete %s", test_dir);
(void)&test_compare_print;
}
#else
void test_file(void) {
// if we don't have the writer, nothing to do
}
#endif
#endif