Repair embedded dependency build and test setup

Isolate vendored test registration, namespace project error codes, and update dependency revisions. Fix mutable sprite path handling, out-of-tree fixtures, and charviewer initialization while documenting the outstanding character-to-sprite refcount bug.

Co-authored-by: Codex (GPT-5) <noreply@openai.com>
This commit is contained in:
2026-07-29 18:01:05 -04:00
parent 74867ea82e
commit cf9ebb206f
22 changed files with 209 additions and 64 deletions

4
.gitmodules vendored
View File

@@ -15,10 +15,10 @@
url = git@github.com:libsdl-org/SDL_ttf.git
[submodule "deps/libsdlerror"]
path = deps/libakerror
url = https://source.home.aklabs.net/andrew/libsdlerror.git
url = https://source.starfort.tech/andrew/libakerror.git
[submodule "deps/libakstdlib"]
path = deps/libakstdlib
url = https://source.home.aklabs.net/andrew/libakstdlib.git
url = https://source.starfort.tech/andrew/libakstdlib.git
[submodule "deps/jansson"]
path = deps/jansson
url = git@github.com:akheron/jansson.git

44
AGENTS.md Normal file
View File

@@ -0,0 +1,44 @@
# Repository Guidelines
## Project Structure & Module Organization
This is a C library intended to support the development of video games. Public C headers live in `include/akgl/`; keep declarations there aligned with their implementations in `src/`. Tests are standalone C programs under `tests/`, with JSON, image, and map fixtures in `tests/assets/`. The `util/` directory contains the `charviewer` utility and its sample assets. Third-party and companion libraries are vendored in `deps/`. Treat `build/`, generated `akgl.pc` files, and `include/akgl/SDL_GameControllerDB.h` as build outputs rather than hand-maintained source.
## Build, Test, and Development Commands
Configure an out-of-tree development build:
```sh
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
```
Build all library, utility, and test targets:
```sh
cmake --build build --parallel
```
Run the complete test suite with failure details:
```sh
ctest --test-dir build --output-on-failure
```
Run one test while iterating, for example `ctest --test-dir build -R sprite --output-on-failure`. The `rebuild.sh` script also installs into a developer-specific `/home/andrew/local` prefix and removes existing outputs; prefer the portable commands above unless that exact workflow is intended.
## Coding Style & Naming Conventions
Follow the surrounding C style: braces on the next line for function bodies, spaces inside control-flow parentheses, and short, focused functions. Preserve the indentation of the file being edited; older files contain both spaces and tabs. Public symbols use the `akgl_` prefix, public types use `akgl_TypeName`, and constants/macros use `AKGL_UPPER_SNAKE_CASE`. Name matching header/source pairs by feature, such as `include/akgl/sprite.h` and `src/sprite.c`. No repository-wide formatter or linter is configured, so avoid unrelated formatting churn.
## Rules
- Add yourself (agent program name, model name and version) as a co-author on every commit message
- Avoid dynamic memory allocation. Use the `akgl_heap_*` methods to retrieve necessary objects at runtime, and to release them when done. If the akgl heap facilities don't provide the kind of object needed, create a new heap layer to support that type of object.
## Testing Guidelines
Tests use simple executable return codes and are registered through CTest in `CMakeLists.txt`; there is no declared coverage threshold. Add focused tests as `tests/<feature>.c`, create a matching `test_<feature>` target, and register it with `add_test`. Put reusable fixtures in `tests/assets/` and keep paths compatible with tests launched from the build tree.
## Commit & Pull Request Guidelines
Recent commits use concise, imperative summaries such as `Fix a scale bug...` and often explain related changes in one sentence. Keep each commit scoped and describe user-visible behavior. Pull requests should summarize the change, identify affected modules, list the CMake/CTest commands run, and link relevant issues. Include screenshots only for rendering, map, or `charviewer` changes.

View File

