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>
549 lines
18 KiB
C
549 lines
18 KiB
C
#include <SDL3/SDL.h>
|
|
#include <SDL3_image/SDL_image.h>
|
|
#include <jansson.h>
|
|
|
|
#include <akerror.h>
|
|
#include <akgl/util.h>
|
|
#include <akgl/heap.h>
|
|
#include <akgl/registry.h>
|
|
#include <akgl/tilemap.h>
|
|
#include <akgl/actor.h>
|
|
#include <akgl/game.h>
|
|
#include <akgl/json_helpers.h>
|
|
|
|
#include "testutil.h"
|
|
|
|
/**
|
|
* @brief akgl_get_json_tilemap_property must give back its scratch strings on every path.
|
|
*
|
|
* It claims two pool strings per call -- one for the property name it is
|
|
* comparing, one for the declared type -- and used to leave the block through a
|
|
* `SUCCEED_RETURN` from inside `ATTEMPT` on the path where it *found* what it
|
|
* was asked for. That returns past `CLEANUP`, so the ordinary, successful
|
|
* lookup was the leaking one, at two of the pool's 256 entries a time. A map
|
|
* load does this several times per layer.
|
|
*
|
|
* Three paths, run more times than the pool has entries: found, absent, and
|
|
* present-but-wrong-type. Against the old code the found loop exhausts the pool
|
|
* and comes back AKGL_ERR_HEAP long before it finishes.
|
|
*/
|
|
akerr_ErrorContext *test_tilemap_property_lookup_releases_strings(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
json_t *jsondoc = NULL;
|
|
json_error_t jsonerr;
|
|
akgl_String *pathstr = NULL;
|
|
int baseline = 0;
|
|
int propnum = 0;
|
|
int i = 0;
|
|
|
|
ATTEMPT {
|
|
CATCH(errctx, akgl_heap_next_string(&pathstr));
|
|
snprintf(
|
|
(char *)&pathstr->data,
|
|
AKGL_MAX_STRING_LENGTH,
|
|
"%s%s",
|
|
SDL_GetBasePath(),
|
|
"assets/snippets/test_tilemap_get_json_tilemap_property.json"
|
|
);
|
|
jsondoc = json_load_file((char *)&pathstr->data, 0, (json_error_t *)&jsonerr);
|
|
FAIL_ZERO_BREAK(errctx, jsondoc, AKERR_NULLPOINTER, "Failure loading json fixture: %s", (char *)jsonerr.text);
|
|
|
|
baseline = test_string_pool_used();
|
|
|
|
// The success path. Twice the pool size, so a leak of even one entry
|
|
// per call cannot finish.
|
|
for ( i = 0; i < (AKGL_MAX_HEAP_STRING * 2); i++ ) {
|
|
CATCH(errctx, akgl_get_json_properties_integer(jsondoc, "state", &propnum));
|
|
}
|
|
TEST_ASSERT(
|
|
errctx,
|
|
test_string_pool_used() == baseline,
|
|
"%d successful property lookups left %d pool strings claimed, expected %d",
|
|
(AKGL_MAX_HEAP_STRING * 2),
|
|
test_string_pool_used(),
|
|
baseline);
|
|
|
|
// The absent path, which reports AKERR_KEY from after FINISH.
|
|
for ( i = 0; i < (AKGL_MAX_HEAP_STRING * 2); i++ ) {
|
|
TEST_EXPECT_STATUS(
|
|
errctx,
|
|
AKERR_KEY,
|
|
akgl_get_json_properties_integer(jsondoc, "no_such_property", &propnum),
|
|
"looking up a property the object does not have");
|
|
}
|
|
TEST_ASSERT(
|
|
errctx,
|
|
test_string_pool_used() == baseline,
|
|
"absent-property lookups left %d pool strings claimed, expected %d",
|
|
test_string_pool_used(),
|
|
baseline);
|
|
|
|
// The wrong-type path, which fails from inside the ATTEMPT block with
|
|
// both scratch strings already claimed.
|
|
for ( i = 0; i < (AKGL_MAX_HEAP_STRING * 2); i++ ) {
|
|
TEST_EXPECT_STATUS(
|
|
errctx,
|
|
AKERR_TYPE,
|
|
akgl_get_json_properties_integer(jsondoc, "character", &propnum),
|
|
"reading a string property as an int");
|
|
}
|
|
TEST_ASSERT(
|
|
errctx,
|
|
test_string_pool_used() == baseline,
|
|
"wrong-type lookups left %d pool strings claimed, expected %d",
|
|
test_string_pool_used(),
|
|
baseline);
|
|
} CLEANUP {
|
|
if ( jsondoc != NULL ) {
|
|
json_decref(jsondoc);
|
|
jsondoc = NULL;
|
|
}
|
|
if ( pathstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_tilemap_akgl_get_json_tilemap_property(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
json_t *jsondoc = NULL;
|
|
json_error_t jsonerr;
|
|
akgl_String *tmpstr = NULL;
|
|
int propnum;
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_heap_next_string(&tmpstr));
|
|
snprintf(
|
|
(char *)&tmpstr->data,
|
|
AKGL_MAX_STRING_LENGTH,
|
|
"%s%s",
|
|
SDL_GetBasePath(),
|
|
"assets/snippets/test_tilemap_get_json_tilemap_property.json"
|
|
);
|
|
jsondoc = json_load_file((char *)&tmpstr->data, 0, (json_error_t *)&jsonerr);
|
|
FAIL_ZERO_BREAK(errctx, jsondoc, AKERR_NULLPOINTER, "Failure loading json fixture: %s", (char *)jsonerr.text);
|
|
CATCH(
|
|
errctx,
|
|
akgl_get_json_properties_string(
|
|
jsondoc,
|
|
"character",
|
|
&tmpstr
|
|
)
|
|
);
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
strcmp((char *)&tmpstr->data, "testcharacter"),
|
|
AKERR_VALUE,
|
|
"Incorrect value loaded from property `character` (`testcharacter` vs `%s`)",
|
|
(char *)&tmpstr->data
|
|
);
|
|
CATCH(
|
|
errctx,
|
|
akgl_get_json_properties_integer(
|
|
jsondoc,
|
|
"state",
|
|
&propnum
|
|
)
|
|
);
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
(propnum != 6),
|
|
AKERR_VALUE,
|
|
"Incorrect value loaded from property `state` (6 vs %d)",
|
|
propnum
|
|
);
|
|
} CLEANUP {
|
|
if ( tmpstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(tmpstr));
|
|
}
|
|
if ( jsondoc != NULL ) {
|
|
json_decref(jsondoc);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_compute_tileset_offsets(void)
|
|
{
|
|
int comparison_values[8] = {
|
|
0, // Tile 0 X
|
|
0, // Tile 0 Y
|
|
10, // Tile 1 X
|
|
0, // Tile 1 Y
|
|
0, // Tile 2 X
|
|
10, // Tile 2 Y
|
|
10, // Tile 2 X
|
|
10 // Tile 2 Y
|
|
};
|
|
int *comparison_ptrs[8] = {
|
|
&akgl_gamemap->tilesets[0].tile_offsets[0][0], // Tile 0 X
|
|
&akgl_gamemap->tilesets[0].tile_offsets[0][1], // Tile 0 Y
|
|
&akgl_gamemap->tilesets[0].tile_offsets[1][0], // Tile 1 X
|
|
&akgl_gamemap->tilesets[0].tile_offsets[1][1], // Tile 0 Y
|
|
&akgl_gamemap->tilesets[0].tile_offsets[2][0], // Tile 2 X
|
|
&akgl_gamemap->tilesets[0].tile_offsets[2][1], // Tile 2 Y
|
|
&akgl_gamemap->tilesets[0].tile_offsets[3][0], // Tile 3 X
|
|
&akgl_gamemap->tilesets[0].tile_offsets[3][1], // Tile 3 Y
|
|
};
|
|
int i = 0;
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
akgl_gamemap->tilesets[0].tilecount = 4;
|
|
akgl_gamemap->tilesets[0].columns = 2;
|
|
akgl_gamemap->tilesets[0].firstgid = 1;
|
|
akgl_gamemap->tilesets[0].imageheight = 20;
|
|
akgl_gamemap->tilesets[0].imagewidth = 20;
|
|
akgl_gamemap->tilesets[0].tilecount = 4;
|
|
akgl_gamemap->tilesets[0].tileheight = 10;
|
|
akgl_gamemap->tilesets[0].tilewidth = 10;
|
|
akgl_gamemap->tilesets[0].spacing = 0;
|
|
akgl_gamemap->tilesets[0].margin = 0;
|
|
|
|
PREPARE_ERROR(errctx);
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_tilemap_compute_tileset_offsets(akgl_gamemap, 0));
|
|
// Tile 0 X offset
|
|
for ( i = 0; i < 8; i++ ) {
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
(*comparison_ptrs[i] != comparison_values[i]),
|
|
AKERR_VALUE,
|
|
"Tile offset incorrectly calculated for index %d (%d vs %d)",
|
|
i,
|
|
*comparison_ptrs[i],
|
|
comparison_values[i]
|
|
);
|
|
}
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_load_layer_objects(void)
|
|
{
|
|
akgl_String *pathstr;
|
|
PREPARE_ERROR(errctx);
|
|
json_t *doc = NULL;
|
|
json_t *layers = NULL;
|
|
json_t *objectlayer = NULL;
|
|
json_error_t errdata;
|
|
akgl_Actor *testactor;
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_heap_next_string(&pathstr));
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
|
|
doc = json_load_file((char *)&pathstr->data, 0, &errdata);
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets");
|
|
FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
|
|
CATCH(errctx, akgl_get_json_array_value(doc, "layers", &layers));
|
|
CATCH(errctx, akgl_get_json_array_index_object(layers, 1, &objectlayer));
|
|
CATCH(errctx, akgl_tilemap_load_layer_objects(akgl_gamemap, objectlayer, 1, pathstr));
|
|
|
|
testactor = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "testactor", NULL);
|
|
FAIL_ZERO_BREAK(
|
|
errctx,
|
|
testactor,
|
|
AKERR_NULLPOINTER,
|
|
"Test Actor was not loaded from the test map"
|
|
);
|
|
if ( (testactor->basechar != SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testcharacter", NULL)) ||
|
|
(testactor->layer != 1) ||
|
|
(testactor->state != 6) ||
|
|
(testactor->visible != true) ||
|
|
(testactor->x != 16) ||
|
|
(testactor->y != 16) ) {
|
|
FAIL_BREAK(errctx, AKERR_VALUE, "Test actor was loaded with incorrect values (check gdb)");
|
|
}
|
|
} CLEANUP {
|
|
if ( pathstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
}
|
|
if ( doc != NULL ) {
|
|
json_decref(doc);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_load_layer_tile(void)
|
|
{
|
|
akgl_String *pathstr;
|
|
PREPARE_ERROR(errctx);
|
|
json_t *doc = NULL;
|
|
json_t *layers = NULL;
|
|
json_t *tilelayer = NULL;
|
|
json_t *tiledata = NULL;
|
|
json_error_t errdata;
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_heap_next_string(&pathstr));
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
|
|
doc = json_load_file((char *)&pathstr->data, 0, &errdata);
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets");
|
|
FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
|
|
CATCH(errctx, akgl_get_json_array_value(doc, "layers", &layers));
|
|
CATCH(errctx, akgl_get_json_array_index_object(layers, 0, &tilelayer));
|
|
CATCH(errctx, akgl_get_json_array_value(tilelayer, "data", &tiledata));
|
|
CATCH(errctx, akgl_tilemap_load_layer_tile(akgl_gamemap, tilelayer, 0, pathstr));
|
|
if ( (akgl_gamemap->layers[0].data[0] != 1) ||
|
|
(akgl_gamemap->layers[0].data[1] != 2) ||
|
|
(akgl_gamemap->layers[0].data[2] != 3) ||
|
|
(akgl_gamemap->layers[0].data[3] != 4) ) {
|
|
FAIL_BREAK(errctx, AKERR_VALUE, "Test tilemap layer 0 tiles loaded with incorrect values (check gdb)");
|
|
}
|
|
} CLEANUP {
|
|
if ( pathstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
}
|
|
if ( doc != NULL ) {
|
|
json_decref(doc);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_load_layers(void)
|
|
{
|
|
akgl_String *pathstr;
|
|
PREPARE_ERROR(errctx);
|
|
json_t *doc = NULL;
|
|
json_error_t errdata;
|
|
int i = 0;
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_heap_next_string(&pathstr));
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
|
|
doc = json_load_file((char *)&pathstr->data, 0, &errdata);
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets");
|
|
FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
|
|
CATCH(errctx, akgl_tilemap_load_layers(akgl_gamemap, doc, pathstr));
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
(akgl_gamemap->numlayers != 3),
|
|
AKERR_VALUE,
|
|
"Map layer count incorrect"
|
|
);
|
|
for ( i = 0; i < akgl_gamemap->numlayers; i++ ) {
|
|
if ( (akgl_gamemap->layers[i].opacity != 1) ||
|
|
(akgl_gamemap->layers[i].visible != true) ||
|
|
(akgl_gamemap->layers[i].id != (i + 1)) ||
|
|
(akgl_gamemap->layers[i].x != 0) ||
|
|
(akgl_gamemap->layers[i].y != 0) ) {
|
|
FAIL(errctx, AKERR_VALUE, "Map layer data loaded incorrectly (see gdb)");
|
|
goto _test_akgl_tilemap_load_layers_cleanup;
|
|
}
|
|
}
|
|
// Layer 2 should have 1 object loaded
|
|
if ( (akgl_gamemap->layers[1].objects[0].actorptr != SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "testactor", NULL)) ||
|
|
(akgl_gamemap->layers[1].objects[1].name[0] != '\0' ) ||
|
|
(akgl_gamemap->layers[1].objects[1].id != 0) ) {
|
|
FAIL_BREAK(errctx, AKERR_VALUE, "Map layer 2 should have 1 loaded object (testactor) and nothing else (see gdb)");
|
|
}
|
|
// Layer 1 and 3 should have no objects
|
|
for ( i = 0; i < AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) {
|
|
if ( akgl_gamemap->layers[0].objects[i].id != 0 ) {
|
|
FAIL(errctx, AKERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects");
|
|
goto _test_akgl_tilemap_load_layers_cleanup;
|
|
}
|
|
}
|
|
for ( i = 0; i < AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) {
|
|
if ( akgl_gamemap->layers[2].objects[i].id != 0 ) {
|
|
FAIL(errctx, AKERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects");
|
|
goto _test_akgl_tilemap_load_layers_cleanup;
|
|
}
|
|
}
|
|
// Layers 1 and 3 should have tile data
|
|
if ( (akgl_gamemap->layers[0].data[0] != 1) ||
|
|
(akgl_gamemap->layers[0].data[1] != 2) ||
|
|
(akgl_gamemap->layers[0].data[2] != 3) ||
|
|
(akgl_gamemap->layers[0].data[3] != 4) ||
|
|
(akgl_gamemap->layers[2].data[0] != 0) ||
|
|
(akgl_gamemap->layers[2].data[1] != 5) ||
|
|
(akgl_gamemap->layers[2].data[2] != 0) ||
|
|
(akgl_gamemap->layers[2].data[3] != 6)
|
|
) {
|
|
FAIL_BREAK(errctx, AKERR_VALUE, "Map layers 1 and 3 should have tile data but it is incorrect");
|
|
}
|
|
_test_akgl_tilemap_load_layers_cleanup:
|
|
} CLEANUP {
|
|
if ( pathstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
}
|
|
if ( doc != NULL ) {
|
|
json_decref(doc);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_load_tilesets(void)
|
|
{
|
|
akgl_String *pathstr = NULL;
|
|
PREPARE_ERROR(errctx);
|
|
json_t *doc = NULL;
|
|
json_error_t errdata;
|
|
SDL_Texture *image = NULL;
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_heap_next_string(&pathstr));
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
|
|
doc = json_load_file((char *)&pathstr->data, 0, &errdata);
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets");
|
|
FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
|
|
CATCH(errctx, akgl_tilemap_load_tilesets(akgl_gamemap, doc, pathstr));
|
|
FAIL_NONZERO_BREAK(errctx, (akgl_gamemap->numtilesets != 1), AKERR_VALUE, "Incorrect number of tilesets loaded for map");
|
|
if ( (akgl_gamemap->tilesets[0].columns != 48 ) ||
|
|
(akgl_gamemap->tilesets[0].firstgid != 1) ||
|
|
(akgl_gamemap->tilesets[0].imageheight != 576) ||
|
|
(akgl_gamemap->tilesets[0].imagewidth != 768) ||
|
|
(akgl_gamemap->tilesets[0].margin != 0) ||
|
|
(akgl_gamemap->tilesets[0].spacing != 0) ||
|
|
(akgl_gamemap->tilesets[0].tilecount != 1728) ||
|
|
(akgl_gamemap->tilesets[0].tileheight != 16) ||
|
|
(akgl_gamemap->tilesets[0].tilewidth != 16) ) {
|
|
FAIL_BREAK(errctx, AKERR_VALUE, "Tileset loaded with incorrect values");
|
|
}
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
strcmp((char *)&akgl_gamemap->tilesets[0].name, "World_A1"),
|
|
AKERR_VALUE,
|
|
"Tileset loaded with incorrect name");
|
|
|
|
snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/World_A1.png");
|
|
image = IMG_LoadTexture(akgl_renderer->sdl_renderer, (char *)&pathstr->data);
|
|
FAIL_ZERO_BREAK(errctx, image, AKGL_ERR_SDL, "Failed to load comparison image");
|
|
|
|
CATCH(
|
|
errctx,
|
|
akgl_render_and_compare(
|
|
akgl_gamemap->tilesets[0].texture,
|
|
image,
|
|
0, 0, 768, 576,
|
|
"test_akgl_tilemap_loaded_tileset.png")
|
|
);
|
|
|
|
} CLEANUP {
|
|
if ( pathstr != NULL ) {
|
|
IGNORE(akgl_heap_release_string(pathstr));
|
|
}
|
|
if ( doc != NULL ) {
|
|
json_decref(doc);
|
|
}
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_load(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
memset((void *)akgl_gamemap, 0x00, sizeof(akgl_Tilemap));
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
CATCH(errctx, akgl_tilemap_load("assets/testmap.tmj", akgl_gamemap));
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_draw(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
|
|
akerr_ErrorContext *test_akgl_tilemap_draw_tileset(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
ATTEMPT {
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
ATTEMPT {
|
|
CATCH(errctx, akgl_error_init());
|
|
TEST_TRAP_UNHANDLED_ERRORS();
|
|
akgl_gamemap = &akgl_default_gamemap;
|
|
akgl_renderer = &akgl_default_renderer;
|
|
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
|
|
FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
|
|
}
|
|
|
|
if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_sprite", 768, 576, 0, &akgl_window, &akgl_renderer->sdl_renderer)) {
|
|
FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
|
|
}
|
|
akgl_renderer->draw_texture = &akgl_render_2d_draw_texture;
|
|
|
|
CATCH(errctx, akgl_registry_init());
|
|
CATCH(errctx, akgl_heap_init());
|
|
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
|
|
CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
|
|
CATCH(errctx, akgl_character_load_json("assets/testcharacter.json"));
|
|
|
|
CATCH(errctx, test_tilemap_akgl_get_json_tilemap_property());
|
|
CATCH(errctx, test_tilemap_property_lookup_releases_strings());
|
|
CATCH(errctx, test_akgl_tilemap_compute_tileset_offsets());
|
|
CATCH(errctx, test_akgl_tilemap_load_layer_objects());
|
|
CATCH(errctx, test_akgl_tilemap_load_layer_tile());
|
|
CATCH(errctx, test_akgl_tilemap_load_layers());
|
|
CATCH(errctx, test_akgl_tilemap_load_tilesets());
|
|
//CATCH(errctx, test_akgl_tilemap_load());
|
|
//CATCH(errctx, test_akgl_tilemap_draw_tileset());
|
|
//CATCH(errctx, test_akgl_tilemap_draw());
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH_NORETURN(errctx);
|
|
|
|
}
|