mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 00:10:56 +02:00
ff90062741
* Make ROM-free test tiers actually run on Windows Suite discovery, the extension-point catalog scan, mod test-dir pickup, and the meta-coverage corpus all shelled out to ls/find/test -d, which do not exist in cmd.exe. Every listing came back empty on Windows, so tiers ran 0 suites and still reported ALL TESTS PASSED. Add portable probes to tests/fs_io.lua (same Unix commands on Linux/macOS; dir /b and a shell-free rename-self existence check on Windows) and rewire the four call sites to them. 15/15 engine suites and 2/2 modkit suites now genuinely run and pass on Windows. Fixes #266 * Make the T3 content tier run on Windows The content tier had the same Unix-shell assumptions as the tier discovery fixed in #267, one level down: - run_tests.lua redirected to /dev/null when chaining tier runners - mod_runtime_tests.lua called ffi setenv/unsetenv, which msvcrt lacks (_putenv with an empty value unsets) - modkit_tests.lua captured exit codes with POSIX '; echo 0' (cmd needs /v:on and !errorlevel!), called python3 (python on Windows), and used mkdir -p / rm -rf; cmd mkdir makes parents on its own and read -p as a directory name - the save-editor suites globbed save backups with ls - tools/save-editor/Catalog.lua scraped mod flags with ls; this one affects the real editor on Windows, not just tests With a ROM imported, run_tests.lua goes from 12 failures to the 3 that track the missing audio.lua generation (#268), which fails on any OS and needs a maintainer decision. --------- Co-authored-by: johnjohto <johnjohto@users.noreply.github.com>
150 lines
4.4 KiB
Lua
150 lines
4.4 KiB
Lua
-- io-backed filesystem for the headless Loader (21-testing-and-ci
|
|
-- "headless loader seam"). Loader.new takes opts.fs; under plain luajit
|
|
-- there is no love.filesystem, so this adapter reads a real mod directory
|
|
-- off disk and lets discovery/topo-sort/merge run with no love at all.
|
|
--
|
|
-- Paths handed to the loader are repo-relative ("mods/example_mew_starter"),
|
|
-- the same strings love.filesystem would see, so a mod loaded through here
|
|
-- and one loaded in the game take identical code paths.
|
|
|
|
local FsIo = {}
|
|
|
|
-- shell-quote for the popen/os.execute probes; single quotes survive
|
|
-- spaces and the ' inside a name closes-escapes-reopens
|
|
local function quote(path)
|
|
return "'" .. tostring(path):gsub("'", "'\\''") .. "'"
|
|
end
|
|
|
|
-- The suites also run on Windows checkouts, where cmd has no ls/find/test.
|
|
-- These probes pick the spelling the host shell understands; anything that
|
|
-- listed a directory through a bare Unix command silently returned nothing
|
|
-- there, and a tier built on an empty listing passes vacuously.
|
|
FsIo.isWindows = package.config:sub(1, 1) == "\\"
|
|
|
|
local function shellLines(cmd)
|
|
local lines = {}
|
|
local pipe = io.popen(cmd)
|
|
if not pipe then return lines end
|
|
for line in pipe:lines() do
|
|
if line ~= "" then lines[#lines + 1] = line end
|
|
end
|
|
pipe:close()
|
|
return lines
|
|
end
|
|
|
|
-- names directly inside path (files and directories mixed, like ls -1)
|
|
function FsIo.listDir(path)
|
|
local cmd
|
|
if FsIo.isWindows then
|
|
cmd = 'dir /b "' .. tostring(path) .. '" 2>nul'
|
|
else
|
|
cmd = "ls -1 " .. quote(path) .. " 2>/dev/null"
|
|
end
|
|
local items = shellLines(cmd)
|
|
table.sort(items)
|
|
return items
|
|
end
|
|
|
|
-- every *.lua under dir, recursively, as forward-slash paths
|
|
function FsIo.luaFilesUnder(dir)
|
|
local cmd
|
|
if FsIo.isWindows then
|
|
cmd = 'dir /b /s "' .. tostring(dir) .. '\\*.lua" 2>nul'
|
|
else
|
|
-- -L follows symlinks: a checkout that symlinks src/ (worktrees, the
|
|
-- ROM-free CI probe) would otherwise scan nothing and hand every gate
|
|
-- an empty catalog to pass vacuously against
|
|
cmd = "find -L " .. quote(dir) .. " -name '*.lua' -type f 2>/dev/null"
|
|
end
|
|
local files = {}
|
|
for _, line in ipairs(shellLines(cmd)) do
|
|
files[#files + 1] = (line:gsub("\\", "/"))
|
|
end
|
|
table.sort(files)
|
|
return files
|
|
end
|
|
|
|
-- full paths matching the shell's "prefix"* glob (e.g. save.lua.bak-*)
|
|
function FsIo.globPrefix(prefix)
|
|
local dir, base = tostring(prefix):match("^(.*[/\\])([^/\\]*)$")
|
|
if not dir then dir, base = "", tostring(prefix) end
|
|
if dir == "" then dir = "." end
|
|
local matches = {}
|
|
for _, name in ipairs(FsIo.listDir(dir)) do
|
|
if name:sub(1, #base) == base then
|
|
matches[#matches + 1] = dir .. name
|
|
end
|
|
end
|
|
return matches
|
|
end
|
|
|
|
-- existence probe that never shells out on Windows: directories do not
|
|
-- open() there at all, and rename-self succeeds for anything that exists
|
|
function FsIo.isDir(path)
|
|
local handle = io.open(path, "rb")
|
|
if handle then
|
|
local probe = handle:read(1)
|
|
handle:close()
|
|
if probe ~= nil then return false end
|
|
if FsIo.isWindows then return false end -- opened but empty: a file
|
|
elseif FsIo.isWindows then
|
|
return os.rename(path, path) == true
|
|
end
|
|
local ok = os.execute("test -d " .. quote(path))
|
|
return ok == true or ok == 0
|
|
end
|
|
|
|
function FsIo.new(rootDir)
|
|
local base = rootDir or "."
|
|
|
|
local function abs(path)
|
|
if path == nil or path == "" then return base end
|
|
return base .. "/" .. path
|
|
end
|
|
|
|
local fs = {}
|
|
|
|
function fs.read(path)
|
|
local handle = io.open(abs(path), "rb")
|
|
if not handle then return nil, "nofile" end
|
|
local body = handle:read("*a")
|
|
handle:close()
|
|
return body
|
|
end
|
|
|
|
function fs.write(path, body)
|
|
local handle = io.open(abs(path), "wb")
|
|
if not handle then return false end
|
|
handle:write(body)
|
|
handle:close()
|
|
return true
|
|
end
|
|
|
|
-- files answer without a shell; only the directory case pays for a probe
|
|
function fs.getInfo(path)
|
|
local handle = io.open(abs(path), "rb")
|
|
if handle then
|
|
local probe = handle:read(1)
|
|
handle:close()
|
|
-- a directory opens on some libc builds but reads nothing
|
|
if probe ~= nil then return { type = "file" } end
|
|
end
|
|
if FsIo.isDir(abs(path)) then return { type = "directory" } end
|
|
if handle then return { type = "file" } end
|
|
return nil
|
|
end
|
|
|
|
function fs.load(path)
|
|
return loadfile(abs(path))
|
|
end
|
|
|
|
function fs.getDirectoryItems(path)
|
|
return FsIo.listDir(abs(path))
|
|
end
|
|
|
|
fs.root = base
|
|
return fs
|
|
end
|
|
|
|
return FsIo
|