mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-04 00:20:01 -04:00
166 lines
7.2 KiB
Lua
166 lines
7.2 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
local T = require("tests.harness")
|
|
love = { system = { getOS = function() return "Linux" end } }
|
|
local Extractor = require("src.import.RomExtractorGen3")
|
|
local Importer = require("src.import.RomImporter")
|
|
local CacheFs = require("src.import.CacheFs")
|
|
local getenv = os.getenv
|
|
os.getenv = function(key)
|
|
if key == "POKEPORT_EXTRACT_WORKERS" then return "2" end
|
|
if key == "POKEPORT_NO_THREAD" or key == "POKEPORT_IMPORT_ONLY" then return nil end
|
|
return getenv(key)
|
|
end
|
|
|
|
local plan = { tasks = {{ id = "a" }, { id = "b" }, { id = "c" }}, sequential = { "gba" } }
|
|
local function nested(mode)
|
|
local threads, queue, writes = {}, {}, {}
|
|
local created, sleeps, clears, fallbackLive, fallback, peak = 0, 0, 0, nil, 0, 0
|
|
local function live()
|
|
local count = 0
|
|
for _, th in ipairs(threads) do if th.live then count = count + 1 end end
|
|
return count
|
|
end
|
|
local channel = {
|
|
pop = function()
|
|
if mode == "channel" then error("fixture channel failure") end
|
|
return table.remove(queue, 1)
|
|
end,
|
|
clear = function()
|
|
if mode == "clear" then error("fixture clear failure") end
|
|
clears = clears + 1; queue = {}
|
|
end,
|
|
}
|
|
CacheFs.exists = function() return false end
|
|
CacheFs.write = function(path)
|
|
writes[#writes + 1] = { path = path, live = live() }
|
|
return true
|
|
end
|
|
love = {
|
|
system = { getOS = function() return "Linux" end, getProcessorCount = function() return 8 end },
|
|
timer = { getTime = function() return 10 end, sleep = function()
|
|
sleeps = sleeps + 1
|
|
if sleeps > 6 then error("fixture watchdog") end
|
|
end },
|
|
thread = { getChannel = function() return channel end, newThread = function()
|
|
created = created + 1
|
|
local index = created
|
|
if (mode == "create" and index == 2) or (mode == "refill" and index == 3) then
|
|
error("fixture create failure")
|
|
end
|
|
local th = { joins = 0 }
|
|
function th:start(task, prefix, rom, sha1)
|
|
T.eq(prefix, CacheFs.prefix, mode .. " transfers prefix")
|
|
T.eq(rom, "fixture ROM", mode .. " transfers ROM")
|
|
T.eq(sha1, "fixture SHA", mode .. " transfers SHA")
|
|
if mode == "start" and index == 2 then error("fixture start failure") end
|
|
if mode == "false" and index == 2 then return false end
|
|
self.live = true
|
|
peak = math.max(peak, live())
|
|
if mode == "sideeffect" and index == 2 then error("fixture post-start failure") end
|
|
if mode == "report" then
|
|
queue[#queue + 1] = { type = "progress", task = task, fraction = .1 }
|
|
elseif mode == "error" then
|
|
self.live = false
|
|
self.err = "fixture worker error"
|
|
else
|
|
queue[#queue + 1] = { type = "done", task = task, ok = mode ~= "doneerror", error = "fixture task failure" }
|
|
end
|
|
return true
|
|
end
|
|
function th:getError() return self.err end
|
|
function th:wait()
|
|
self.joins = self.joins + 1
|
|
if mode == "join" and index == 1 then error("fixture join failure") end
|
|
self.live = false
|
|
queue[#queue + 1] = { type = "progress", task = "late", fraction = 1 }
|
|
end
|
|
threads[#threads + 1] = th
|
|
return th
|
|
end },
|
|
}
|
|
local ex = setmetatable({ romData = "fixture ROM", plan = plan }, Extractor)
|
|
ex.ensureSha1 = function() return "fixture SHA" end
|
|
ex.writeRequiredMarkers = function() end
|
|
ex.report = function(self, fraction)
|
|
if mode == "report" and fraction > .03 and not self.reportFailed then
|
|
self.reportFailed = true
|
|
error("fixture report failure")
|
|
end
|
|
end
|
|
ex.runGbaExtract = function()
|
|
fallback = fallback + 1
|
|
fallbackLive = live()
|
|
return true
|
|
end
|
|
local ok, result = pcall(ex.run, ex)
|
|
if mode == "join" or mode == "clear" then
|
|
T.eq(ok, false, mode .. " cleanup failure aborts import")
|
|
T.eq(fallback, 0, mode .. " cleanup failure cannot enter sequential fallback")
|
|
T.check(tostring(result):find("fixture " .. mode .. " failure", 1, true), mode .. " cleanup error retains cause")
|
|
T.eq(#writes, 0, mode .. " cleanup failure publishes no successful pool output")
|
|
local published = false
|
|
local imp = setmetatable({ workState = "working", _extract = {
|
|
version = "firered", prefix = "fixture/", thread = {},
|
|
progress = { pop = function() return nil end },
|
|
result = { pop = function() return { ok = ok, error = result } end },
|
|
} }, Importer)
|
|
imp._completeImport = function() published = true end
|
|
imp:_pumpExtract()
|
|
T.eq(published, false, mode .. " worker result prevents readiness publication")
|
|
T.eq(imp.workState, "error", mode .. " worker result exposes import failure")
|
|
else
|
|
T.check(ok and result.extractOk, mode .. " import succeeds after completed pool or safe fallback")
|
|
T.eq(live(), 0, mode .. " no writer survives return")
|
|
T.eq(clears, 1, mode .. " clears private channel after joins")
|
|
T.eq(#queue, 0, mode .. " drains late messages")
|
|
if mode == "success" then
|
|
T.eq(fallback, 0, "success uses parallel output")
|
|
else
|
|
T.eq(fallback, 1, mode .. " uses existing sequential fallback")
|
|
T.eq(fallbackLive, 0, mode .. " joins before sequential write")
|
|
end
|
|
if mode == "false" then T.eq(sleeps, 0, "false start rejected before dead polling") end
|
|
for _, write in ipairs(writes) do T.eq(write.live, 0, mode .. " final output occurs after joins") end
|
|
end
|
|
T.check(peak <= 2, mode .. " promptly joins completed workers within the resource limit")
|
|
for index, th in ipairs(threads) do T.eq(th.joins, 1, mode .. " joins owned thread " .. index) end
|
|
end
|
|
for _, mode in ipairs({ "create", "refill", "start", "sideeffect", "false", "report", "channel", "error", "doneerror", "success", "join", "clear" }) do
|
|
nested(mode)
|
|
end
|
|
|
|
for _, version in ipairs({ "red", "gold", "firered" }) do
|
|
for _, mode in ipairs({ "false", "throw", "true", "nil", "create" }) do
|
|
local channels = {}
|
|
local thread = {
|
|
start = function()
|
|
if mode == "throw" then error("fixture outer start failure") end
|
|
if mode == "false" then return false end
|
|
if mode == "true" then return true end
|
|
end,
|
|
getError = function() return nil end,
|
|
}
|
|
love = { thread = {
|
|
newThread = function() if mode == "create" then error("fixture outer create failure") end; return thread end,
|
|
getChannel = function(name)
|
|
channels[name] = channels[name] or { clear = function() end, pop = function() return nil end }
|
|
return channels[name]
|
|
end,
|
|
} }
|
|
local imp = setmetatable({ romSha1 = "fixture SHA", romData = "fixture ROM", workState = "working" }, Importer)
|
|
local success = mode == "true" or mode == "nil"
|
|
local started = imp:_startExtractThread(version, "fixture/", "fixture ROM", "fixture")
|
|
T.eq(started, success, version .. " " .. mode .. " outer startup result")
|
|
T.eq(imp._extract ~= nil, success, version .. " " .. mode .. " tracks only started workers")
|
|
T.eq(imp.romData, not success and "fixture ROM" or nil, version .. " " .. mode .. " retains fallback ROM")
|
|
if not success then
|
|
local published = false
|
|
imp._completeImport = function() published = true end
|
|
imp:_pumpExtract()
|
|
T.eq(published, false, version .. " " .. mode .. " cannot publish readiness")
|
|
end
|
|
end
|
|
end
|
|
os.getenv = getenv
|
|
T.finish("importer_worker_failure_cleanup")
|