Files
gen1recomp/tests/engine/input_pad_watchdog_test.lua
bryanthaboi 72a17b27dd CLOSES #2444
2026-09-24 08:39:43 -04:00

202 lines
5.8 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local T = require("tests.harness")
local check, eq = T.check, T.eq
local Input = require("src.core.Input")
local SwitchDiagnostics = require("src.debug.SwitchDiagnostics")
local realJoystick = love.joystick
local physical = {}
local pad = {
isGamepad = function() return true end,
isGamepadDown = function(_, button) return physical[button] == true end,
getGamepadAxis = function() return 0 end,
getName = function() return "Xbox Controller" end,
getGUID = function() return "030000005e040000" end,
isConnected = function() return true end,
}
love.joystick = {
getJoystickCount = function() return 1 end,
getJoysticks = function() return { pad } end,
}
Input:init()
Input:applyBindings(nil)
Input:reset()
Input:step()
physical.a = true
Input:step()
check(Input:wasPressed("a"), "lost press event still yields one A edge")
check(Input:isDown("a"), "lost press event still holds A")
Input:step()
check(not Input:wasPressed("a"), "held A does not re-edge")
physical.a = false
Input:step()
check(not Input:isDown("a"), "lost release event still releases A")
physical.a = true
Input:gamepadpressed(pad, "a")
Input:step()
check(Input:wasPressed("a"), "event + poll agree on the press")
Input:step()
check(not Input:wasPressed("a"), "event + poll never double-edge")
physical.a = false
Input:gamepadreleased(pad, "a")
Input:step()
check(not Input:isDown("a"), "event release stays released")
physical.a = true
Input:gamepadpressed(pad, "a")
Input:step()
physical.a = false
Input:step()
check(not Input:isDown("a"), "stuck A clears when the pad reports it up")
Input:step()
physical.a = true
Input:step()
check(Input:wasPressed("a"), "next physical A press works without alt-tab")
physical.a = false
Input:step()
Input:armCapture()
physical.b = true
Input:step()
check(not Input:isDown("b"), "capture-armed press is not injected")
physical.b = false
Input:step()
Input:disarmCapture()
physical.back = true
Input:gamepadpressed(pad, "back")
Input:step()
physical.a = true
Input:step()
check(not Input:wasPressed("a"), "SELECT+A display chord is not injected as A")
physical.a = false
physical.back = false
Input:gamepadreleased(pad, "back")
Input:step()
Input:applyBindings({ speedUp = { pad = "start" } })
physical.start = true
Input:step()
check(not Input:isDown("start"), "a speed-bound button is not injected")
physical.start = false
Input:step()
Input:applyBindings(nil)
local function countEdges(btn, steps)
local n = 0
for _ = 1, steps do
Input:step()
if Input:wasPressed(btn) then n = n + 1 end
end
return n
end
physical.back = true
Input:padEventSeen("back")
Input:gamepadpressed(pad, "back")
Input:step()
physical.a = true
Input:padEventSeen("a")
Input:step()
physical.back = false
Input:gamepadreleased(pad, "back")
eq(countEdges("a", 10), 0, "SELECT+A chord, SELECT released first: no phantom A")
check(not Input:isDown("a"), "SELECT+A chord, SELECT released first: A not held")
physical.a = false
Input:step()
physical.a = true
Input:step()
check(Input:wasPressed("a"), "a fresh A after the chord still gets repaired")
physical.a = false
Input:step()
for _, b in ipairs({ "a", "b", "start", "back" }) do
physical[b] = true
Input:padEventSeen(b)
Input:gamepadpressed(pad, b)
end
Input:step()
Input:reset()
local resetEdges = 0
for _ = 1, 10 do
Input:step()
for _, b in ipairs({ "a", "b", "start", "select" }) do
if Input:wasPressed(b) then resetEdges = resetEdges + 1 end
end
end
eq(resetEdges, 0, "soft reset with the chord held: no re-edge on the title")
physical.back = false
eq(countEdges("a", 5) + countEdges("b", 5), 0, "soft reset: A/B do not edge once SELECT lets go")
physical.a, physical.b, physical.start = false, false, false
Input:step()
physical.a = true
Input:padEventSeen("a")
Input:gamepadpressed(pad, "a")
Input:step()
Input:reset()
eq(countEdges("a", 10), 0, "focus reset with A held: no phantom A")
physical.a = false
Input:step()
local AutoInput = require("src.core.gen2.AutoInput")
physical.a = true
Input:padEventSeen("a")
Input:gamepadpressed(pad, "a")
Input:step()
local auto = AutoInput.new()
auto:start("CATCH_TUTORIAL", Input)
local autoEdges, autoHeld = 0, 0
for _ = 1, 30 do
auto:step(Input)
Input:step()
if Input:wasPressed("a") then autoEdges = autoEdges + 1 end
if Input:isDown("a") then autoHeld = autoHeld + 1 end
end
eq(autoEdges, 0, "AutoInput stream: a held pad A never presses A")
eq(autoHeld, 0, "AutoInput stream: a held pad A is never held")
auto:stop(Input)
physical.a = false
Input:gamepadreleased(pad, "a")
Input:step()
Input:step()
love.filesystem.remove("pad-reconcile.log")
SwitchDiagnostics._resetForTests()
love.filesystem.write("switch-debug.txt", "")
SwitchDiagnostics._resetForTests()
love.filesystem.write("switch-debug.txt", "")
SwitchDiagnostics.onJoystickEvent("gamepadpressed", pad, "a")
physical.a = true
Input:reset()
Input:reconcile()
local log = love.filesystem.read("pad-reconcile.log") or ""
check(log:find("=== reconcile", 1, true) ~= nil, "focus-in reconcile writes a trace block")
check(log:find("down=a", 1, true) ~= nil, "trace records what SDL reports held")
check(log:find("gamepadpressed", 1, true) ~= nil, "trace carries the recent event ring")
check((love.filesystem.read("switch.log") or ""):find("reconcile", 1, true) ~= nil,
"switch.log is flushed on reconcile")
physical.a = false
love.filesystem.remove("switch-debug.txt")
love.filesystem.remove("pad-reconcile.log")
love.filesystem.remove("switch.log")
SwitchDiagnostics._resetForTests()
love.joystick = { getJoystickCount = function() return 0 end, getJoysticks = function() return {} end }
Input:reset()
Input:gamepadpressed(nil, "a")
Input:step()
check(Input:isDown("a"), "no pads connected leaves event state alone")
Input:gamepadreleased(nil, "a")
Input:step()
love.joystick = realJoystick
eq(type(Input.pollPads), "function", "pollPads exported")
T.finish()