diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/macros.h | 14 | ||||
| -rw-r--r-- | source/state.h | 57 | ||||
| -rw-r--r-- | source/states/menu.c | 98 | ||||
| -rw-r--r-- | source/template.c | 303 | ||||
| -rw-r--r-- | source/term.h | 13 | ||||
| -rw-r--r-- | source/test-impl.inc.h | 53 | ||||
| -rw-r--r-- | source/test.c | 453 | ||||
| -rw-r--r-- | source/test.h | 122 | ||||
| -rw-r--r-- | source/tests/basic.c | 79 | ||||
| -rw-r--r-- | source/tests/led.c | 38 |
10 files changed, 0 insertions, 1230 deletions
diff --git a/source/macros.h b/source/macros.h deleted file mode 100644 index 9a9db83..0000000 --- a/source/macros.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef EXITEST_MACROS_H_INCLUDED -#define EXITEST_MACROS_H_INCLUDED - -#define STR(_s) STR2(_s) -#define STR2(_s) #_s - -#define PASTE(_x, _y) PASTE2(_x, _y) -#define PASTE2(_x, _y) _x ## _y - -#define COMMA , - -#define ARRLEN(_a) sizeof(_a) / sizeof(*(_a)) - -#endif /* include guard */ diff --git a/source/state.h b/source/state.h deleted file mode 100644 index 2f70527..0000000 --- a/source/state.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef EXITEST_STATE_H_INCLUDED -#define EXITEST_STATE_H_INCLUDED - -#include <stdint.h> -#include <stddef.h> - -struct et_state; - -typedef union { - uintptr_t i; - void *p; -} et_state_init_data; - -extern const et_state_init_data et_init_data_null; - -struct et_next_state { - const struct et_state *state; - et_state_init_data init_data; -}; - -/* Sets up the state. The second argument (void *) should point to at least state->private_size bytes - * of data. The state assumes responsibility for clearing/initializing this buffer before use. - * - * Returns < 0 for error, >= 0 for success. - * This function conceptually takes ownership of init_data. The implications of this depend - * on what init_data actually contains -- sometimes it's globally shared data. - * - * State authors: Free any data filled into init_data regardless of error states. - * The caller will not touch init_data again. */ -typedef int (et_state_init_f)(const struct et_state *, void *, et_state_init_data); - -/* Ticks the state. - * - * Returns < 0 for error (still call cleanup or exit(...)), 0 for "continue", >0 for "advance state". - * If a state requests to advance to the next state, the info will be in *next_state (which should be non-NULL). - * Note that next_state->init_data may have malloc()'d data which must be handed off by passing to the init function - * of the appropriate state. */ -typedef int (et_state_tick_f)(const struct et_state *, void *, struct et_next_state *); -typedef void (et_state_cleanup_f)(const struct et_state *, void *); - -struct et_state { - et_state_init_f *init_f; - et_state_tick_f *tick_f; - et_state_cleanup_f *cleanup_f; - size_t private_size; -}; - -/* convenience macros because these existed as functions at one point - * (note: state expression is evaluated twice. make sure it doesn't have side effects) */ - -#define et_state_init(_st, _data, _init_data) ((_st)->init_f(_st, _data, _init_data)) -#define et_state_tick(_st, _data, _next) ((_st)->tick_f(_st, _data, _next)) -#define et_state_cleanup(_st, _data) ((_st)->cleanup_f(_st, _data)) - -extern const struct et_state *et_state_setup; - -#endif /* include guard */ diff --git a/source/states/menu.c b/source/states/menu.c deleted file mode 100644 index ea5134b..0000000 --- a/source/states/menu.c +++ /dev/null @@ -1,98 +0,0 @@ -#include <stdio.h> -#include <gccore.h> -#include <wiiuse/wpad.h> -#include <stdlib.h> - -#define EXITEST_STATE_INTERNAL -#include "../state.h" -#include "../term.h" -#include "../test.h" - -static void print_setup_menu(int selected_slot) { - fputs(TERM_CUR_UP(2), stdout); - printf(TERM_CLEARLINE "- [DL/DR] EXI Slot %c\n", selected_slot == 0 ? 'A' : 'B'); - printf(TERM_CLEARLINE "Press A to begin testing.\n"); -} - -static int st_setup_init(const struct et_state *state, void *state_data, et_state_init_data init_data) { - u32 *setup_data = state_data; - - *setup_data = 0; - - printf("\nTest setup menu:\n\n\n\n"); - print_setup_menu(*setup_data); - - return 0; -} - -static int begin_testing(u32 menu_state, struct et_next_state *next_state) { - struct et_test_plan *plan = calloc(1, sizeof(struct et_test_plan)); - if (!plan) { - puts("Failed to allocate new test plan!"); - return -1; - } - - size_t num_tests; - const struct et_test *const *all_tests = et_get_tests(&num_tests); - - struct et_test_plan_entry *plan_entries = calloc(num_tests, sizeof(struct et_test_plan_entry)); - if (!plan_entries) { - puts("Failed to allocate new test plan entries!"); - free(plan); - return -1; - } - - for (size_t i = 0; i < num_tests; ++i) { - plan_entries[i].test = all_tests[i]; - plan_entries[i].status = TEST_PRIMORDIAL; - plan_entries[i].ext_status = NULL; - plan_entries[i].ext_status_free = NULL; - } - - plan->exi_channel = (int)menu_state; - plan->next_state = et_state_summarize_test_plan; - plan->next_state_init.p = plan; - plan->num_tests = num_tests; - plan->current_test = 0; - plan->tests = plan_entries; - - next_state->state = et_state_begin_testing; - next_state->init_data.p = plan; - - putchar('\n'); - - return 1; -} - -static int st_setup_tick(const struct et_state *state, void *state_data, struct et_next_state *next_state) { - u32 *setup_data = state_data; - u32 wpad_pressed = WPAD_ButtonsDown(0); - u32 pad_pressed = PAD_ButtonsDown(0); - - if (wpad_pressed & WPAD_BUTTON_LEFT || wpad_pressed & WPAD_BUTTON_RIGHT || pad_pressed & PAD_BUTTON_LEFT || pad_pressed & PAD_BUTTON_RIGHT) { - *setup_data = !*setup_data; - print_setup_menu(*setup_data); - } - - if (wpad_pressed & WPAD_BUTTON_A || pad_pressed & PAD_BUTTON_A) { - /* set up test plan */ - return begin_testing(*setup_data, next_state); - } - - return 0; -} - -static void st_setup_cleanup(const struct et_state *state, void *state_data) { - /* do nothing */ -} - -const et_state_init_data et_init_data_null = { .p = NULL }; - -static struct et_state et__state_setup = { - .init_f = &st_setup_init, - .tick_f = &st_setup_tick, - .cleanup_f = &st_setup_cleanup, - .private_size = sizeof(u32) -}; - -const struct et_state *et_state_setup = &et__state_setup; diff --git a/source/template.c b/source/template.c deleted file mode 100644 index 8665774..0000000 --- a/source/template.c +++ /dev/null @@ -1,303 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <gccore.h> -#include <wiiuse/wpad.h> -#include <inttypes.h> -#include <string.h> - -#include "term.h" -#include "state.h" - -static void *xfb = NULL; -static GXRModeObj *rmode = NULL; - -#define TARGET_IOS 58 - -static void initialize(int again) { - VIDEO_Init(); - - rmode = VIDEO_GetPreferredMode(NULL); - xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - - console_init(xfb, 20, 20, rmode->fbWidth - 20, rmode->xfbHeight - 20, rmode->fbWidth * VI_DISPLAY_PIX_SZ); - - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(xfb); - VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - VIDEO_WaitVSync(); - if (rmode->viTVMode & VI_NON_INTERLACE) { - VIDEO_WaitVSync(); - } - - fputs(TERM_CLEAR TERM_CUR_POS(2, 5), stdout); - printf("USB Gecko (newnewexi) test tool by figboot\n\n"); - - s32 iosver = IOS_GetVersion(); - s32 iosrev = IOS_GetRevision(); - printf("You're running IOS%" PRId32 " rev %" PRId32 ".\n", iosver, iosrev); - - if (iosver != TARGET_IOS) { - if (again) { - printf(TERM_CSI("93m") "Failed to set up IOS" STR(TARGET_IOS) "! Bailing out.\n"); - exit(1); - } - - printf("Trying to load IOS" STR(TARGET_IOS) "...\n"); - - s32 res; - if ((res = IOS_ReloadIOS(TARGET_IOS)) < 0) { - printf(TERM_CSI("93m") "Failed to set up IOS" STR(TARGET_IOS) "! IOS_ReloadIOS returned %" PRId32 "\n", res); - exit(1); - } - - free(MEM_K1_TO_K0(xfb)); - initialize(1); - } -} - -static void __attribute__((noreturn)) fatal_error(void) { - printf("The program will now abort due to the fatal error.\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - void *state_data = NULL; - size_t state_data_size = 0; - struct et_next_state next; - const struct et_state *cur_state = NULL; - int stateres = 0; - - u32 wpad_pressed; - u32 pad_pressed; - - initialize(0); - - WPAD_Init(); - PAD_Init(); - - printf("Done initializing.\n"); - - /* Set up initial state */ - cur_state = et_state_setup; - - if (cur_state->private_size > 0) { - state_data = malloc(cur_state->private_size); - state_data_size = cur_state->private_size; - if (!state_data) { - printf("Failed to allocate private space for setup state %p!", cur_state); - fatal_error(); - } - } - - if ((stateres = et_state_init(cur_state, state_data, et_init_data_null)) < 0) { - printf("Failed to init setup state %p: %d\n", cur_state, stateres); - fatal_error(); - } - - while (SYS_MainLoop()) { - WPAD_ScanPads(); - PAD_ScanPads(); - - wpad_pressed = WPAD_ButtonsDown(0); - pad_pressed = PAD_ButtonsDown(0); - - if (wpad_pressed & WPAD_BUTTON_HOME || pad_pressed & PAD_BUTTON_START) { - printf("Exit button pressed. Goodbye!\n"); - exit(0); - } - - stateres = et_state_tick(cur_state, state_data, &next); - if (stateres < 0) { - printf("Failed to tick state %p: %d\n", cur_state, stateres); - fatal_error(); - } - - if (stateres > 0) { - /* tear down the old state first... */ - et_state_cleanup(cur_state, state_data); - - cur_state = next.state; - - if (state_data_size < cur_state->private_size) { - void *new_data = realloc(state_data, cur_state->private_size); - if (!new_data) { - printf("Failed to allocate %zu bytes for new state %p\n", cur_state->private_size, cur_state); - fatal_error(); - } - - state_data = new_data; - state_data_size = cur_state->private_size; - } - - if ((stateres = et_state_init(cur_state, state_data, next.init_data)) < 0) { - printf("Failed to init new state %p: %d\n", cur_state, stateres); - fatal_error(); - } - } - - VIDEO_WaitVSync(); - } - - return 0; -} - -#if 0 - -//--------------------------------------------------------------------------------- -int main(int argc, char **argv) { -//--------------------------------------------------------------------------------- - - // Initialise the video system - VIDEO_Init(); - - // This function initialises the attached controllers - WPAD_Init(); - - // Obtain the preferred video mode from the system - // This will correspond to the settings in the Wii menu - rmode = VIDEO_GetPreferredMode(NULL); - - // Allocate memory for the display in the uncached region - xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - - // Initialise the console, required for printf - console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); - - // Set up the video registers with the chosen mode - VIDEO_Configure(rmode); - - // Tell the video hardware where our display memory is - VIDEO_SetNextFramebuffer(xfb); - - // Make the display visible - VIDEO_SetBlack(false); - - // Flush the video register changes to the hardware - VIDEO_Flush(); - - // Wait for Video setup to complete - VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - - - // The console understands VT terminal escape codes - // This positions the cursor on row 2, column 0 - // we can use variables for this with format codes too - // e.g. printf ("\x1b[%d;%dH", row, column ); - printf("\x1b[2;0H"); - - - printf("Hello World!\n"); - -#define UGCHAN EXI_CHANNEL_1 /* slot B */ -#define UGDEV EXI_DEVICE_0 /* memory card */ - - if (EXI_Lock(UGCHAN, UGDEV, NULL) == 1) { - EXI_Select(UGCHAN, UGDEV, EXI_SPEED32MHZ); - uint32_t cmd = 0x90000000; - int res = 0; - if (EXI_Imm(UGCHAN, &cmd, sizeof(cmd), EXI_READWRITE, NULL) == 0) res |= 0x01; - if (EXI_Sync(UGCHAN) == 0) res |= 0x02; - - if (res == 0) { - printf("Got: 0x%08x\n", cmd); - if (cmd != 0x04700000) { - printf("This is probably not a USB gecko...\n"); - } - } else { - printf("EXI read/write error :( %#04x\n", res); - } - - EXI_Deselect(UGCHAN); - EXI_Unlock(UGCHAN); - } else { - printf("Failed to lock EXI channel %d %d\n", UGCHAN, UGDEV); - goto waitexit; - } - - for (int i = 0; i < 10; ++i) { - if (EXI_Lock(UGCHAN, UGDEV, NULL) == 0) { - printf("Failed to lock EXI for LED\n"); - break; - } - - int res = 0; - - if (EXI_Select(UGCHAN, UGDEV, EXI_SPEED32MHZ) == 0) res |= 0x01; - uint32_t cmd = (i & 1) ? 0x80000000 : 0x70000000; - if (EXI_Imm(UGCHAN, &cmd, sizeof(cmd), EXI_WRITE, NULL) == 0) res |= 0x02; - if (EXI_Sync(UGCHAN) == 0) res |= 0x04; - if (EXI_Deselect(UGCHAN) == 0) res |= 0x08; - - if (EXI_Unlock(UGCHAN) == 0) res |= 0x10; - - printf("Error for LED: 0x%02x\n", res); - - for (int j = 0; j < 60; ++j) { - VIDEO_WaitVSync(); - } - } - - - for (int j = 0; j < 10; ++j) { - if (EXI_Lock(UGCHAN, UGDEV, NULL) == 1) { - printf("Sending a zillion bytes to your so-called \"usb gecko\"...\n"); - - for (int i = 0; i < 4096; ++i) { - EXI_Select(UGCHAN, UGDEV, EXI_SPEED32MHZ); - - uint16_t cmd = 0xB000 | (uint16_t)(((i & 63) + 32) << 4); - if (EXI_Imm(UGCHAN, &cmd, sizeof(cmd), EXI_READWRITE, NULL) == 0) { - printf("Failed when writing %d\n", i); - goto error; - } - - if (EXI_Sync(UGCHAN) == 0) { - printf("Failed sync when writing %d\n", i); - goto error; - } - - if ((cmd & 0x0400) == 0) { - printf("Out of FIFO space at %d\n", i); - goto error; - } - - EXI_Deselect(UGCHAN); - continue; -error: - EXI_Deselect(UGCHAN); - break; - } - - EXI_Unlock(UGCHAN); - } else { - printf("Failed to lock EXI channel %d %d\n", UGCHAN, UGDEV); - goto waitexit; - } - } - -waitexit: - printf("That's all! Press HOME to exit.\n"); - - while(SYS_MainLoop()) { - - // Call WPAD_ScanPads each loop, this reads the latest controller states - WPAD_ScanPads(); - - // WPAD_ButtonsDown tells us which buttons were pressed in this loop - // this is a "one shot" state which will not fire again until the button has been released - u32 pressed = WPAD_ButtonsDown(0); - - // We return to the launcher application via exit - if ( pressed & WPAD_BUTTON_HOME ) exit(0); - - // Wait for the next frame - VIDEO_WaitVSync(); - } - - return 0; -} -#endif diff --git a/source/term.h b/source/term.h deleted file mode 100644 index cc65615..0000000 --- a/source/term.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef EXITEST_TERM_H_INCLUDED -#define EXITEST_TERM_H_INCLUDED - -#include "macros.h" - -#define TERM_ESC "\x1b" -#define TERM_CSI(_s) TERM_ESC "[" _s -#define TERM_CLEAR TERM_CSI("2J") -#define TERM_CLEARLINE TERM_CSI("2K") -#define TERM_CUR_POS(_row, _col) TERM_CSI(STR(_row) ";" STR(_col) "f") -#define TERM_CUR_UP(_rows) TERM_CSI(STR(_rows) "A") - -#endif /* include guard */ diff --git a/source/test-impl.inc.h b/source/test-impl.inc.h deleted file mode 100644 index 8e1559f..0000000 --- a/source/test-impl.inc.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ET__ALLOW_TEST_IMPL_INC -#error "Do not include this file directly! Include test.h instead." -#endif - -#undef ET__ALLOW_TEST_IMPL_INC - -/* This file will be included in .c files which implement tests. */ -#ifdef _CLANGD -#include <gccore.h> -#include "test.h" -/* include stuff this file uses here to make clangd happy (will not be visible to test.h) */ -#endif - -struct et_test_state_private_base { - struct et_test_plan *plan; -}; - -/* function prototypes for test-internal stuff */ -int test_next(struct et_test_plan *plan, struct et_next_state *next_state); - -int test_state_init(const struct et_state *state, void *data, et_state_init_data init_data); -void test_state_cleanup(const struct et_state *state, void *data); - -void test_plan_entry_set_error(struct et_test_plan_entry *entry, const char *fmt, ...) __attribute__((format(printf, 2, 3))); -void test_plan_entry_set_error_static(struct et_test_plan_entry *entry, const char *str); - -et_test_status test_exi_readwrite(struct et_test_plan *plan, void *data, size_t data_sz, et_test_error base); - -/* Expands to a struct initializer for a struct et_test */ -#define TEST_STRUCT_INIT_FULL(_namestr, _flags, _init_f, _tick_f, _cleanup_f, _private_type) \ -{ \ - .parent = { \ - .init_f = test_state_init, \ - .tick_f = _tick_f, \ - .cleanup_f = test_state_cleanup, \ - .private_size = sizeof(_private_type), \ - }, \ - .test_init_f = _init_f, \ - .test_cleanup_f = _cleanup_f, \ - .name = _namestr, \ - .flags = _flags \ -} - -#define TEST__STRUCT_NAME(_name) PASTE(TEST_SYM(_name), __struct) -#define TEST__STRUCT_DEF(_name, _ini) static const struct et_test TEST__STRUCT_NAME(_name) = _ini -#define TEST__STRUCT_SANITY_CHECK(_ty) static_assert(sizeof(_ty) >= sizeof(struct et_test_state_private_base), "private data type too small (does it include base?)") -#define TEST__SYM_DEF(_name) const struct et_test *const TEST_SYM(_name) = &TEST__STRUCT_NAME(_name) -#define TEST_DEF(_name, _namestr, _flags, _init_f, _tick_f, _cleanup_f, _private_type) \ - TEST__STRUCT_SANITY_CHECK(_private_type); \ - TEST__STRUCT_DEF(_name, TEST_STRUCT_INIT_FULL(_namestr, _flags, _init_f, _tick_f, _cleanup_f, _private_type)); \ - TEST__SYM_DEF(_name) - -#define test_current(_plan) ((_plan)->tests + (_plan)->current_test) 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; -} diff --git a/source/test.h b/source/test.h deleted file mode 100644 index 81a107a..0000000 --- a/source/test.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef EXITEST_TEST_H_INCLUDED -#define EXITEST_TEST_H_INCLUDED - -#include <gccore.h> -#include <assert.h> -#include "state.h" -#include "macros.h" - -typedef enum { - TEST_INTERACTIVE = 0x01u, - TEST_PAUSE_ON_ERROR = 0x02u /* for "essential" tests that should pass */ -} et_test_flags; - -struct et_test; -struct et_test_plan; - -struct et_test { - struct et_state parent; - const char *name; - et_test_flags flags; - - et_state_init_f *test_init_f; - et_state_cleanup_f *test_cleanup_f; -}; - -/* 0000eeee eeeeeeee eeeeeeee x00000ss */ -/* e = error code bits, - * x = 1 if this test is still in the "clean" (not executed) state - * ss = status type (pass/fail/etc.) */ -typedef u32 et_test_status; - -typedef enum { - TEST_ENONE = (u32)0, - - /* test was aborted (i.e., requested to go to next test without setting a status code) */ - TEST_EABORT, - - /* minimum private test error (define test-private error codes as TEST_EPRIV_BASE+n) */ - TEST_EPRIV_BASE = 32, - - /* largest error code allowed */ - TEST_EMAX = ((u32)1 << 20) - 1 -} et_test_error; - -typedef enum { - TEST_SSKIPPED = 0u, - TEST_SERROR, - TEST_SFAIL, - TEST_SPASS, - - TEST_SMAX = ((u32)1 << 2) - 1 -} et_test_status_type; - -#define TEST_STATUS_GET_ERROR(_status) (et_test_error)(((u32)(_status) >> 8) & TEST_EMAX) -#define TEST_STATUS_GET_TYPE(_status) (et_test_status_type)((u32)(_status) & TEST_SMAX) -#define TEST_STATUS_IS_CLEAN(_status) (!!((u32)(_status) & 0x80u)) - -#define TEST_MAKE_STATUS(_type, _error, _clean) (et_test_status) \ - (((u32)(_type) & TEST_SMAX) | \ - (((u32)(_clean) & 1) << 7) | \ - (((u32)(_error) & TEST_EMAX) << 8)) - -#define TEST_SKIPPED TEST_MAKE_STATUS(TEST_SSKIPPED, TEST_ENONE, 0) -#define TEST_FAIL TEST_MAKE_STATUS(TEST_SFAIL, TEST_ENONE, 0) -#define TEST_PASS TEST_MAKE_STATUS(TEST_SPASS, TEST_ENONE, 0) -#define TEST_ERROR(_e) TEST_MAKE_STATUS(TEST_SERROR, _e, 0) - -#define TEST_PRIMORDIAL TEST_MAKE_STATUS(TEST_SSKIPPED, TEST_ENONE, 1) - -/* Expected setup date: pointer to test plan */ -extern const struct et_state *et_state_begin_testing; -extern const struct et_state *et_state_summarize_test_plan; - -const char *et_test_get_status_mnemonic(et_test_status status); - -struct et_test_plan_entry { - const struct et_test *test; - et_test_status status; - const char *ext_status; - void (*ext_status_free)(void *); -}; - -/* a properly initialized test plan will have all the tests' statuses set to TEST_PRIMORDIAL */ -struct et_test_plan { - int exi_channel; - - /* this state will be set after testing is complete. - * NOTE: next_state_init should somehow point to this test plan, - * since it's probably dynamically allocated and will be leaked if not - * freed. */ - const struct et_state *next_state; - et_state_init_data next_state_init; - - size_t num_tests; - size_t current_test; - struct et_test_plan_entry *tests; -}; - -void et_test_plan_entry_cleanup(struct et_test_plan_entry *entry); - -#define FOREACH_TEST(O, _sep) \ - O(check_id) _sep \ - O(bad_commands) _sep \ - O(flash_led) - -#define TEST_SYM(_name) PASTE(et_test_, _name) -#define DECL_TEST(_name) extern const struct et_test *const TEST_SYM(_name) - -FOREACH_TEST(DECL_TEST, ;); -#undef DECL_TEST - -const struct et_test *const *et_get_tests(size_t *ntests); - -#if defined(ET_TEST_IMPL) || defined(TEST_INTERNAL) -#define ET__ALLOW_TEST_IMPL_INC -#include "test-impl.inc.h" -#else -#undef FOREACH_TEST -#undef TEST_SYM -#endif - -#endif /* include guard */ diff --git a/source/tests/basic.c b/source/tests/basic.c deleted file mode 100644 index 4f34ba4..0000000 --- a/source/tests/basic.c +++ /dev/null @@ -1,79 +0,0 @@ -#define ET_TEST_IMPL -#include "../test.h" -#include <inttypes.h> -#include <stdio.h> - -#define TEST_ITERATIONS 16 - -struct test_with_iterations { - struct et_test_state_private_base base; - int iteration; -}; - -static int init_test_with_iterations(const struct et_state *state, void *data, et_state_init_data init_data) { - struct test_with_iterations *priv = data; - priv->iteration = 0; - return 0; -} - -static int check_id_tick(const struct et_state *state, void *data, struct et_next_state *next_state) { - struct test_with_iterations *priv = data; - struct et_test_plan *test_plan = priv->base.plan; - struct et_test_plan_entry *cur = test_current(test_plan); - et_test_status status = TEST_PASS; - u16 command = 0x9000; - - if ((status = test_exi_readwrite(test_plan, &command, sizeof(command), TEST_EPRIV_BASE)) != TEST_PASS) { - cur->status = status; - return test_next(test_plan, next_state); - } - - if (command != 0x0470) { - test_plan_entry_set_error(cur, "bad identifier from gecko: expected 0x0470, got 0x%04" PRIx16, command); - status = TEST_FAIL; - } - - cur->status = status; - - if (status != TEST_PASS || ++priv->iteration >= TEST_ITERATIONS) { - return test_next(test_plan, next_state); - } - - return 0; -} - -TEST_DEF(check_id, "Identify Gecko (0x09)", TEST_PAUSE_ON_ERROR, init_test_with_iterations, check_id_tick, NULL, struct test_with_iterations); - -static int bad_commands_tick(const struct et_state *state, void *data, struct et_next_state *next_state) { - struct test_with_iterations *priv = data; - struct et_test_plan *test_plan = priv->base.plan; - struct et_test_plan_entry *cur = test_current(test_plan); - et_test_status status = TEST_PASS; - - static const u16 bad_commands[] = { 0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0xe000, 0xf000 }; - - for (int i = 0; i < ARRLEN(bad_commands); ++i) { - u16 orig_cmd = bad_commands[i]; - u16 cmd = orig_cmd; - if ((status = test_exi_readwrite(test_plan, &cmd, sizeof(cmd), TEST_EPRIV_BASE)) != TEST_PASS) { - goto test_fail; - } - - if (cmd != 0x0000) { - test_plan_entry_set_error(cur, "got bogus response from invalid command 0x%04" PRIx16 ": 0x%04" PRIx16, orig_cmd, cmd); - status = TEST_FAIL; - break; - } - } - -test_fail: - cur->status = status; - - if (status != TEST_PASS || ++priv->iteration >= TEST_ITERATIONS) { - return test_next(test_plan, next_state); - } - - return 0; -} - -TEST_DEF(bad_commands, "Bad commands", 0, init_test_with_iterations, bad_commands_tick, NULL, struct test_with_iterations); diff --git a/source/tests/led.c b/source/tests/led.c deleted file mode 100644 index 82913ae..0000000 --- a/source/tests/led.c +++ /dev/null @@ -1,38 +0,0 @@ -#define ET_TEST_IMPL -#include "../test.h" -#include <inttypes.h> - -static int flash_led_tick(const struct et_state *state, void *data, struct et_next_state *next_state) { - struct et_test_state_private_base *priv = data; - struct et_test_plan *plan = priv->plan; - struct et_test_plan_entry *cur = test_current(plan); - et_test_status status = TEST_PASS; - - for (int i = 0; i < 1024; ++i) { - u16 cmd_orig, cmd; - - if (i & 1) { - /* LED off */ - cmd_orig = 0x7000; - } else { - /* LED on */ - cmd_orig = 0x8000; - } - - cmd = cmd_orig; - if ((status = test_exi_readwrite(plan, &cmd, sizeof(cmd), TEST_EPRIV_BASE)) != TEST_PASS) { - break; - } - - if (cmd != 0x0000) { - status = TEST_FAIL; - test_plan_entry_set_error(cur, "spurious response to 0x%04" PRIx16 ": 0x%04" PRIx16 " (should be 0x0000)", cmd_orig, cmd); - break; - } - } - - cur->status = status; - return test_next(plan, next_state); -} - -TEST_DEF(flash_led, "Flash LED", 0, NULL, flash_led_tick, NULL, struct et_test_state_private_base); |
