#include "akerror.h" #include "err_capture.h" #include /* * The registry entry points report failure the same way every other function in * this library does: they return akerr_ErrorContext *. A refused reservation is * therefore an ordinary error, and everything that works on an ordinary error * must work on it -- CATCH, HANDLE, PASS, propagation to the caller, and the * stack trace an unhandled one prints. * * This is what distinguishes the current design from the integer return codes * it replaced: a consumer that ignores the result gets a compiler warning * (AKERR_NOIGNORE), and a consumer that catches it but does not handle it gets * the error propagated out of its init function rather than a silently * unreserved range. */ #define TEST_OWNER "exception-test" /* A consumer init function in the shape the documentation recommends. */ static akerr_ErrorContext AKERR_NOIGNORE *component_init(int base, const char *owner) { PREPARE_ERROR(errctx); ATTEMPT { CATCH(errctx, akerr_reserve_status_range(base, 4, owner)); CATCH(errctx, akerr_register_status_name(owner, base, "Component Error")); } CLEANUP { } PROCESS(errctx) { } FINISH(errctx, true); SUCCEED_RETURN(errctx); } int main(void) { akerr_capture_install(); akerr_init(); PREPARE_ERROR(errctx); /* A successful reservation raises nothing at all. */ AKERR_CHECK_SUCCEEDS(component_init(256, TEST_OWNER)); AKERR_CHECK(strcmp(akerr_name_for_status(256, NULL), "Component Error") == 0); AKERR_CHECK(akerr_slots_in_use() == 0); /* * A collision propagates out of the component's init and is caught here. * HANDLE proves the status is a real, distinct exception a consumer can * dispatch on -- not an opaque nonzero int. */ int handled_overlap = 0; ATTEMPT { CATCH(errctx, component_init(258, "other-component")); } CLEANUP { } PROCESS(errctx) { } HANDLE(errctx, AKERR_STATUS_RANGE_OVERLAP) { handled_overlap = 1; } HANDLE_DEFAULT(errctx) { handled_overlap = -1; } FINISH_NORETURN(errctx); AKERR_CHECK(handled_overlap == 1); AKERR_CHECK(akerr_slots_in_use() == 0); /* The name-side refusals dispatch the same way. */ int handled_foreign = 0; ATTEMPT { CATCH(errctx, akerr_register_status_name("interloper", 256, "Hijack")); } CLEANUP { } PROCESS(errctx) { } HANDLE(errctx, AKERR_STATUS_NAME_FOREIGN) { handled_foreign = 1; } FINISH_NORETURN(errctx); AKERR_CHECK(handled_foreign == 1); AKERR_CHECK(strcmp(akerr_name_for_status(256, NULL), "Component Error") == 0); /* * An unhandled refusal carries a stack trace naming the real owner, so the * report a consumer gets is the library's normal one. */ akerr_capture_reset(); ATTEMPT { CATCH(errctx, akerr_reserve_status_range(AKERR_VALUE, 1, "encroacher")); } CLEANUP { } PROCESS(errctx) { } HANDLE_DEFAULT(errctx) { LOG_ERROR_WITH_MESSAGE(errctx, "Reservation refused"); } FINISH_NORETURN(errctx); AKERR_CHECK_CONTAINS("Reservation refused"); AKERR_CHECK_CONTAINS("Status Range Overlap"); AKERR_CHECK_CONTAINS(AKERR_LIBRARY_OWNER); AKERR_CHECK_CONTAINS("encroacher"); AKERR_CHECK_CONTAINS("error.c"); /* Nothing above stranded a pool slot. */ AKERR_CHECK(akerr_slots_in_use() == 0); fprintf(stderr, "err_status_exception ok\n"); return 0; }