diff options
| -rw-r--r-- | source/macros.h | 2 | ||||
| -rw-r--r-- | source/states/menu.c | 2 | ||||
| -rw-r--r-- | source/test-impl.inc.h | 4 | ||||
| -rw-r--r-- | source/test.c | 107 | ||||
| -rw-r--r-- | source/test.h | 4 | ||||
| -rw-r--r-- | source/tests/basic.c | 97 | ||||
| -rw-r--r-- | source/tests/led.c | 38 |
7 files changed, 207 insertions, 47 deletions
diff --git a/source/macros.h b/source/macros.h index d2231ff..9a9db83 100644 --- a/source/macros.h +++ b/source/macros.h @@ -9,4 +9,6 @@ #define COMMA , +#define ARRLEN(_a) sizeof(_a) / sizeof(*(_a)) + #endif /* include guard */ diff --git a/source/states/menu.c b/source/states/menu.c index 15e0b3c..ea5134b 100644 --- a/source/states/menu.c +++ b/source/states/menu.c @@ -59,6 +59,8 @@ static int begin_testing(u32 menu_state, struct et_next_state *next_state) { next_state->state = et_state_begin_testing; next_state->init_data.p = plan; + putchar('\n'); + return 1; } diff --git a/source/test-impl.inc.h b/source/test-impl.inc.h index 2ecb7a6..8e1559f 100644 --- a/source/test-impl.inc.h +++ b/source/test-impl.inc.h @@ -24,6 +24,8 @@ 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) \ { \ @@ -33,6 +35,8 @@ void test_plan_entry_set_error_static(struct et_test_plan_entry *entry, const ch .cleanup_f = test_state_cleanup, \ .private_size = sizeof(_private_type), \ }, \ + .test_init_f = _init_f, \ + .test_cleanup_f = _cleanup_f, \ .name = _namestr, \ .flags = _flags \ } diff --git a/source/test.c b/source/test.c index 2013e04..c59deaf 100644 --- a/source/test.c +++ b/source/test.c @@ -4,9 +4,11 @@ #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; @@ -188,14 +190,34 @@ static const struct et_state state_begin_testing_struct = { 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 et_test_plan *ptest_plan = (*(struct et_test_plan **)state_data = init_data.p); + 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; @@ -208,12 +230,15 @@ static int summ_test_plan_init(const struct et_state *state, void *state_data, e printf("\nTesting complete.\nPASS: %zu, FAIL: %zu, ERROR: %zu, SKIP: %zu, UNK: %zu\n\n", passed, failed, errored, skipped, unknown); - puts("Press A to test again. Press HOME/START to exit."); + 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); @@ -228,11 +253,45 @@ static int summ_test_plan_tick(const struct et_state *state, void *state_data, s 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 et_test_plan *ptest_plan = *(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); @@ -243,7 +302,7 @@ static void summ_test_plan_cleanup(const struct et_state *state, void *state_dat } static const struct et_state summ_test_plan_struct = { - .private_size = sizeof(struct et_test_plan *), + .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 @@ -352,3 +411,43 @@ const char *et_test_get_status_mnemonic(et_test_status status) { 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 index d45c2fe..81a107a 100644 --- a/source/test.h +++ b/source/test.h @@ -99,7 +99,9 @@ struct et_test_plan { void et_test_plan_entry_cleanup(struct et_test_plan_entry *entry); #define FOREACH_TEST(O, _sep) \ - O(check_id) + 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) diff --git a/source/tests/basic.c b/source/tests/basic.c index 8645b19..4f34ba4 100644 --- a/source/tests/basic.c +++ b/source/tests/basic.c @@ -1,66 +1,79 @@ #define ET_TEST_IMPL #include "../test.h" #include <inttypes.h> +#include <stdio.h> -et_test_status test_exi_readwrite(struct et_test_plan *plan, void *data, size_t data_sz, et_test_error base, struct et_next_state *next_state) { - struct et_test_plan_entry *cur = test_current(plan); - int chan = plan->exi_channel; - et_test_status status = TEST_PASS; - s32 exi_res; +#define TEST_ITERATIONS 16 - 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; - } +struct test_with_iterations { + struct et_test_state_private_base base; + int iteration; +}; - 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; - } +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; +} - 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; +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 ((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; + if (command != 0x0470) { + test_plan_entry_set_error(cur, "bad identifier from gecko: expected 0x0470, got 0x%04" PRIx16, command); + status = TEST_FAIL; } -fail_data: - EXI_Deselect(chan); + cur->status = status; -fail_select: - EXI_Unlock(chan); + if (status != TEST_PASS || ++priv->iteration >= TEST_ITERATIONS) { + return test_next(test_plan, next_state); + } -fail_lock: - cur->status = status; - return status; + return 0; } -static int check_id_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 *test_plan = priv->plan; +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; - u16 command = 0x9000; - if ((status = test_exi_readwrite(test_plan, &command, sizeof(command), TEST_EPRIV_BASE, next_state)) != TEST_PASS) { - return test_next(test_plan, next_state); + 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; + } } - if (command != 0x0470) { - test_plan_entry_set_error(cur, "bad identifier from gecko: expected 0x0470, got 0x%04" PRIx16, command); - status = TEST_FAIL; +test_fail: + cur->status = status; + + if (status != TEST_PASS || ++priv->iteration >= TEST_ITERATIONS) { + return test_next(test_plan, next_state); } - cur->status = TEST_PASS; - return test_next(test_plan, next_state); + return 0; } -TEST_DEF(check_id, "Identify Gecko (0x09)", TEST_PAUSE_ON_ERROR, NULL, check_id_tick, NULL, struct et_test_state_private_base); +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 new file mode 100644 index 0000000..82913ae --- /dev/null +++ b/source/tests/led.c @@ -0,0 +1,38 @@ +#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); |
