test(switch): cover widenMono newSoundData hop and run NX suites in T0

The Yellow boot suite only recorded newSource, so a missing sound.newSoundData
wrap (the silent hole that motivated the full-surface overlay) could still go
green. Force mono on the Source stub, record newSoundData, and assert the cry
re-read lands on yellow/. Also pin the three NX suites in scripts/test.sh T0
and fix the seed_chunk global leak in the fallback suite.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Andrew Quenehen
2026-08-03 16:20:45 -03:00
parent 12ff4dba2e
commit 9ef5c8dead
4 changed files with 38 additions and 2 deletions
+5
View File
@@ -67,6 +67,11 @@ run_tier() {
run_tier "T0 switch CI workflow content gate" "$LUA" tests/switch_ci_workflows_test.lua
run_tier "T0 switch transfer docs gate" "$LUA" tests/switch_transfer_docs_test.lua
# NX Blue/Yellow asset overlay: ROM-free, must run on every checkout so a
# Sound.lua / overlay regression is not gated only on switch-changes paths.
run_tier "T0 NX asset overlay fallback" "$LUA" tests/engine/assets_version_fallback_test.lua
run_tier "T0 NX generated-path static guard" "$LUA" tests/engine/nx_generated_guard_test.lua
run_tier "T0 NX Yellow/Blue boot (dynamic paths)" "$LUA" tests/engine/nx_yellow_boot_test.lua
run_tier "T1/T2 engine invariants + parity gates" "$LUA" tests/run_engine.lua
run_tier "T4 mod-SDK" "$LUA" tests/run_modkit.lua
@@ -44,7 +44,7 @@ eq(Assets.resolve(PNG), PNG,
local rawWrite = love.filesystem.write
local rawRemove = love.filesystem.remove
local rawGetInfo = love.filesystem.getInfo
seed_chunk = "assets/generated/boot_chunk.lua"
local seed_chunk = "assets/generated/boot_chunk.lua"
love.filesystem.write("yellow/" .. seed_chunk, "return 42")
Overlay.install()
+28 -1
View File
@@ -76,12 +76,26 @@ love.filesystem.read = function(path, ...)
return rawRead(path, ...)
end
-- widenMono re-reads the caller's bare path via newSoundData; without this
-- recorder the boot suite only saw newSource and could not catch a missing
-- sound.newSoundData wrap (the silent hole that motivated the full-surface
-- overlay). Installed before Overlay.install so the overlay wraps us.
local rawNewSoundData = love.sound.newSoundData
love.sound.newSoundData = function(samples, ...)
record("sounddata", samples)
return rawNewSoundData(samples, ...)
end
local Source = {}
Source.__index = Source
function Source:play() self.playing = true end
function Source:stop() self.playing = false end
function Source:setVolume() end
function Source:isPlaying() return self.playing end
-- Mono so Sound.widenMono actually reaches newSoundData (the stub's path
-- form then fails inside the pcall and widenMono keeps this Source --
-- enough to prove the overlay rewrote the re-read path).
function Source:getChannelCount() return 1 end
love.audio = {
newSource = function(path, mode)
@@ -212,11 +226,23 @@ eq(pre and pre.nidoFrames and pre.nidoFrames[3]
"data-driven nidorino frame resolves to the yellow/ copy")
-- Yellow's voiced Pikachu clip: a FORMATTED path (cry_%02d.wav), invisible
-- to the static guard
-- to the static guard. playPikaCry also runs widenMono, which re-reads the
-- same bare path via newSoundData -- that second hop is what the full-surface
-- overlay exists to cover.
local cry = Sound.playPikaCry({ audio = { pikaCries = 1 } }, 1)
check(cry ~= nil, "playPikaCry returns a source on NX Yellow")
eq(cry and cry.path, "yellow/assets/generated/audio/pika_cries/cry_01.wav",
"the formatted pika-cry path resolves to the yellow/ copy")
local sawCrySoundData = false
for _, r in ipairs(recorded) do
if r.kind == "sounddata"
and r.path == "yellow/assets/generated/audio/pika_cries/cry_01.wav" then
sawCrySoundData = true
break
end
end
check(sawCrySoundData,
"widenMono re-read the cry via newSoundData with the yellow/ path")
check(countRecorded("yellow/assets/generated/") >= 20,
"the boot pulled its generated art through the yellow/ prefix")
@@ -312,6 +338,7 @@ Overlay.uninstall()
check(not Overlay.isInstalled(), "overlay uninstalls")
love.graphics.newImage = rawNewImage
love.filesystem.read = rawRead
love.sound.newSoundData = rawNewSoundData
love.audio = nil
for _, path in ipairs(seeded) do
love.filesystem.remove(path)
+4
View File
@@ -180,6 +180,10 @@ mustContain(test_sh, "tests/switch_ci_workflows_test.lua", "scripts/test.sh")
mustContain(test_sh, "T0 switch CI workflow content gate", "scripts/test.sh")
mustContain(test_sh, "tests/switch_transfer_docs_test.lua", "scripts/test.sh")
mustContain(test_sh, "T0 switch transfer docs gate", "scripts/test.sh")
-- NX suites also run in the unified entry point (not only switch-selftest)
mustContain(test_sh, "tests/engine/assets_version_fallback_test.lua", "scripts/test.sh")
mustContain(test_sh, "tests/engine/nx_generated_guard_test.lua", "scripts/test.sh")
mustContain(test_sh, "tests/engine/nx_yellow_boot_test.lua", "scripts/test.sh")
mustContain(build_doc, "tests/switch_ci_workflows_test.lua", "switch-build.md path list")
mustContain(build_doc, "tests/switch_transfer_docs_test.lua", "switch-build.md path list")