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

240 lines
9.1 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 = T.check
local eq = T.eq
local GameVersion = require("src.core.GameVersion")
local Platform = require("src.core.Platform")
local WorkerFs = require("src.core.WorkerFs")
local savedSystem = love.system
local savedThread = love.thread
local savedVersion = GameVersion.get()
local savedRead = love.filesystem.read
local savedLoad = love.filesystem.load
local function setOS(osName)
love.system = { getOS = function() return osName end }
Platform._resetForTests()
end
local function fakeFs(files)
local fs = { reads = {} }
function fs.read(path)
fs.reads[#fs.reads + 1] = path
local bytes = files[path]
if bytes then return bytes end
return nil, path .. " does not exist"
end
return fs
end
local function readsInclude(fs, path)
for _, p in ipairs(fs.reads) do
if p == path then return true end
end
return false
end
local fs = fakeFs({ ["firered/a.bin"] = "P", ["a.bin"] = "B", ["b.bin"] = "B2" })
eq(WorkerFs.read("firered/", "a.bin", fs), "P", "prefixed copy wins")
eq(WorkerFs.read("firered/", "b.bin", fs), "B2", "bare fallback when the prefixed copy is absent")
fs = fakeFs({ ["firered/a.bin"] = "P", ["a.bin"] = "B" })
eq(WorkerFs.read(nil, "a.bin", fs), "B", "nil prefix reads only the bare path")
eq(#fs.reads, 1, "nil prefix never probes a prefixed path")
eq(WorkerFs.read("", "a.bin", fs), "B", "empty prefix is the desktop path")
local miss, err = WorkerFs.read("firered/", "c.bin", fakeFs({}))
eq(miss, nil, "double miss is nil")
eq(err, "c.bin does not exist", "double miss returns the bare read's error")
local cache = WorkerFs.cache(nil, fakeFs({ ["leafgreen/x"] = "L", ["x"] = "X" }))
eq(cache:read("x"), "X", "cache with no prefix reads bare")
cache:setPrefix("leafgreen/")
eq(cache:read("x"), "L", "setPrefix switches the cache to the versioned tree")
cache:setPrefix("")
eq(cache:read("x"), "X", "setPrefix('') returns to bare reads")
setOS("OS X")
for _, id in ipairs(GameVersion.ORDER) do
GameVersion.set(id)
eq(WorkerFs.prefix(), nil, "desktop hands workers no prefix for " .. id)
end
setOS("NX")
for _, id in ipairs(GameVersion.ORDER) do
GameVersion.set(id)
eq(WorkerFs.prefix(), GameVersion.cachePrefix(id), "NX hands workers " .. id .. "'s prefix")
end
local function newChannel()
local c = { items = {} }
function c:push(v) self.items[#self.items + 1] = v end
function c:pop() return table.remove(self.items, 1) end
function c:demand() return table.remove(self.items, 1) end
function c:clear() self.items = {} end
function c:getCount() return #self.items end
return c
end
local channels
local function resetChannels()
channels = {}
love.thread = {
getChannel = function(name)
channels[name] = channels[name] or newChannel()
return channels[name]
end,
}
end
for _, name in ipairs({ "love.thread", "love.sound", "love.timer", "love.filesystem" }) do
package.preload[name] = package.preload[name] or function() return true end
end
love.timer = love.timer or {}
love.timer.getTime = love.timer.getTime or os.clock
love.timer.sleep = love.timer.sleep or function() end
local function serve(files)
local reads = {}
love.filesystem.read = function(path)
reads[#reads + 1] = path
if files[path] then return files[path] end
return nil, "Could not open file " .. path .. ". Does not exist."
end
love.filesystem.load = function(path)
if path:sub(1, 4) == "src/" then return loadfile(path) end
return savedLoad(path)
end
return reads
end
local function contains(list, value)
for _, v in ipairs(list) do
if v == value then return true end
end
return false
end
local function runChipWorker(audio, files)
resetChannels()
local reads = serve(files)
local saved = package.loaded["src.core.WorkerFs"]
package.loaded["src.core.WorkerFs"] = nil
local cmd = love.thread.getChannel("chipaudio_cmd")
cmd:push({ cmd = "play", gen = 1, header = { engine = 1 }, allowLoops = true, audio = audio })
cmd:push({ cmd = "quit" })
local chunk = assert(loadfile("src/core/chip_worker.lua"))
chunk()
package.loaded["src.core.WorkerFs"] = saved
local errors = {}
for _, m in ipairs(love.thread.getChannel("chipaudio_out").items) do
if m.error then errors[#errors + 1] = m.error end
end
return reads, errors
end
local function programError(errors)
for _, e in ipairs(errors) do
if tostring(e):find("could not read sound programs", 1, true) then return true end
end
return false
end
local PROG = "assets/generated/audio/programs.bin"
local PROG_BYTES = string.rep("\0", 0x4000 * 2)
local ChipAudio = require("src.core.ChipAudio")
local fullAudio = { audio = { programFile = PROG, bankOrder = { 2, 8 }, waveBanks = {} } }
for _, id in ipairs({ "red", "blue", "yellow", "gold", "silver", "crystal" }) do
setOS("NX")
GameVersion.set(id)
local prefix = GameVersion.cachePrefix(id)
local nxOnly = { [prefix .. PROG] = PROG_BYTES }
local slim = ChipAudio._slimAudioForTest(fullAudio)
local reads, errors = runChipWorker(slim, nxOnly)
check(contains(reads, prefix .. PROG), "NX chip worker reads " .. prefix .. PROG)
check(not programError(errors), "NX chip worker finds programs.bin for " .. id)
local bare = { programFile = PROG, bankOrder = { 2, 8 }, waveBanks = {} }
local _, bareErrors = runChipWorker(bare, nxOnly)
check(programError(bareErrors), "without the prefix the NX chip worker misses programs.bin for " .. id .. " (#2449 class)")
end
setOS("OS X")
GameVersion.set("blue")
local slimDesktop = ChipAudio._slimAudioForTest(fullAudio)
eq(slimDesktop.programPrefix, nil, "desktop chip payload carries no prefix")
local deskReads, deskErrors = runChipWorker(slimDesktop, { [PROG] = PROG_BYTES })
eq(deskReads[1], PROG, "desktop chip worker reads the mounted bare path first")
check(not contains(deskReads, "blue/" .. PROG), "desktop chip worker never probes a prefixed path")
check(not programError(deskErrors), "desktop chip worker finds programs.bin")
local ChipSynth = require("src.core.ChipSynth")
ChipSynth.invalidateBanks()
serve({ ["red/" .. PROG] = string.rep("R", 0x8000), ["blue/" .. PROG] = string.rep("U", 0x8000) })
local redBanks = ChipSynth._loadBanksForTest({ audio = { programFile = PROG, programPrefix = "red/", bankOrder = { 1, 2 } } })
local blueBanks = ChipSynth._loadBanksForTest({ audio = { programFile = PROG, programPrefix = "blue/", bankOrder = { 1, 2 } } })
eq(redBanks[1]:sub(1, 1), "R", "red prefix loads red banks")
eq(blueBanks[1]:sub(1, 1), "U", "bank cache is keyed by prefix, not just programFile")
ChipSynth.invalidateBanks()
local ROOT = "data/generated/gba/audio"
local INDEX = "return { songs = {} }"
local function runM4aWorker(prefix, files)
resetChannels()
local reads = serve(files)
local saved = package.loaded["src.core.WorkerFs"]
package.loaded["src.core.WorkerFs"] = nil
local cmd = love.thread.getChannel("game3_m4a_cmd")
cmd:push({ cmd = "install", root = ROOT, prefix = prefix })
cmd:push({ cmd = "quit" })
local chunk = assert(loadfile("src/core/game3/m4a_worker.lua"))
chunk()
package.loaded["src.core.WorkerFs"] = saved
return reads, love.thread.getChannel("game3_m4a_status")
end
for _, id in ipairs({ "firered", "leafgreen" }) do
setOS("NX")
GameVersion.set(id)
local prefix = WorkerFs.prefix()
eq(prefix, id .. "/", "NX m4a install prefix for " .. id)
local nxOnly = { [prefix .. ROOT .. "/index.lua"] = INDEX }
local reads, status = runM4aWorker(prefix, nxOnly)
check(contains(reads, prefix .. ROOT .. "/index.lua"), "NX m4a worker reads the " .. id .. " tree")
eq(status:getCount(), 0, "NX m4a worker installs for " .. id)
local _, bareStatus = runM4aWorker(nil, nxOnly)
local failed = bareStatus:pop()
check(type(failed) == "table" and failed.installFailed, "without the prefix the NX m4a worker misses the pack for " .. id .. " (#2449)")
end
setOS("OS X")
GameVersion.set("firered")
local deskM4aReads, deskStatus = runM4aWorker(WorkerFs.prefix(), { [ROOT .. "/index.lua"] = INDEX })
eq(deskStatus:getCount(), 0, "desktop m4a worker installs from the mounted path")
check(not contains(deskM4aReads, "firered/" .. ROOT .. "/index.lua"), "desktop m4a worker never probes a prefixed path")
love.filesystem.read = savedRead
love.filesystem.load = savedLoad
setOS("NX")
GameVersion.set("red")
local CacheFs = require("src.import.CacheFs")
local savedPrefix = CacheFs.prefix
CacheFs.prefix = "leafgreen/"
local REL = "data/generated/gba/items/pack.lua"
check(CacheFs.write(REL, "return {}"), "import worker write succeeds on NX")
eq(love.filesystem.read("leafgreen/" .. REL), "return {}", "import worker writes land under the version prefix on NX")
eq(love.filesystem.read(REL), nil, "import worker never writes the bare path on NX")
check(CacheFs.exists(REL), "import worker exists() sees its own prefixed write")
eq(CacheFs.readActive(REL), "return {}", "readActive in a worker state (GameVersion red) still finds the prefixed file")
love.filesystem.remove("leafgreen/" .. REL)
CacheFs.prefix = savedPrefix
love.system = savedSystem
love.thread = savedThread
Platform._resetForTests()
GameVersion.set(savedVersion)
T.finish("worker fs nx")