/** * @file ui.c * @brief Unit tests for the UI subsystem: lifecycle, arena sizing and validation. * * Everything here runs without a window, a renderer, or SDL_Init at all -- * akgl_ui_init takes its layout dimensions as parameters precisely so that a * test can bring the subsystem up headless. Only akgl_error_init() is needed * first, so that AKGL_ERR_UI carries its name into any trace these tests * produce. */ #include #include #include #include "testutil.h" /** * @brief Init must refuse bad dimensions and an uninitialized resize. * * Runs before anything initializes the subsystem, because the "not * initialized" refusal is half of what it asserts. */ akerr_ErrorContext *test_ui_validation(void) { PREPARE_ERROR(e); ATTEMPT { TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_ui_init(0, 240), "akgl_ui_init accepted a zero width"); TEST_EXPECT_STATUS(e, AKERR_OUTOFBOUNDS, akgl_ui_init(320, -1), "akgl_ui_init accepted a negative height"); TEST_EXPECT_STATUS(e, AKGL_ERR_UI, akgl_ui_resize(320, 240), "akgl_ui_resize worked on an uninitialized subsystem"); } CLEANUP { } PROCESS(e) { } FINISH(e, true); SUCCEED_RETURN(e); } /** * @brief Init, double-init refusal, idempotent shutdown, re-init. * * The double init has to be refused rather than absorbed: a second * Clay_Initialize over a live context would silently discard every element id * and scroll position the first one held, which is the kind of reset a game * only notices as "the menu forgot where it was". */ akerr_ErrorContext *test_ui_lifecycle(void) { PREPARE_ERROR(e); ATTEMPT { TEST_EXPECT_OK(e, akgl_ui_init(320, 240), "first akgl_ui_init failed"); TEST_EXPECT_STATUS(e, AKGL_ERR_UI, akgl_ui_init(320, 240), "a second akgl_ui_init was not refused"); TEST_EXPECT_OK(e, akgl_ui_resize(640, 480), "resize on a live subsystem failed"); TEST_EXPECT_OK(e, akgl_ui_shutdown(), "akgl_ui_shutdown failed"); TEST_EXPECT_OK(e, akgl_ui_shutdown(), "a second akgl_ui_shutdown was not a no-op"); TEST_EXPECT_OK(e, akgl_ui_init(320, 240), "re-init after shutdown failed"); TEST_EXPECT_OK(e, akgl_ui_shutdown(), "shutdown after re-init failed"); } CLEANUP { IGNORE(akgl_ui_shutdown()); } PROCESS(e) { } FINISH(e, true); SUCCEED_RETURN(e); } /** * @brief A too-small arena must refuse at init, loudly, and recover. * * The narrowed limit stands in for a consumer who raised AKGL_UI_MAX_ELEMENTS * without raising AKGL_UI_ARENA_BYTES: the refusal at startup, with both * numbers in the message, is the whole safety story -- past init, clay trusts * the arena completely. Same shape as the collision arena's exhaustion test, * for the same reason: rebuilding with a smaller ceiling is not something CI * can do. */ akerr_ErrorContext *test_ui_arena_exhaustion(void) { PREPARE_ERROR(e); ATTEMPT { akgl_ui_arena_limit(64); TEST_EXPECT_STATUS(e, AKGL_ERR_UI, akgl_ui_init(320, 240), "akgl_ui_init accepted an arena clay cannot fit in"); akgl_ui_arena_limit(0); TEST_EXPECT_OK(e, akgl_ui_init(320, 240), "akgl_ui_init failed after the arena limit was restored"); TEST_EXPECT_OK(e, akgl_ui_shutdown(), "shutdown after the exhaustion test failed"); } CLEANUP { akgl_ui_arena_limit(0); IGNORE(akgl_ui_shutdown()); } PROCESS(e) { } FINISH(e, true); SUCCEED_RETURN(e); } int main(void) { PREPARE_ERROR(errctx); ATTEMPT { CATCH(errctx, akgl_error_init()); CATCH(errctx, test_ui_validation()); CATCH(errctx, test_ui_lifecycle()); CATCH(errctx, test_ui_arena_exhaustion()); } CLEANUP { } PROCESS(errctx) { } FINISH_NORETURN(errctx); }