@@ -4,7 +4,25 @@ project(akgl LANGUAGES C)
include(CTest)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
# Vendored projects own their test suites. Suppress their CTest registration
# while embedded so the top-level suite contains only targets built here.
set(AKGL_SUPPRESS_DEPENDENCY_TESTS TRUE)
function(add_test)
if(NOT AKGL_SUPPRESS_DEPENDENCY_TESTS)
_add_test(${ARGV})
endif()
endfunction()
function(set_tests_properties)
if(NOT AKGL_SUPPRESS_DEPENDENCY_TESTS)
_set_tests_properties(${ARGV})
endif()
endfunction()
set(JANSSON_WITHOUT_TESTS ON CACHE BOOL "Do not build vendored Jansson tests" FORCE)
set(JANSSON_EXAMPLES OFF CACHE BOOL "Do not build vendored Jansson examples" FORCE)
set(JANSSON_BUILD_DOCS OFF CACHE BOOL "Do not build vendored Jansson docs" FORCE)
add_subdirectory(deps/jansson EXCLUDE_FROM_ALL)
add_subdirectory(deps/libakerror EXCLUDE_FROM_ALL)
add_subdirectory(deps/libakstdlib EXCLUDE_FROM_ALL)
@@ -13,6 +31,13 @@ add_subdirectory(deps/SDL_image EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL_mixer EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL_ttf EXCLUDE_FROM_ALL)
# Reserve error-name table entries for libakgl-specific status codes.
target_compile_definitions(akerror PUBLIC
AKERR_MAX_ERR_VALUE=256
)
set(AKGL_SUPPRESS_DEPENDENCY_TESTS FALSE)
else()
find_package(PkgConfig REQUIRED)
@@ -48,6 +73,11 @@ set(libdir "\${exec_prefix}/lib")
set(includedir "\${prefix}/include")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/akgl.pc.in ${CMAKE_CURRENT_BINARY_DIR}/akgl.pc @ONLY)
# Tests use both relative paths and SDL_GetBasePath(), so stage fixtures beside
# test executables in every out-of-tree build.
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/tests/assets"
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(
OUTPUT ${GAMECONTROLLERDB_H}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/mkcontrollermappings.sh ${CMAKE_CURRENT_SOURCE_DIR}
@@ -100,6 +130,11 @@ add_test(NAME tilemap COMMAND test_tilemap)
add_test(NAME util COMMAND test_util)
add_test(NAME semver_unit COMMAND test_semver_unit)
set_tests_properties(
actor bitmasks character registry sprite staticstring tilemap util semver_unit
PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests"
)
# Specify include directories for the library's headers (if applicable)
target_include_directories(akgl PUBLIC
include/

15
TODO.md Normal file
View File

@@ -0,0 +1,15 @@
# TODO
1. **Make character-to-sprite state bindings release their references symmetrically.**
`akgl_character_sprite_add()` increments the sprite refcount for every state-map
binding, but no corresponding removal API exists. Replacing a state binding
also leaves the previous sprite's refcount incremented. When
`akgl_heap_release_character()` drops the character refcount to zero, it clears
the character registry entry and zeroes the structure without enumerating
`state_sprites`, decrementing the bound sprites, or destroying the SDL property
map. Implement binding removal/replacement so each removed binding releases
exactly one sprite reference. On final character release, enumerate every
remaining binding (the existing `akgl_character_state_sprites_iterate()` release
path may be reusable), release each reference, destroy `state_sprites`, and then
clear the character. Add tests for removal, replacement, duplicate sprite
bindings across multiple states, and final character release.

View File

@@ -12,7 +12,10 @@
#define RESTORE_GCC_WARNINGS \
_Pragma("GCC diagnostic pop")
#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1
#define AKGL_ERR_LOGICINTERRUPT AKERR_LAST_ERRNO_VALUE + 2
#define AKGL_ERR_SDL (AKERR_LAST_ERRNO_VALUE + 18)
#define AKGL_ERR_REGISTRY (AKERR_LAST_ERRNO_VALUE + 19)
#define AKGL_ERR_HEAP (AKERR_LAST_ERRNO_VALUE + 20)
#define AKGL_ERR_BEHAVIOR (AKERR_LAST_ERRNO_VALUE + 21)
#define AKGL_ERR_LOGICINTERRUPT (AKERR_LAST_ERRNO_VALUE + 22)
#endif // _ERROR_H_

View File

@@ -274,6 +274,36 @@ akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child)
FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Parent object has no remaining child slots left");
}
void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name)
{
PREPARE_ERROR(errctx);
akgl_Iterator *opflags = (akgl_Iterator *)userdata;
akgl_Actor *obj = NULL;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, name, AKERR_NULLPOINTER, "registry_iterate_actor received NULL property name");
FAIL_ZERO_BREAK(errctx, opflags, AKERR_NULLPOINTER, "received NULL iterator flags");
obj = SDL_GetPointerProperty(registry, name, NULL);
FAIL_ZERO_BREAK(errctx, obj, AKERR_KEY, "registry_iterate_actor received property name that was not in the registry");
if (AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) && obj->layer != opflags->layerid) {
break;
}
if (AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_UPDATE)) {
CATCH(errctx, obj->updatefunc(obj));
}
if (AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_TILEMAPSCALE)) {
CATCH(errctx, akgl_tilemap_scale_actor(gamemap, obj));
} else {
obj->scale = 1.0;
}
if (AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_RENDER)) {
CATCH(errctx, obj->renderfunc(obj));
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);

