Report the failures that used to be crashes
Closes Defects items 30 and 31 and Known-and-still-open items 1, 2, 5, 9 and 11. Both string accessors in json_helpers.c ended their ATTEMPT block with FINISH(errctx, false), which swallows the failure, and then strncpy'd through the pointer akgl_heap_next_string never set. So the one condition the pool exists to report -- it is full, which in practice means something is not releasing -- arrived as a segfault somewhere else entirely. It is FINISH(errctx, true) now, and tests/json_helpers.c claims every slot and asserts AKGL_ERR_HEAP comes back out of both. That test segfaults against the old code, which is also how the tilemap leak test in the previous commit confirmed this one. akgl_tilemap_release tested layers[i].texture and destroyed tilesets[i].texture, so every tileset texture was freed twice on one release and no image layer's texture was freed at all. Pointers are cleared as they go, so a second release is safe instead of a use-after-free. akgl_game_update_fps called game.lowfpsfunc() unguarded, on a path taken on frame one because fps is 0 for the first second. Only akgl_game_init installs it, and renderer.h documents the other path deliberately -- a host that owns its window binds a backend instead. It installs the default when it finds NULL. akgl_controller_pushmap and akgl_controller_default checked only the upper bound, so a negative id indexed before akgl_controlmaps. The two test-harness helpers were quietly worthless. akgl_render_and_compare drew t1 on both passes, so it always reported a match and every image assertion built on it asserted nothing; and akgl_compare_sdl_surfaces memcmp'd s1->pitch * s1->h bytes out of both surfaces without checking that the second was the same size, so a smaller one was read past its end. Both fixed, both tested. tests/util.c also now calls the collide-point test it has defined and never run. 25/25 pass, memcheck clean, reindent --check and check_error_protocol clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
tests/util.c
67
tests/util.c
@@ -356,6 +356,69 @@ akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
|
||||
* The loop runs well past AKERR_MAX_ARRAY_ERROR on purpose: at the old
|
||||
* behaviour this test does not fail, it terminates the suite.
|
||||
*/
|
||||
/**
|
||||
* @brief akgl_compare_sdl_surfaces must check geometry before it memcmps.
|
||||
*
|
||||
* It compared `s1->pitch * s1->h` bytes out of both surfaces without looking at
|
||||
* the second one's dimensions, so a smaller s2 was read past its end rather
|
||||
* than reported as a mismatch. Benign in practice and immediately fatal under a
|
||||
* memory checker, which is the reason to fix it rather than leave it.
|
||||
*/
|
||||
akerr_ErrorContext *test_akgl_compare_sdl_surfaces_checks_geometry(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
SDL_Surface *big = NULL;
|
||||
SDL_Surface *small = NULL;
|
||||
SDL_Surface *twin = NULL;
|
||||
|
||||
ATTEMPT {
|
||||
big = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_RGBA8888);
|
||||
twin = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_RGBA8888);
|
||||
small = SDL_CreateSurface(8, 8, SDL_PIXELFORMAT_RGBA8888);
|
||||
FAIL_ZERO_BREAK(errctx, big, AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
FAIL_ZERO_BREAK(errctx, twin, AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
FAIL_ZERO_BREAK(errctx, small, AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
|
||||
FAIL_ZERO_BREAK(errctx, SDL_FillSurfaceRect(big, NULL, 0), AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
FAIL_ZERO_BREAK(errctx, SDL_FillSurfaceRect(twin, NULL, 0), AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
FAIL_ZERO_BREAK(errctx, SDL_FillSurfaceRect(small, NULL, 0), AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
|
||||
TEST_EXPECT_OK(errctx, akgl_compare_sdl_surfaces(big, twin),
|
||||
"comparing two identical surfaces");
|
||||
|
||||
// The one that used to read 4 KiB past the end of an 8x8 surface.
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_VALUE, akgl_compare_sdl_surfaces(big, small),
|
||||
"comparing a 32x32 surface against an 8x8 one");
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_VALUE, akgl_compare_sdl_surfaces(small, big),
|
||||
"comparing an 8x8 surface against a 32x32 one");
|
||||
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_NULLPOINTER, akgl_compare_sdl_surfaces(NULL, big),
|
||||
"comparing a NULL first surface");
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_NULLPOINTER, akgl_compare_sdl_surfaces(big, NULL),
|
||||
"comparing a NULL second surface");
|
||||
|
||||
// Same dimensions, different format: the pixels are not comparable even
|
||||
// though the byte count might be.
|
||||
SDL_DestroySurface(small);
|
||||
small = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_RGB24);
|
||||
FAIL_ZERO_BREAK(errctx, small, AKGL_ERR_SDL, "%s", SDL_GetError());
|
||||
TEST_EXPECT_STATUS(errctx, AKERR_VALUE, akgl_compare_sdl_surfaces(big, small),
|
||||
"comparing two surfaces of different pixel formats");
|
||||
} CLEANUP {
|
||||
if ( big != NULL ) {
|
||||
SDL_DestroySurface(big);
|
||||
}
|
||||
if ( twin != NULL ) {
|
||||
SDL_DestroySurface(twin);
|
||||
}
|
||||
if ( small != NULL ) {
|
||||
SDL_DestroySurface(small);
|
||||
}
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *test_akgl_path_relative_releases_contexts(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
@@ -398,8 +461,12 @@ int main(void)
|
||||
CATCH(errctx, test_akgl_rectangle_points_nullpointers());
|
||||
CATCH(errctx, test_akgl_rectangle_points_math());
|
||||
CATCH(errctx, test_akgl_collide_point_rectangle_nullpointers());
|
||||
// Defined since forever and never called until 0.5.0. TODO.md, "Known
|
||||
// and still open" item 9.
|
||||
CATCH(errctx, test_akgl_collide_point_rectangle_logic());
|
||||
CATCH(errctx, test_akgl_collide_rectangles_nullpointers());
|
||||
CATCH(errctx, test_akgl_collide_rectangles_logic());
|
||||
CATCH(errctx, test_akgl_compare_sdl_surfaces_checks_geometry());
|
||||
CATCH(errctx, test_akgl_path_relative_releases_contexts());
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
|
||||
Reference in New Issue
Block a user