diff --git a/scripts/test.sh b/scripts/test.sh index b759d434..9a2b4dd7 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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 diff --git a/tests/engine/assets_version_fallback_test.lua b/tests/engine/assets_version_fallback_test.lua index 333d5af9..464f17e9 100644 --- a/tests/engine/assets_version_fallback_test.lua +++ b/tests/engine/assets_version_fallback_test.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() diff --git a/tests/engine/nx_yellow_boot_test.lua b/tests/engine/nx_yellow_boot_test.lua index c7fe36bb..96270190 100644 --- a/tests/engine/nx_yellow_boot_test.lua +++ b/tests/engine/nx_yellow_boot_test.lua @@ -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) diff --git a/tests/switch_ci_workflows_test.lua b/tests/switch_ci_workflows_test.lua index a7690432..8225cc46 100644 --- a/tests/switch_ci_workflows_test.lua +++ b/tests/switch_ci_workflows_test.lua @@ -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")