diff options
| author | 2026-06-29 03:00:12 -0500 | |
|---|---|---|
| committer | 2026-06-29 03:00:12 -0500 | |
| commit | 6bb84fe005fc151df1a67259242da9c0eaf3fe93 (patch) | |
| tree | 683d6e160bf48fd0c235635be874a8fef697402c /source/test.c | |
| parent | add new tests (diff) | |
begin work on pc side
Diffstat (limited to 'source/test.c')
| -rw-r--r-- | source/test.c | 453 |
1 files changed, 0 insertions, 453 deletions
diff --git a/source/test.c b/source/test.c deleted file mode 100644 index c59deaf..0000000 --- a/source/test.c +++ /dev/null @@ -1,453 +0,0 @@ -#include <assert.h> -#include <stdio.h> -#include <gccore.h> -#include <wiiuse/wpad.h> -#include <stdlib.h> -#include <stdarg.h> -#include <inttypes.h> - -#define TEST_INTERNAL -#include "test.h" -#include "term.h" - -typedef u32 et_test_status; - -#define test_current(_plan) ((_plan)->tests + (_plan)->current_test) - -static int crit_test_fail_init(const struct et_state *state, void *data, et_state_init_data init_data) { - struct et_test_plan *plan = init_data.p; - struct et_test_plan_entry *last_ent; - - *(void **)data = plan; - - assert(plan->current_test > 0); /* NOTE: current_test has been incremented by this point */ - - last_ent = plan->tests + (plan->current_test - 1); - - printf("\nCritical test failed: [%4s] %s\n", et_test_get_status_mnemonic(last_ent->status), last_ent->test->name); - if (last_ent->ext_status) { - printf("Test says:\n%s\n", last_ent->ext_status); - } else { - printf("No extended status.\n"); - } - - putchar('\n'); - - printf("Press A to continue.\n"); - printf("Press B to finish testing.\n"); - printf("Press HOME/START to exit.\n"); - - return 0; -} - -static int crit_test_fail_tick(const struct et_state *state, void *data, struct et_next_state *next_state) { - struct et_test_plan *plan = *(void **)data; - u32 wpad_pressed = WPAD_ButtonsDown(0); - u32 pad_pressed = PAD_ButtonsDown(0); - - if (wpad_pressed & WPAD_BUTTON_A || pad_pressed & PAD_BUTTON_A) { - next_state->state = et_state_begin_testing; /* also works to resume testing */ - next_state->init_data.p = plan; /* test plan */ - return 1; - } - - if (wpad_pressed & WPAD_BUTTON_B || pad_pressed & PAD_BUTTON_B) { - next_state->state = plan->next_state; - next_state->init_data = plan->next_state_init; - return 1; - } - - if (wpad_pressed & WPAD_BUTTON_HOME || pad_pressed & PAD_BUTTON_START) { - printf("Goodbye!\n"); - exit(0); - } - - return 0; -} - -static void crit_test_fail_cleanup(const struct et_state *state, void *data) { - /* do nothing */ -} - -static const struct et_state state_crit_test_fail = { - .private_size = sizeof(struct et_test_plan *), - .init_f = crit_test_fail_init, - .tick_f = crit_test_fail_tick, - .cleanup_f = crit_test_fail_cleanup -}; - -int test_next(struct et_test_plan *plan, struct et_next_state *next_state) { - /* NOTE: this function doesn't increment current_test because the incremented value - * would be visible in the cleanup function of the test state. */ - - /* NOTE: this condition will not trigger for states that leak their TEST_PRIMORDIAL status. - * I don't care about this situation so much, since that should only happen for bugged tests. */ - const struct et_test_plan_entry *ent = test_current(plan); - switch (TEST_STATUS_GET_TYPE(ent->status)) { - case TEST_SERROR: - case TEST_SFAIL: - if (ent->test->flags & TEST_PAUSE_ON_ERROR) { - next_state->state = &state_crit_test_fail; - next_state->init_data.p = plan; - return 1; - } - break; - default: - /* do nothing */ - (void)0; - } - - if (plan->current_test == plan->num_tests - 1) { - /* this is the last state... we're done! */ - next_state->state = plan->next_state; - next_state->init_data = plan->next_state_init; - } else { - next_state->state = &plan->tests[plan->current_test + 1].test->parent; - next_state->init_data.p = plan; - } - - return 1; /* always return 1: advance to next state */ -} - -int test_state_init(const struct et_state *state, void *data, et_state_init_data init_data) { - struct et_test_plan *plan = init_data.p; - struct et_test_state_private_base *testdata = data; - struct et_test_plan_entry *cur = test_current(plan); - const struct et_test *test = (const struct et_test *)state; - - testdata->plan = plan; - - assert(cur->test == test); /* ensure correct test was loaded */ - assert(TEST_STATUS_IS_CLEAN(cur->status)); - - if (test->test_init_f) { - return test->test_init_f(state, data, init_data); - } - - return 0; -} - -void test_state_cleanup(const struct et_state *state, void *data) { - struct et_test_state_private_base *testdata = data; - struct et_test_plan_entry *cur = test_current(testdata->plan); - const struct et_test *test = (const struct et_test *)state; - - if (test->test_cleanup_f) { - test->test_cleanup_f(state, data); - } - - if (TEST_STATUS_IS_CLEAN(cur->status)) { - /* test didn't set status :( */ - test_plan_entry_set_error_static(cur, "test aborted"); - cur->status = TEST_ERROR(TEST_EABORT); - } - - printf("[%4s] %s\n", et_test_get_status_mnemonic(cur->status), test->name); - - ++testdata->plan->current_test; -} - -static int begin_testing_init(const struct et_state *state, void *state_data, et_state_init_data init_data) { - struct et_test_plan *test_plan = init_data.p; - struct et_test_plan **pst_test_plan = state_data; - *pst_test_plan = test_plan; - return 0; -} - -static int begin_testing_tick(const struct et_state *state, void *state_data, struct et_next_state *next_state) { - struct et_test_plan **ptest_plan = state_data; - struct et_test_plan *test_plan = *ptest_plan; - - assert(test_plan->current_test <= test_plan->num_tests); - - if (test_plan->current_test < test_plan->num_tests) { - const struct et_test_plan_entry *next_test = test_plan->tests + test_plan->current_test; - assert(test_plan->tests); - assert(next_test->status == TEST_PRIMORDIAL); - - next_state->state = &next_test->test->parent; - next_state->init_data.p = test_plan; - } else { - /* no tests to run in this plan */ - next_state->state = test_plan->next_state; - next_state->init_data = test_plan->next_state_init; - } - - return 1; - -} - -static void begin_testing_cleanup(const struct et_state *state, void *state_data) { - /* do nothing */ -} - -static const struct et_state state_begin_testing_struct = { - .private_size = sizeof(const struct et_test_plan *), - .init_f = begin_testing_init, - .tick_f = begin_testing_tick, - .cleanup_f = begin_testing_cleanup -}; - -const struct et_state *et_state_begin_testing = &state_begin_testing_struct; - -struct summ_test_plan_private { - struct et_test_plan *plan; - size_t selected_test; -}; - -#define SUMM_MENU_ROWS 3 - -static void summ_test_print_menu(struct summ_test_plan_private *priv) { - const struct et_test *test = priv->plan->tests[priv->selected_test].test; - - fputs(TERM_CUR_UP(SUMM_MENU_ROWS), stdout); - puts(TERM_CLEARLINE "Press A to test again. Press HOME/START to exit."); - puts(TERM_CLEARLINE "Press +/X to show output for [DL/DR]:"); - printf(TERM_CLEARLINE "%s\n", test->name); -} - -static int summ_test_plan_init(const struct et_state *state, void *state_data, et_state_init_data init_data) { - struct summ_test_plan_private *priv = state_data; - struct et_test_plan *ptest_plan = init_data.p; - size_t passed = 0; - size_t failed = 0; - size_t errored = 0; - size_t skipped = 0; - size_t unknown = 0; - - priv->plan = ptest_plan; - priv->selected_test = 0; - - for (size_t i = 0; i < ptest_plan->num_tests; ++i) { - switch (TEST_STATUS_GET_TYPE(ptest_plan->tests[i].status)) { - case TEST_SPASS: ++passed; break; - case TEST_SFAIL: ++failed; break; - case TEST_SERROR: ++errored; break; - case TEST_SSKIPPED: ++skipped; break; - default: ++unknown; break; - } - } - - printf("\nTesting complete.\nPASS: %zu, FAIL: %zu, ERROR: %zu, SKIP: %zu, UNK: %zu\n\n", passed, failed, errored, skipped, unknown); - - for (int i = 0; i < SUMM_MENU_ROWS; ++i) putchar('\n'); - summ_test_print_menu(priv); - - return 0; -} - -static int summ_test_plan_tick(const struct et_state *state, void *state_data, struct et_next_state *next_state) { - struct summ_test_plan_private *priv = state_data; - - u32 wpad_buttons = WPAD_ButtonsDown(0); - u32 pad_buttons = PAD_ButtonsDown(0); - - if (wpad_buttons & WPAD_BUTTON_A || pad_buttons & PAD_BUTTON_A) { - next_state->state = et_state_setup; - next_state->init_data = et_init_data_null; - return 1; - } - - if (wpad_buttons & WPAD_BUTTON_HOME || pad_buttons & PAD_BUTTON_START) { - puts("Goodbye!"); - exit(0); - } - - if (wpad_buttons & WPAD_BUTTON_LEFT || pad_buttons & PAD_BUTTON_LEFT) { - if (priv->selected_test > 0) { - --priv->selected_test; - } else { - priv->selected_test = priv->plan->num_tests - 1; - } - - summ_test_print_menu(priv); - } - - if (wpad_buttons & WPAD_BUTTON_RIGHT || pad_buttons & PAD_BUTTON_RIGHT) { - if (priv->selected_test < priv->plan->num_tests - 1) { - ++priv->selected_test; - } else { - priv->selected_test = 0; - } - - summ_test_print_menu(priv); - } - - if (wpad_buttons & WPAD_BUTTON_1 || pad_buttons & PAD_BUTTON_X) { - struct et_test_plan_entry *entry = priv->plan->tests + priv->selected_test; - printf("\nExtended status for %s:\n", entry->test->name); - if (entry->ext_status) { - puts(entry->ext_status); - } else { - puts("(no status)"); - } - - for (int i = 0; i < SUMM_MENU_ROWS + 1; ++i) putchar('\n'); - summ_test_print_menu(priv); - } - - return 0; -} - -static void summ_test_plan_cleanup(const struct et_state *state, void *state_data) { - struct summ_test_plan_private *priv = state_data; - struct et_test_plan *ptest_plan = priv->plan; - - for (size_t i = 0; i < ptest_plan->num_tests; ++i) { - et_test_plan_entry_cleanup(ptest_plan->tests + i); - } - - free(ptest_plan->tests); - free(ptest_plan); -} - -static const struct et_state summ_test_plan_struct = { - .private_size = sizeof(struct summ_test_plan_private), - .init_f = summ_test_plan_init, - .tick_f = summ_test_plan_tick, - .cleanup_f = summ_test_plan_cleanup -}; - -const struct et_state *et_state_summarize_test_plan = &summ_test_plan_struct; - -#define COUNT_TESTS(_x) 1 -#define NUM_TESTS FOREACH_TEST(COUNT_TESTS, +) -static const struct et_test *all_tests[NUM_TESTS + 1]; -static bool tests_array_initialized = false; - -const struct et_test *const *et_get_tests(size_t *ntests) { - size_t idx = 0; - - if (ntests) *ntests = NUM_TESTS; - if (tests_array_initialized) return all_tests; - -#define A(_name) all_tests[idx++] = TEST_SYM(_name) - FOREACH_TEST(A, ;); -#undef A - - all_tests[idx] = NULL; - - tests_array_initialized = true; - return all_tests; -} - -static const char *error_oom = "(failed to allocate error message)"; -static const char *error_bad_fmt = "(bad extended error format)"; - -void et_test_plan_entry_cleanup(struct et_test_plan_entry *entry) { - if (entry->ext_status_free) { - /* okay to cast away const here. we're careful about making sure this pointer is non-NULL iff this pointer - * was malloc'd */ - entry->ext_status_free((void *)entry->ext_status); - } - - entry->ext_status = NULL; - entry->ext_status_free = NULL; -} - -void test_plan_entry_set_error(struct et_test_plan_entry *entry, const char *fmt, ...) { - int res; - va_list vp; - char *dest; - size_t dest_sz; - - et_test_plan_entry_cleanup(entry); - - va_start(vp, fmt); - res = vsnprintf(NULL, 0, fmt, vp); - va_end(vp); - - if (res < 0) { - entry->ext_status = error_bad_fmt; - entry->ext_status_free = NULL; - return; - } - - dest_sz = MIN((size_t)res + 1, (size_t)1024); - dest = malloc(dest_sz); - dest[0] = '\0'; - - if (!dest) { - entry->ext_status = error_oom; - entry->ext_status_free = NULL; - return; - } - - va_start(vp, fmt); - res = vsnprintf(dest, dest_sz, fmt, vp); - va_end(vp); - - dest[dest_sz - 1] = '\0'; - - assert(res >= 0); - - entry->ext_status = dest; - entry->ext_status_free = free; -} - -void test_plan_entry_set_error_static(struct et_test_plan_entry *entry, const char *str) { - et_test_plan_entry_cleanup(entry); - entry->ext_status = str; - entry->ext_status_free = NULL; -} - -const char *et_test_get_status_mnemonic(et_test_status status) { - switch (TEST_STATUS_GET_TYPE(status)) { - case TEST_SPASS: return "PASS"; - case TEST_SFAIL: return "FAIL"; - case TEST_SSKIPPED: - if (TEST_STATUS_IS_CLEAN(status)) { - return "SKI!"; - } else { - return "SKIP"; - } - case TEST_SERROR: - switch (TEST_STATUS_GET_ERROR(status)) { - case TEST_ENONE: return "ERR0"; - case TEST_EABORT: return "ERRA"; - default: return "ERRP"; - } - default: - return "UNKS"; - } -} - -et_test_status test_exi_readwrite(struct et_test_plan *plan, void *data, size_t data_sz, et_test_error base) { - struct et_test_plan_entry *cur = test_current(plan); - int chan = plan->exi_channel; - et_test_status status = TEST_PASS; - s32 exi_res; - - if ((exi_res = EXI_Lock(chan, EXI_DEVICE_0, NULL)) <= 0) { - status = TEST_ERROR(base); - test_plan_entry_set_error(cur, "failed to lock EXI channel: %" PRId32, exi_res); - goto fail_lock; - } - - if ((exi_res = EXI_Select(chan, EXI_DEVICE_0, EXI_SPEED32MHZ)) <= 0) { - status = TEST_ERROR(base + 1); - test_plan_entry_set_error(cur, "failed to select EXI channel: %" PRId32, exi_res); - goto fail_select; - } - - if ((exi_res = EXI_Imm(chan, data, data_sz, EXI_READWRITE, NULL)) <= 0) { - status = TEST_ERROR(base + 2); - test_plan_entry_set_error(cur, "failed to send EXI data: %" PRId32, exi_res); - goto fail_data; - } - - if ((exi_res = EXI_Sync(chan)) <= 0) { - status = TEST_ERROR(base + 3); - test_plan_entry_set_error(cur, "failed to complete transfer: %" PRId32, exi_res); - goto fail_data; - } - -fail_data: - EXI_Deselect(chan); - -fail_select: - EXI_Unlock(chan); - -fail_lock: - return status; -} |
