Closes internal-consistency items 16 and 17. Ten *_RETURN macros sat inside ATTEMPT blocks, which return past CLEANUP and skip every release in it. The one that mattered was the success path of akgl_get_json_tilemap_property: it leaked two of the string pool's 256 entries on every lookup that *found* what it was asked for, and a map load does that several times per layer. tests/tilemap.c now runs each of its three paths -- found, absent, wrong type -- twice the pool size and asserts the pool is where it started. Against the old code that test does not merely fail, it segfaults, which is Defects item 30 seen from the outside: pool exhaustion arriving as a NULL strncpy rather than as AKGL_ERR_HEAP. Two of the conversions needed more than swapping the macro. In akgl_get_json_tilemap_property a plain break would have fallen through to the "property not found" FAIL_RETURN after FINISH, reporting a miss for something found, so the success path sets a flag. In akgl_collide_rectangles the eight early exits are followed by `*collide = false;`, which would have overwritten the hit that broke out; each corner test writes the flag itself, so that line is gone rather than moved. The same function also released its scratch string once per loop iteration while continuing to use it, so the slot was free while still live -- one claim now covers the whole scan. akgl_controller_default is the other behavioural one: its SUCCEED_RETURN was the last statement in the ATTEMPT block, so the path that falls out of FINISH reached the closing brace of a non-void function. scripts/check_error_protocol.py keeps both rules enforced -- a *_RETURN inside ATTEMPT, and a return out of a HANDLE block -- as the error_protocol test. Neither produces a compiler diagnostic and neither fails a test run until the pool it drains is empty, which is why both have already shipped once. For item 17: the eight typed JSON accessors that validated their container and then wrote through dest unconditionally now check key and dest as the two string accessors always did; the null physics backend checks its actors like the arcade one; and akgl_render_2d_frame_start, _frame_end and _shutdown check self, which the first two read straight through. tests/renderer.c calls all three with NULL, which segfaulted before. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
432 lines
18 KiB
C
432 lines
18 KiB
C
/**
|
|
* @file json_helpers.c
|
|
* @brief Unit tests for the typed JSON accessors.
|
|
*
|
|
* These exercise the accessors directly rather than through sprite, character,
|
|
* or tilemap loading, so the error paths are reachable without building a
|
|
* malformed asset for every case.
|
|
*/
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <jansson.h>
|
|
#include <string.h>
|
|
#include <akerror.h>
|
|
|
|
#include <akgl/error.h>
|
|
#include <akgl/json_helpers.h>
|
|
#include <akgl/heap.h>
|
|
#include <akgl/registry.h>
|
|
#include <akgl/staticstring.h>
|
|
|
|
#include "testutil.h"
|
|
|
|
/** @brief Fixture document shared by every test in this file. */
|
|
static json_t *fixture = NULL;
|
|
|
|
/** @brief Load tests/assets/snippets/test_json_helpers.json into @ref fixture. */
|
|
static akerr_ErrorContext *load_fixture(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
json_error_t jsonerr;
|
|
akgl_String *pathstr = NULL;
|
|
|
|
ATTEMPT {
|
|
CATCH(e, akgl_heap_next_string(&pathstr));
|
|
snprintf(
|
|
(char *)&pathstr->data,
|
|
AKGL_MAX_STRING_LENGTH,
|
|
"%s%s",
|
|
SDL_GetBasePath(),
|
|
"assets/snippets/test_json_helpers.json");
|
|
fixture = json_load_file((char *)&pathstr->data, 0, &jsonerr);
|
|
FAIL_ZERO_BREAK(e, fixture, AKERR_IO,
|
|
"Unable to load the JSON fixture: %s", (char *)jsonerr.text);
|
|
} CLEANUP {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
/**
|
|
* @brief Every accessor must refuse a NULL key or destination, not dereference it.
|
|
*
|
|
* Only the two string accessors did. The other eight validated the container
|
|
* and then wrote through @p dest unconditionally, so which arguments a caller
|
|
* could safely get wrong depended on which typed accessor they happened to
|
|
* call -- and the ones that crashed were the ones used most.
|
|
*/
|
|
akerr_ErrorContext *test_json_accessor_null_arguments(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
int intval = 0;
|
|
float floatval = 0;
|
|
double dblval = 0;
|
|
bool boolval = false;
|
|
json_t *jsonval = NULL;
|
|
akgl_String *strval = NULL;
|
|
|
|
ATTEMPT {
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_object_value(fixture, NULL, &jsonval),
|
|
"object accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_object_value(fixture, "nested", NULL),
|
|
"object accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_boolean_value(fixture, NULL, &boolval),
|
|
"boolean accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_boolean_value(fixture, "enabled", NULL),
|
|
"boolean accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_integer_value(fixture, NULL, &intval),
|
|
"integer accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_integer_value(fixture, "count", NULL),
|
|
"integer accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_number_value(fixture, NULL, &floatval),
|
|
"number accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_number_value(fixture, "ratio", NULL),
|
|
"number accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_double_value(fixture, NULL, &dblval),
|
|
"double accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_double_value(fixture, "ratio", NULL),
|
|
"double accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_value(fixture, NULL, &jsonval),
|
|
"array accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_value(fixture, "integers", NULL),
|
|
"array accessor with a NULL destination");
|
|
|
|
// The index accessors take no key, but they take a destination.
|
|
CATCH(e, akgl_get_json_array_value(fixture, "integers", &jsonval));
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_integer(jsonval, 0, NULL),
|
|
"array index integer accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_object(jsonval, 0, NULL),
|
|
"array index object accessor with a NULL destination");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_string(jsonval, 0, NULL),
|
|
"array index string accessor with a NULL destination");
|
|
|
|
// Nothing above should have claimed a pool string on the way to
|
|
// refusing, which is the other half of "refuse rather than proceed".
|
|
TEST_ASSERT(e, strval == NULL, "a refused accessor wrote to its destination anyway");
|
|
} CLEANUP {
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_scalar_accessors(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
int intval = 0;
|
|
float floatval = 0;
|
|
bool boolval = false;
|
|
json_t *objval = NULL;
|
|
json_t *arrayval = NULL;
|
|
|
|
ATTEMPT {
|
|
TEST_EXPECT_OK(e, akgl_get_json_integer_value(fixture, "count", &intval), "read count");
|
|
TEST_ASSERT(e, intval == 42, "count read as %d, expected 42", intval);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_integer_value(fixture, "negative", &intval), "read negative");
|
|
TEST_ASSERT(e, intval == -17, "negative read as %d, expected -17", intval);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_number_value(fixture, "ratio", &floatval), "read ratio");
|
|
TEST_ASSERT_FEQ(e, floatval, 2.5f, "ratio read as %f, expected 2.5", floatval);
|
|
|
|
// A JSON integer is a number, so the float accessor accepts it too.
|
|
TEST_EXPECT_OK(e, akgl_get_json_number_value(fixture, "count", &floatval), "read count as a number");
|
|
TEST_ASSERT_FEQ(e, floatval, 42.0f, "count read as number %f, expected 42", floatval);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_boolean_value(fixture, "enabled", &boolval), "read enabled");
|
|
TEST_ASSERT(e, boolval == true, "enabled read as false");
|
|
TEST_EXPECT_OK(e, akgl_get_json_boolean_value(fixture, "disabled", &boolval), "read disabled");
|
|
TEST_ASSERT(e, boolval == false, "disabled read as true");
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_object_value(fixture, "nested", &objval), "read nested");
|
|
TEST_ASSERT(e, objval != NULL, "nested object came back NULL");
|
|
TEST_EXPECT_OK(e, akgl_get_json_integer_value(objval, "innercount", &intval), "read nested innercount");
|
|
TEST_ASSERT(e, intval == 7, "nested innercount read as %d, expected 7", intval);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_array_value(fixture, "integers", &arrayval), "read integers array");
|
|
TEST_ASSERT(e, json_array_size(arrayval) == 3,
|
|
"integers array had %d entries, expected 3", (int)json_array_size(arrayval));
|
|
} CLEANUP {
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_double_value(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
double dblval = 0;
|
|
|
|
ATTEMPT {
|
|
TEST_EXPECT_OK(e, akgl_get_json_double_value(fixture, "ratio", &dblval), "read ratio as a double");
|
|
TEST_ASSERT(e, dblval > 2.4999 && dblval < 2.5001,
|
|
"ratio read as double %f, expected 2.5", dblval);
|
|
|
|
// Integers satisfy json_is_number, so the double accessor takes them.
|
|
TEST_EXPECT_OK(e, akgl_get_json_double_value(fixture, "count", &dblval), "read count as a double");
|
|
TEST_ASSERT(e, dblval > 41.999 && dblval < 42.001,
|
|
"count read as double %f, expected 42", dblval);
|
|
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_double_value(fixture, "absent", &dblval),
|
|
"double accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_double_value(fixture, "name", &dblval),
|
|
"double accessor on a string value");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_double_value(NULL, "ratio", &dblval),
|
|
"double accessor on a NULL object");
|
|
} CLEANUP {
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_string_accessor(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
akgl_String *allocated = NULL;
|
|
akgl_String *reused = NULL;
|
|
|
|
ATTEMPT {
|
|
// A NULL destination makes the accessor claim a heap string.
|
|
TEST_EXPECT_OK(e, akgl_get_json_string_value(fixture, "name", &allocated),
|
|
"read name into a NULL destination");
|
|
TEST_ASSERT(e, allocated != NULL, "the accessor did not allocate a destination string");
|
|
TEST_ASSERT(e, strcmp((char *)&allocated->data, "json helper fixture") == 0,
|
|
"name read as \"%s\"", (char *)&allocated->data);
|
|
|
|
// A caller-supplied destination is written in place, not replaced.
|
|
CATCH(e, akgl_heap_next_string(&reused));
|
|
CATCH(e, akgl_string_initialize(reused, "placeholder"));
|
|
{
|
|
akgl_String *before = reused;
|
|
TEST_EXPECT_OK(e, akgl_get_json_string_value(fixture, "name", &reused),
|
|
"read name into a preallocated destination");
|
|
TEST_ASSERT(e, reused == before,
|
|
"the accessor replaced a caller-supplied destination pointer");
|
|
}
|
|
TEST_ASSERT(e, strcmp((char *)&reused->data, "json helper fixture") == 0,
|
|
"in-place read produced \"%s\"", (char *)&reused->data);
|
|
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_string_value(fixture, "absent", &reused),
|
|
"string accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_string_value(fixture, "count", &reused),
|
|
"string accessor on an integer value");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_string_value(NULL, "name", &reused),
|
|
"string accessor on a NULL object");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_string_value(fixture, NULL, &reused),
|
|
"string accessor with a NULL key");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_string_value(fixture, "name", NULL),
|
|
"string accessor with a NULL destination");
|
|
} CLEANUP {
|
|
IGNORE(akgl_heap_release_string(allocated));
|
|
IGNORE(akgl_heap_release_string(reused));
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_array_index_accessors(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
json_t *integers = NULL;
|
|
json_t *strings = NULL;
|
|
json_t *objects = NULL;
|
|
json_t *mixed = NULL;
|
|
json_t *element = NULL;
|
|
akgl_String *strval = NULL;
|
|
int intval = 0;
|
|
|
|
ATTEMPT {
|
|
CATCH(e, akgl_get_json_array_value(fixture, "integers", &integers));
|
|
CATCH(e, akgl_get_json_array_value(fixture, "strings", &strings));
|
|
CATCH(e, akgl_get_json_array_value(fixture, "objects", &objects));
|
|
CATCH(e, akgl_get_json_array_value(fixture, "mixed", &mixed));
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_array_index_integer(integers, 0, &intval), "integers[0]");
|
|
TEST_ASSERT(e, intval == 10, "integers[0] read as %d, expected 10", intval);
|
|
TEST_EXPECT_OK(e, akgl_get_json_array_index_integer(integers, 2, &intval), "integers[2]");
|
|
TEST_ASSERT(e, intval == 30, "integers[2] read as %d, expected 30", intval);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_array_index_string(strings, 1, &strval), "strings[1]");
|
|
TEST_ASSERT(e, strcmp((char *)&strval->data, "beta") == 0,
|
|
"strings[1] read as \"%s\", expected \"beta\"", (char *)&strval->data);
|
|
|
|
TEST_EXPECT_OK(e, akgl_get_json_array_index_object(objects, 1, &element), "objects[1]");
|
|
TEST_EXPECT_OK(e, akgl_get_json_integer_value(element, "id", &intval), "objects[1].id");
|
|
TEST_ASSERT(e, intval == 2, "objects[1].id read as %d, expected 2", intval);
|
|
|
|
// One past the end, and far past the end, are both out of bounds.
|
|
TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_get_json_array_index_integer(integers, 3, &intval),
|
|
"integers[3] is one past the end");
|
|
TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_get_json_array_index_integer(integers, 99, &intval),
|
|
"integers[99] is far past the end");
|
|
TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_get_json_array_index_object(objects, 5, &element),
|
|
"objects[5] is past the end");
|
|
TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_get_json_array_index_string(strings, 5, &strval),
|
|
"strings[5] is past the end");
|
|
|
|
// The mixed array holds one of each type, so every accessor can be shown
|
|
// to reject the entries that are not its own.
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_array_index_integer(mixed, 1, &intval),
|
|
"integer accessor on a string element");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_array_index_string(mixed, 0, &strval),
|
|
"string accessor on an integer element");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_array_index_object(mixed, 0, &element),
|
|
"object accessor on an integer element");
|
|
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_integer(NULL, 0, &intval),
|
|
"integer index accessor on a NULL array");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_object(NULL, 0, &element),
|
|
"object index accessor on a NULL array");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_string(NULL, 0, &strval),
|
|
"string index accessor on a NULL array");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_index_string(strings, 0, NULL),
|
|
"string index accessor with a NULL destination");
|
|
} CLEANUP {
|
|
IGNORE(akgl_heap_release_string(strval));
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_type_and_key_errors(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
int intval = 0;
|
|
float floatval = 0;
|
|
bool boolval = false;
|
|
json_t *objval = NULL;
|
|
json_t *arrayval = NULL;
|
|
|
|
ATTEMPT {
|
|
// Missing keys.
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_integer_value(fixture, "absent", &intval),
|
|
"integer accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_number_value(fixture, "absent", &floatval),
|
|
"number accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_boolean_value(fixture, "absent", &boolval),
|
|
"boolean accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_object_value(fixture, "absent", &objval),
|
|
"object accessor on a missing key");
|
|
TEST_EXPECT_STATUS(e, AKERR_KEY, akgl_get_json_array_value(fixture, "absent", &arrayval),
|
|
"array accessor on a missing key");
|
|
|
|
// Wrong types.
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_integer_value(fixture, "name", &intval),
|
|
"integer accessor on a string");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_integer_value(fixture, "ratio", &intval),
|
|
"integer accessor on a real");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_number_value(fixture, "name", &floatval),
|
|
"number accessor on a string");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_boolean_value(fixture, "count", &boolval),
|
|
"boolean accessor on an integer");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_object_value(fixture, "integers", &objval),
|
|
"object accessor on an array");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE, akgl_get_json_array_value(fixture, "nested", &arrayval),
|
|
"array accessor on an object");
|
|
|
|
// NULL containers.
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_integer_value(NULL, "count", &intval),
|
|
"integer accessor on a NULL object");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_number_value(NULL, "ratio", &floatval),
|
|
"number accessor on a NULL object");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_boolean_value(NULL, "enabled", &boolval),
|
|
"boolean accessor on a NULL object");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_object_value(NULL, "nested", &objval),
|
|
"object accessor on a NULL object");
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER, akgl_get_json_array_value(NULL, "integers", &arrayval),
|
|
"array accessor on a NULL object");
|
|
} CLEANUP {
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
akerr_ErrorContext *test_json_with_default(void)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
int dest = 0;
|
|
int defval = 99;
|
|
akerr_ErrorContext *keyerr = NULL;
|
|
akerr_ErrorContext *typeerr = NULL;
|
|
int junk = 0;
|
|
|
|
ATTEMPT {
|
|
// A NULL error means the read succeeded, so the default is not applied.
|
|
dest = 1;
|
|
TEST_EXPECT_OK(e, akgl_get_json_with_default(NULL, (void *)&defval, (void *)&dest, sizeof(int)),
|
|
"with_default on a NULL error");
|
|
TEST_ASSERT(e, dest == 1, "with_default overwrote a successful read (dest is now %d)", dest);
|
|
|
|
// An AKERR_KEY failure substitutes the default.
|
|
dest = 1;
|
|
keyerr = akgl_get_json_integer_value(fixture, "absent", &junk);
|
|
TEST_ASSERT(e, keyerr != NULL, "the missing-key read unexpectedly succeeded");
|
|
TEST_EXPECT_OK(e, akgl_get_json_with_default(keyerr, (void *)&defval, (void *)&dest, sizeof(int)),
|
|
"with_default on an AKERR_KEY error");
|
|
TEST_ASSERT(e, dest == 99, "with_default did not apply the default (dest is %d)", dest);
|
|
keyerr = NULL;
|
|
|
|
// An unrelated failure is not swallowed, so the caller still sees it.
|
|
dest = 1;
|
|
typeerr = akgl_get_json_integer_value(fixture, "name", &junk);
|
|
TEST_ASSERT(e, typeerr != NULL, "the wrong-type read unexpectedly succeeded");
|
|
TEST_EXPECT_STATUS(e, AKERR_TYPE,
|
|
akgl_get_json_with_default(typeerr, (void *)&defval, (void *)&dest, sizeof(int)),
|
|
"with_default must propagate an unrelated error");
|
|
TEST_ASSERT(e, dest == 1, "with_default applied the default for an unrelated error");
|
|
typeerr = NULL;
|
|
|
|
// NULL arguments alongside a real error are a contract violation.
|
|
keyerr = akgl_get_json_integer_value(fixture, "absent", &junk);
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER,
|
|
akgl_get_json_with_default(keyerr, NULL, (void *)&dest, sizeof(int)),
|
|
"with_default with a NULL default value");
|
|
keyerr = NULL;
|
|
keyerr = akgl_get_json_integer_value(fixture, "absent", &junk);
|
|
TEST_EXPECT_STATUS(e, AKERR_NULLPOINTER,
|
|
akgl_get_json_with_default(keyerr, (void *)&defval, NULL, sizeof(int)),
|
|
"with_default with a NULL destination");
|
|
keyerr = NULL;
|
|
} CLEANUP {
|
|
if ( keyerr != NULL ) {
|
|
keyerr->handled = true;
|
|
keyerr = akerr_release_error(keyerr);
|
|
}
|
|
if ( typeerr != NULL ) {
|
|
typeerr->handled = true;
|
|
typeerr = akerr_release_error(typeerr);
|
|
}
|
|
} PROCESS(e) {
|
|
} FINISH(e, true);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "dummy");
|
|
SDL_SetHint(SDL_HINT_AUDIO_DRIVER, "dummy");
|
|
|
|
ATTEMPT {
|
|
CATCH(errctx, akgl_error_init());
|
|
TEST_TRAP_UNHANDLED_ERRORS();
|
|
CATCH(errctx, akgl_heap_init());
|
|
CATCH(errctx, akgl_registry_init());
|
|
CATCH(errctx, load_fixture());
|
|
|
|
CATCH(errctx, test_json_accessor_null_arguments());
|
|
CATCH(errctx, test_json_scalar_accessors());
|
|
CATCH(errctx, test_json_double_value());
|
|
CATCH(errctx, test_json_string_accessor());
|
|
CATCH(errctx, test_json_array_index_accessors());
|
|
CATCH(errctx, test_json_type_and_key_errors());
|
|
CATCH(errctx, test_json_with_default());
|
|
} CLEANUP {
|
|
if ( fixture != NULL ) {
|
|
json_decref(fixture);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH_NORETURN(errctx);
|
|
}
|