Expand error release and handler test coverage
All checks were successful
libakerror CI Build / cmake_build (push) Successful in 2m46s
libakerror CI Build / coverage (push) Successful in 2m45s
libakerror CI Build / mutation_test (push) Successful in 8m0s

This commit is contained in:
2026-07-30 02:00:54 -04:00
parent 9f0034a56e
commit 539293cc1c
4 changed files with 178 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
#include "akerror.h"
#include "err_capture.h"
#include <string.h>
/*
* akerr_release_error() drops one reference and only recycles the slot when the
* last one goes away. Two of its refcount edges had no test:
*
* - refcount > 1: the release must decrement and return the context intact,
* not wipe it out from under the reference that is still held.
* - refcount == 0: releasing a context nobody holds must not underflow the
* count; it wipes and returns NULL like any other fully-released slot.
*
* The macro API never produces a refcount above 1 (ENSURE_ERROR_READY only
* increments when it acquires a fresh slot), so this test sets the count
* directly to model a caller that took an extra reference.
*/
int main(void)
{
akerr_capture_install();
akerr_init();
AKERR_CHECK(akerr_slots_in_use() == 0);
/* Check out a slot and give it two holders. */
akerr_ErrorContext *held = akerr_next_error();
AKERR_CHECK(held != NULL);
int slotid = held->arrayid;
held->refcount = 2;
held->status = AKERR_VALUE;
snprintf((char *)held->message, AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH,
"still referenced");
/* First release: one holder left, so the context survives untouched. */
akerr_ErrorContext *ret = akerr_release_error(held);
AKERR_CHECK(ret == held);
AKERR_CHECK(held->refcount == 1);
AKERR_CHECK(held->status == AKERR_VALUE);
AKERR_CHECK(strcmp(held->message, "still referenced") == 0);
AKERR_CHECK(akerr_slots_in_use() == 1);
/* Second release: last holder gone, so the slot is wiped and recycled. */
ret = akerr_release_error(held);
AKERR_CHECK(ret == NULL);
AKERR_CHECK(held->refcount == 0);
AKERR_CHECK(held->status == 0);
AKERR_CHECK(held->message[0] == '\0');
/* The wipe must preserve the slot's identity and stacktrace cursor. */
AKERR_CHECK(held->arrayid == slotid);
AKERR_CHECK(held->stacktracebufptr == (char *)&held->stacktracebuf);
AKERR_CHECK(akerr_slots_in_use() == 0);
/*
* Releasing an unheld slot: refcount is already 0, so there is nothing to
* decrement and the count must not go negative.
*/
akerr_ErrorContext *unheld = akerr_next_error();
AKERR_CHECK(unheld != NULL);
AKERR_CHECK(unheld->refcount == 0);
ret = akerr_release_error(unheld);
AKERR_CHECK(ret == NULL);
AKERR_CHECK(unheld->refcount == 0);
AKERR_CHECK(akerr_slots_in_use() == 0);
/* The pool is still healthy afterwards. */
akerr_ErrorContext *probe = akerr_next_error();
AKERR_CHECK(probe != NULL);
fprintf(stderr, "err_release_refcount ok\n");
return 0;
}