diff --git a/include/test/battle.h b/include/test/battle.h index 431e0f98f9..c323207b30 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -219,6 +219,17 @@ * { * KNOWN_FAILING; // #2596. * + * KNOWN_CRASHING + * Marks a test as crashing due to a bug. If there is an issue number + * associated with the bug it should be included in a comment. If the + * test passes the developer will be notified to remove KNOWN_CRASHING. + * For example: + * TEST("Crashes") + * { + * KNOWN_CRASHING; // #7255 + * void (*f)(void) = NULL; + * f(); // Crashes! + * * PARAMETRIZE * Runs a test multiple times. i will be set to which parameter is * running, and results will contain an entry for each parameter, e.g.: diff --git a/include/test/test.h b/include/test/test.h index 092b603ec0..835634df7b 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -54,6 +54,14 @@ struct TestRunnerState u32 timeoutSeconds; }; +struct PersistentTestRunnerState +{ + u32 address:28; + u32 state:1; + u32 expectCrash:1; + u32 unused_30:2; +}; + extern const u8 gTestRunnerN; extern const u8 gTestRunnerI; extern const char gTestRunnerArgv[256]; @@ -71,11 +79,13 @@ extern const struct TestRunner gFunctionTestRunner; extern struct FunctionTestRunnerState *gFunctionTestRunnerState; extern struct TestRunnerState gTestRunnerState; +extern struct PersistentTestRunnerState gPersistentTestRunnerState; void CB2_TestRunner(void); void Test_ExpectedResult(enum TestResult); void Test_ExpectLeaks(bool32); +void Test_ExpectCrash(bool32); void Test_ExitWithResult(enum TestResult, u32 stopLine, const char *fmt, ...); u32 SourceLine(u32 sourceLineOffset); u32 SourceLineOffset(u32 sourceLine); @@ -220,6 +230,9 @@ static inline struct Benchmark BenchmarkStop(void) #define KNOWN_LEAKING \ Test_ExpectLeaks(TRUE) +#define KNOWN_CRASHING \ + Test_ExpectCrash(TRUE) + #define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter) #define PARAMETRIZE_LABEL(f, label) if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter && (Test_MgbaPrintf(":N%s: " f " (%d/%d)", gTestRunnerState.test->name, label, gFunctionTestRunnerState->runParameter + 1, gFunctionTestRunnerState->parameters), 1)) diff --git a/ld_script_test.ld b/ld_script_test.ld index d279d6b4f6..db6c638883 100644 --- a/ld_script_test.ld +++ b/ld_script_test.ld @@ -6,9 +6,10 @@ gInitialMainCB2 = CB2_TestRunner; MEMORY { - EWRAM (rwx) : ORIGIN = 0x2000000, LENGTH = 256K - IWRAM (rwx) : ORIGIN = 0x3000000, LENGTH = 32K - ROM (rx) : ORIGIN = 0x8000000, LENGTH = 32M + EWRAM (rwx) : ORIGIN = 0x2000000, LENGTH = 256K + IWRAM (rwx) : ORIGIN = 0x3000000, LENGTH = 0x7F00 + IWRAM_PERSISTENT (rwx) : ORIGIN = 0x3007F00, LENGTH = 0x100 + ROM (rx) : ORIGIN = 0x8000000, LENGTH = 32M } SECTIONS { @@ -60,13 +61,11 @@ SECTIONS { /* .persistent starts at 0x3007F00 */ /* WARNING: This is the end of the IRQ stack, if there's too * much data it WILL be overwritten. */ - - . = 0x03007F00; .iwram.persistent (NOLOAD) : ALIGN(4) { test/*.o(.persistent); - } > IWRAM + } > IWRAM_PERSISTENT /* BEGIN ROM DATA */ . = 0x8000000; @@ -135,6 +134,7 @@ SECTIONS { ALIGN(4) { __start_tests = .; + test/test_test_runner.o(.tests); /* Sanity checks first. */ test/*.o(.tests); __stop_tests = .; test/*.o(.text); diff --git a/test/test_runner.c b/test/test_runner.c index a1460365e0..99ba3f42ab 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -22,10 +22,7 @@ enum { CURRENT_TEST_STATE_RUN, }; -__attribute__((section(".persistent"))) static struct { - u32 address:28; - u32 state:1; -} sCurrentTest = {0}; +__attribute__((section(".persistent"))) struct PersistentTestRunnerState gPersistentTestRunnerState = {0}; void TestRunner_Battle(const struct Test *); @@ -184,15 +181,16 @@ top: gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SET; // The current test restarted the ROM (e.g. by jumping to NULL). - if (sCurrentTest.address != 0) + if (gPersistentTestRunnerState.address != 0) { gTestRunnerState.test = __start_tests; - while ((uintptr_t)gTestRunnerState.test != sCurrentTest.address) + while ((uintptr_t)gTestRunnerState.test != gPersistentTestRunnerState.address) { AssignCostToRunner(); gTestRunnerState.test++; } - if (sCurrentTest.state == CURRENT_TEST_STATE_ESTIMATE) + + if (gPersistentTestRunnerState.state == CURRENT_TEST_STATE_ESTIMATE) { u32 runner = MinCostProcess(); gTestRunnerState.processCosts[runner] += 1; @@ -211,6 +209,9 @@ top: gTestRunnerState.state = STATE_REPORT_RESULT; gTestRunnerState.result = TEST_RESULT_CRASH; } + + if (gPersistentTestRunnerState.expectCrash) + gTestRunnerState.expectedResult = TEST_RESULT_CRASH; } else { @@ -252,8 +253,8 @@ top: REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second. REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK; - sCurrentTest.address = (uintptr_t)gTestRunnerState.test; - sCurrentTest.state = CURRENT_TEST_STATE_ESTIMATE; + gPersistentTestRunnerState.address = (uintptr_t)gTestRunnerState.test; + gPersistentTestRunnerState.state = CURRENT_TEST_STATE_ESTIMATE; // If AssignCostToRunner fails, we want to report the failure. gTestRunnerState.state = STATE_REPORT_RESULT; @@ -266,7 +267,8 @@ top: case STATE_RUN_TEST: gTestRunnerState.state = STATE_REPORT_RESULT; - sCurrentTest.state = CURRENT_TEST_STATE_RUN; + gPersistentTestRunnerState.state = CURRENT_TEST_STATE_RUN; + gPersistentTestRunnerState.expectCrash = FALSE; SeedRng(0); SeedRng2(0); if (gTestRunnerState.test->runner->setUp) @@ -423,6 +425,13 @@ void Test_ExpectLeaks(bool32 expectLeaks) gTestRunnerState.expectLeaks = expectLeaks; } +void Test_ExpectCrash(bool32 expectCrash) +{ + gPersistentTestRunnerState.expectCrash = expectCrash; + if (expectCrash) + Test_ExpectedResult(TEST_RESULT_CRASH); +} + static void FunctionTest_SetUp(void *data) { (void)data; diff --git a/test/test_test_runner.c b/test/test_test_runner.c new file mode 100644 index 0000000000..670be27674 --- /dev/null +++ b/test/test_test_runner.c @@ -0,0 +1,9 @@ +#include "global.h" +#include "test/test.h" + +TEST("Tests resume after CRASH") +{ + KNOWN_CRASHING; + void (*f)(void) = NULL; + f(); +}