Take libakerror 2.0.1 and drop the workaround it makes obsolete
Every libakgl test suite could report success while failing. libakerror's default unhandled-error handler ended in exit(errctx->status), an exit status is one byte wide, and libakgl's band starts at 256 -- so AKGL_ERR_SDL, the most common failure a library built on SDL can have, exited 0 and CTest recorded a pass. tests/character.c aborted at its second of four tests on a bad renderer and was green for months. 0.5.0 worked around that here with TEST_TRAP_UNHANDLED_ERRORS() in tests/testutil.h, and TODO.md ended the entry saying any consumer's suites have the same problem and it was worth raising upstream. It was. 2.0.1 fixes it at the source: akerr_exit() owns the mapping and the default handler calls it, so 0 exits 0, 1 through 255 exit themselves, and anything else exits AKERR_EXIT_STATUS_UNREPRESENTABLE (125). The trap and its 21 call sites are gone. Verified by putting the original failure back rather than by reading the release notes: a FAIL_BREAK(AKGL_ERR_SDL) in tests/character.c's main exits 125 and CTest reports a failure. A standalone consumer raising the same status unhandled exits 125 where it exited 0 before. tests/actor.c installs its own handler and called exit(errctx->status) from it, which is the same defect one layer up. It calls akerr_exit() now. 2.0.0 also makes the error pool and the status registry thread safe, which libakgl needs more than it knew: audio_stream_callback raises error contexts on SDL's audio thread. With an unlocked pool that callback and the main thread could scan AKERR_ARRAY_ERROR at the same time and be handed the same slot. The comment there says so. This is a hard dependency floor, not a preference. 2.0.0 moved __akerr_last_ignored to thread-local storage and made akerr_next_error() return a context that already holds its reference, and both expand at libakgl's call sites -- and at a consumer's, because akerror.h is part of libakgl's public interface. Mixing headers and libraries across that line double-counts every reference and never returns a pool slot. The soname moved to libakerror.so.2; include/akgl/error.h now also feature- tests AKERR_EXIT_STATUS_UNREPRESENTABLE, which is the narrowest probe for 2.0.1 since libakerror publishes no version macro. 0.7.0 for that reason: libakgl's own ABI is unchanged, but the one it re-exports through its headers is not. TODO.md records the pkg-config gap this makes sharper -- akgl.pc names no dependencies at all, so nothing tells a pkg-config consumer which libakerror it needs. Clean build, 26/26 ctest, memcheck clean, warning-clean at -Wall -Werror. libakgl.so.0.7 links libakerror.so.2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8T5FAYXE8HEJqFLCYwNNc
This commit is contained in:
18
AGENTS.md
18
AGENTS.md
@@ -592,13 +592,17 @@ if ( defaulted != NULL ) {
|
||||
}
|
||||
```
|
||||
|
||||
**Every suite's `main()` must call `TEST_TRAP_UNHANDLED_ERRORS()` immediately
|
||||
after `akgl_error_init()`.** libakerror's default unhandled-error handler ends
|
||||
in `exit(errctx->status)`, `exit` keeps only the low byte, and libakgl's status
|
||||
band starts at 256 — so a suite that failed with `AKGL_ERR_SDL` exited 0 and
|
||||
CTest recorded a pass. That is not hypothetical: `tests/character.c` aborted at
|
||||
its second of four tests on a bad renderer and was green until 0.5.0. The trap
|
||||
in `tests/testutil.h` collapses any status a byte cannot carry onto 1. 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. Coverage mode wraps the suite in a CTest fixture so counters are reset before tests and reports are generated afterward.
|
||||
**Never `exit(3)` on an akerr status — call `akerr_exit()`.** An exit status is
|
||||
one byte wide and libakgl's status band starts at 256, so `exit(AKGL_ERR_SDL)`
|
||||
is a wait status of 0 and a shell sees a clean run. Every suite in this
|
||||
directory once reported success on the most common failure a library built on
|
||||
SDL can have: `tests/character.c` aborted at its second of four tests on a bad
|
||||
renderer and was green until 0.5.0. `akerr_exit()` (libakerror 2.0.1) maps 0 to
|
||||
0, 1–255 to themselves, and anything else to
|
||||
`AKERR_EXIT_STATUS_UNREPRESENTABLE` (125). The default unhandled-error handler
|
||||
calls it, so an ordinary suite needs no trap of its own — `tests/testutil.h`
|
||||
carried one until 0.6.0 and it is gone. A suite that installs its *own* handler
|
||||
still has to call `akerr_exit()` from it; `tests/actor.c` does. 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. Coverage mode wraps the suite in a CTest fixture so counters are reset before tests and reports are generated afterward.
|
||||
|
||||
### Physics simulations
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
||||
# The single source of truth for the library version. It drives the generated
|
||||
# include/akgl/version.h, the shared library's VERSION and SOVERSION, and the
|
||||
# Version field in akgl.pc. Bump it here and nowhere else.
|
||||
project(akgl VERSION 0.6.0 LANGUAGES C)
|
||||
project(akgl VERSION 0.7.0 LANGUAGES C)
|
||||
|
||||
# Memory checking reuses the suites that already exist -- `ctest -T memcheck`
|
||||
# runs every registered test under valgrind -- rather than adding programs of its
|
||||
@@ -135,8 +135,17 @@ akgl_add_vendored_dependency(SDL3_image::SDL3_image deps/SDL_image)
|
||||
akgl_add_vendored_dependency(SDL3_mixer::SDL3_mixer deps/SDL_mixer)
|
||||
akgl_add_vendored_dependency(SDL3_ttf::SDL3_ttf deps/SDL_ttf)
|
||||
|
||||
# libakerror 1.0.0 sizes its own status-name registry; consumers no longer do.
|
||||
# libakgl claims its status codes at runtime in akgl_heap_init() instead.
|
||||
# libakerror sizes its own status-name registry; consumers no longer do. libakgl
|
||||
# claims its status codes at runtime in akgl_heap_init() instead.
|
||||
#
|
||||
# The floor is 2.0.1, and it is a hard one. 2.0.0 moved __akerr_last_ignored to
|
||||
# thread-local storage and made akerr_next_error() return a context that already
|
||||
# holds its reference -- both of which expand at *libakgl's* call sites through
|
||||
# IGNORE and the FAIL macros, and at a *consumer's* too, because akerror.h is
|
||||
# part of libakgl's public interface. A tree that mixes headers and libraries
|
||||
# across that line double-counts every reference and never returns a pool slot.
|
||||
# The soname moved to libakerror.so.2 to stop it happening by accident; the
|
||||
# #error in include/akgl/error.h catches a stale header in an install tree.
|
||||
|
||||
set(AKGL_SUPPRESS_DEPENDENCY_TESTS FALSE)
|
||||
|
||||
@@ -165,7 +174,8 @@ endif()
|
||||
# No version here: libakerror ships no akerrorConfigVersion.cmake, so asking
|
||||
# for one makes find_package reject every install. The floor is enforced by
|
||||
# the #error in include/akgl/error.h instead, which feature-tests
|
||||
# AKERR_FIRST_CONSUMER_STATUS.
|
||||
# AKERR_FIRST_CONSUMER_STATUS and AKERR_EXIT_STATUS_UNREPRESENTABLE -- the
|
||||
# latter arriving in 2.0.1, which is the version libakgl needs.
|
||||
if(NOT TARGET akerror::akerror)
|
||||
find_package(akerror REQUIRED)
|
||||
endif()
|
||||
|
||||
51
TODO.md
51
TODO.md
@@ -412,20 +412,25 @@ the symbol collision described in item 4 — and exited 0 because that status wa
|
||||
enough that `TODO.md` recorded the suite as passing and wondered which change
|
||||
had fixed it. Nothing had; it had stopped running.
|
||||
|
||||
**Fixed** in `tests/testutil.h`: `TEST_TRAP_UNHANDLED_ERRORS()` installs a
|
||||
handler that collapses any status a byte cannot carry onto 1, and every suite's
|
||||
`main()` calls it immediately after `akgl_error_init()`. `tests/error.c` calls
|
||||
it after its first test instead, because that suite has no `akgl_error_init()`
|
||||
in `main()` and libakerror's lazy `akerr_init()` would overwrite the handler.
|
||||
**Fixed twice.** 0.5.0 worked around it here, with a
|
||||
`TEST_TRAP_UNHANDLED_ERRORS()` in `tests/testutil.h` that installed a handler
|
||||
collapsing any status a byte cannot carry onto 1. Once installed, no other suite
|
||||
changed colour, so this was not masking anything beyond `character` — but it
|
||||
could have been at any time, and nothing would have said so.
|
||||
|
||||
Once installed, no other suite changed colour, so this was not masking anything
|
||||
beyond `character` — but it could have been at any time, and nothing would have
|
||||
said so.
|
||||
That entry ended "any consumer's test suites have this problem; that is worth
|
||||
raising upstream", and it was. **libakerror 2.0.1 fixes it at the source**:
|
||||
`akerr_exit()` owns the status-to-exit-code mapping and the default handler
|
||||
calls it, so 0 exits 0, 1 through 255 exit themselves, and anything else exits
|
||||
`AKERR_EXIT_STATUS_UNREPRESENTABLE` (125) rather than a low byte that is either
|
||||
a lie or a claim of success. There is no wider `exit()` to reach for —
|
||||
`_exit()`, `_Exit()`, `quick_exit()` and the raw `exit_group` syscall all
|
||||
truncate the same way, and even `waitid()` reports the truncated value.
|
||||
|
||||
Worth knowing: the fix belongs here rather than in libakerror only because
|
||||
`exit(status)` is a reasonable thing for a library to do when statuses fit in a
|
||||
byte. libakerror's own band does. Consumers' bands start at 256 by construction,
|
||||
so any consumer's test suites have this problem. That is worth raising upstream.
|
||||
The workaround is gone as of 0.6.0, along with its 21 call sites. Verified by
|
||||
putting the original failure back: a `FAIL_BREAK(AKGL_ERR_SDL)` in
|
||||
`tests/character.c`'s `main` now exits 125 and CTest records a failure, where it
|
||||
exited 0 and passed before.
|
||||
|
||||
## Coverage status
|
||||
|
||||
@@ -1884,6 +1889,28 @@ currently promises the opposite. `aksl_strncpy` already reports exactly that
|
||||
status when the bytes do not fit, so the change is to stop capping `n` at
|
||||
`size - 1` and let it raise.
|
||||
|
||||
## akgl.pc names no dependencies
|
||||
|
||||
`akgl.pc.in` carries `Libs: -L${libdir} -lakgl` and no `Requires:` or
|
||||
`Requires.private:` line, so a consumer that finds libakgl through pkg-config is
|
||||
told nothing about libakerror, libakstdlib, SDL3, SDL3_image, SDL3_mixer,
|
||||
SDL3_ttf or jansson.
|
||||
|
||||
That has always been wrong, and libakerror 2.x makes it sharper: `akerror.h` is
|
||||
part of libakgl's *public* interface -- `IGNORE` and the `FAIL_*` macros expand
|
||||
in the consumer's own translation units and reference `__akerr_last_ignored`,
|
||||
which is thread-local as of 2.0.0. A consumer that builds against whatever
|
||||
`akerror.h` happens to be on its include path and links whatever `libakerror.so`
|
||||
the loader finds can get a mismatch that pkg-config had every opportunity to
|
||||
prevent and did not mention.
|
||||
|
||||
The fix is a `Requires:` line for the libraries whose headers libakgl's headers
|
||||
include (akerror, akstdlib, SDL3) and `Requires.private:` for the rest, plus a
|
||||
version floor of `akerror >= 2.0.1`. Not done here because it changes what
|
||||
`pkg-config --libs akgl` emits for every existing consumer, and that wants to be
|
||||
its own commit with an install-tree test behind it. The CMake path is not
|
||||
affected -- `akglConfig.cmake` re-finds the targets.
|
||||
|
||||
## libakstdlib wrappers not yet adopted
|
||||
|
||||
`libakgl` links `libakstdlib` and uses ten of its wrappers. It calls the raw
|
||||
|
||||
2
deps/libakerror
vendored
2
deps/libakerror
vendored
Submodule deps/libakerror updated: 79936362d0...5eaa956f50
@@ -24,7 +24,20 @@
|
||||
* See deps/libakerror/UPGRADING.md.
|
||||
*/
|
||||
#ifndef AKERR_FIRST_CONSUMER_STATUS
|
||||
#error "libakgl requires libakerror >= 1.0.0: the akerror.h on the include path predates the status registry. Rebuild and reinstall libakerror."
|
||||
#error "libakgl requires libakerror >= 2.0.1: the akerror.h on the include path predates the status registry. Rebuild and reinstall libakerror."
|
||||
#endif
|
||||
// 2.0.0 is an ABI break -- akerr_next_error() returns a context that already
|
||||
// holds its reference, and __akerr_last_ignored is thread-local -- and both of
|
||||
// those expand at *this* library's call sites through IGNORE and the FAIL
|
||||
// macros. A libakgl built against a 1.x header and linked against 2.x
|
||||
// double-counts every reference and never returns a slot to the pool. The
|
||||
// soname moved to libakerror.so.2 so the two cannot be mixed by accident, but
|
||||
// the header can still be stale in an install tree, so check it here too.
|
||||
//
|
||||
// AKERR_EXIT_STATUS_UNREPRESENTABLE arrived in 2.0.1 and is the narrowest thing
|
||||
// to probe for: libakerror publishes no version macro.
|
||||
#ifndef AKERR_EXIT_STATUS_UNREPRESENTABLE
|
||||
#error "libakgl requires libakerror >= 2.0.1: the akerror.h on the include path predates akerr_exit(). Rebuild and reinstall libakerror."
|
||||
#endif
|
||||
|
||||
// Silences -Wformat-truncation on a string concatenation that genuinely may not
|
||||
|
||||
@@ -273,6 +273,14 @@ static void SDLCALL audio_stream_callback(void *userdata, SDL_AudioStream *strea
|
||||
if ( errctx != NULL ) {
|
||||
// There is nobody to return an error to on the audio thread, and
|
||||
// refusing to write leaves SDL underrunning. Report and go quiet.
|
||||
//
|
||||
// Raising an error here at all is only safe from libakerror 2.0.0
|
||||
// on. Before that the pool was unlocked, so this callback and the
|
||||
// main thread could scan AKERR_ARRAY_ERROR at the same time and be
|
||||
// handed the same slot -- two threads writing one context, and one
|
||||
// of them releasing it out from under the other. 2.0.0 makes
|
||||
// finding a free slot and claiming it one operation under a lock.
|
||||
// libakgl requires it; see the guard in include/akgl/error.h.
|
||||
LOG_ERROR_WITH_MESSAGE(errctx, "** AUDIO CALLBACK **");
|
||||
errctx->handled = true;
|
||||
errctx = akerr_release_error(errctx);
|
||||
|
||||
@@ -30,7 +30,9 @@ void handle_unhandled_error_noexit(akerr_ErrorContext *errctx)
|
||||
return;
|
||||
}
|
||||
if ( UNHANDLED_ERROR_BEHAVIOR == UNHANDLED_ERROR_EXIT ) {
|
||||
exit(errctx->status);
|
||||
// akerr_exit rather than exit(3): a status past 255 does not survive a
|
||||
// wait status, and this suite's are AKGL_* codes, which all are.
|
||||
akerr_exit(errctx->status);
|
||||
}
|
||||
if ( UNHANDLED_ERROR_BEHAVIOR == UNHANDLED_ERROR_SET ) {
|
||||
unhandled_error_context = errctx;
|
||||
@@ -1037,7 +1039,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_registry_init_actor());
|
||||
CATCH(errctx, akgl_registry_init_sprite());
|
||||
CATCH(errctx, akgl_registry_init_spritesheet());
|
||||
|
||||
@@ -602,7 +602,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
FAIL_ZERO_BREAK(
|
||||
errctx,
|
||||
SDL_Init(SDL_INIT_AUDIO),
|
||||
|
||||
@@ -286,7 +286,6 @@ int main(void)
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
akgl_renderer = &akgl_default_renderer;
|
||||
|
||||
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
|
||||
|
||||
@@ -858,7 +858,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
FAIL_ZERO_BREAK(
|
||||
errctx,
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD),
|
||||
|
||||
@@ -638,7 +638,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
akgl_renderer = &akgl_default_renderer;
|
||||
|
||||
FAIL_ZERO_BREAK(
|
||||
|
||||
@@ -95,12 +95,9 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
// Unlike every other suite, this one has no akgl_error_init() in
|
||||
// main() -- the first test is what brings the subsystem up. The trap
|
||||
// therefore goes after it: libakerror installs its own default handler
|
||||
// from the lazy akerr_init() inside that first call, and would
|
||||
// overwrite anything set beforehand.
|
||||
// main() -- the first test is what brings the subsystem up, and
|
||||
// asserting that it does is the point of it.
|
||||
CATCH(errctx, test_error_init_owns_the_status_band());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, test_error_init_is_idempotent());
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
|
||||
@@ -775,7 +775,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_heap_init());
|
||||
CATCH(errctx, akgl_registry_init());
|
||||
|
||||
|
||||
@@ -362,7 +362,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_heap_init());
|
||||
CATCH(errctx, akgl_registry_init());
|
||||
|
||||
|
||||
@@ -528,7 +528,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_heap_init());
|
||||
CATCH(errctx, akgl_registry_init());
|
||||
CATCH(errctx, load_fixture());
|
||||
|
||||
@@ -901,7 +901,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_heap_init());
|
||||
CATCH(errctx, akgl_registry_init());
|
||||
CATCH(errctx, akgl_registry_init_properties());
|
||||
|
||||
@@ -752,7 +752,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
akgl_renderer = &akgl_default_renderer;
|
||||
akgl_physics = &akgl_default_physics;
|
||||
akgl_camera = &akgl_default_camera;
|
||||
|
||||
@@ -784,7 +784,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, akgl_heap_init());
|
||||
CATCH(errctx, akgl_registry_init());
|
||||
CATCH(errctx, akgl_registry_init_properties());
|
||||
|
||||
@@ -616,7 +616,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
// SDL_GetTicksNS() counts from SDL's own initialization, and
|
||||
// akgl_physics_simulate measures dt against it. Without this the clock
|
||||
// epoch would be established by the first call inside the first
|
||||
|
||||
@@ -288,7 +288,6 @@ int main(void)
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, test_akgl_registry_init_creation_failures());
|
||||
CATCH(errctx, test_akgl_get_property_copies_only_the_value());
|
||||
CATCH(errctx, test_actor_state_string_names());
|
||||
|
||||
@@ -250,7 +250,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
memset(&bound, 0x00, sizeof(akgl_RenderBackend));
|
||||
|
||||
FAIL_ZERO_BREAK(
|
||||
|
||||
@@ -242,7 +242,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
akgl_renderer = &akgl_default_renderer;
|
||||
|
||||
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
|
||||
|
||||
@@ -241,7 +241,6 @@ int main(void)
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
printf("test_fresh_heap_gives_string ....\n");
|
||||
test_fresh_heap_gives_strings();
|
||||
reset_string_heap();
|
||||
|
||||
@@ -43,40 +43,6 @@ static inline int test_string_pool_used(void)
|
||||
return used;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exit with a status the shell can actually see.
|
||||
*
|
||||
* libakerror's default unhandled-error handler ends in `exit(errctx->status)`.
|
||||
* `exit` keeps only the low byte of what it is given, and libakgl's own
|
||||
* statuses start at `AKERR_FIRST_CONSUMER_STATUS`, which is 256 -- so
|
||||
* #AKGL_ERR_SDL exits 0, and CTest records a pass. Every suite in this
|
||||
* directory that failed for the most common reason in this library therefore
|
||||
* reported success. `tests/character.c` did exactly that: it aborted at its
|
||||
* second of four tests on a bad renderer and was green for months.
|
||||
*
|
||||
* This collapses any status that a byte cannot carry onto 1. It does not log --
|
||||
* `FINISH_NORETURN` has already logged the context and its stack by the time it
|
||||
* calls the handler.
|
||||
*/
|
||||
static inline void test_handler_unhandled_error(akerr_ErrorContext *errctx)
|
||||
{
|
||||
int status = ( errctx == NULL ) ? 1 : (errctx->status & 0xFF);
|
||||
|
||||
if ( status == 0 ) {
|
||||
status = 1;
|
||||
}
|
||||
exit(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Install test_handler_unhandled_error(). Call once, after akgl_error_init().
|
||||
*
|
||||
* akgl_error_init() reaches akerr_init(), which installs libakerror's default
|
||||
* handler, so this has to come after it rather than before.
|
||||
*/
|
||||
#define TEST_TRAP_UNHANDLED_ERRORS() \
|
||||
(akerr_handler_unhandled_error = &test_handler_unhandled_error)
|
||||
|
||||
/** @brief Fail the enclosing ATTEMPT block unless @p cond holds. */
|
||||
#define TEST_ASSERT(e, cond, ...) \
|
||||
if ( ! (cond) ) { \
|
||||
|
||||
@@ -392,7 +392,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
memset(&testbackend, 0x00, sizeof(akgl_RenderBackend));
|
||||
FAIL_ZERO_BREAK(
|
||||
errctx,
|
||||
|
||||
@@ -742,7 +742,6 @@ int main(void)
|
||||
|
||||
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");
|
||||
|
||||
@@ -457,7 +457,6 @@ int main(void)
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, test_akgl_rectangle_points_nullpointers());
|
||||
CATCH(errctx, test_akgl_rectangle_points_math());
|
||||
CATCH(errctx, test_akgl_collide_point_rectangle_nullpointers());
|
||||
|
||||
@@ -99,7 +99,6 @@ int main(void)
|
||||
|
||||
ATTEMPT {
|
||||
CATCH(errctx, akgl_error_init());
|
||||
TEST_TRAP_UNHANDLED_ERRORS();
|
||||
CATCH(errctx, test_version_string_matches_components());
|
||||
CATCH(errctx, test_version_linked_matches_compiled());
|
||||
CATCH(errctx, test_version_at_least_boundaries());
|
||||
|
||||
Reference in New Issue
Block a user