Files
gen1recomp/tests/rom_importer_choose_version_test.lua
T
Colson Rice 921f565f1d Import the cart Choose ROM asked for
findPendingRom answered with the first dump in the save directory whose SHA-1
mapped to any not-yet-ready version.  On a device with no file picker that
scan IS the import, so with four dumps in the folder, choosing Red imported
and decoded Blue (#1274).

It now takes an optional version and narrows to it.  The two Choose paths
pass self.chooseVersion, since a Choose names the cart it is for.  The two
Android USB-drop scans pass nothing and still take the first pending cart of
any version, which is what they are for.

A chosen version with no dump present now imports nothing rather than the
wrong cart, and falls through to the notice that says where to put the file.
2026-08-21 22:21:41 -04:00

127 lines
4.7 KiB
Lua

-- #1274: with several cartridge dumps sitting in the save directory, Choose
-- ROM imported whichever version was not yet ready rather than the one the
-- player picked. Select Red, get Blue.
--
-- The fallback exists for devices with no file picker (handheld Linux with
-- no zenity or kdialog, and the Android USB-drop path), where chooseRom
-- returns nil and the importer scans the save directory instead. That scan,
-- findPendingRom, answered with the first dump whose SHA-1 mapped to any
-- not-yet-ready version, so the selection was dropped on the floor.
--
-- Self-contained: `luajit tests/rom_importer_choose_version_test.lua`
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local S = require("tests.harness").suite("rom importer honours the chosen version")
local eq = S.eq
local check = S.check
local RomImporter = require("src.import.RomImporter")
local GameVersion = require("src.core.GameVersion")
local ROM_BYTES = 1024 * 1024
-- One dump per version, each a blob of the right size whose first byte says
-- which cart it is. love.data.hash is stubbed to answer with the real SHA-1
-- from GameVersion, so GameVersion.forSha1 does its own lookup unchanged.
local MARK = { R = "red", B = "blue", Y = "yellow" }
local FILES = {
["blue.gb"] = string.rep("B", ROM_BYTES),
["red.gb"] = string.rep("R", ROM_BYTES),
["yellow.gbc"] = string.rep("Y", ROM_BYTES),
}
-- Deliberately not alphabetical and not selection order: the bug returned
-- whatever getDirectoryItems listed first.
local LISTING = { "blue.gb", "red.gb", "yellow.gbc" }
love.system = love.system or {}
love.data = love.data or {}
local saved = {
getOS = love.system.getOS,
hash = love.data.hash,
encode = love.data.encode,
getDirectoryItems = love.filesystem.getDirectoryItems,
getInfo = love.filesystem.getInfo,
read = love.filesystem.read,
getSaveDirectory = love.filesystem.getSaveDirectory,
}
-- Not OS X, Windows or Linux, so chooseRom returns nil without shelling out
-- to a dialog that may or may not exist on the machine running the suite.
-- The fallback under test is the same one every pickerless device takes.
love.system.getOS = function() return "Unknown" end
love.filesystem.getSaveDirectory = function() return "/tmp/pokemon-love2d" end
love.filesystem.getDirectoryItems = function() return LISTING end
love.filesystem.getInfo = function(name, filter)
if FILES[name] then return { type = "file", size = #FILES[name] } end
return nil
end
love.filesystem.read = function(name) return FILES[name] end
-- RomImporter's sha1 helper is hash then encode-to-hex, so the pair is
-- stubbed together: hash answers with the digest already in the form
-- GameVersion stores, and encode hands it back untouched.
love.data.hash = function(_, data)
local version = MARK[data:sub(1, 1)]
return version and GameVersion.info(version).sha1 or "0"
end
love.data.encode = function(_, _, digest) return digest end
local function freshImporter()
return setmetatable({
android = false,
nativePicker = false,
workState = nil,
ready = { red = false, blue = false, yellow = false },
notice = nil,
modNotice = nil,
saveNotice = {},
baseRoms = {},
chooseVersion = nil,
startData = function(self, data, displayName)
self._started = { data = data, name = displayName }
end,
startPath = function(self, path) self._startedPath = path end,
}, RomImporter)
end
-- ------- the report: pick Red, get Red
local ri = freshImporter()
ri:choose("red")
check(ri._started ~= nil, "a pickerless Choose still imports from the save dir")
eq(ri._started and ri._started.name, "red.gb",
"and it imports the cart that was chosen, not the first pending one")
-- ------- the same for a version listed after another pending dump
ri = freshImporter()
ri:choose("yellow")
eq(ri._started and ri._started.name, "yellow.gbc",
"Yellow is imported for Yellow, with Blue and Red still pending")
-- ------- a chosen version with no dump present imports nothing
ri = freshImporter()
ri.ready = { red = false, blue = false, yellow = false, gold = false }
ri:choose("gold")
check(ri._started == nil,
"choosing a version whose dump is absent imports no other cart")
-- ------- an already-imported cart is still never re-extracted (#167)
ri = freshImporter()
ri.ready = { red = true, blue = false, yellow = false }
ri:choose("red")
check(ri._started == nil,
"and a version already imported is not extracted a second time")
for name, fn in pairs(saved) do
if name == "getOS" then love.system.getOS = fn
elseif name == "hash" then love.data.hash = fn
elseif name == "encode" then love.data.encode = fn
else love.filesystem[name] = fn end
end
S.finish()