diff --git a/Makefile b/Makefile index 028fd8612c..abd395c057 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,11 @@ ELF = $(ROM:.gba=.elf) MAP = $(ROM:.gba=.map) SYM = $(ROM:.gba=.sym) +ifeq ($(MODERN),0) TEST_OBJ_DIR_NAME := build/test +else +TEST_OBJ_DIR_NAME := build/modern-test +endif TESTELF = $(ROM:.gba=-test.elf) HEADLESSELF = $(ROM:.gba=-test-headless.elf) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index e1cb76e784..ffe481086b 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1456,6 +1456,11 @@ .4byte \jumpInstr .endm + .macro tryreflecttype failInstr:req + callnative BS_TryReflectType + .4byte \failInstr + .endm + @ Used to active a different Max Move effects. .macro setmaxmoveeffect callnative BS_SetMaxMoveEffect @@ -1729,11 +1734,6 @@ .4byte \failInstr .endm - .macro tryreflecttype failInstr:req - various BS_ATTACKER, VARIOUS_TRY_REFLECT_TYPE - .4byte \failInstr - .endm - .macro trysoak failInstr:req various BS_ATTACKER, VARIOUS_TRY_SOAK .4byte \failInstr diff --git a/include/battle.h b/include/battle.h index ba6dcd3e60..cd314eb728 100644 --- a/include/battle.h +++ b/include/battle.h @@ -753,12 +753,16 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER #define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0)) #define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) + +#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY) + #define SET_BATTLER_TYPE(battlerId, type) \ { \ gBattleMons[battlerId].type1 = type; \ gBattleMons[battlerId].type2 = type; \ gBattleMons[battlerId].type3 = TYPE_MYSTERY; \ } + #define RESTORE_BATTLER_TYPE(battlerId) \ { \ gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \ diff --git a/include/config/battle.h b/include/config/battle.h index 480107ab3b..e0c3ae2acc 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -117,7 +117,7 @@ #define B_SHADOW_TAG_ESCAPE GEN_LATEST // In Gen4+, if both sides have a Pokémon with Shadow Tag, all battlers can escape. Before, neither side could escape this situation. #define B_MOODY_ACC_EVASION GEN_LATEST // In Gen8, Moody CANNOT raise Accuracy and Evasion anymore. #define B_FLASH_FIRE_FROZEN GEN_LATEST // In Gen5+, Flash Fire can trigger even when frozen, when it couldn't before. -#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same ability, as opposed to 50% previously. +#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same Nature, as opposed to 50% previously. In Gen9, it has no out-of-battle effect. #define B_SYNCHRONIZE_TOXIC GEN_LATEST // In Gen5+, if a Pokémon with Synchronize is badly poisoned, the opponent will also become badly poisoned. Previously, the opponent would become regular poisoned. #define B_UPDATED_INTIMIDATE GEN_LATEST // In Gen8, Intimidate doesn't work on opponents with the Inner Focus, Scrappy, Own Tempo or Oblivious abilities. It also activates Rattled. #define B_OBLIVIOUS_TAUNT GEN_LATEST // In Gen6+, Pokémon with Oblivious can't be taunted. diff --git a/include/test/test.h b/include/test/test.h index 5b3ab8af0c..e9c920bd0b 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -47,6 +47,7 @@ struct TestRunnerState u8 expectedResult; bool8 expectLeaks:1; bool8 inBenchmark:1; + bool8 tearDown:1; u32 timeoutSeconds; }; diff --git a/src/battle_main.c b/src/battle_main.c index 7a70771af9..173fc45468 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1883,7 +1883,7 @@ static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i) void ModifyPersonalityForNature(u32 *personality, u32 newNature) { u32 nature = GetNatureFromPersonality(*personality); - s32 diff = abs(nature - newNature); + s32 diff = abs((s32)nature - (s32)newNature); s32 sign = (nature > newNature) ? 1 : -1; if (diff > NUM_NATURES / 2) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 610d6284b5..82d184b88f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9398,38 +9398,6 @@ static void Cmd_various(void) } return; } - case VARIOUS_TRY_REFLECT_TYPE: - { - VARIOUS_ARGS(const u8 *failInstr); - if (GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species) == SPECIES_ARCEUS - || GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species) == SPECIES_SILVALLY) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) != TYPE_MYSTERY) - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 1); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1); - gBattlescriptCurrInstr = cmd->nextInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) != TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY) - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 0); - gBattlescriptCurrInstr = cmd->nextInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1); - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } case VARIOUS_TRY_SOAK: { VARIOUS_ARGS(const u8 *failInstr); @@ -16250,3 +16218,49 @@ void BS_JumpIfTerrainAffected(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_TryReflectType(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species); + u8 targetType1 = GetBattlerType(gBattlerTarget, 0); + u8 targetType2 = GetBattlerType(gBattlerTarget, 1); + u8 targetType3 = GetBattlerType(gBattlerTarget, 2); + + if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (IS_BATTLER_TYPELESS(gBattlerTarget)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = TYPE_NORMAL; + gBattleMons[gBattlerAttacker].type2 = TYPE_NORMAL; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = targetType2; + gBattleMons[gBattlerAttacker].type2 = targetType2; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = targetType1; + gBattleMons[gBattlerAttacker].type2 = targetType1; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattleMons[gBattlerAttacker].type1 = targetType1; + gBattleMons[gBattlerAttacker].type2 = targetType2; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/data/items.h b/src/data/items.h index a3bd7e3f39..1532a71983 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -6041,8 +6041,8 @@ const struct Item gItems[] = .holdEffectParam = 10, .description = sRazorFangDesc, .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .type = EVO_HELD_ITEM_TYPE, + .fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC, .flingPower = 30, }, diff --git a/src/data/pokemon/item_effects.h b/src/data/pokemon/item_effects.h index 81be2ca115..2c7401059e 100644 --- a/src/data/pokemon/item_effects.h +++ b/src/data/pokemon/item_effects.h @@ -618,6 +618,7 @@ const u8 *const gItemEffectTable[ITEMS_COUNT] = [ITEM_METAL_COAT] = gItemEffect_EvoItem, [ITEM_KINGS_ROCK] = gItemEffect_EvoItem, [ITEM_RAZOR_CLAW] = gItemEffect_EvoItem, + [ITEM_RAZOR_FANG] = gItemEffect_EvoItem, [ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem, [ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem, [ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem, diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 3162c5ed23..8335b37e7d 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -415,6 +415,7 @@ static u8 PickWildMonNature(void) } } } +#if B_SYNCHRONIZE_NATURE < GEN_9 // check synchronize for a pokemon with the same ability if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) && GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE @@ -422,6 +423,7 @@ static u8 PickWildMonNature(void) { return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES; } +#endif // random nature return Random() % NUM_NATURES; diff --git a/test/battle/move_effect/reflect_type.c b/test/battle/move_effect/reflect_type.c new file mode 100644 index 0000000000..e873516034 --- /dev/null +++ b/test/battle/move_effect/reflect_type.c @@ -0,0 +1,186 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Reflect Type fails if the user is Terastallized"); +TO_DO_BATTLE_TEST("Reflect Type succeeds against a Terastallized target and copies its Tera type"); + +SINGLE_BATTLE_TEST("Reflect Type does not affect any of Arceus' forms") +{ + u32 j; + static const u16 sArceusFormSpeciesIdTable[] = { + SPECIES_ARCEUS, + SPECIES_ARCEUS_FIGHTING, + SPECIES_ARCEUS_FLYING, + SPECIES_ARCEUS_POISON, + SPECIES_ARCEUS_GROUND, + SPECIES_ARCEUS_ROCK, + SPECIES_ARCEUS_BUG, + SPECIES_ARCEUS_GHOST, + SPECIES_ARCEUS_STEEL, + SPECIES_ARCEUS_FIRE, + SPECIES_ARCEUS_WATER, + SPECIES_ARCEUS_GRASS, + SPECIES_ARCEUS_ELECTRIC, + SPECIES_ARCEUS_PSYCHIC, + SPECIES_ARCEUS_ICE, + SPECIES_ARCEUS_DRAGON, + SPECIES_ARCEUS_DARK, + SPECIES_ARCEUS_FAIRY, + }; + u16 species = SPECIES_NONE; + + for (j = 0; j < ARRAY_COUNT(sArceusFormSpeciesIdTable); j++) + { + PARAMETRIZE { species = sArceusFormSpeciesIdTable[j]; } + } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type does not affect any of Silvally's forms") +{ + u32 j; + static const u16 sSilvallyFormSpeciesIdTable[] = { + SPECIES_SILVALLY, + SPECIES_SILVALLY_FIGHTING, + SPECIES_SILVALLY_FLYING, + SPECIES_SILVALLY_POISON, + SPECIES_SILVALLY_GROUND, + SPECIES_SILVALLY_ROCK, + SPECIES_SILVALLY_BUG, + SPECIES_SILVALLY_GHOST, + SPECIES_SILVALLY_STEEL, + SPECIES_SILVALLY_FIRE, + SPECIES_SILVALLY_WATER, + SPECIES_SILVALLY_GRASS, + SPECIES_SILVALLY_ELECTRIC, + SPECIES_SILVALLY_PSYCHIC, + SPECIES_SILVALLY_ICE, + SPECIES_SILVALLY_DRAGON, + SPECIES_SILVALLY_DARK, + SPECIES_SILVALLY_FAIRY, + }; + u16 species = SPECIES_NONE; + + for (j = 0; j < ARRAY_COUNT(sSilvallyFormSpeciesIdTable); j++) + { + PARAMETRIZE { species = sSilvallyFormSpeciesIdTable[j]; } + } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type does not affect Pokémon with no types") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_POLIWRATH); + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Burn Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); + HP_BAR(opponent); + MESSAGE("Arcanine burned itself out!"); + MESSAGE("Foe Poliwrath used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type copies a target's dual types") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_POLIWRATH); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Arcanine's type changed to match the Foe Poliwrath's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_WATER); + EXPECT_EQ(player->type2, TYPE_FIGHTING); + EXPECT_EQ(player->type3, TYPE_MYSTERY); + } +} + +SINGLE_BATTLE_TEST("Reflect Type copies a target's pure type") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[0] == TYPE_ROCK); + ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[1] == TYPE_ROCK); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_SUDOWOODO); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Arcanine's type changed to match the Foe Sudowoodo's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_ROCK); + EXPECT_EQ(player->type2, TYPE_ROCK); + EXPECT_EQ(player->type3, TYPE_MYSTERY); + } +} + +SINGLE_BATTLE_TEST("Reflect Type defaults to Normal type for the user's type1 and type2 if the target only has a 3rd type") +{ + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARCANINE); + } WHEN { + TURN { MOVE(opponent, MOVE_BURN_UP); } + TURN { MOVE(player, MOVE_FORESTS_CURSE); } + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + // Turn 1 + MESSAGE("Foe Arcanine used Burn Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, opponent); + HP_BAR(player); + MESSAGE("Foe Arcanine burned itself out!"); + // Turn 2 + MESSAGE("Wobbuffet used Forest'sCurs!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, player); + MESSAGE("Grass type was added to Foe Arcanine!"); + // Turn 3 + MESSAGE("Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Wobbuffet's type changed to match the Foe Arcanine's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_NORMAL); + EXPECT_EQ(player->type2, TYPE_NORMAL); + EXPECT_EQ(player->type3, TYPE_GRASS); + } +} diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index a0e94dbbec..99bea0e8cc 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -4,6 +4,7 @@ #include "battle_main.h" #include "data.h" #include "malloc.h" +#include "random.h" #include "string_util.h" #include "constants/item.h" #include "constants/abilities.h" @@ -119,3 +120,17 @@ TEST("CreateNPCTrainerPartyForTrainer generates different personalities for diff EXPECT(testParty[0].box.personality != testParty[1].box.personality); Free(testParty); } + +TEST("ModifyPersonalityForNature can set any nature") +{ + u32 personality, nature, j, k; + for (j = 0; j < 64; j++) + { + for (k = 0; k < NUM_NATURES; k++) + { + PARAMETRIZE { personality = Random32(); nature = k; } + } + } + ModifyPersonalityForNature(&personality, nature); + EXPECT_EQ(GetNatureFromPersonality(personality), nature); +} diff --git a/test/test_runner.c b/test/test_runner.c index c3b2f12be5..83bab5d0ba 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -56,9 +56,10 @@ static bool32 PrefixMatch(const char *pattern, const char *string) enum { STATE_INIT, - STATE_NEXT_TEST, + STATE_ASSIGN_TEST, STATE_RUN_TEST, STATE_REPORT_RESULT, + STATE_NEXT_TEST, STATE_EXIT, }; @@ -152,17 +153,15 @@ void CB2_TestRunner(void) } else { - gTestRunnerState.state = STATE_NEXT_TEST; - gTestRunnerState.test = __start_tests - 1; + gTestRunnerState.state = STATE_ASSIGN_TEST; + gTestRunnerState.test = __start_tests; } gTestRunnerState.exitCode = 0; gTestRunnerState.skipFilename = NULL; break; - case STATE_NEXT_TEST: - gTestRunnerState.test++; - + case STATE_ASSIGN_TEST: if (gTestRunnerState.test == __stop_tests) { gTestRunnerState.state = STATE_EXIT; @@ -172,6 +171,7 @@ void CB2_TestRunner(void) if (gTestRunnerState.test->runner != &gAssumptionsRunner && !PrefixMatch(gTestRunnerArgv, gTestRunnerState.test->name)) { + gTestRunnerState.state = STATE_NEXT_TEST; return; } @@ -191,6 +191,8 @@ void CB2_TestRunner(void) sCurrentTest.address = (uintptr_t)gTestRunnerState.test; sCurrentTest.state = CURRENT_TEST_STATE_ESTIMATE; + // If AssignCostToRunner fails, we want to report the failure. + gTestRunnerState.state = STATE_REPORT_RESULT; if (AssignCostToRunner() == gTestRunnerI) gTestRunnerState.state = STATE_RUN_TEST; else @@ -204,7 +206,10 @@ void CB2_TestRunner(void) SeedRng(0); SeedRng2(0); if (gTestRunnerState.test->runner->setUp) + { gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); + gTestRunnerState.tearDown = TRUE; + } // NOTE: Assumes that the compiler interns __FILE__. if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) // Assumption fails for tests in this file. { @@ -216,13 +221,17 @@ void CB2_TestRunner(void) gTestRunnerState.test->runner->run(gTestRunnerState.test->data); } break; + case STATE_REPORT_RESULT: REG_TM2CNT_H = 0; gTestRunnerState.state = STATE_NEXT_TEST; - if (gTestRunnerState.test->runner->tearDown) + if (gTestRunnerState.tearDown && gTestRunnerState.test->runner->tearDown) + { gTestRunnerState.test->runner->tearDown(gTestRunnerState.test->data); + gTestRunnerState.tearDown = FALSE; + } if (gTestRunnerState.result == TEST_RESULT_PASS && !gTestRunnerState.expectLeaks) @@ -342,6 +351,11 @@ void CB2_TestRunner(void) break; + case STATE_NEXT_TEST: + gTestRunnerState.state = STATE_ASSIGN_TEST; + gTestRunnerState.test++; + break; + case STATE_EXIT: MgbaExit_(gTestRunnerState.exitCode); break; diff --git a/tools/mgba-rom-test-hydra/main.c b/tools/mgba-rom-test-hydra/main.c index 123d8a2552..c2189ac0df 100644 --- a/tools/mgba-rom-test-hydra/main.c +++ b/tools/mgba-rom-test-hydra/main.c @@ -2,9 +2,9 @@ * parses the output to display human-readable progress. * * Output lines starting with "GBA Debug: :" are parsed as commands to - * Hydra, other output lines starting with "GBA Debug: " are parsed as - * output from the current test, and any other lines are parsed as - * output from the mgba-rom-test process itself. + * Hydra, other output lines starting with "GBA Debug: " or with "GBA: " + * are parsed as output from the current test, and any other lines are + * parsed as output from the mgba-rom-test process itself. * * COMMANDS * N: Sets the test name to the remainder of the line. @@ -75,10 +75,17 @@ static void handle_read(int i, struct Runner *runner) { eol++; size_t n = eol - sol; - if (runner->input_buffer_size >= strlen("GBA Debug: ") - && !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: "))) + char *soc; + if (runner->input_buffer_size >= strlen("GBA: ") + && !strncmp(sol, "GBA: ", strlen("GBA: "))) { - char *soc = sol + strlen("GBA Debug: "); + soc = sol + strlen("GBA: "); + goto buffer_output; + } + else if (runner->input_buffer_size >= strlen("GBA Debug: ") + && !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: "))) + { + soc = sol + strlen("GBA Debug: "); if (soc[0] == ':') { switch (soc[1])