Files
gen1recomp/tests/engine/hostshell_win_restart_2721.lua
T

138 lines
5.3 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
local EXE = "D:\\- Oyun install\\0 - Pokemon Gameboy D\xC4\xB1\xC5\x9F\xC4\xB1 Oyunlar"
.. "\\- Pok\xC3\xA9mon Gen 1 Recompilation Project\\gen1recomp-win64\\gen1recomp.exe"
local DIR = "D:\\- Oyun install\\0 - Pokemon Gameboy D\xC4\xB1\xC5\x9F\xC4\xB1 Oyunlar"
.. "\\- Pok\xC3\xA9mon Gen 1 Recompilation Project\\gen1recomp-win64"
local function utf16Units(s)
local n, i = 0, 1
while i <= #s do
local b = s:byte(i)
if b < 0x80 then i = i + 1
elseif b < 0xE0 then i = i + 2
elseif b < 0xF0 then i = i + 3
else i = i + 4 n = n + 1 end
n = n + 1
end
return n
end
local conversions, creates, closes = {}, {}, 0
local createResult = 1
local fakeFfi = { os = "Windows" }
function fakeFfi.cdef() end
function fakeFfi.new(ct)
if ct == "PP_STARTUPINFOW" then return { cb = 0 } end
if ct == "PP_PROCESS_INFORMATION" then return { hProcess = "proc", hThread = "thread" } end
return {}
end
function fakeFfi.sizeof(ct)
if ct == "PP_STARTUPINFOW" then return 104 end
return 0
end
fakeFfi.C = {
MultiByteToWideChar = function(cp, flags, s, n, w, wn)
conversions[#conversions + 1] = { cp = cp, flags = flags, s = s, n = n, fill = w ~= nil }
local units = utf16Units(s)
if w ~= nil then
assert(wn == units, "buffer sized from the probe")
w.src = s
end
return units
end,
CreateProcessW = function(app, cmd, pa, ta, inherit, flags, env, cwd, si, pi)
creates[#creates + 1] = {
app = app and app.src, cmd = cmd and cmd.src, cwd = cwd and cwd.src,
inherit = inherit, flags = flags, cb = si.cb, pa = pa, ta = ta, env = env, pi = pi,
}
return createResult
end,
CloseHandle = function() closes = closes + 1 return 1 end,
}
package.loaded.ffi = fakeFfi
local quits = {}
love = {
system = { getOS = function() return "Windows" end },
filesystem = {
isFused = function() return true end,
getSource = function() return EXE end,
getExecutablePath = function() return EXE end,
},
event = {
quit = function(...)
quits[#quits + 1] = { n = select("#", ...), arg = (...) }
end,
},
}
package.loaded["src.core.Platform"] = { canSpawnProcess = function() return true end }
local shellCalls = 0
os.execute = function() shellCalls = shellCalls + 1 return 0 end
io.popen = function() shellCalls = shellCalls + 1 return nil end
local HostShell = require("src.core.HostShell")
local WinApi = require("src.core.WinApi")
eq(WinApi.quoteArg("plain"), "plain", "bare argument stays bare")
eq(WinApi.quoteArg("two words"), '"two words"', "space forces quotes")
eq(WinApi.quoteArg(""), '""', "empty argument is an explicit empty string")
eq(WinApi.quoteArg("C:\\dir with space\\"), '"C:\\dir with space\\\\"',
"trailing backslash doubled before the closing quote")
eq(WinApi.quoteArg('say "hi"'), '"say \\"hi\\""', "embedded quote escaped")
eq(WinApi.quoteArg('a\\"b'), '"a\\\\\\"b"', "backslashes before a quote doubled plus one")
eq(WinApi.quoteArg("a\\\\b c"), '"a\\\\b c"', "backslashes not before a quote stay literal")
eq(WinApi.quoteArg("Pok\xC3\xA9mon"), "Pok\xC3\xA9mon", "non-ASCII bytes pass through untouched")
eq(WinApi.commandLine(EXE, { "--x=1", "a b" }), '"' .. EXE .. '" --x=1 "a b"',
"argv0 always quoted, args quoted per CommandLineToArgvW")
eq(WinApi.dirOf(EXE), DIR, "cwd is the exe's folder")
eq(WinApi.dirOf("D:\\gen1recomp.exe"), "D:\\", "drive root keeps its backslash")
HostShell.restart()
eq(shellCalls, 0, "restart never goes through cmd.exe or the ANSI CRT")
eq(#creates, 1, "restart spawns the exe once")
local c = creates[1] or {}
eq(c.app, EXE, "CreateProcessW gets the exe as the exact UTF-8 bytes, widened")
eq(c.cmd, '"' .. EXE .. '"', "command line is the quoted exe")
eq(c.cwd, DIR, "working directory is the install folder")
eq(c.flags, 0x00000008 + 0x00000200, "DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP")
eq(c.inherit, 0, "no inherited handles")
eq(c.cb, 104, "STARTUPINFOW.cb set")
local sawExe = false
for _, conv in ipairs(conversions) do
check(conv.cp == 65001, "every conversion is CP_UTF8, got " .. tostring(conv.cp))
if conv.s == EXE and conv.fill then
sawExe = true
eq(conv.n, #EXE, "conversion covers every UTF-8 byte")
end
end
check(sawExe, "MultiByteToWideChar asked to widen the reporter's UTF-8 path")
eq(closes, 2, "process and thread handles closed")
eq(#quits, 1, "restart quits once after spawning")
eq(quits[1] and quits[1].n, 0, "plain quit, the new process is the restart")
createResult = 0
HostShell.restart()
eq(#creates, 2, "second restart attempts the spawn")
eq(closes, 2, "failed spawn closes nothing")
eq(#quits, 2, "failed spawn still quits once")
eq(quits[2] and quits[2].arg, "restart", "failed spawn falls back to the in-process restart")
eq(shellCalls, 0, "fallback never shells out")
createResult = 1
check(HostShell.spawnSelfDetached({ "--display-companion=50000,tok en" }) == true,
"companion spawn succeeds")
local s = creates[3] or {}
eq(s.cmd, '"' .. EXE .. '" "--display-companion=50000,tok en"', "companion argv quoted")
eq(s.cwd, DIR, "companion runs from the install folder")
createResult = 0
check(HostShell.spawnSelfDetached({}) == false, "companion spawn failure reported")
eq(shellCalls, 0, "companion never shells out")
T.finish("hostshell_win_restart_2721")