Add physics, heap, json_helpers, game, and actor test suites
Raise line coverage from 39.6 to 61.8 percent with four new suites and an
extension to the actor suite, and register every suite through a single
CMake list so a new test file cannot be left out of the coverage fixture.
Give the test targets a build-tree RPATH and prepend the build tree to
LD_LIBRARY_PATH for CTest, so a developer with a previously installed
libakgl.so exercises the library that was just compiled.
Fix six defects the new tests exposed:
- akgl_physics_simulate read self->gravity_time before its NULL check, so a
NULL backend crashed instead of reporting AKERR_NULLPOINTER.
- akgl_game_save transposed CLEANUP and PROCESS, which placed the fclose
inside the PROCESS switch. An ordinary save never flushed or closed its
stream and produced an empty file.
- akgl_game_save_actors wrote each name table terminator from the address of
a single char, emitting stack contents into the save file and a sentinel
the loader could not recognize.
- akgl_game_load_objectnamemap used CATCH directly inside while(1), where the
break leaves the loop rather than propagating, so a truncated name table
loaded as a successful game.
- akgl_Actor_cmhf_up_on and _down_on dereferenced actor->basechar with no
NULL check, unlike their left and right counterparts.
- akgl_actor_logic_movement checked actor twice instead of checking
actor->basechar before dereferencing it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 02:03:21 -04:00
|
|
|
/**
|
|
|
|
|
* @file testutil.h
|
|
|
|
|
* @brief Shared assertion helpers and headless bootstrap for the libakgl test suites.
|
|
|
|
|
*
|
|
|
|
|
* The akerror ATTEMPT/CATCH/PROCESS/FINISH macros are verbose when what a test
|
|
|
|
|
* wants to say is "this call must fail with exactly this status". These helpers
|
|
|
|
|
* wrap that pattern.
|
|
|
|
|
*
|
|
|
|
|
* All of the TEST_* assertion macros expand to a `break` on failure, so they
|
|
|
|
|
* must be used directly inside an ATTEMPT block. Inside a `for` or `while`
|
|
|
|
|
* nested in an ATTEMPT they would break the inner loop instead; use
|
|
|
|
|
* TEST_ASSERT_FLAG in that case and check the flag after the loop.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef _AKGL_TESTUTIL_H_
|
|
|
|
|
#define _AKGL_TESTUTIL_H_
|
|
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
|
#include <akerror.h>
|
|
|
|
|
#include <akgl/error.h>
|
|
|
|
|
|
|
|
|
|
/** @brief Fail the enclosing ATTEMPT block unless @p cond holds. */
|
Reindent all C sources to the canonical stroustrup style
Mechanical whitespace-only change across src/, include/, tests/, and
util/, applied with scripts/reindent.sh. No behavioral change.
Most files already conformed. The genuine outliers indented at 2 columns
(json_helpers.c, util.c from akgl_rectangle_points onward, assets.c,
staticstring.c, akgl_actor_add_child, util.h, staticstring.h) or used
spaces where the canonical form is a tab (draw.c). cc-mode also re-aligns
line-continuation backslashes in multi-line macros to its default
c-backslash-column, which is required for the tree to be a fixed point of
the indenter.
Verified whitespace-only: `git diff -w` over this change is empty apart
from three trailing blank lines removed at EOF in src/heap.c,
src/registry.c, and tests/tilemap.c. The build produces no new errors and
ctest is unchanged at 13/14, with `character` still the one intentional
failure.
The tree is now a fixed point of `scripts/reindent.sh --check`.
include/akgl/SDL_GameControllerDB.h is generated and excluded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:37 -04:00
|
|
|
#define TEST_ASSERT(e, cond, ...) \
|
|
|
|
|
if ( ! (cond) ) { \
|
|
|
|
|
FAIL_BREAK(e, AKGL_ERR_BEHAVIOR, __VA_ARGS__); \
|
Add physics, heap, json_helpers, game, and actor test suites
Raise line coverage from 39.6 to 61.8 percent with four new suites and an
extension to the actor suite, and register every suite through a single
CMake list so a new test file cannot be left out of the coverage fixture.
Give the test targets a build-tree RPATH and prepend the build tree to
LD_LIBRARY_PATH for CTest, so a developer with a previously installed
libakgl.so exercises the library that was just compiled.
Fix six defects the new tests exposed:
- akgl_physics_simulate read self->gravity_time before its NULL check, so a
NULL backend crashed instead of reporting AKERR_NULLPOINTER.
- akgl_game_save transposed CLEANUP and PROCESS, which placed the fclose
inside the PROCESS switch. An ordinary save never flushed or closed its
stream and produced an empty file.
- akgl_game_save_actors wrote each name table terminator from the address of
a single char, emitting stack contents into the save file and a sentinel
the loader could not recognize.
- akgl_game_load_objectnamemap used CATCH directly inside while(1), where the
break leaves the loop rather than propagating, so a truncated name table
loaded as a successful game.
- akgl_Actor_cmhf_up_on and _down_on dereferenced actor->basechar with no
NULL check, unlike their left and right counterparts.
- akgl_actor_logic_movement checked actor twice instead of checking
actor->basechar before dereferencing it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 02:03:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Run @p stmt and require that it reports exactly @p expected.
|
|
|
|
|
*
|
|
|
|
|
* Releases whatever context @p stmt returns, so a test can assert many failure
|
|
|
|
|
* paths in a row without draining AKERR_ARRAY_ERROR. Pass 0 for @p expected to
|
|
|
|
|
* require success.
|
|
|
|
|
*/
|
Reindent all C sources to the canonical stroustrup style
Mechanical whitespace-only change across src/, include/, tests/, and
util/, applied with scripts/reindent.sh. No behavioral change.
Most files already conformed. The genuine outliers indented at 2 columns
(json_helpers.c, util.c from akgl_rectangle_points onward, assets.c,
staticstring.c, akgl_actor_add_child, util.h, staticstring.h) or used
spaces where the canonical form is a tab (draw.c). cc-mode also re-aligns
line-continuation backslashes in multi-line macros to its default
c-backslash-column, which is required for the tree to be a fixed point of
the indenter.
Verified whitespace-only: `git diff -w` over this change is empty apart
from three trailing blank lines removed at EOF in src/heap.c,
src/registry.c, and tests/tilemap.c. The build produces no new errors and
ctest is unchanged at 13/14, with `character` still the one intentional
failure.
The tree is now a fixed point of `scripts/reindent.sh --check`.
include/akgl/SDL_GameControllerDB.h is generated and excluded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:37 -04:00
|
|
|
#define TEST_EXPECT_STATUS(e, expected, stmt, desc) \
|
|
|
|
|
{ \
|
|
|
|
|
akerr_ErrorContext *__tec = (stmt); \
|
|
|
|
|
int __tst = ( __tec == NULL ) ? 0 : __tec->status; \
|
|
|
|
|
if ( __tec != NULL ) { \
|
|
|
|
|
__tec->handled = true; \
|
|
|
|
|
__tec = akerr_release_error(__tec); \
|
|
|
|
|
} \
|
|
|
|
|
if ( __tst != (expected) ) { \
|
|
|
|
|
FAIL_BREAK( \
|
|
|
|
|
e, \
|
|
|
|
|
AKGL_ERR_BEHAVIOR, \
|
|
|
|
|
"%s: expected status %d (%s), got %d (%s)", \
|
|
|
|
|
desc, \
|
|
|
|
|
(int)(expected), \
|
|
|
|
|
akerr_name_for_status((int)(expected), NULL), \
|
|
|
|
|
__tst, \
|
|
|
|
|
akerr_name_for_status(__tst, NULL)); \
|
|
|
|
|
} \
|
Add physics, heap, json_helpers, game, and actor test suites
Raise line coverage from 39.6 to 61.8 percent with four new suites and an
extension to the actor suite, and register every suite through a single
CMake list so a new test file cannot be left out of the coverage fixture.
Give the test targets a build-tree RPATH and prepend the build tree to
LD_LIBRARY_PATH for CTest, so a developer with a previously installed
libakgl.so exercises the library that was just compiled.
Fix six defects the new tests exposed:
- akgl_physics_simulate read self->gravity_time before its NULL check, so a
NULL backend crashed instead of reporting AKERR_NULLPOINTER.
- akgl_game_save transposed CLEANUP and PROCESS, which placed the fclose
inside the PROCESS switch. An ordinary save never flushed or closed its
stream and produced an empty file.
- akgl_game_save_actors wrote each name table terminator from the address of
a single char, emitting stack contents into the save file and a sentinel
the loader could not recognize.
- akgl_game_load_objectnamemap used CATCH directly inside while(1), where the
break leaves the loop rather than propagating, so a truncated name table
loaded as a successful game.
- akgl_Actor_cmhf_up_on and _down_on dereferenced actor->basechar with no
NULL check, unlike their left and right counterparts.
- akgl_actor_logic_movement checked actor twice instead of checking
actor->basechar before dereferencing it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 02:03:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @brief Require that @p stmt succeeds, reporting @p desc if it does not. */
|
|
|
|
|
#define TEST_EXPECT_OK(e, stmt, desc) \
|
|
|
|
|
TEST_EXPECT_STATUS(e, 0, stmt, desc)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Require that @p stmt fails, without pinning which status it reports.
|
|
|
|
|
*
|
|
|
|
|
* For paths that are delegated to a dependency, where the exact status is that
|
|
|
|
|
* dependency's business and asserting it would make the test brittle.
|
|
|
|
|
*/
|
|
|
|
|
#define TEST_EXPECT_ANY_ERROR(e, stmt, desc) \
|
|
|
|
|
{ \
|
|
|
|
|
akerr_ErrorContext *__tec = (stmt); \
|
|
|
|
|
int __tst = ( __tec == NULL ) ? 0 : __tec->status; \
|
|
|
|
|
if ( __tec != NULL ) { \
|
|
|
|
|
__tec->handled = true; \
|
|
|
|
|
__tec = akerr_release_error(__tec); \
|
|
|
|
|
} \
|
|
|
|
|
if ( __tst == 0 ) { \
|
|
|
|
|
FAIL_BREAK(e, AKGL_ERR_BEHAVIOR, "%s: expected a failure, got success", desc); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @brief Require that two floats agree to within AKGL_TEST_EPSILON. */
|
|
|
|
|
#define AKGL_TEST_EPSILON 0.0001f
|
|
|
|
|
|
|
|
|
|
#define TEST_ASSERT_FEQ(e, actual, expected, ...) \
|
|
|
|
|
if ( fabsf((float)(actual) - (float)(expected)) > AKGL_TEST_EPSILON ) { \
|
|
|
|
|
FAIL_BREAK(e, AKGL_ERR_BEHAVIOR, __VA_ARGS__); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Record a failure into a flag instead of breaking.
|
|
|
|
|
*
|
|
|
|
|
* For assertions inside a loop nested in an ATTEMPT block, where `break` would
|
|
|
|
|
* only leave the loop.
|
|
|
|
|
*/
|
|
|
|
|
#define TEST_ASSERT_FLAG(flag, cond) \
|
|
|
|
|
if ( ! (cond) ) { \
|
|
|
|
|
(flag) = false; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // _AKGL_TESTUTIL_H_
|