Files
gen1recomp/tests/game3_summary_test.lua
T

210 lines
8.4 KiB
Lua

-- Automated tests for Game3 Pokemon Summary Screen
-- Validates:
-- 1. Chrome asset loading from data/generated/gba (game3 launcher scope)
-- 2. Anti-PP swap trap protection (atomic swapMoves)
-- 3. Dynamic trade memo detection (OT Name / ID / SID diff against PlayerState)
-- 4. Page transitions (INFO -> SKILLS -> MOVES -> MOVE_DETAIL)
-- 5. Modal Stack integration with PartyMenu
local Game3 = require("src.core.game3.init")
local PartyMenu = require("src.ui.game3.party_menu")
local SummaryMenu = require("src.ui.game3.summary_menu")
local SummaryData = require("src.core.game3.summary_data")
local SummaryChrome = require("src.ui.game3.summary_chrome")
local Stack = require("src.ui.game3.stack")
print("=== Running Game3 Summary Screen Tests ===")
-- 1. Asset Chrome
local manifest = SummaryChrome.manifest()
assert(manifest ~= nil, "Manifest must load from data/generated/gba")
assert(manifest.pages.INFO == 0)
assert(manifest.pages.SKILLS == 1)
assert(manifest.pages.MOVES == 2)
assert(manifest.coords and manifest.coords.dexNo and manifest.coords.dexNo.x == 167,
"INFO dexNo must sit in right pane (pret WIN_INFO_3)")
assert(manifest.coords.atk and manifest.coords.atk.y == 38,
"SKILLS atk Y must match pret PrintSkillsPage")
assert(manifest.coords.expPointsLabel and manifest.coords.expPointsLabel.x == 74,
"SKILLS expPointsLabel must sit at X=74 (pret PokeSum_PrintExpPoints_NextLv)")
assert(manifest.coords.nextLvLabel and manifest.coords.nextLvLabel.x == 74,
"SKILLS nextLvLabel must sit at X=74 (pret PokeSum_PrintExpPoints_NextLv)")
assert(manifest.moveSlots and manifest.moveSlots[1].typeX == 123,
"MOVES type badge X must match pret WIN_MOVES_5")
print("[PASS] Asset Chrome & Manifest verified")
-- 1b. Move names resolve from ROM gMoveNames (not "MOVE 33")
local Pokemon = require("src.core.game3.pokemon")
assert(Pokemon.moveName(33) == "TACKLE", "move 33 must be TACKLE")
assert(Pokemon.moveName(45) == "GROWL", "move 45 must be GROWL")
print("[PASS] Move name lookup verified")
-- 2. Party & Mon Setup
local party = {
{
species = 1,
name = "BULBASAUR",
level = 5,
hp = 20,
maxHp = 20,
attack = 11,
defense = 11,
spAtk = 13,
spDef = 13,
speed = 10,
moves = { 33, 45 }, -- Tackle (35 PP), Growl (40 PP)
pp = { 35, 40 },
otName = "RED",
otId = 12345,
personality = 0,
},
{
species = 4,
name = "CHARMANDER",
level = 5,
hp = 19,
maxHp = 19,
attack = 12,
defense = 9,
spAtk = 14,
spDef = 11,
speed = 13,
moves = { 10, 45 }, -- Scratch, Growl
pp = { 35, 40 },
otName = "BLUE",
otId = 54321,
personality = 25,
}
}
-- 3. Dynamic Trade Memo Check
local playerState = { playerName = "RED", playerId = 12345, secretId = 0 }
local memoRed = SummaryData.formatTrainerMemo(party[1], playerState)
assert(string.find(memoRed[2], "Met in a trade") == nil, "Original trainer mon should NOT be marked trade")
local memoBlue = SummaryData.formatTrainerMemo(party[2], playerState)
assert(string.find(memoBlue[2], "Met in a trade") ~= nil, "Different OT must be dynamically marked as trade")
print("[PASS] Dynamic Trainer Memo trade detection verified")
-- 4. PartyMenu -> SummaryMenu Navigation
PartyMenu.show(party, nil, {})
assert(PartyMenu.isOpen() == true)
assert(Stack.top().id == "party")
local input = {
_pressed = { a = true },
wasPressed = function(self, k) return self._pressed[k] == true end,
isDown = function() return false end,
}
PartyMenu.handleInput(input) -- list -> action
assert(PartyMenu.mode == "action")
PartyMenu.handleInput(input) -- action -> SUMMARY
assert(SummaryMenu.isOpen() == true, "SummaryMenu must open")
assert(Stack.top().id == "summary", "SummaryMenu must be on top of Stack")
print("[PASS] PartyMenu to SummaryMenu modal Stack navigation verified")
-- 5. Page Navigation & Animated Slide
input._pressed = { right = true }
SummaryMenu.handleInput(input)
assert(SummaryMenu._slide.active == true, "Slide animation starts on page change")
SummaryMenu.update(0.2)
assert(SummaryMenu._slide.active == false, "Slide completes after duration")
assert(SummaryMenu._page == 1, "Should be on SKILLS page")
input._pressed = { right = true }
SummaryMenu.handleInput(input)
SummaryMenu.update(0.2)
assert(SummaryMenu._page == 2, "Should be on MOVES page")
print("[PASS] Page navigation and animated sliding verified")
-- 6. Move Detail & Atomic Move Swapping
input._pressed = { a = true }
SummaryMenu.handleInput(input)
assert(SummaryMenu._page == 3, "Should enter MOVE DETAIL mode")
input._pressed = { a = true } -- pick slot 1
SummaryMenu.handleInput(input)
assert(SummaryMenu._swapSlot == 1, "Slot 1 selected for swap")
input._pressed = { down = true }
SummaryMenu.handleInput(input)
assert(SummaryMenu._moveCursor == 2, "Cursor moved to slot 2")
input._pressed = { a = true } -- execute atomic swap
SummaryMenu.handleInput(input)
assert(SummaryMenu._swapSlot == nil, "Swap slot cleared after swap")
assert(party[1].moves[1] == 45 and party[1].moves[2] == 33, "Move IDs swapped")
assert(party[1].pp[1] == 40 and party[1].pp[2] == 35, "PP swapped atomically with move IDs")
print("[PASS] Atomic move swap and anti-PP swap trap verified")
-- 7. Returning to PartyMenu
input._pressed = { b = true }
SummaryMenu.handleInput(input) -- detail -> moves
assert(SummaryMenu._page == 2)
input._pressed = { b = true }
SummaryMenu.handleInput(input) -- close summary
assert(SummaryMenu.isOpen() == false)
assert(Stack.top().id == "party", "Top layer should return to PartyMenu")
print("[PASS] Summary exit cleanly returns to PartyMenu")
-- 8. Top Bar Progress Indicator Verification
local function read_top_bar_pixel(pageName, x, y)
local f = io.open("data/generated/gba/pokemon/summary/" .. pageName .. ".rgba", "rb")
if not f then return nil end
local data = f:read("*a")
f:close()
local idx = (y * 240 + x) * 4 + 1
return data:byte(idx), data:byte(idx + 1), data:byte(idx + 2)
end
-- Slot 1 (X=112), Slot 2 (X=128), Slot 3 (X=144) at Y=8
local function is_yellow_bg(r, g, b)
return r > 200 and g > 200 and b < 180
end
local function is_cyan_bg(r, g, b)
return r < 150 and g > 170 and b > 180
end
local r1, g1, b1 = read_top_bar_pixel("page_info", 112, 8)
local r2, g2, b2 = read_top_bar_pixel("page_info", 128, 8)
local r3, g3, b3 = read_top_bar_pixel("page_info", 144, 8)
assert(is_yellow_bg(r1, g1, b1), "Page INFO slot 1 is active (yellow tab)")
assert(is_cyan_bg(r2, g2, b2), "Page INFO slot 2 is inactive (cyan)")
assert(is_cyan_bg(r3, g3, b3), "Page INFO slot 3 is inactive (cyan)")
local sr1, sg1, sb1 = read_top_bar_pixel("page_skills", 112, 8)
local sr2, sg2, sb2 = read_top_bar_pixel("page_skills", 128, 8)
local sr3, sg3, sb3 = read_top_bar_pixel("page_skills", 144, 8)
assert(is_yellow_bg(sr1, sg1, sb1), "Page SKILLS slot 1 is yellow tab")
assert(is_yellow_bg(sr2, sg2, sb2), "Page SKILLS slot 2 is active (yellow tab)")
assert(is_cyan_bg(sr3, sg3, sb3), "Page SKILLS slot 3 is inactive (cyan)")
local mr1, mg1, mb1 = read_top_bar_pixel("page_moves", 112, 8)
local mr2, mg2, mb2 = read_top_bar_pixel("page_moves", 128, 8)
local mr3, mg3, mb3 = read_top_bar_pixel("page_moves", 144, 8)
assert(is_yellow_bg(mr1, mg1, mb1), "Page MOVES slot 1 is yellow tab")
assert(is_yellow_bg(mr2, mg2, mb2), "Page MOVES slot 2 is yellow tab")
assert(is_yellow_bg(mr3, mg3, mb3), "Page MOVES slot 3 is active (yellow tab)")
print("[PASS] Top bar page progress indicators across all pages verified")
-- 6. Verify Party.giveMon & Catching.storeCaught attach OT Name and OT ID
local Party = require("src.core.game3.party")
local Catching = require("src.core.game3.battle.catching")
local testSession = { name = "ASH", trainerId = 45678, party = {} }
Party.giveMon(testSession, 1, 5)
assert(#testSession.party == 1)
assert(testSession.party[1].ot == "ASH", "giveMon must set ot name")
assert(testSession.party[1].otName == "ASH", "giveMon must set otName")
assert(testSession.party[1].otId == 45678, "giveMon must attach session trainerId")
local foeBattler = { mon = { species = 19, level = 3, hp = 10, maxHp = 10 } }
local catchRes = Catching.storeCaught(testSession, foeBattler, 4)
assert(catchRes.success == true)
assert(catchRes.mon.ot == "ASH", "storeCaught must set ot name")
assert(catchRes.mon.otName == "ASH", "storeCaught must set otName")
assert(catchRes.mon.otId == 45678, "storeCaught must attach session trainerId")
print("[PASS] Party.giveMon and Catching.storeCaught OT Name and OT ID attachment verified")
print("=== ALL TESTS PASSED SUCCESSFULLY! ===")