Files
libakerror/tests/err_library_status_fatal.c

32 lines
1.2 KiB
C
Raw Normal View History

Use the library's own error idioms inside the library Four things in src/error.c did by hand what the macros already do, or skipped checks the library would have caught for a consumer. akerr_copy_string() returned void and validated only its capacity, while writing through a caller-supplied pointer for a caller-supplied length. It is now __akerr_copy_string() and raises: AKERR_NULLPOINTER for a NULL destination or source, AKERR_VALUE for a capacity with no room for a terminator. Both call sites PASS it, and the owner copy in akerr_reserve_status_range() now gates the commit, so a failed copy cannot leave a range claimed under an empty owner. It is exported under the internal prefix rather than static so tests/err_copy_string.c can drive those guards; nothing else can reach them. __akerr_name_library_status() and the band reservation in akerr_init() hand-rolled the log/handler/release sequence. Both now use ATTEMPT/CATCH/PROCESS/FINISH_NORETURN. PASS does not fit: both sites are void and have no caller to propagate to, so the terminal form of the same idiom is the right one -- an unhandled failure prints its stack trace and goes to akerr_handler_unhandled_error, which terminates, exactly as before but without the bespoke plumbing. The legacy set path in akerr_name_for_status() had the same shape and now handles its refusal with HANDLE_DEFAULT, converting it to the "Unknown Error" sentinel. Every remaining `if (x) { FAIL_RETURN }` in the registry is now FAIL_ZERO_RETURN or FAIL_NONZERO_RETURN, and akerr_register_status_name() checks both owner and name before passing either down -- akerr_store_status_name() reads a NULL owner as "caller did not identify itself" for the legacy path, so a NULL arriving through the owned entry point would have skipped the ownership check entirely. New tests: err_copy_string (the guards above), err_library_status_fatal (WILL_FAIL -- proves a refused library-status registration terminates). Tests: ctest 31/31, mutation 80.7% (was 77.5%), line coverage 98.9%. Branch coverage on src/error.c drops 64.5% -> 50.4%, just over its gate: each FAIL_* site carries ~6 branch outcomes of error-construction machinery that only run when that failure fires, and each PASS around a call that cannot fail carries ~25, so added validation lowers the ratio by construction. Recorded in TODO.md item 7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 21:53:33 -04:00
#include "akerror.h"
#include "err_capture.h"
/*
* The library naming one of its own codes is not allowed to fail quietly.
* __akerr_name_library_status() runs from akerr_init() and from the generated
* errno table, neither of which has a caller to raise into, so a refusal there
* goes through FINISH_NORETURN: stack trace, then akerr_handler_unhandled_error,
* which terminates the process.
*
* In a correct build that can only happen with a name table too small to hold
* the library's own entries, which no test can configure (the slot count is
* PRIVATE to the library target). Calling the helper for a status the library
* does not own reaches the same refusal, so this test covers the terminal path
* itself.
*
* Registered in AKERR_WILL_FAIL_TESTS: reaching the end of main() means the
* failure was swallowed, and that is the bug this test exists to catch.
*/
int main(void)
{
akerr_init();
/* Nobody has reserved 9999, so this registration is refused. */
__akerr_name_library_status(9999, "Not The Library's To Name");
fprintf(stderr, "err_library_status_fatal: a refused library-status "
"registration did NOT terminate\n");
return 0;
}