mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
209 lines
9.4 KiB
Lua
209 lines
9.4 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
local LaunchOptions = require("src.core.LaunchOptions")
|
|
local savedSystem = love.system
|
|
local savedGetenv = os.getenv
|
|
local env = {}
|
|
|
|
os.getenv = function(name)
|
|
if env[name] ~= nil then return env[name] end
|
|
return savedGetenv(name)
|
|
end
|
|
|
|
do
|
|
local request = LaunchOptions.parseURI(
|
|
"gen1recomp++://launch?game=r&cart=custom_cart&slot=slot%202&sync=0&update=1&launcher=1")
|
|
eq(request.game, "red", "URI aliases normalize to the canonical game")
|
|
eq(request.cart, "custom_cart", "URI cart ids are preserved")
|
|
check(request.cartSpecified, "URI records an explicit cart value")
|
|
eq(request.slot, "slot 2", "URI values are percent-decoded")
|
|
eq(request.sync, false, "sync=0 disables sync")
|
|
eq(request.update, true, "update=1 enables updates")
|
|
eq(request.launcher, true, "launcher=1 forces the launcher")
|
|
check(request.gameSpecified, "URI records an explicit game value")
|
|
end
|
|
|
|
do
|
|
local request = LaunchOptions.parseURI(
|
|
"GEN1RECOMP++://LAUNCH?game=blue&sync=true&update=off")
|
|
eq(request.game, "blue", "URI scheme and host are case-insensitive")
|
|
eq(request.sync, true, "boolean URI values accept true")
|
|
eq(request.update, false, "boolean URI values accept off")
|
|
local direct = LaunchOptions.parseURI("gen1recomp++://launch?game=red")
|
|
eq(direct.launcher, nil, "omitted launcher does not force the launcher")
|
|
eq(direct.sync, nil, "omitted sync keeps the normal sync default")
|
|
eq(direct.update, nil, "omitted update keeps the normal update default")
|
|
eq(LaunchOptions.uriFor("r", { cart = "custom cart", slot = "slot 2" }),
|
|
"gen1recomp++://launch?game=red&slot=slot%202",
|
|
"URI builder ignores invalid cart ids")
|
|
eq(LaunchOptions.uriFor("red", { cart = "custom_cart", slot = "slot 2" }),
|
|
"gen1recomp++://launch?game=red&cart=custom_cart&slot=slot%202",
|
|
"URI builder encodes cart and slot values")
|
|
check(LaunchOptions.parseURI("gen1recomp++://other?game=red") == nil,
|
|
"unknown URI hosts are rejected")
|
|
check(LaunchOptions.parseURI("https://launch?game=red") == nil,
|
|
"unknown URI schemes are rejected")
|
|
check(LaunchOptions.parseURI("gen1recomp++://launch?game=%") == nil,
|
|
"malformed percent escapes are rejected")
|
|
check(LaunchOptions.parseURI("gen1recomp++://launch/red") == nil,
|
|
"path-style URI variants are rejected")
|
|
check(LaunchOptions.isLaunchURI("gen1recomp++://launch?game=%") ,
|
|
"recognized URI authority survives malformed query values")
|
|
end
|
|
|
|
do
|
|
love.system = {
|
|
getOS = function() return "Android" end,
|
|
getLaunchGame = function() return "yellow" end,
|
|
getLaunchURI = function()
|
|
return "gen1recomp++://launch?game=gold&slot=slot9&sync=0&update=1"
|
|
end,
|
|
}
|
|
env.POKEPORT_GAME = "silver"
|
|
env.POKEPORT_SLOT = "slot8"
|
|
env.POKEPORT_LAUNCH_SYNC = "1"
|
|
env.POKEPORT_LAUNCH_UPDATE = "0"
|
|
|
|
local resolved = LaunchOptions.resolveRequest({
|
|
"--game=red", "--slot=slot2", "--no-sync", "--update" }, {})
|
|
eq(resolved.game, "red", "command-line game overrides URI and environment")
|
|
eq(resolved.slot, "slot2", "command-line slot overrides URI and environment")
|
|
eq(resolved.tasks.sync, false, "command-line no-sync overrides URI")
|
|
eq(resolved.tasks.update, true, "command-line update overrides URI")
|
|
|
|
resolved = LaunchOptions.resolveRequest({}, {})
|
|
eq(resolved.game, "gold", "URI game overrides the Android legacy intent and environment")
|
|
eq(resolved.slot, "slot9", "URI slot overrides the environment")
|
|
eq(resolved.tasks.sync, false, "URI sync overrides the environment")
|
|
eq(resolved.tasks.update, true, "URI update overrides the environment")
|
|
|
|
love.system.getLaunchURI = function()
|
|
return "gen1recomp++://launch?game=not-a-game&launcher=0"
|
|
end
|
|
env.POKEPORT_GAME = "silver"
|
|
env.POKEPORT_FORCE_LAUNCHER = "1"
|
|
resolved = LaunchOptions.resolveRequest({}, {})
|
|
eq(resolved.game, nil, "unsupported URI games do not fall through to the environment")
|
|
eq(resolved.launcher, false, "launcher=0 overrides the environment")
|
|
end
|
|
|
|
do
|
|
local uriCalls = 0
|
|
love.system = {
|
|
getLaunchURI = function()
|
|
uriCalls = uriCalls + 1
|
|
if uriCalls == 1 then
|
|
return "gen1recomp++://launch?game=red&sync=0&update=1"
|
|
end
|
|
return nil
|
|
end,
|
|
}
|
|
env.POKEPORT_LAUNCH_SYNC = nil
|
|
env.POKEPORT_LAUNCH_UPDATE = nil
|
|
local resolved = LaunchOptions.resolveRequest({}, {})
|
|
eq(uriCalls, 1, "one launch request consumes one cold-start URI")
|
|
eq(resolved.game, "red", "cold-start URI remains available to boot resolution")
|
|
eq(resolved.tasks.sync, false, "cold-start URI sync reaches preflight")
|
|
eq(resolved.tasks.update, true, "cold-start URI update reaches preflight")
|
|
end
|
|
|
|
do
|
|
love.system.getLaunchURI = function() return nil end
|
|
env.POKEPORT_GAME = nil
|
|
env.POKEPORT_SLOT = nil
|
|
env.POKEPORT_LAUNCH_SYNC = nil
|
|
env.POKEPORT_LAUNCH_UPDATE = nil
|
|
local request = LaunchOptions.fromGame("g")
|
|
eq(request.game, "gold", "legacy Android game intents use shared normalization")
|
|
eq(request.tasks.update, false, "legacy Android intents do not run updates")
|
|
end
|
|
|
|
local function read(path)
|
|
local file = assert(io.open(path, "r"))
|
|
local body = file:read("*a")
|
|
file:close()
|
|
return body
|
|
end
|
|
|
|
local main = read("main.lua")
|
|
local java = read("mobile/android/love/src/main/java/org/love2d/android/GameActivity.java")
|
|
local androidManifest = read("mobile/android/app/src/main/AndroidManifest.xml")
|
|
local plist = read("mobile/ios/overlays/love-ios.plist")
|
|
local artifactWorkflow = read(".github/workflows/platform-artifact-comment.yml")
|
|
|
|
check(main:find("function love.handlers.intent_uri", 1, true) ~= nil,
|
|
"main.lua defines the URI intent handler")
|
|
check(main:find("LaunchOptions.parseURI(filename)", 1, true) ~= nil,
|
|
"iOS SDL URL drop events are parsed before file import")
|
|
check(main:find("startLaunchRequest(request or {})", 1, true) ~= nil,
|
|
"malformed recognized URLs fall back to the launcher")
|
|
check(main:find("tasks = request.tasks or {}", 1, true) ~= nil,
|
|
"URI launches use the shared prelaunch task request")
|
|
check(main:find("LaunchOptions.pollURI()", 1, true) ~= nil,
|
|
"iOS warm URL opens are polled through the shared request path")
|
|
check(java:find("nativeOnLaunchURI", 1, true) ~= nil,
|
|
"Android forwards warm URI intents to native")
|
|
check(java:find("getLaunchURI", 1, true) ~= nil,
|
|
"Android exposes the cold-start URI")
|
|
check(androidManifest:find('android:scheme="gen1recomp++"', 1, true) ~= nil,
|
|
"Android registers the gen1recomp++ scheme")
|
|
check(androidManifest:find('android:host="launch"', 1, true) ~= nil,
|
|
"Android restricts the URI host to launch")
|
|
check(plist:find("CFBundleURLTypes", 1, true) ~= nil,
|
|
"iOS registers URL types")
|
|
check(plist:find("gen1recomp++", 1, true) ~= nil,
|
|
"iOS registers the gen1recomp++ scheme")
|
|
check(artifactWorkflow:find("workflows: [ci]", 1, true) ~= nil,
|
|
"artifact comments are driven by the unified CI workflow")
|
|
check(artifactWorkflow:find("cancel-in-progress: false", 1, true) ~= nil,
|
|
"artifact comment runs are serialized per branch")
|
|
check(artifactWorkflow:find('comment-tag: platform-build-result', 1, true) ~= nil,
|
|
"all platform artifacts use one stable comment tag")
|
|
check(artifactWorkflow:find("gen1recomp-android-apk", 1, true) ~= nil,
|
|
"the unified artifact comment includes Android")
|
|
check(artifactWorkflow:find("gen1recomp-win64", 1, true) ~= nil,
|
|
"the unified artifact comment includes Windows")
|
|
check(artifactWorkflow:find("gen1recomp-linux-x86_64", 1, true) ~= nil,
|
|
"the unified artifact comment includes Linux x86_64")
|
|
local pickerBridge = read("mobile/ios/native/GRPickerBridge.swift")
|
|
local bootstrap = read("mobile/ios/native/GRBootstrap.m")
|
|
local iosPatch = read("mobile/ios/patch_love_src.py")
|
|
local launcherView = read("src/import/LauncherView.lua")
|
|
local webClip = read("src/core/WebClip.lua")
|
|
check(pickerBridge:find("installWebClipWithLabel:url:icon:iconLength:", 1, true) ~= nil,
|
|
"iOS exposes managed Home Screen profile installation")
|
|
check(pickerBridge:find("SFSafariViewController", 1, true) ~= nil,
|
|
"iOS presents the profile through Safari")
|
|
check(bootstrap:find("safari_isHTTPFamilyURL", 1, true) ~= nil,
|
|
"iOS accepts the profile data URL in Safari")
|
|
check(bootstrap:find("UIApplicationLaunchOptionsURLKey", 1, true) ~= nil,
|
|
"iOS captures cold-start launch URLs")
|
|
check(bootstrap:find("GRApplicationOpenURL", 1, true) ~= nil,
|
|
"iOS captures warm launch URLs")
|
|
check(bootstrap:find("GRSceneWillConnect", 1, true) ~= nil,
|
|
"iOS captures scene cold-start URLs")
|
|
check(bootstrap:find("GRSceneOpenURLContexts", 1, true) ~= nil,
|
|
"iOS captures scene warm launch URLs")
|
|
check(iosPatch:find('"installWebClip", w_installWebClip', 1, true) ~= nil,
|
|
"iOS patches the WebClip bridge into love.system")
|
|
check(iosPatch:find('"pollLaunchURI", w_pollLaunchURI', 1, true) ~= nil,
|
|
"iOS exposes a warm launch URI poll")
|
|
check(launcherView:find("LONG_PRESS_SECONDS", 1, true) ~= nil,
|
|
"iOS launcher tracks long presses on ready cartridges")
|
|
check(launcherView:find("Add to Home Screen", 1, true) ~= nil,
|
|
"iOS launcher exposes the Home Screen action")
|
|
check(launcherView:find("Home Screen", 1, true) ~= nil,
|
|
"iOS exposes a Home Screen action for each installed cart")
|
|
check(webClip:find("LaunchOptions.uriFor", 1, true) ~= nil,
|
|
"WebClip URLs use the shared launch URI builder")
|
|
check(webClip:find("Pokémon Red", 1, true) ~= nil,
|
|
"Red Home Screen entries use the accented Pokémon label")
|
|
|
|
os.getenv = savedGetenv
|
|
love.system = savedSystem
|
|
T.finish("URI launch arguments")
|