From dd4aface93939b162bf43a7f26eaef6be7cb7014 Mon Sep 17 00:00:00 2001 From: bryanthaboi Date: Tue, 4 Aug 2026 09:41:44 -0400 Subject: [PATCH] test fixes --- tests/love_stub.lua | 7 +++ tests/mod_battle_tests.lua | 3 +- tests/parity_J.lua | 6 ++- tests/parity_battle_blackout_pals.lua | 3 +- tests/parity_battle_intro_cry.lua | 3 +- tests/parity_dig_pic.lua | 3 +- tests/run_tests.lua | 69 ++++++++++++++++++--------- 7 files changed, 66 insertions(+), 28 deletions(-) diff --git a/tests/love_stub.lua b/tests/love_stub.lua index 56927689..0e35f8af 100644 --- a/tests/love_stub.lua +++ b/tests/love_stub.lua @@ -348,6 +348,7 @@ function ImageData:getDimensions() return self.w, self.h end function ImageData:getPixel() return 0, 0, 0, 1 end function ImageData:setPixel() end function ImageData:mapPixel() end +function ImageData:paste() end function ImageData:encode() return { getString = function() return "" end } end stub.image = { @@ -362,6 +363,12 @@ stub.image = { end, } +-- Headless runs report the desktop OS so platform gates (GamepadMap's NX +-- check, the touch-overlay filter) take their desktop branch. +stub.system = { + getOS = function() return "OS X" end, +} + -- Desktop / headless: full-window safe area (matches LÖVE's fallback). stub.window = { getSafeArea = function() diff --git a/tests/mod_battle_tests.lua b/tests/mod_battle_tests.lua index 34f696c5..e42df2e4 100644 --- a/tests/mod_battle_tests.lua +++ b/tests/mod_battle_tests.lua @@ -52,7 +52,8 @@ local function makeGame(party) function stack:pop() return table.remove(self.states) end function stack:top() return self.states[#self.states] end return { data = Data, save = save, stack = stack, - input = { wasPressed = function() return true end } } + input = { wasPressed = function() return true end, + isDown = function() return true end } } end local function pump(battle, limit) diff --git a/tests/parity_J.lua b/tests/parity_J.lua index 67b61d8c..4e807a9a 100644 --- a/tests/parity_J.lua +++ b/tests/parity_J.lua @@ -283,7 +283,8 @@ end do local pressed = {} local tb = freshBattle() - tb.game = { input = { wasPressed = function(_, k) return pressed[k] or false end }, + tb.game = { input = { wasPressed = function(_, k) return pressed[k] or false end, + isDown = function(_, k) return pressed[k] or false end }, stack = { top = function() return tb end }, save = Game.save } tb.kind = "wild" @@ -359,7 +360,8 @@ do local fg = { data = Data, save = require("src.core.SaveData").newGame(), - input = { wasPressed = function(_, k) return pressed[k] or false end }, + input = { wasPressed = function(_, k) return pressed[k] or false end, + isDown = function(_, k) return pressed[k] or false end }, stack = stack, } fg.save.party = { Pokemon.new(Data, "BULBASAUR", 20) } diff --git a/tests/parity_battle_blackout_pals.lua b/tests/parity_battle_blackout_pals.lua index ea398041..42637f51 100644 --- a/tests/parity_battle_blackout_pals.lua +++ b/tests/parity_battle_blackout_pals.lua @@ -38,7 +38,8 @@ local function makeGame(party) function stack:pop() return table.remove(self.states) end function stack:top() return self.states[#self.states] end return { data = Data, save = save, stack = stack, - input = { wasPressed = function(_, b) return press[b] == true end } } + input = { wasPressed = function(_, b) return press[b] == true end, + isDown = function(_, b) return press[b] == true end } } end local function step(battle) diff --git a/tests/parity_battle_intro_cry.lua b/tests/parity_battle_intro_cry.lua index 641dd1b1..cd7dcd11 100644 --- a/tests/parity_battle_intro_cry.lua +++ b/tests/parity_battle_intro_cry.lua @@ -44,7 +44,8 @@ local function makeGame(party) function stack:pop() return table.remove(self.states) end function stack:top() return self.states[#self.states] end return { data = Data, save = save, stack = stack, - input = { wasPressed = function() return false end } } + input = { wasPressed = function() return false end, + isDown = function() return false end } } end local game = makeGame({ Pokemon.new(Data, "BULBASAUR", 50) }) diff --git a/tests/parity_dig_pic.lua b/tests/parity_dig_pic.lua index cc9edd87..cd7cce9b 100644 --- a/tests/parity_dig_pic.lua +++ b/tests/parity_dig_pic.lua @@ -28,7 +28,8 @@ local function makeGame(species, level, moves) function stack:pop() return table.remove(self.states) end function stack:top() return self.states[#self.states] end return { data = Data, save = save, stack = stack, - input = { wasPressed = function() return true end } } + input = { wasPressed = function() return true end, + isDown = function() return true end } } end local function pumpToMenu(battle) diff --git a/tests/run_tests.lua b/tests/run_tests.lua index 6119e18c..c50f93f3 100644 --- a/tests/run_tests.lua +++ b/tests/run_tests.lua @@ -2631,7 +2631,13 @@ do local popped = false local og = { data = Data, save = SD.newGame(), input = OInput, stack = { pop = function() popped = true end }, - writeOptions = function(self) SD.saveOptions(self.save.options) end } + writeOptions = function(self) SD.saveOptions(self.save.options) end, + -- the PERFORMANCE row routes through Game:applyOptions; the + -- stub carries the headless slice of it (the tier record), + -- the display modules are re-applied at the end of the suite + applyOptions = function(self, o) + require("src.core.Performance").applyOptions(o) + end } local om = OptionsMenu.new(og) local function press(btn) OInput.pressed = { [btn] = true } @@ -2655,9 +2661,9 @@ do "A switches the battle screen to the WIDE layout") press("a") eq(og.save.options.battleLayout, "og", "BATTLE LAYOUT wraps back to OG") - for _ = 1, 2 do press("down") end - eq(om.index, 6, "cursor reaches MUSIC VOL") - eq(om.scroll, 2, "viewport scrolls to keep MUSIC VOL on screen") + for _ = 1, 5 do press("down") end + eq(om.index, 9, "cursor reaches MUSIC VOL") + eq(om.scroll, 5, "viewport scrolls to keep MUSIC VOL on screen") press("left") eq(og.save.options.musicVol, 6, "left lowers MUSIC VOL") press("right") @@ -2672,25 +2678,33 @@ do press("a") eq(og.save.options.musicFilter, 0, "MUSIC FILTER wraps back to OFF") press("down") - eq(om.index, 9, "cursor reaches COLORS") + eq(om.index, 12, "cursor reaches PERFORMANCE") + press("a") + eq(og.save.options.performance, "high", "A cycles PERFORMANCE to HIGH") + eq(require("src.core.Performance").tier, "high", + "the live tier tracks the PERFORMANCE option") + for _ = 1, 3 do press("a") end + eq(og.save.options.performance, "auto", "PERFORMANCE wraps back to AUTO") + press("down") + eq(om.index, 13, "cursor reaches COLORS") press("a") for _ = 1, 4 do press("a") end press("down") - eq(om.index, 10, "cursor reaches TILT") + eq(om.index, 14, "cursor reaches TILT") press("a") eq(og.save.options.tilt, 1, "A cycles TILT to 15") eq(Tilt.level, 1, "Tilt level tracks TILT option") press("a"); press("a"); press("a") eq(og.save.options.tilt, 0, "TILT wraps back to OFF") press("down") - eq(om.index, 11, "cursor reaches GBC FX") + eq(om.index, 15, "cursor reaches GBC FX") press("a") eq(og.save.options.gbcfx, 1, "A cycles GBC FX to 1") eq(GBCFX.level, 1, "GBCFX level tracks GBC FX option") for _ = 1, 4 do press("a") end eq(og.save.options.gbcfx, 0, "GBC FX wraps back to OFF") press("down") - eq(om.index, 12, "cursor reaches ZOOM") + eq(om.index, 16, "cursor reaches ZOOM") local ZoomOpt = require("src.render.Zoom") press("a") eq(og.save.options.zoom, 1, "A cycles ZOOM to IN1") @@ -2698,7 +2712,7 @@ do press("left") eq(og.save.options.zoom, 0, "left steps ZOOM back to FIT") press("down") - eq(om.index, 13, "cursor reaches VOID FILL") + eq(om.index, 17, "cursor reaches VOID FILL") local TR = require("src.render.TileRenderer") press("a") eq(og.save.options.voidFill, "water", "A cycles VOID FILL to WATER") @@ -2708,7 +2722,7 @@ do press("a") eq(og.save.options.voidFill, "trees", "VOID FILL wraps back to TREES") press("down") - eq(om.index, 14, "cursor reaches VIDEO MODE") + eq(om.index, 18, "cursor reaches VIDEO MODE") press("a") eq(og.save.options.videoMode, "borderless", "A cycles VIDEO MODE to BORDERLESS") @@ -2716,7 +2730,9 @@ do eq(og.save.options.videoMode, "windowed", "VIDEO MODE wraps back to WINDOWED") press("down") - eq(om.index, 15, "cursor reaches MAX FPS") + eq(om.index, 19, "cursor reaches FAITHFUL RATIO") + press("down") + eq(om.index, 20, "cursor reaches MAX FPS") press("a") eq(og.save.options.fpsCap, 75, "A cycles MAX FPS up from 60 to 75") eq(FrameCap.current, 75, "the live render cap tracks the MAX FPS option") @@ -2725,7 +2741,7 @@ do for _ = 1, #FrameCap.STEPS - 1 do press("a") end eq(og.save.options.fpsCap, 60, "MAX FPS wraps back to 60") press("down") - eq(om.index, 16, "cursor reaches GAME SPEED") + eq(om.index, 21, "cursor reaches GAME SPEED") press("a") eq(og.save.options.speed, 2, "A cycles GAME SPEED to 2X") -- Driven by the level list rather than a literal press count: adding a @@ -2734,19 +2750,19 @@ do for _ = 1, #GameSpeed.LEVELS - 1 do press("a") end eq(og.save.options.speed, 1, "GAME SPEED wraps back to NORMAL") press("down") - eq(om.index, 17, "cursor reaches MODS") + eq(om.index, 22, "cursor reaches MODS") press("down") - eq(om.index, 18, "cursor reaches CONTROLS") + eq(om.index, 23, "cursor reaches CONTROLS") press("down") - eq(om.index, 19, "CANCEL stays the fixed final row") - eq(om.scroll, 14, "CANCEL keeps the last option boxes on screen") + eq(om.index, 24, "CANCEL stays the fixed final row") + eq(om.scroll, 19, "CANCEL keeps the last option boxes on screen") om:draw() -- smoke: scrolled layout draws under the headless stub press("a") check(popped, "A on CANCEL closes the options menu") local om2 = OptionsMenu.new(og) OInput.pressed = { up = true }; om2:update(1 / 60); OInput.pressed = {} - eq(om2.index, 19, "up from the top wraps to CANCEL") - eq(om2.scroll, 14, "wrapping to CANCEL scrolls to the tail") + eq(om2.index, 24, "up from the top wraps to CANCEL") + eq(om2.scroll, 19, "wrapping to CANCEL scrolls to the tail") -- headless-safe: no love.audio, setters only update internal state require("src.core.Music").applyOptions(og.save.options) require("src.core.Sound").applyOptions(og.save.options) @@ -2809,16 +2825,19 @@ do menu:update(0) eq(game.popCount(), 1, "Menu START-press closes when startCloses (start menu's PAD_START mask; no beep per HandleMenuInput_)") + -- both DisplayTwoOptionMenu branches hold 15 frames with the menu still + -- on screen before the answer lands (ChoiceBox.pending), so pump the + -- hold out after the press game = stubGame({ a = true }) local yes local box = ChoiceBox.new(game, function(v) yes = v end) - box:update(0) + for _ = 0, require("src.core.Timing").YES_NO_ANSWER do box:update(0) end eq(yes, true, "ChoiceBox A on YES chooses true") game = stubGame({ b = true }) local no box = ChoiceBox.new(game, function(v) no = v end) - box:update(0) + for _ = 0, require("src.core.Timing").YES_NO_ANSWER do box:update(0) end eq(no, false, "ChoiceBox B chooses false") end end @@ -2837,7 +2856,8 @@ do local qreturned = 0 local qg = { data = Data, save = qsave, stack = qstack, - input = { wasPressed = function(_, k) return qpressed[k] end }, + input = { wasPressed = function(_, k) return qpressed[k] end, + isDown = function(_, k) return qpressed[k] or false end }, returnToTitle = function() qreturned = qreturned + 1 end, } local qmenu = StartMenuQ.new(qg) @@ -2858,7 +2878,12 @@ do check(qbox ~= qmenu and qbox ~= nil and qbox.pages ~= nil, "QUIT pushes a confirmation textbox") eq(qbox.pages[1][1], "RETURN TO MAIN", "confirm asks RETURN TO MAIN MENU?") - qbox.onDone() + -- opts.choice: the box pushes the YES/NO itself once the last page has + -- typed out, so pump it rather than reaching for the old onDone hook + for _ = 1, 600 do + if qstack:top() ~= qbox then break end + qbox:update(1 / 60) + end local qchoice = qstack:top() check(qchoice ~= qbox and qchoice ~= nil and qchoice.onChoose ~= nil, "textbox is followed by a YES/NO choice")