test(demo): add structured unit test runner
This commit is contained in:
@@ -36,4 +36,10 @@ if errorlevel 1 (
|
||||
|
||||
echo built "%DEMO_EXE%"
|
||||
echo built "%TEST_EXE%"
|
||||
|
||||
if /I "%1"=="test" (
|
||||
"%TEST_EXE%"
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
endlocal
|
||||
|
||||
555
demo/unit_test.c
555
demo/unit_test.c
@@ -1,69 +1,307 @@
|
||||
#include "ikv.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static int fail(const char *message)
|
||||
#define ANSI_WHITE "\x1b[37m"
|
||||
#define ANSI_RED "\x1b[31m"
|
||||
#define ANSI_GREEN "\x1b[32m"
|
||||
#define ANSI_YELLOW "\x1b[33m"
|
||||
#define ANSI_RESET "\x1b[0m"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fputs(message, stderr);
|
||||
fputc('\n', stderr);
|
||||
char message[256];
|
||||
} test_context_t;
|
||||
|
||||
typedef int (*test_fn_t)(test_context_t *context);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
test_fn_t fn;
|
||||
} test_case_t;
|
||||
|
||||
static int fail(test_context_t *context, const char *message)
|
||||
{
|
||||
snprintf(context->message, sizeof(context->message), "%s", message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_text_roundtrip(void)
|
||||
static void cleanup_file(const char *path)
|
||||
{
|
||||
const char *src =
|
||||
if (path)
|
||||
remove(path);
|
||||
}
|
||||
|
||||
static int expect_true(test_context_t *context, bool condition, const char *message)
|
||||
{
|
||||
if (!condition)
|
||||
return fail(context, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expect_string(test_context_t *context, const char *actual, const char *expected, const char *label)
|
||||
{
|
||||
if (strcmp(actual ? actual : "", expected ? expected : "") != 0)
|
||||
{
|
||||
snprintf(context->message, sizeof(context->message), "%s: got \"%s\" expected \"%s\"",
|
||||
label,
|
||||
actual ? actual : "",
|
||||
expected ? expected : "");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_object_metadata_and_scalars(test_context_t *context)
|
||||
{
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *child = NULL;
|
||||
ikv_node_t *numbers = NULL;
|
||||
int result = 0;
|
||||
|
||||
if (!root)
|
||||
return fail(context, "failed to create root object");
|
||||
|
||||
ikv_object_set_string(root, "title", "demo");
|
||||
ikv_object_set_int(root, "count", 42);
|
||||
child = ikv_object_add_object(root, "child");
|
||||
numbers = ikv_object_add_array(root, "numbers", IKV_INT);
|
||||
|
||||
if (!child || !numbers)
|
||||
result = fail(context, "failed to build object graph");
|
||||
else
|
||||
{
|
||||
ikv_object_set_bool(child, "alive", true);
|
||||
ikv_array_add_int(numbers, 7);
|
||||
ikv_array_add_int(numbers, 8);
|
||||
|
||||
if ((result = expect_string(context, ikv_node_key(root), "root", "root key")) == 0 &&
|
||||
(result = expect_true(context, ikv_node_type(root) == IKV_OBJECT, "root type mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_object_size(root) == 4u, "object size mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_array_size(numbers) == 2u, "array size mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_array_element_type(numbers) == IKV_INT, "array element type mismatch")) == 0 &&
|
||||
(result = expect_string(context, ikv_as_string(ikv_object_get(root, "title")), "demo", "title mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_as_int(ikv_object_get(root, "count")) == 42, "count mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_as_bool(ikv_object_get(child, "alive")), "bool mismatch")) == 0)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_object_replace_and_missing_lookup(test_context_t *context)
|
||||
{
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
int result = 0;
|
||||
|
||||
if (!root)
|
||||
return fail(context, "failed to create root object");
|
||||
|
||||
ikv_object_set_int(root, "value", 1);
|
||||
ikv_object_set_int(root, "value", 99);
|
||||
|
||||
if ((result = expect_true(context, ikv_object_size(root) == 1u, "object replacement grew size")) == 0 &&
|
||||
(result = expect_true(context, ikv_as_int(ikv_object_get(root, "value")) == 99, "replacement value mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_object_get(root, "missing") == NULL, "missing key should be null")) == 0)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_array_type_rules(test_context_t *context)
|
||||
{
|
||||
ikv_node_t *strings = ikv_create_array("strings", IKV_STRING);
|
||||
ikv_node_t *mixed = ikv_create_array("mixed", IKV_NULL);
|
||||
int result = 0;
|
||||
|
||||
if (!strings || !mixed)
|
||||
{
|
||||
ikv_free(strings);
|
||||
ikv_free(mixed);
|
||||
return fail(context, "failed to create arrays");
|
||||
}
|
||||
|
||||
ikv_array_add_string(strings, "a");
|
||||
ikv_array_add_int(strings, 10);
|
||||
|
||||
ikv_array_add_int(mixed, 1);
|
||||
ikv_array_add_object(mixed);
|
||||
|
||||
if ((result = expect_true(context, ikv_array_size(strings) == 1u, "typed array accepted wrong type")) == 0 &&
|
||||
(result = expect_true(context, ikv_array_size(mixed) == 2u, "mixed array size mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_array_element_type(strings) == IKV_STRING, "typed array element type mismatch")) == 0 &&
|
||||
(result = expect_true(context, ikv_array_element_type(mixed) == IKV_NULL, "mixed array should downgrade to IKV_NULL")) == 0)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
ikv_free(strings);
|
||||
ikv_free(mixed);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_text_parse_variants(test_context_t *context)
|
||||
{
|
||||
const char *v1_src =
|
||||
"ikv1 \"root\"\n"
|
||||
"{\n"
|
||||
" \"name\" \"legacy\"\n"
|
||||
" \"count\" 7\n"
|
||||
"}\n";
|
||||
ikv_node_t *root = ikv_parse_string(src);
|
||||
const char *v2_src =
|
||||
"ikv2 \"root\"\n"
|
||||
"{\n"
|
||||
" \"flag\" true\n"
|
||||
" \"ratio\" 1.5\n"
|
||||
"}\n";
|
||||
const char *plain_src =
|
||||
"{\n"
|
||||
" \"title\" \"plain\"\n"
|
||||
"}\n";
|
||||
ikv_node_t *root = ikv_parse_string(v1_src);
|
||||
int result = 0;
|
||||
|
||||
if (!root)
|
||||
return fail("text parse failed");
|
||||
if (ikv_node_type(root) != IKV_OBJECT)
|
||||
return fail("text root type mismatch");
|
||||
if (strcmp(ikv_as_string(ikv_object_get(root, "name")), "legacy") != 0)
|
||||
return fail("text string value mismatch");
|
||||
if (ikv_as_int(ikv_object_get(root, "count")) != 7)
|
||||
return fail("text int value mismatch");
|
||||
|
||||
return fail(context, "v1 text parse failed");
|
||||
if ((result = expect_string(context, ikv_as_string(ikv_object_get(root, "name")), "legacy", "v1 name mismatch")) != 0 ||
|
||||
(result = expect_true(context, ikv_as_int(ikv_object_get(root, "count")) == 7, "v1 count mismatch")) != 0)
|
||||
{
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
ikv_free(root);
|
||||
|
||||
root = ikv_parse_string(v2_src);
|
||||
if (!root)
|
||||
return fail(context, "v2 text parse failed");
|
||||
if ((result = expect_true(context, ikv_as_bool(ikv_object_get(root, "flag")), "v2 flag mismatch")) != 0 ||
|
||||
(result = expect_true(context, ikv_as_float(ikv_object_get(root, "ratio")) > 1.4, "v2 ratio mismatch")) != 0)
|
||||
{
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
ikv_free(root);
|
||||
|
||||
root = ikv_parse_string(plain_src);
|
||||
if (!root)
|
||||
return fail(context, "plain object parse failed");
|
||||
result = expect_string(context, ikv_as_string(ikv_object_get(root, "title")), "plain", "plain title mismatch");
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_text_invalid_input(test_context_t *context)
|
||||
{
|
||||
if (ikv_parse_string(NULL) != NULL)
|
||||
return fail(context, "null text input should fail");
|
||||
if (ikv_parse_string("ikv1 \"root\" { \"unterminated\" ") != NULL)
|
||||
return fail(context, "invalid text should fail");
|
||||
if (ikv_parse_string("ikv2 root [") != NULL)
|
||||
return fail(context, "invalid array text should fail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_binary_v1_roundtrip(void)
|
||||
static int test_text_file_roundtrip(test_context_t *context)
|
||||
{
|
||||
const char *path = "demo_test_text.ikv";
|
||||
ikv_node_t *root = ikv_create_object("save");
|
||||
ikv_node_t *loaded = NULL;
|
||||
int result = 0;
|
||||
|
||||
cleanup_file(path);
|
||||
if (!root)
|
||||
return fail(context, "failed to create text file root");
|
||||
|
||||
ikv_object_set_string(root, "mode", "text");
|
||||
if (!ikv_write_file(path, root))
|
||||
result = fail(context, "text file write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikv_parse_file(path);
|
||||
if (!loaded)
|
||||
result = fail(context, "text file parse failed");
|
||||
else
|
||||
result = expect_string(context, ikv_as_string(ikv_object_get(loaded, "mode")), "text", "text file value mismatch");
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
ikv_free(root);
|
||||
cleanup_file(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_binary_v1_memory_roundtrip(test_context_t *context)
|
||||
{
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *loaded = NULL;
|
||||
uint8_t *data = NULL;
|
||||
uint32_t size = 0u;
|
||||
int status = 0;
|
||||
int result = 0;
|
||||
|
||||
if (!root)
|
||||
return fail("v1 root allocation failed");
|
||||
return fail(context, "failed to create v1 root");
|
||||
|
||||
ikv_object_set_int(root, "value", 11);
|
||||
ikv_object_set_string(root, "name", "v1");
|
||||
|
||||
if (!ikvb_write_memory_version(root, &data, &size, IKV_VERSION_1))
|
||||
status = fail("v1 binary write failed");
|
||||
result = fail(context, "v1 binary write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikvb_parse_memory(data, size);
|
||||
if (!loaded)
|
||||
status = fail("v1 binary parse failed");
|
||||
else if (ikv_as_int(ikv_object_get(loaded, "value")) != 11)
|
||||
status = fail("v1 binary value mismatch");
|
||||
result = fail(context, "v1 binary parse failed");
|
||||
else if ((result = expect_true(context, ikv_as_int(ikv_object_get(loaded, "value")) == 11, "v1 int mismatch")) == 0)
|
||||
result = expect_string(context, ikv_as_string(ikv_object_get(loaded, "name")), "v1", "v1 string mismatch");
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
free(data);
|
||||
ikv_free(root);
|
||||
return status;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_binary_v2_lazy_root(void)
|
||||
static int test_binary_v1_file_roundtrip(test_context_t *context)
|
||||
{
|
||||
const char *path = "demo_test_v1.ikvb";
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *loaded = NULL;
|
||||
int result = 0;
|
||||
|
||||
cleanup_file(path);
|
||||
if (!root)
|
||||
return fail(context, "failed to create v1 file root");
|
||||
|
||||
ikv_object_set_bool(root, "legacy", true);
|
||||
if (!ikvb_write_file_version(path, root, IKV_VERSION_1))
|
||||
result = fail(context, "v1 file write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikvb_parse_file(path);
|
||||
if (!loaded)
|
||||
result = fail(context, "v1 file parse failed");
|
||||
else
|
||||
result = expect_true(context, ikv_as_bool(ikv_object_get(loaded, "legacy")), "v1 file bool mismatch");
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
ikv_free(root);
|
||||
cleanup_file(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_binary_v2_memory_lazy_roundtrip(test_context_t *context)
|
||||
{
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *nested = NULL;
|
||||
@@ -71,58 +309,287 @@ static int test_binary_v2_lazy_root(void)
|
||||
ikv_node_t *loaded_nested = NULL;
|
||||
uint8_t *data = NULL;
|
||||
uint32_t size = 0u;
|
||||
int status = 0;
|
||||
int result = 0;
|
||||
|
||||
if (!root)
|
||||
return fail("v2 root allocation failed");
|
||||
return fail(context, "failed to create v2 memory root");
|
||||
|
||||
ikv_object_set_string(root, "title", "fast");
|
||||
nested = ikv_object_add_object(root, "nested");
|
||||
if (!nested)
|
||||
{
|
||||
ikv_free(root);
|
||||
return fail("v2 nested allocation failed");
|
||||
return fail(context, "failed to create nested object");
|
||||
}
|
||||
|
||||
ikv_object_set_bool(nested, "flag", true);
|
||||
ikv_object_set_float(nested, "speed", 9.25);
|
||||
|
||||
if (!ikvb_write_memory_version(root, &data, &size, IKV_VERSION_2))
|
||||
status = fail("v2 binary write failed");
|
||||
result = fail(context, "v2 binary memory write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikvb_parse_memory(data, size);
|
||||
if (!loaded)
|
||||
status = fail("v2 binary parse failed");
|
||||
else if (ikv_object_size(loaded) != 2u)
|
||||
status = fail("v2 root key count mismatch");
|
||||
else if (strcmp(ikv_as_string(ikv_object_get(loaded, "title")), "fast") != 0)
|
||||
status = fail("v2 root lazy lookup failed");
|
||||
else
|
||||
result = fail(context, "v2 binary memory parse failed");
|
||||
else if ((result = expect_true(context, ikv_object_size(loaded) == 2u, "v2 root key count mismatch")) == 0 &&
|
||||
(result = expect_string(context, ikv_as_string(ikv_object_get(loaded, "title")), "fast", "v2 title mismatch")) == 0)
|
||||
{
|
||||
loaded_nested = ikv_object_get(loaded, "nested");
|
||||
if (!loaded_nested)
|
||||
status = fail("v2 nested lazy lookup failed");
|
||||
else if (!ikv_as_bool(ikv_object_get(loaded_nested, "flag")))
|
||||
status = fail("v2 nested bool mismatch");
|
||||
result = fail(context, "v2 nested lazy lookup failed");
|
||||
else if ((result = expect_true(context, ikv_as_bool(ikv_object_get(loaded_nested, "flag")), "v2 nested bool mismatch")) == 0)
|
||||
result = expect_true(context, ikv_as_float(ikv_object_get(loaded_nested, "speed")) > 9.0, "v2 nested float mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
free(data);
|
||||
ikv_free(root);
|
||||
return status;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_binary_v2_file_lazy_roundtrip(test_context_t *context)
|
||||
{
|
||||
const char *path = "demo_test_v2.ikvb";
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *inventory = NULL;
|
||||
ikv_node_t *loaded = NULL;
|
||||
ikv_node_t *loaded_inventory = NULL;
|
||||
int result = 0;
|
||||
|
||||
cleanup_file(path);
|
||||
if (!root)
|
||||
return fail(context, "failed to create v2 file root");
|
||||
|
||||
inventory = ikv_object_add_array(root, "inventory", IKV_STRING);
|
||||
if (!inventory)
|
||||
{
|
||||
ikv_free(root);
|
||||
return fail(context, "failed to create inventory array");
|
||||
}
|
||||
|
||||
ikv_object_set_int(root, "count", 3);
|
||||
ikv_array_add_string(inventory, "a");
|
||||
ikv_array_add_string(inventory, "b");
|
||||
ikv_array_add_string(inventory, "c");
|
||||
|
||||
if (!ikvb_write_file(path, root))
|
||||
result = fail(context, "v2 file write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikv_parse_file(path);
|
||||
if (!loaded)
|
||||
result = fail(context, "v2 file parse failed");
|
||||
else if ((result = expect_true(context, ikv_as_int(ikv_object_get(loaded, "count")) == 3, "v2 file int mismatch")) == 0)
|
||||
{
|
||||
loaded_inventory = ikv_object_get(loaded, "inventory");
|
||||
if (!loaded_inventory)
|
||||
result = fail(context, "v2 file inventory lazy lookup failed");
|
||||
else if ((result = expect_true(context, ikv_array_size(loaded_inventory) == 3u, "v2 inventory size mismatch")) == 0)
|
||||
result = expect_string(context, ikv_as_string(ikv_array_get(loaded_inventory, 1u)), "b", "v2 inventory value mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
ikv_free(root);
|
||||
cleanup_file(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_detection_apis(test_context_t *context)
|
||||
{
|
||||
const char *text_path = "demo_test_detect_text.ikv";
|
||||
const char *binary_path = "demo_test_detect_bin.ikvb";
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
int result = 0;
|
||||
|
||||
cleanup_file(text_path);
|
||||
cleanup_file(binary_path);
|
||||
if (!root)
|
||||
return fail(context, "failed to create detection root");
|
||||
|
||||
ikv_object_set_int(root, "value", 5);
|
||||
if ((result = expect_true(context, ikv_detect_text_version("ikv1 \"r\" {}") == IKV_VERSION_1, "text v1 detection failed")) != 0 ||
|
||||
(result = expect_true(context, ikv_detect_text_version("ikv2 \"r\" {}") == IKV_VERSION_2, "text v2 detection failed")) != 0 ||
|
||||
(result = expect_true(context, ikv_detect_text_version("{\"x\" 1}") == IKV_VERSION_UNKNOWN, "plain text detection mismatch")) != 0)
|
||||
{
|
||||
ikv_free(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!ikv_write_file_version(text_path, root, IKV_VERSION_2) ||
|
||||
!ikvb_write_file_version(binary_path, root, IKV_VERSION_1))
|
||||
{
|
||||
ikv_free(root);
|
||||
cleanup_file(text_path);
|
||||
cleanup_file(binary_path);
|
||||
return fail(context, "failed to prepare detection files");
|
||||
}
|
||||
|
||||
if ((result = expect_true(context, ikv_detect_file_version(text_path, false) == IKV_VERSION_2, "text file detection failed")) == 0 &&
|
||||
(result = expect_true(context, ikv_detect_file_version(binary_path, true) == IKV_VERSION_1, "binary file detection failed")) == 0)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
ikv_free(root);
|
||||
cleanup_file(text_path);
|
||||
cleanup_file(binary_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_explicit_version_apis(test_context_t *context)
|
||||
{
|
||||
const char *text_path = "demo_test_explicit_text.ikv";
|
||||
const char *binary_path = "demo_test_explicit_bin.ikvb";
|
||||
ikv_node_t *root = ikv_create_object("root");
|
||||
ikv_node_t *loaded = NULL;
|
||||
int result = 0;
|
||||
|
||||
cleanup_file(text_path);
|
||||
cleanup_file(binary_path);
|
||||
if (!root)
|
||||
return fail(context, "failed to create explicit api root");
|
||||
|
||||
ikv_object_set_string(root, "kind", "explicit");
|
||||
|
||||
if (!ikv_write_file_version(text_path, root, IKV_VERSION_1))
|
||||
result = fail(context, "explicit text write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikv_parse_file_version(text_path, IKV_VERSION_1);
|
||||
if (!loaded)
|
||||
result = fail(context, "explicit text parse failed");
|
||||
else
|
||||
result = expect_string(context, ikv_as_string(ikv_object_get(loaded, "kind")), "explicit", "explicit text value mismatch");
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
loaded = NULL;
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
if (!ikvb_write_file_version(binary_path, root, IKV_VERSION_2))
|
||||
result = fail(context, "explicit binary write failed");
|
||||
else
|
||||
{
|
||||
loaded = ikvb_parse_file_version(binary_path, IKV_VERSION_2);
|
||||
if (!loaded)
|
||||
result = fail(context, "explicit binary parse failed");
|
||||
else
|
||||
result = expect_string(context, ikv_as_string(ikv_object_get(loaded, "kind")), "explicit", "explicit binary value mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
ikv_free(loaded);
|
||||
ikv_free(root);
|
||||
cleanup_file(text_path);
|
||||
cleanup_file(binary_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int test_invalid_argument_guards(test_context_t *context)
|
||||
{
|
||||
uint8_t *data = NULL;
|
||||
uint32_t size = 0u;
|
||||
|
||||
if (!expect_true(context, !ikv_write_file(NULL, NULL), "ikv_write_file should reject nulls") &&
|
||||
!expect_true(context, !ikvb_write_file(NULL, NULL), "ikvb_write_file should reject nulls") &&
|
||||
!expect_true(context, !ikvb_write_memory(NULL, &data, &size), "ikvb_write_memory should reject null root") &&
|
||||
!expect_true(context, ikv_parse_file("definitely_missing_file.ikv") == NULL, "missing text file should fail") &&
|
||||
!expect_true(context, ikvb_parse_file("definitely_missing_file.ikvb") == NULL, "missing binary file should fail") &&
|
||||
!expect_true(context, ikvb_parse_memory("bad", 3u) == NULL, "short binary blob should fail") &&
|
||||
!expect_true(context, ikv_parse_file_version("definitely_missing_file.ikv", IKV_VERSION_2) == NULL, "explicit parse missing file should fail") &&
|
||||
!expect_true(context, ikvb_parse_file_version("definitely_missing_file.ikvb", IKV_VERSION_1) == NULL, "explicit binary parse missing file should fail"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const test_case_t test_cases[] = {
|
||||
{"object metadata and scalars", test_object_metadata_and_scalars},
|
||||
{"object replace and missing lookup", test_object_replace_and_missing_lookup},
|
||||
{"array type rules", test_array_type_rules},
|
||||
{"text parse variants", test_text_parse_variants},
|
||||
{"text invalid input", test_text_invalid_input},
|
||||
{"text file roundtrip", test_text_file_roundtrip},
|
||||
{"binary v1 memory roundtrip", test_binary_v1_memory_roundtrip},
|
||||
{"binary v1 file roundtrip", test_binary_v1_file_roundtrip},
|
||||
{"binary v2 memory lazy roundtrip", test_binary_v2_memory_lazy_roundtrip},
|
||||
{"binary v2 file lazy roundtrip", test_binary_v2_file_lazy_roundtrip},
|
||||
{"detection apis", test_detection_apis},
|
||||
{"explicit version apis", test_explicit_version_apis},
|
||||
{"invalid argument guards", test_invalid_argument_guards},
|
||||
};
|
||||
|
||||
static void log_case(bool passed, unsigned int index, unsigned int total, long elapsed_ms, const char *name, const char *message)
|
||||
{
|
||||
const char *color = passed ? ANSI_GREEN : ANSI_RED;
|
||||
const char *label = passed ? "PASS" : "FAIL";
|
||||
|
||||
printf("%s[%s%s%s]%s ( %4ldms ) [%u/%u] %s",
|
||||
ANSI_WHITE,
|
||||
color,
|
||||
label,
|
||||
ANSI_WHITE,
|
||||
ANSI_RESET,
|
||||
elapsed_ms,
|
||||
index,
|
||||
total,
|
||||
name);
|
||||
|
||||
if (!passed && message && message[0] != 0)
|
||||
printf(" %s- %s%s", ANSI_YELLOW, message, ANSI_RESET);
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if (test_text_roundtrip() != 0)
|
||||
return 1;
|
||||
if (test_binary_v1_roundtrip() != 0)
|
||||
return 1;
|
||||
if (test_binary_v2_lazy_root() != 0)
|
||||
return 1;
|
||||
const unsigned int total = (unsigned int)(sizeof(test_cases) / sizeof(test_cases[0]));
|
||||
unsigned int passed = 0u;
|
||||
|
||||
puts("all tests passed");
|
||||
for (unsigned int i = 0; i < total; ++i)
|
||||
{
|
||||
test_context_t context;
|
||||
clock_t start_time = 0;
|
||||
clock_t end_time = 0;
|
||||
long elapsed_ms = 0;
|
||||
int result = 0;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
start_time = clock();
|
||||
result = test_cases[i].fn(&context);
|
||||
end_time = clock();
|
||||
elapsed_ms = (long)(((end_time - start_time) * 1000) / CLOCKS_PER_SEC);
|
||||
|
||||
if (result == 0)
|
||||
++passed;
|
||||
|
||||
log_case(result == 0, i + 1u, total, elapsed_ms, test_cases[i].name, context.message);
|
||||
}
|
||||
|
||||
if (passed != total)
|
||||
{
|
||||
printf("%s[%sFAIL%s]%s %u/%u tests passed\n",
|
||||
ANSI_WHITE,
|
||||
ANSI_RED,
|
||||
ANSI_WHITE,
|
||||
ANSI_RESET,
|
||||
passed,
|
||||
total);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s[%sPASS%s]%s %u/%u tests passed\n",
|
||||
ANSI_WHITE,
|
||||
ANSI_GREEN,
|
||||
ANSI_WHITE,
|
||||
ANSI_RESET,
|
||||
passed,
|
||||
total);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user