View File

@@ -277,7 +277,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, cha
controlmap->jsid = jsid;
controlmap->target = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, actorname, NULL);
FAIL_ZERO_BREAK(errctx, controlmap->target, AKERR_REGISTRY, "Actor %s not found in registry", actorname);
FAIL_ZERO_BREAK(errctx, controlmap->target, AKGL_ERR_REGISTRY, "Actor %s not found in registry", actorname);
// ---- KEYBOARD CONTROLS ----

View File

@@ -20,6 +20,10 @@ akerr_ErrorContext *akgl_heap_init()
PREPARE_ERROR(errctx);
int i = 0;
akerr_name_for_status(AKGL_ERR_SDL, "SDL Error");
akerr_name_for_status(AKGL_ERR_REGISTRY, "Registry Error");
akerr_name_for_status(AKGL_ERR_HEAP, "Heap Error");
akerr_name_for_status(AKGL_ERR_BEHAVIOR, "Behavior Error");
akerr_name_for_status(AKGL_ERR_LOGICINTERRUPT, "Logic Interrupt");
PASS(errctx, akgl_heap_init_actor());
for ( i = 0; i < AKGL_MAX_HEAP_SPRITE; i++) {
memset(&HEAP_SPRITE[i], 0x00, sizeof(akgl_Sprite));
@@ -55,7 +59,7 @@ akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest)
*dest = &HEAP_ACTOR[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused actor on the heap");
FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused actor on the heap");
}
akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest)
@@ -68,7 +72,7 @@ akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest)
*dest = &HEAP_SPRITE[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused sprite on the heap");
FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused sprite on the heap");
}
akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest)
@@ -81,7 +85,7 @@ akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest)
*dest = &HEAP_SPRITESHEET[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused spritesheet on the heap");
FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused spritesheet on the heap");
}
akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest)
@@ -94,7 +98,7 @@ akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest)
*dest = &HEAP_CHARACTER[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused character on the heap");
FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused character on the heap");
}
akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest)
@@ -108,7 +112,7 @@ akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest)
HEAP_STRING[i].refcount += 1;
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused string on the heap");
FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused string on the heap");
}
akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr)

View File

