api upgrades, windows dev tools, and android fixes (#299)

* pop up a fake save

* add CI to dev branch

* Make ROM-free test tiers actually run on Windows (#267)

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

Co-authored-by: johnjohto <johnjohto@users.noreply.github.com>

* intro api upgrade (#294)

* intro api upgrade

* api updates

* fix tests

* updated templates

* android fixes for mods and saves (#297)

* Android SCALING fixes (#298)

* android fixes for mods and saves

* perhaps this is the true scaling android issue fix

---------

Co-authored-by: johnjohto <johtoboy@atomicmail.io>
Co-authored-by: johnjohto <johnjohto@users.noreply.github.com>
This commit is contained in:
bryanthaboi
2026-07-27 09:29:24 -04:00
committed by GitHub
parent 043f6b2426
commit 803f86d5b3
68 changed files with 2902 additions and 300 deletions
+10 -6
View File
@@ -63,13 +63,17 @@ end
-- engine tier, the SDK cases, and any tests a shipped mod carries
local function testCorpus()
local files, bodies = {}, {}
local pipe = io.popen(
"ls tests/*.lua tests/engine/*.lua tests/modkit/cases/*.lua mods/*/tests/*.lua 2>/dev/null")
if pipe then
for line in pipe:lines() do
if line ~= "" then files[#files + 1] = line end
local FsIo = require("tests.fs_io")
local function addLuaFrom(dir)
for _, name in ipairs(FsIo.listDir(dir)) do
if name:match("%.lua$") then files[#files + 1] = dir .. "/" .. name end
end
pipe:close()
end
addLuaFrom("tests")
addLuaFrom("tests/engine")
addLuaFrom("tests/modkit/cases")
for _, mod in ipairs(FsIo.listDir("mods")) do
if not mod:find(".", 1, true) then addLuaFrom("mods/" .. mod .. "/tests") end
end
for _, path in ipairs(files) do
bodies[path] = slurp(path) or ""
+18
View File
@@ -190,4 +190,22 @@ do
check(err ~= nil, "the no-root case carries a reason")
end
-- ------- uninstall: rejects bad ids without needing a real mods tree
do
local ok, err = LauncherMods.uninstall("")
eq(ok, nil, "empty id is rejected")
check(tostring(err):find("missing", 1, true) ~= nil, "empty-id reason")
ok, err = LauncherMods.uninstall("../escape")
eq(ok, nil, "path-like ids are rejected")
check(tostring(err):find("invalid", 1, true) ~= nil, "path-id reason")
ok, err = LauncherMods.uninstall("ghost")
-- Without a mods/ghost tree (and with the love stub's getInfo), uninstall
-- either needs LOVE or reports not installed -- never silently succeeds.
eq(ok, nil, "a missing mod does not uninstall")
check(err ~= nil, "missing-mod uninstall carries a reason")
end
T.finish("launcher_mods")
+35
View File
@@ -165,6 +165,41 @@ do
T.eq(SaveData.createSlot("red"), "slot3", "ids increment past the highest")
end
-- ---------------------------------------------- deleteSlot
do
local files = fresh()
local a = SaveData.createSlot("red")
local b = SaveData.createSlot("red")
SaveData.setActiveSlot("red", b)
local save = SaveData.newGame()
save.player.name = "KEEP"
T.check(SaveData.writeSlot("red", a, save), "seed slot1 with a save")
save.player.name = "GONE"
T.check(SaveData.writeSlot("red", b, save), "seed slot2 with a save")
local ok, err = SaveData.deleteSlot("red", b)
T.check(ok, "deleteSlot removes the active slot: " .. tostring(err))
T.eq(files["saves/red/slot2.lua"], nil, "slot2's file is gone")
T.check(files["saves/red/slot1.lua"] ~= nil, "the other slot's file stays")
local opts = SaveSerializer.decode(files["options.lua"])
T.eq(opts.saveSlots.red.active, a, "active falls back to the remaining slot")
T.eq(#opts.saveSlots.red.list, 1, "the deleted id is dropped from the list")
T.eq(opts.saveSlots.red.list[1], a, "only slot1 remains registered")
ok = SaveData.deleteSlot("red", a)
T.check(ok, "deleting the last slot succeeds")
opts = SaveSerializer.decode(files["options.lua"])
T.eq(#opts.saveSlots.red.list, 0, "the registry list is empty")
T.eq(opts.saveSlots.red.active, nil, "active clears when no slots remain")
T.eq(#SaveData.listSlots("red"), 0, "listSlots reports an empty install")
local bad, badErr = SaveData.deleteSlot("red", "slot99")
T.check(not bad, "deleting an unknown slot fails")
T.check(tostring(badErr):find("not registered", 1, true) ~= nil,
"unknown-slot error is user-presentable")
end
-- ---------------------------------------------- saveNames follows the slot
do