diff --git a/include/battle.h b/include/battle.h index 466a9138d..00c7081fb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -658,6 +658,7 @@ struct BattleStruct u8 storedLunarDance:4; // Each battler as a bit. u16 supremeOverlordModifier[MAX_BATTLERS_COUNT]; u8 itemPartyIndex[MAX_BATTLERS_COUNT]; + u8 itemMoveIndex[MAX_BATTLERS_COUNT]; bool8 trainerSlideHalfHpMsgDone; u8 trainerSlideFirstCriticalHitMsgState:2; u8 trainerSlideFirstSuperEffectiveHitMsgState:2; diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 751584076..d1c60343a 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -1443,7 +1443,7 @@ static void RecordedOpponentHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); - gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); + gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedOpponentBufferExecCompleted(); } diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 1a56220d7..46f3de4f1 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -1467,7 +1467,7 @@ static void RecordedPlayerHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); - gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); + gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedPlayerBufferExecCompleted(); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1187267b2..084288431 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16536,10 +16536,10 @@ void BS_ItemRestorePP(void) { // Check whether to apply to all moves. if (effect[4] & ITEM4_HEAL_PP_ONE) { - i = gChosenMovePos; - loopEnd = gChosenMovePos + 1; + i = gBattleStruct->itemMoveIndex[gBattlerAttacker]; + loopEnd = i + 1; } - else + else { i = 0; loopEnd = MAX_MON_MOVES; diff --git a/src/party_menu.c b/src/party_menu.c index 773495a4d..aa37b1c69 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4944,7 +4944,7 @@ static void TryUseItemOnMove(u8 taskId) else { gBattleStruct->itemPartyIndex[gBattlerInMenuId] = GetPartyIdFromBattleSlot(gPartyMenu.slotId); - gChosenMovePos = ptr->data1; + gBattleStruct->itemMoveIndex[gBattlerInMenuId] = ptr->data1; gPartyMenuUseExitCallback = TRUE; RemoveBagItem(gSpecialVar_ItemId, 1); ScheduleBgCopyTilemapToVram(2); diff --git a/test/ability_compound_eyes.c b/test/ability_compound_eyes.c index d90604d5c..6bdb15fa7 100644 --- a/test/ability_compound_eyes.c +++ b/test/ability_compound_eyes.c @@ -6,7 +6,7 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") PASSES_RANDOMLY(91, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_THUNDER].accuracy == 70); - PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; + PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_THUNDER); } @@ -16,16 +16,13 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") } } -// This fails even though the ability works correctly. The failure is due to -// a statistical anomaly in the test system where FISSURE hits 3 times more often -// than we expect. SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves") { PASSES_RANDOMLY(30, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30); ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO); - PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; + PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_FISSURE); } diff --git a/test/ability_dry_skin.c b/test/ability_dry_skin.c index 6c28d0c42..855872d76 100644 --- a/test/ability_dry_skin.c +++ b/test/ability_dry_skin.c @@ -33,10 +33,13 @@ SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain") SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%", s16 damage) { + u32 ability; + PARAMETRIZE { ability = ABILITY_EFFECT_SPORE; } + PARAMETRIZE { ability = ABILITY_DRY_SKIN; } GIVEN { ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_PARASECT) { Ability(ABILITY_DRY_SKIN); }; + OPPONENT(SPECIES_PARASECT) { Ability(ability); }; } WHEN { TURN {MOVE(player, MOVE_EMBER); } } SCENE { diff --git a/test/ability_volt_absorb.c b/test/ability_volt_absorb.c index 18902eaa2..6b9296268 100644 --- a/test/ability_volt_absorb.c +++ b/test/ability_volt_absorb.c @@ -63,8 +63,9 @@ SINGLE_BATTLE_TEST("Volt Absorb is only triggered once on multi strike moves") } } -DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon", s16 damage1, s16 damage2) // Fixed issue #1961 +DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon") // Fixed issue #1961 { + s16 damage1, damage2; GIVEN { ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); ASSUME(gBattleMoves[MOVE_EXPLOSION].type == TYPE_NORMAL); @@ -78,12 +79,11 @@ DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from dama ABILITY_POPUP(playerLeft, ABILITY_VOLT_ABSORB); HP_BAR(playerLeft, hp: TEST_MAX_HP / 4 + 1); MESSAGE("Jolteon restored HP using its Volt Absorb!"); - HP_BAR(playerRight, captureDamage: &results->damage1); - HP_BAR(opponentRight, captureDamage: &results->damage2); - } - FINALLY { - EXPECT_NE(results[0].damage1, 0); - EXPECT_NE(results[0].damage2, 0); + HP_BAR(playerRight, captureDamage: &damage1); + HP_BAR(opponentRight, captureDamage: &damage2); + } THEN { + EXPECT_NE(damage1, 0); + EXPECT_NE(damage2, 0); } } diff --git a/test/hold_effect_berserk_gene.c b/test/hold_effect_berserk_gene.c index 361d14861..103a0a1a3 100644 --- a/test/hold_effect_berserk_gene.c +++ b/test/hold_effect_berserk_gene.c @@ -11,14 +11,11 @@ SINGLE_BATTLE_TEST("Berserk Gene sharply raises attack at the start of battle", u16 useItem; PARAMETRIZE { useItem = FALSE; } PARAMETRIZE { useItem = TRUE; } - if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION); GIVEN { - PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }; + PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { - MOVE(player, MOVE_TACKLE); - } + TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); } } SCENE { if (useItem) { @@ -38,17 +35,13 @@ SINGLE_BATTLE_TEST("Berserk Gene activates on switch in", s16 damage) u16 useItem; PARAMETRIZE { useItem = FALSE; } PARAMETRIZE { useItem = TRUE; } - if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION); GIVEN { PLAYER(SPECIES_WYNAUT); - PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }; + PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { - SWITCH(player, 1); - } TURN { - MOVE(player, MOVE_TACKLE); - } + TURN { SWITCH(player, 1); } + TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); } } SCENE { if (useItem) { diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c index fc0a6de95..22f564b85 100644 --- a/test/hold_effect_mirror_herb.c +++ b/test/hold_effect_mirror_herb.c @@ -9,8 +9,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) { u32 item; - PARAMETRIZE{ item = ITEM_NONE; } - PARAMETRIZE{ item = ITEM_MIRROR_HERB; } + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_MIRROR_HERB; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); } @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 } } -SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks", s16 damage) +SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks") { GIVEN { ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP); diff --git a/test/item_effect_increase_stat.c b/test/item_effect_increase_stat.c index bbaf20e2f..310932040 100644 --- a/test/item_effect_increase_stat.c +++ b/test/item_effect_increase_stat.c @@ -126,7 +126,7 @@ SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) } } -SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 damage) +SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat") { ASSUME(gBattleMoves[MOVE_SING].accuracy == 55); diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 4662e9f25..6fd58fe82 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -183,4 +183,4 @@ SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP by 25% of its max HP(30 } #undef TEST_HP -#undef MAX_HP \ No newline at end of file +#undef MAX_HP diff --git a/test/move_effect_bide.c b/test/move_effect_bide.c index 3a5c38735..0b0e18408 100644 --- a/test/move_effect_bide.c +++ b/test/move_effect_bide.c @@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns") MESSAGE("Wobbuffet unleashed energy!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player); HP_BAR(opponent, captureDamage: &bideDamage); - } FINALLY { - EXPECT_EQ(bideDamage, damage1 + damage2); + } THEN { + EXPECT_EQ(bideDamage, 2 * (damage1 + damage2)); } } diff --git a/test/move_effect_triple_kick.c b/test/move_effect_triple_kick.c index d3187aefe..e0a91b011 100644 --- a/test/move_effect_triple_kick.c +++ b/test/move_effect_triple_kick.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gBattleMoves[MOVE_TRIPLE_KICK].effect & EFFECT_TRIPLE_KICK); } -SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each hit") +SINGLE_BATTLE_TEST("Triple Kick damage is increased by its base damage for each hit") { s16 firstHit; s16 secondHit; @@ -24,8 +24,8 @@ SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each HP_BAR(opponent, captureDamage: &secondHit); ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_KICK, player); HP_BAR(opponent, captureDamage: &thirdHit); - } FINALLY { - EXPECT_EQ(secondHit, firstHit * 2); - EXPECT_EQ(thirdHit, firstHit * 3); + } THEN { + EXPECT_MUL_EQ(firstHit, Q_4_12(2.0), secondHit); + EXPECT_MUL_EQ(firstHit, Q_4_12(3.0), thirdHit); } } diff --git a/test/move_flag_three_strikes.c b/test/move_flag_three_strikes.c index 7ea7844c4..b597b4efe 100644 --- a/test/move_flag_three_strikes.c +++ b/test/move_flag_three_strikes.c @@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move") HP_BAR(opponent, captureDamage: &secondHit); ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_DIVE, player); HP_BAR(opponent, captureDamage: &thirdHit); - } FINALLY { + } THEN { EXPECT_EQ(firstHit, secondHit); EXPECT_EQ(secondHit, thirdHit); EXPECT_EQ(firstHit, thirdHit); @@ -49,7 +49,7 @@ SINGLE_BATTLE_TEST("Surging Strikes hits 3 times with each hit being a critical ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, player); HP_BAR(opponent, captureDamage: &thirdHit); MESSAGE("A critical hit!"); - } FINALLY { + } THEN { EXPECT_EQ(firstHit, secondHit); EXPECT_EQ(secondHit, thirdHit); EXPECT_EQ(firstHit, thirdHit); diff --git a/test/terrain_electric.c b/test/terrain_electric.c index e9c34ef92..78f490c42 100644 --- a/test/terrain_electric.c +++ b/test/terrain_electric.c @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Electric Terrain activates Electric Seed and Mimicry") MESSAGE("Using Electric Seed, the Defense of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Electr!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_ELECTRIC); } } diff --git a/test/terrain_grassy.c b/test/terrain_grassy.c index 7668c49bd..21d64f61e 100644 --- a/test/terrain_grassy.c +++ b/test/terrain_grassy.c @@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Grassy Terrain activates Grassy Seed and Mimicry") MESSAGE("Using Grassy Seed, the Defense of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Grass!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_GRASS); } } diff --git a/test/terrain_misty.c b/test/terrain_misty.c index 4f8793d9d..f60122075 100644 --- a/test/terrain_misty.c +++ b/test/terrain_misty.c @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Misty Terrain activates Misty Seed and Mimicry") MESSAGE("Using Misty Seed, the Sp. Def of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Fairy!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_FAIRY); } } diff --git a/test/terrain_psychic.c b/test/terrain_psychic.c index 3ecfa99e0..f9f95a47d 100644 --- a/test/terrain_psychic.c +++ b/test/terrain_psychic.c @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain activates Psychic Seed and Mimicry") MESSAGE("Using Psychic Seed, the Sp. Def of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Psychc!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_PSYCHIC); } } @@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain increases power of Psychic-type moves by 30/ } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user") { GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); HP(1); } @@ -76,7 +76,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers") { KNOWN_FAILING; GIVEN { @@ -91,7 +91,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents") { KNOWN_FAILING; GIVEN { @@ -106,7 +106,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies", s16 damage) +DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies") { GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } @@ -122,7 +122,7 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") { KNOWN_FAILING; GIVEN { diff --git a/test/test_battle.h b/test/test_battle.h index 4c563c91d..205c69919 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -897,7 +897,9 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve /* Finally */ -#define FINALLY for (; gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE)) +#define FINALLY for (ValidateFinally(__LINE__); gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE)) + +void ValidateFinally(u32 sourceLine); /* Expect */ diff --git a/test/test_runner.c b/test/test_runner.c index 64cb20e26..bfd7b47a5 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -42,7 +42,14 @@ static bool32 PrefixMatch(const char *pattern, const char *string) } } -enum { STATE_INIT, STATE_NEXT_TEST, STATE_REPORT_RESULT, STATE_EXIT }; +enum +{ + STATE_INIT, + STATE_NEXT_TEST, + STATE_RUN_TEST, + STATE_REPORT_RESULT, + STATE_EXIT, +}; void CB2_TestRunner(void) { @@ -81,6 +88,26 @@ void CB2_TestRunner(void) return; } + MgbaPrintf_(":N%s", gTestRunnerState.test->name); + gTestRunnerState.result = TEST_RESULT_PASS; + gTestRunnerState.expectedResult = TEST_RESULT_PASS; + gTestRunnerState.expectLeaks = FALSE; + if (gTestRunnerHeadless) + gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS; + else + gTestRunnerState.timeoutSeconds = UINT_MAX; + InitHeap(gHeap, HEAP_SIZE); + EnableInterrupts(INTR_FLAG_TIMER2); + REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second. + REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK; + + // NOTE: Assumes that the compiler interns __FILE__. + if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) + { + gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL; + return; + } + // Greedily assign tests to processes based on estimated cost. // TODO: Make processCosts a min heap. if (gTestRunnerState.test->runner != &gAssumptionsRunner) @@ -98,40 +125,26 @@ void CB2_TestRunner(void) } } + if (minCostProcess == gTestRunnerI) + gTestRunnerState.state = STATE_RUN_TEST; + else + gTestRunnerState.state = STATE_NEXT_TEST; + + // XXX: If estimateCost exits only on some processes then + // processCosts will be inconsistent. if (gTestRunnerState.test->runner->estimateCost) gTestRunnerState.processCosts[minCostProcess] += gTestRunnerState.test->runner->estimateCost(gTestRunnerState.test->data); else gTestRunnerState.processCosts[minCostProcess] += 1; - - if (minCostProcess != gTestRunnerI) - return; } - MgbaPrintf_(":N%s", gTestRunnerState.test->name); + break; + + case STATE_RUN_TEST: gTestRunnerState.state = STATE_REPORT_RESULT; - gTestRunnerState.result = TEST_RESULT_PASS; - gTestRunnerState.expectedResult = TEST_RESULT_PASS; - gTestRunnerState.expectLeaks = FALSE; - if (gTestRunnerHeadless) - gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS; - else - gTestRunnerState.timeoutSeconds = UINT_MAX; - InitHeap(gHeap, HEAP_SIZE); - EnableInterrupts(INTR_FLAG_TIMER2); - REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second. - REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK; - - // NOTE: Assumes that the compiler interns __FILE__. - if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) - { - gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL; - } - else - { - if (gTestRunnerState.test->runner->setUp) - gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); - gTestRunnerState.test->runner->run(gTestRunnerState.test->data); - } + if (gTestRunnerState.test->runner->setUp) + gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); + gTestRunnerState.test->runner->run(gTestRunnerState.test->data); break; case STATE_REPORT_RESULT: @@ -345,6 +358,8 @@ static void Intr_Timer2(void) } else { + if (gTestRunnerState.state == STATE_RUN_TEST) + gTestRunnerState.state = STATE_REPORT_RESULT; gTestRunnerState.result = TEST_RESULT_TIMEOUT; ReinitCallbacks(); IRQ_LR = ((uintptr_t)JumpToAgbMainLoop & ~1) + 4; @@ -354,16 +369,19 @@ static void Intr_Timer2(void) void Test_ExitWithResult(enum TestResult result, const char *fmt, ...) { - bool32 handled = FALSE; gTestRunnerState.result = result; ReinitCallbacks(); - if (gTestRunnerState.test->runner->handleExitWithResult) - handled = gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result); - if (!handled && gTestRunnerState.result != gTestRunnerState.expectedResult) + if (gTestRunnerState.state == STATE_REPORT_RESULT + && gTestRunnerState.test->runner->handleExitWithResult) { - va_list va; - va_start(va, fmt); - MgbaVPrintf_(fmt, va); + if (!gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result) + && gTestRunnerState.result != gTestRunnerState.expectedResult) + { + va_list va; + va_start(va, fmt); + MgbaVPrintf_(fmt, va); + va_end(va); + } } JumpToAgbMainLoop(); } diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 7740783b2..cfe5db9d6 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -134,6 +134,8 @@ static void BattleTest_SetUp(void *data) Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE = AllocZerod(%d)", sizeof(*STATE)); InvokeTestFunction(test); STATE->parameters = STATE->parametersCount; + if (STATE->parametersCount == 0 && test->resultsSize > 0) + Test_ExitWithResult(TEST_RESULT_INVALID, "results without PARAMETRIZE"); STATE->results = AllocZeroed(test->resultsSize * STATE->parameters); if (!STATE->results) Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE->results = AllocZerod(%d)", sizeof(test->resultsSize * STATE->parameters)); @@ -939,6 +941,8 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx) { + const struct BattleTest *test = gTestRunnerState.test->data; + INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results"); INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials); STATE->rngTag = ctx.tag; STATE->runTrial = 0; @@ -1564,6 +1568,10 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c } INVALID_IF(i == MAX_MON_MOVES, "USE_ITEM on invalid move: %d", ctx.move); } + else + { + i = 0; + } PushBattlerAction(sourceLine, battlerId, RECORDED_ACTION_TYPE, B_ACTION_USE_ITEM); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, (ctx.itemId >> 8) & 0xFF); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, ctx.itemId & 0xFF); @@ -1719,7 +1727,6 @@ void QueueMessage(u32 sourceLine, const u8 *pattern) }; } - void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEventContext ctx) { s32 battlerId = battler - gBattleMons; @@ -1759,3 +1766,11 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve }}, }; } + +void ValidateFinally(u32 sourceLine) +{ + // Defer this error until after estimating the cost. + if (STATE->results == NULL) + return; + INVALID_IF(STATE->parametersCount == 0, "FINALLY without PARAMETRIZE"); +}