@@ -58,7 +58,7 @@ static akerr_ErrorContext *akgl_sprite_load_json_spritesheet(json_t *json, akgl_
if ( *sheet == NULL ) {
CATCH(errctx, akgl_heap_next_spritesheet(sheet));
CATCH(errctx, akgl_get_json_integer_value((json_t *)spritesheet_json, "frame_width", &ss_frame_width));
CATCH(errctx, akgl_get_json_integer_value((json_t *)spritesheet_json, "frame_height", &ss_frame_width));
CATCH(errctx, akgl_get_json_integer_value((json_t *)spritesheet_json, "frame_height", &ss_frame_height));
CATCH(errctx,
akgl_spritesheet_initialize(
(akgl_SpriteSheet *)*sheet,
@@ -85,12 +85,20 @@ akerr_ErrorContext *akgl_sprite_load_json(char *filename)
akgl_Sprite *obj = NULL;
akgl_SpriteSheet *sheet = NULL;
akgl_String *spritename = NULL;
//string *tmpstr = NULL;
akgl_String *filename_copy = NULL;
int i = 0;
FAIL_ZERO_RETURN(errctx, filename, AKERR_NULLPOINTER, "Received null filename");
ATTEMPT {
CATCH(errctx, akgl_heap_next_sprite(&obj));
CATCH(errctx, akgl_heap_next_string(&filename_copy));
FAIL_NONZERO_BREAK(
errctx,
strlen(filename) >= AKGL_MAX_STRING_LENGTH,
AKERR_OUTOFBOUNDS,
"Sprite filename exceeds temporary string capacity"
);
SDL_strlcpy(filename_copy->data, filename, AKGL_MAX_STRING_LENGTH);
//CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, akgl_heap_next_string(&spritename));
@@ -105,7 +113,7 @@ akerr_ErrorContext *akgl_sprite_load_json(char *filename)
"Error while loading sprite from %s on line %d: %s", filename, error.line, error.text
);
CATCH(errctx, akgl_sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename)));
CATCH(errctx, akgl_sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename_copy->data)));
CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &spritename));
CATCH(errctx,
akgl_sprite_initialize(
@@ -132,6 +140,7 @@ akerr_ErrorContext *akgl_sprite_load_json(char *filename)
IGNORE(akgl_heap_release_spritesheet(sheet));
}
IGNORE(akgl_heap_release_string(spritename));
IGNORE(akgl_heap_release_string(filename_copy));
//IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);

View File

@@ -2,6 +2,7 @@
handle_unhandled_error(errctx);
#include <akerror.h>
#include <akgl/error.h>
#define UNHANDLED_ERROR_EXIT 0
#define UNHANDLED_ERROR_SET 1
@@ -42,7 +43,7 @@ akerr_ErrorContext *akgl_actor_update_noop(akgl_Actor *obj)
// Currently the renderer assumes there is a global variable named `renderer`
int akgl_actor_rendered;
akerr_ErrorContext *akgl_actor_render_noop(akgl_Actor *obj, SDL_Renderer *r)
akerr_ErrorContext *akgl_actor_render_noop(akgl_Actor *obj)
{
PREPARE_ERROR(errctx);
akgl_actor_rendered = 1;
@@ -129,13 +130,13 @@ akerr_ErrorContext *test_registry_actor_iterator_updaterender(void)
FAIL_ZERO_BREAK(
unhandled_error_context,
akgl_actor_updated,
AKERR_BEHAVIOR,
AKGL_ERR_BEHAVIOR,
"actor->updatefunc not called by the iterator"
);
FAIL_ZERO_BREAK(
unhandled_error_context,
akgl_actor_rendered,
AKERR_BEHAVIOR,
AKGL_ERR_BEHAVIOR,
"actor->renderfunc not called by the iterator"
);
} CLEANUP {
@@ -235,7 +236,7 @@ akerr_ErrorContext *test_actor_manage_children(void)
CATCH(errctx, parent->addchild(parent, child));
} CLEANUP {
if ( errctx == NULL ) {
FAIL(errctx, AKERR_BEHAVIOR, "addchild does not throw AKERR_RELATIONSHIP when child already has a parent");
FAIL(errctx, AKGL_ERR_BEHAVIOR, "addchild does not throw AKERR_RELATIONSHIP when child already has a parent");
}
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_RELATIONSHIP) {
@@ -248,7 +249,7 @@ akerr_ErrorContext *test_actor_manage_children(void)
CATCH(errctx, parent->addchild(parent, child));
} CLEANUP {
if ( errctx == NULL ) {
FAIL(errctx, AKERR_BEHAVIOR, "addchild does not throw AKERR_OUTOFBOUNDS when all children already set");
FAIL(errctx, AKGL_ERR_BEHAVIOR, "addchild does not throw AKERR_OUTOFBOUNDS when all children already set");
}
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_OUTOFBOUNDS) {

View File

@@ -1,7 +1,8 @@
{
"name": "testcharacter",
"velocity_x": 0.20,
"velocity_y": 0.20,
"movementspeed": 1,
"acceleration_x": 0.20,
"acceleration_y": 0.20,
"sprite_mappings": [
{
"state": [
@@ -12,7 +13,7 @@
},
{
"state": [
"ACTOR_STATE_DEAD"
"AKGL_ACTOR_STATE_DEAD"
],
"sprite": "testsprite2"
}

View File

@@ -1,6 +1,6 @@
{
"spritesheet": {
"filename": "assets/spritesheet.png",
"filename": "spritesheet.png",
"frame_width": 48,
"frame_height": 48
},

View File

@@ -1,6 +1,6 @@
{
"spritesheet": {
"filename": "assets/spritesheet.png",
"filename": "spritesheet.png",
"frame_width": 48,
"frame_height": 48
},

View File

@@ -50,6 +50,7 @@ akerr_ErrorContext *test_character_sprite_mgmt()
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, akgl_character_initialize(testchar, "testchar"));
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
@@ -89,6 +90,7 @@ akerr_ErrorContext *test_character_iterate_state_sprites()
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, akgl_character_initialize(testchar, "testchar"));
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
@@ -169,8 +171,8 @@ akerr_ErrorContext *test_akgl_character_load_json()
CATCH(errctx, testcharacter->sprite_get(testcharacter, AKGL_ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_DEAD");
FAIL_ZERO_BREAK(errctx, (testcharacter->vx != 0.200000003), AKERR_VALUE, "Wrong X velocity for test character");
FAIL_ZERO_BREAK(errctx, (testcharacter->vy != 0.200000003), AKERR_VALUE, "Wrong Y velocity for test character");
FAIL_ZERO_BREAK(errctx, (testcharacter->ax != 0.200000003), AKERR_VALUE, "Wrong X acceleration for test character");
FAIL_ZERO_BREAK(errctx, (testcharacter->ay != 0.200000003), AKERR_VALUE, "Wrong Y acceleration for test character");
// Release our handles on the sprites so the character's heap_release can reduce them to 0
CATCH(errctx, akgl_heap_release_sprite(testsprite));

View File

@@ -68,7 +68,7 @@ int main(void)
AKGL_REGISTRY_CHARACTER,
"little guy",
NULL);
FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKERR_REGISTRY, "Can't load character 'little guy' from the registry");
FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKGL_ERR_REGISTRY, "Can't load character 'little guy' from the registry");
actorptr->movement_controls_face = false;
actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT);
actorptr->x = 320;

View File

@@ -43,7 +43,7 @@ akerr_ErrorContext *test_akgl_registry_init(RegistryFuncPtr funcptr)
} PROCESS(errctx) {
} FINISH(errctx, true);
FAIL_RETURN(errctx, AKERR_BEHAVIOR, "SDL memory allocator fails but registry reports successful property creation");
FAIL_RETURN(errctx, AKGL_ERR_BEHAVIOR, "SDL memory allocator fails but registry reports successful property creation");
}
akerr_ErrorContext *test_akgl_registry_init_creation_failures(void)

View File

@@ -1,5 +1,6 @@
#include <string.h>
#include <akerror.h>
#include <akgl/error.h>
#include <akgl/heap.h>
#include <akgl/staticstring.h>
@@ -111,7 +112,7 @@ akerr_ErrorContext *test_akgl_string_initialize(void)
FAIL_NONZERO_BREAK(errctx, strcmp((char *)&ptr->data, "Test value"), AKERR_VALUE, "Expected 'Test value', got %s", (char *)&ptr->data);
CATCH(errctx, akgl_heap_release_string(NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Failure to properly handle NULL pointer");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Failure to properly handle NULL pointer");
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);

View File

@@ -26,7 +26,7 @@ akerr_ErrorContext *test_tilemap_akgl_get_json_tilemap_property(void)
AKGL_MAX_STRING_LENGTH,
"%s%s",
SDL_GetBasePath(),
"assets/snippets/test_tilemap_akgl_get_json_tilemap_property.json"
"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);

View File

@@ -1,5 +1,6 @@
#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/error.h>
#include <akgl/util.h>
akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
@@ -10,7 +11,7 @@ akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_rectangle_points(NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with all NULL pointers");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with all NULL pointers");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -19,7 +20,7 @@ akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_rectangle_points(NULL, &testrect));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL SDL_FRect pointer");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL SDL_FRect pointer");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -28,7 +29,7 @@ akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_rectangle_points(&points, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL RectanglePoints pointer");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL RectanglePoints pointer");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -63,7 +64,7 @@ akerr_ErrorContext *test_akgl_rectangle_points_math(void)
points.bottomright.y != 32 ) {
FAIL_BREAK(
errctx,
AKERR_BEHAVIOR,
AKGL_ERR_BEHAVIOR,
"akgl_rectangle_points incorrectly calculated points for {x=0, y=0, w=32, h=32} to {topleft={%d, %d}, topright={%d, %d}, bottomleft={%d, %d}, bottomright={%d, %d}}",
points.topleft.x, points.topleft.y,
points.topright.x, points.topright.y,
@@ -87,7 +88,7 @@ akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, *, NULL) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_point_rectangle(*, *, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -96,7 +97,7 @@ akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, NULL, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, NULL, *) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_point_rectangle(*, NULL, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -105,7 +106,7 @@ akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_point_rectangle(NULL, &testrectpoints, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, *, *) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, *, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -114,7 +115,7 @@ akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_point_rectangle(NULL, NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, NULL, NULL) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, NULL, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -142,14 +143,14 @@ akerr_ErrorContext *test_akgl_collide_point_rectangle_logic(void)
CATCH(errctx, akgl_rectangle_points(&testrectpoints, &testrect));
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
testpoint.x = 48;
testpoint.y = 48;
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == true ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Invalid collision reported");
}
} CLEANUP {
} PROCESS(errctx) {
@@ -167,7 +168,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, *, NULL) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_rectangles(*, *, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -176,7 +177,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_rectangles(&testrect1, NULL, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, NULL, *) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_rectangles(*, NULL, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -185,7 +186,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_rectangles(NULL, &testrect2, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, *, *) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_rectangles(NULL, *, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -194,7 +195,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
ATTEMPT {
CATCH(errctx, akgl_collide_rectangles(NULL, NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, NULL, NULL) failed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "akgl_collide_rectangles(NULL, NULL, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
@@ -222,14 +223,14 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
// Collision overlapping on the top left
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping on the top right
testrect1.x = 64;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping on the bottom left
@@ -237,7 +238,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.y = 32;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping on the bottom right
@@ -245,7 +246,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.y = 32;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the top edge
@@ -255,7 +256,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.h = 32;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the left edge
@@ -265,7 +266,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.h = 80;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the right edge
@@ -275,7 +276,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.h = 80;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the bottom edge
@@ -285,7 +286,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.h = 32;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Valid collision missed");
}
// Not colliding
@@ -295,7 +296,7 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
testrect1.h = 16;
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == true ) {
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
FAIL_BREAK(errctx, AKGL_ERR_BEHAVIOR, "Invalid collision reported");
}
} CLEANUP {

View File

@@ -48,12 +48,11 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
strcpy((char *)&game.name, "charviewer");
strcpy((char *)&game.version, "0.0.1");
strcpy((char *)&game.uri, "net.aklabs.libakgl.charviewer");
game.screenwidth = 640;
game.screenheight = 480;
CATCH(errctx, akgl_game_init());
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init());
CATCH(errctx, akgl_set_property("game.screenwidth", "640"));
CATCH(errctx, akgl_set_property("game.screenheight", "480"));
CATCH(errctx, akgl_render_init2d(renderer));
CATCH(errctx, akgl_physics_init_null(physics));
} CLEANUP {
} PROCESS(errctx) {
@@ -94,7 +93,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
AKGL_REGISTRY_CHARACTER,
"little guy",
NULL);
FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKERR_REGISTRY, "Can't load character 'little guy' from the registry");
FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKGL_ERR_REGISTRY, "Can't load character 'little guy' from the registry");
actorptr->movement_controls_face = false;
actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_DOWN);
actorptr->x = 320;
@@ -145,9 +144,9 @@ SDL_AppResult SDL_AppIterate(void *appstate)
AKGL_BITMASK_CLEAR(opflags.flags);
AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_UPDATE);
AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_RENDER);
akgl_draw_background(game.screenwidth, game.screenheight);
akgl_draw_background((int)camera->w, (int)camera->h);
ATTEMPT {
CATCH(errctx, akgl_tilemap_draw(renderer, (akgl_Tilemap *)&gamemap, &camera, i));
CATCH(errctx, akgl_tilemap_draw(gamemap, camera, i));
SDL_EnumerateProperties(AKGL_REGISTRY_ACTOR, &akgl_registry_iterate_actor, (void *)&opflags);
} CLEANUP {
} PROCESS(errctx) {
@@ -155,7 +154,7 @@ SDL_AppResult SDL_AppIterate(void *appstate)
LOG_ERROR(errctx);
return SDL_APP_FAILURE;
} FINISH_NORETURN(errctx);
SDL_RenderPresent(renderer);
SDL_RenderPresent(renderer->sdl_renderer);
return SDL_APP_CONTINUE;
}