Fix crashing tests looping (#7299)

This commit is contained in:
Martin Griffin
2025-07-09 08:02:55 +01:00
committed by GitHub
parent 94ddf6dea0
commit f4cc802656
5 changed files with 58 additions and 16 deletions

View File

@@ -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.:

View File

@@ -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))

View File

@@ -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);

View File

@@ -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;

9
test/test_test_runner.c Normal file
View File

@@ -0,0 +1,9 @@
#include "global.h"
#include "test/test.h"
TEST("Tests resume after CRASH")
{
KNOWN_CRASHING;
void (*f)(void) = NULL;
f();
}