103 lines
3.5 KiB
C
103 lines
3.5 KiB
C
|
|
/**
|
||
|
|
* @file error.c
|
||
|
|
* @brief Unit tests for the libakgl status band: reservation, ownership and names.
|
||
|
|
*
|
||
|
|
* The libakerror registry is process-global, so these tests assert against
|
||
|
|
* whatever akgl_error_init() left behind rather than building their own state.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
#include <akerror.h>
|
||
|
|
|
||
|
|
#include <akgl/error.h>
|
||
|
|
|
||
|
|
#include "testutil.h"
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief akgl_error_init() must own the libakgl status band and name every code in it.
|
||
|
|
*
|
||
|
|
* A code whose name never registered degrades to "Unknown Error" in every stack
|
||
|
|
* trace that carries it, and a band we never reserved is one another component
|
||
|
|
* can name out from under us. Both stay silent until something has already gone
|
||
|
|
* wrong, so assert them directly rather than waiting to read a useless trace.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext *test_error_init_owns_the_status_band(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(e);
|
||
|
|
static const struct {
|
||
|
|
int status;
|
||
|
|
const char *name;
|
||
|
|
} expected[] = {
|
||
|
|
{ AKGL_ERR_SDL, "SDL Error" },
|
||
|
|
{ AKGL_ERR_REGISTRY, "Registry Error" },
|
||
|
|
{ AKGL_ERR_HEAP, "Heap Error" },
|
||
|
|
{ AKGL_ERR_BEHAVIOR, "Behavior Error" },
|
||
|
|
{ AKGL_ERR_LOGICINTERRUPT, "Logic Interrupt" }
|
||
|
|
};
|
||
|
|
bool named = true;
|
||
|
|
int i = 0;
|
||
|
|
|
||
|
|
ATTEMPT {
|
||
|
|
CATCH(e, akgl_error_init());
|
||
|
|
|
||
|
|
TEST_ASSERT(e, (int)(sizeof(expected) / sizeof(expected[0])) == AKGL_ERR_COUNT,
|
||
|
|
"the libakgl status band holds %d codes but %d are named here",
|
||
|
|
AKGL_ERR_COUNT, (int)(sizeof(expected) / sizeof(expected[0])));
|
||
|
|
|
||
|
|
for ( i = 0; i < (int)(sizeof(expected) / sizeof(expected[0])); i++ ) {
|
||
|
|
TEST_ASSERT_FLAG(named,
|
||
|
|
strcmp(akerr_name_for_status(expected[i].status, NULL),
|
||
|
|
expected[i].name) == 0);
|
||
|
|
}
|
||
|
|
TEST_ASSERT(e, named,
|
||
|
|
"akgl_error_init did not register the expected name for every AKGL_ERR_* code");
|
||
|
|
|
||
|
|
// The reservation is what makes those names ours. Without it the
|
||
|
|
// registrations above would still succeed for anyone who asked.
|
||
|
|
TEST_EXPECT_STATUS(e, AKERR_STATUS_NAME_FOREIGN,
|
||
|
|
akerr_register_status_name("not-libakgl", AKGL_ERR_HEAP, "Squatter"),
|
||
|
|
"a foreign owner was allowed to rename a libakgl status");
|
||
|
|
TEST_EXPECT_STATUS(e, AKERR_STATUS_RANGE_OVERLAP,
|
||
|
|
akerr_reserve_status_range(AKGL_ERR_BASE, AKGL_ERR_COUNT, "not-libakgl"),
|
||
|
|
"a foreign owner was allowed to reserve the libakgl status band");
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(e) {
|
||
|
|
} FINISH(e, true);
|
||
|
|
SUCCEED_RETURN(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Calling akgl_error_init() twice must be a no-op, not a self-collision.
|
||
|
|
*
|
||
|
|
* Nothing in libakgl orders initialization for an embedding program, so a second
|
||
|
|
* call has to be harmless. libakerror only treats an *identical* reservation as
|
||
|
|
* a repeat -- a subset or superset raises -- which makes this a real constraint
|
||
|
|
* on AKGL_ERR_BASE and AKGL_ERR_COUNT, not a triviality.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext *test_error_init_is_idempotent(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(e);
|
||
|
|
|
||
|
|
ATTEMPT {
|
||
|
|
TEST_EXPECT_OK(e, akgl_error_init(), "the second akgl_error_init failed");
|
||
|
|
TEST_EXPECT_OK(e, akgl_error_init(), "the third akgl_error_init failed");
|
||
|
|
TEST_ASSERT(e, strcmp(akerr_name_for_status(AKGL_ERR_SDL, NULL), "SDL Error") == 0,
|
||
|
|
"re-running akgl_error_init lost the name for AKGL_ERR_SDL");
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(e) {
|
||
|
|
} FINISH(e, true);
|
||
|
|
SUCCEED_RETURN(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(errctx);
|
||
|
|
|
||
|
|
ATTEMPT {
|
||
|
|
CATCH(errctx, test_error_init_owns_the_status_band());
|
||
|
|
CATCH(errctx, test_error_init_is_idempotent());
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(errctx) {
|
||
|
|
} FINISH_NORETURN(errctx);
|
||
|
|
}
|