mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
318 lines
12 KiB
Lua
318 lines
12 KiB
Lua
#!/usr/bin/env luajit
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local FakeRelay = require("tests.g3link_fake_relay")
|
|
|
|
local store = { flags = {}, vars = {} }
|
|
local session = { store = store, name = "RED", trainerId = 0x12345, gender = 1, party = {},
|
|
map = "FR_VIRIDIAN_CITY_POKEMON_CENTER_2F" }
|
|
local input = { pressed = {} }
|
|
function input:wasPressed(k) return self.pressed[k] == true end
|
|
local game = { data = {}, save = { player = { name = "RED" }, options = {} }, input = input,
|
|
session = session }
|
|
|
|
package.loaded["src.core.game3.runtime"] = {
|
|
getSession = function() return session end,
|
|
isActive = function() return true end,
|
|
_game = game,
|
|
}
|
|
|
|
local romBundle = require("tests.game3_cache").bundle()
|
|
if not romBundle then
|
|
package.loaded["src.core.game3.rom_text"] = {
|
|
plain = function(key) return key end, box = function(key) return key end,
|
|
ascii = function(key) return key end, has = function() return true end,
|
|
ir = function(key) return { { t = "text", s = key } } end,
|
|
key = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end,
|
|
at = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end,
|
|
count = function() return 0 end, list = function() return {} end,
|
|
lazy = function(map) return setmetatable({}, { __index = function(_, k) return map[k] end }) end,
|
|
}
|
|
end
|
|
|
|
local ctx = { specialVars = {}, stringVars = {} }
|
|
local adapters = { log = function() end }
|
|
package.loaded["src.core.game3.scripting.space"] = {
|
|
store = store,
|
|
mapId = "FR_VIRIDIAN_CITY_POKEMON_CENTER_2F",
|
|
vm = { ctx = ctx, adapters = adapters },
|
|
ensureBundle = function() return romBundle end,
|
|
}
|
|
|
|
local arena = { why = nil, calls = 0 }
|
|
local LIVE = { engine = 3, engineVersion = "1.0.0", fingerprint = "f00dcafe", kind = "vanilla",
|
|
version = "firered" }
|
|
package.loaded["src.online.ArenaData"] = {
|
|
liveProfile3 = function(g, rulesetId)
|
|
arena.calls = arena.calls + 1
|
|
arena.game = g
|
|
if arena.why then return nil, arena.why end
|
|
local p = {}
|
|
for k, v in pairs(LIVE) do p[k] = v end
|
|
p.rulesetId = rulesetId
|
|
return p
|
|
end,
|
|
}
|
|
|
|
local Client = FakeRelay.client({ state = "offline" })
|
|
package.loaded["src.online.Client"] = Client
|
|
|
|
local Connect = { starts = {}, _state = "offline", _error = nil }
|
|
function Connect.start(opts)
|
|
Connect.starts[#Connect.starts + 1] = opts
|
|
if Connect.failStart then return false, Connect.failStart end
|
|
if Client._state == "online" then
|
|
Client.setProfiles(opts.profiles)
|
|
return true
|
|
end
|
|
Connect._state = "connecting"
|
|
return true
|
|
end
|
|
function Connect.state() return Connect._state end
|
|
function Connect.error() return Connect._error end
|
|
function Connect.disconnect() Connect._state = "offline" Connect.disconnected = true end
|
|
package.loaded["src.online.Connect"] = Connect
|
|
|
|
local Natives = require("src.core.game3.scripting.natives")
|
|
local NativesLink = require("src.core.game3.scripting.natives_link")
|
|
local Link = require("src.core.game3.link")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
|
|
local function result()
|
|
return tonumber(Flags.getVar(store, ctx, Link.VAR_RESULT)) or 0
|
|
end
|
|
|
|
local function adapter()
|
|
Flags.setVar(store, ctx, Link.VAR_RESULT, 7)
|
|
return Natives.special(ctx, NativesLink.SPECIAL.IsWirelessAdapterConnected, adapters)
|
|
end
|
|
|
|
local function reset()
|
|
Link.reset()
|
|
Client._state = "offline"
|
|
Client._profiles = {}
|
|
Client.clear()
|
|
Connect.starts = {}
|
|
Connect._state = "offline"
|
|
Connect._error = nil
|
|
Connect.failStart = nil
|
|
Connect.disconnected = nil
|
|
arena.why = nil
|
|
ctx.nativePoll = nil
|
|
input.pressed = {}
|
|
end
|
|
|
|
print("[test] 1. headless and offline the adapter reads not connected")
|
|
reset()
|
|
local yielded, value = adapter()
|
|
eq(yielded, false, "no prompt without a screen")
|
|
eq(value, 0, "IsWirelessAdapterConnected is FALSE")
|
|
eq(result(), 0, "VAR_RESULT is FALSE")
|
|
eq(#Connect.starts, 0, "nothing tried to connect")
|
|
|
|
print("[test] 2. online with the live Gen 3 profile the adapter is connected")
|
|
reset()
|
|
Client._state = "online"
|
|
Client._profiles = { { engine = 1, fingerprint = "gen1" }, LIVE }
|
|
yielded, value = adapter()
|
|
eq(value, 1, "TRUE")
|
|
eq(result(), 1, "VAR_RESULT is TRUE")
|
|
eq(arena.game, game, "the profile is computed from the live game")
|
|
Client._profiles = { { engine = 3, fingerprint = "someone-else", engineVersion = "1.0.0" } }
|
|
Link._live = nil
|
|
eq(Link.adapterConnected(), false, "a Gen 3 profile with another fingerprint is not accepted")
|
|
|
|
print("[test] 3. online without the profile, the live profile is added and the adapter connects")
|
|
reset()
|
|
Client._state = "online"
|
|
yielded, value = adapter()
|
|
eq(value, 1, "TRUE after Connect.start adds the profile")
|
|
eq(#Connect.starts, 1, "Connect.start ran once (already-online path)")
|
|
local opts = Connect.starts[1] or {}
|
|
eq(opts.source, "game", "source is game")
|
|
eq(opts.version, "firered", "version is the running cart")
|
|
eq(opts.trainerName, "RED", "trainer name from the save")
|
|
eq(opts.profiles and opts.profiles[1] and opts.profiles[1].fingerprint, LIVE.fingerprint,
|
|
"the one profile is the live g3 profile")
|
|
eq(opts.profiles and opts.profiles[1] and opts.profiles[1].rulesetId, "g3_link", "for g3_link")
|
|
eq(opts.presence and opts.presence.where, "game", "presence where is game")
|
|
eq(opts.presence and opts.presence.status, "busy", "presence status is busy")
|
|
|
|
print("[test] 4. offline with a screen: prompt, YES, Connecting..., online")
|
|
reset()
|
|
love = { graphics = {} }
|
|
local Message = require("src.ui.game3.message")
|
|
local Choice = require("src.ui.game3.choice")
|
|
local Strings = require("src.core.Strings")
|
|
yielded = adapter()
|
|
eq(yielded, true, "the script parks on the prompt")
|
|
check(Message.isOpen(), "a message is up")
|
|
eq(Message.currentPage(), Strings("Connect to the Wireless Club?"), "asking to connect")
|
|
for _ = 1, 300 do Message.tick() end
|
|
eq(ctx.nativePoll(), false, "still waiting")
|
|
check(Choice.isOpen(), "YES/NO is offered once the text printed")
|
|
Choice.cursor = 1
|
|
Choice.confirm()
|
|
eq(#Connect.starts, 1, "YES starts the connection")
|
|
eq(Message.currentPage(), Strings("Connecting..."), "Connecting... is shown")
|
|
eq(Message._stay, true, "without a prompt arrow")
|
|
eq(ctx.nativePoll(), false, "waits while connecting")
|
|
Connect._state = "online"
|
|
Client._state = "online"
|
|
Client._profiles = { LIVE }
|
|
eq(ctx.nativePoll(), true, "resumes once online with the profile")
|
|
eq(result(), 1, "VAR_RESULT is TRUE")
|
|
check(not Message.isOpen(), "the message closed")
|
|
|
|
print("[test] 5. NO leaves the adapter unconnected")
|
|
reset()
|
|
Message.reset()
|
|
adapter()
|
|
for _ = 1, 300 do Message.tick() end
|
|
ctx.nativePoll()
|
|
Choice.cursor = 2
|
|
Choice.confirm()
|
|
eq(ctx.nativePoll(), true, "the script resumes")
|
|
eq(result(), 0, "VAR_RESULT is FALSE")
|
|
eq(#Connect.starts, 0, "nothing connected")
|
|
|
|
print("[test] 6. a modded game names the reason and stays unconnected")
|
|
reset()
|
|
Message.reset()
|
|
arena.why = "mods"
|
|
adapter()
|
|
for _ = 1, 300 do Message.tick() end
|
|
ctx.nativePoll()
|
|
Choice.cursor = 1
|
|
Choice.confirm()
|
|
eq(#Connect.starts, 0, "no connection is attempted")
|
|
eq(Message.currentPage() ~= nil and Message.currentPage():find("Mods", 1, true) ~= nil, true,
|
|
"the reason line names the mods")
|
|
eq(ctx.nativePoll(), false, "waits for the player to read it")
|
|
for _ = 1, 300 do Message.tick() end
|
|
Message.advance()
|
|
eq(ctx.nativePoll(), true, "then resumes")
|
|
eq(result(), 0, "VAR_RESULT is FALSE")
|
|
|
|
print("[test] 7. a failed connection names the error")
|
|
reset()
|
|
Message.reset()
|
|
adapter()
|
|
for _ = 1, 300 do Message.tick() end
|
|
ctx.nativePoll()
|
|
Choice.cursor = 1
|
|
Choice.confirm()
|
|
Connect._state = "error"
|
|
Connect._error = "This build is too old for online play. Please update."
|
|
ctx.nativePoll()
|
|
check(Message.currentPage():find("too old", 1, true) ~= nil, "the relay's text is shown")
|
|
for _ = 1, 4 do
|
|
for _ = 1, 300 do Message.tick() end
|
|
Message.advance()
|
|
end
|
|
eq(ctx.nativePoll(), true, "then the script resumes")
|
|
eq(result(), 0, "VAR_RESULT is FALSE")
|
|
|
|
print("[test] 8. B while connecting cancels")
|
|
reset()
|
|
Message.reset()
|
|
adapter()
|
|
for _ = 1, 300 do Message.tick() end
|
|
ctx.nativePoll()
|
|
Choice.cursor = 1
|
|
Choice.confirm()
|
|
input.pressed = { b = true }
|
|
eq(ctx.nativePoll(), true, "B resumes the script")
|
|
eq(Connect.disconnected, true, "and drops the connection attempt")
|
|
eq(result(), 0, "VAR_RESULT is FALSE")
|
|
input.pressed = {}
|
|
|
|
print("[test] 9. the wireless monitor subscribes to plaza counts while it is open")
|
|
reset()
|
|
Message.reset()
|
|
Client._state = "online"
|
|
Client._profiles = { LIVE }
|
|
Client._counts = { union = 3, trade = 2, battle = 4, chat = 1, minigame = 0, total = 9 }
|
|
local LinkMenu = require("src.ui.game3.link_menu")
|
|
local Status = require("src.core.game3.link.status")
|
|
love = nil
|
|
LinkMenu.show({})
|
|
local join = Client.last("joinPlaza")
|
|
eq(join and join[1], "wireless", "the monitor joins the wireless plaza")
|
|
eq(join and join[3] and join[3].name, "RED", "with the cart OT name")
|
|
eq(join and join[3] and join[3].trainerId, 0x12345 % 65536, "a 16-bit trainer id")
|
|
eq(join and join[3] and join[3].gender, 1, "the gender")
|
|
eq(join and join[3] and join[3].version, "firered", "and the version")
|
|
local rows = LinkMenu.rows
|
|
eq(rows[Status.GROUPTYPE.TRADE].count, 2, "TRADE row = plaza_counts.trade")
|
|
eq(rows[Status.GROUPTYPE.BATTLE].count, 4, "BATTLE row = plaza_counts.battle")
|
|
eq(rows[Status.GROUPTYPE.UNION].count, 3, "UNION row = plaza_counts.union")
|
|
eq(rows[Status.GROUPTYPE.TOTAL].count, 9, "TOTAL row = plaza_counts.total")
|
|
Client._counts = { union = 5, trade = 0, battle = 0, total = 5 }
|
|
for _ = 1, LinkMenu.ROWS_FRAMES + 1 do LinkMenu.update(1 / 60) end
|
|
eq(LinkMenu.rows[Status.GROUPTYPE.UNION].count, 5, "rows refresh from new counts")
|
|
LinkMenu.close()
|
|
eq(Client.last("leavePlaza") and Client.last("leavePlaza")[1], "wireless", "closing leaves the plaza")
|
|
|
|
reset()
|
|
LinkMenu.show({})
|
|
eq(Client.count("joinPlaza"), 0, "offline the monitor joins nothing")
|
|
eq(LinkMenu.rows[Status.GROUPTYPE.TOTAL].count, 0, "and counts nothing")
|
|
LinkMenu.close()
|
|
eq(Client.count("leavePlaza"), 0, "nor leaves anything")
|
|
|
|
print("[test] 10. the wireless icon follows the connection state")
|
|
local WirelessIcon = require("src.ui.game3.wireless_icon")
|
|
WirelessIcon.reset()
|
|
Connect._state = "offline"
|
|
Client._state = "offline"
|
|
eq(WirelessIcon.anim(), nil, "offline: hidden")
|
|
Connect._state = "connecting"
|
|
eq(WirelessIcon.anim(), "searching", "connecting: searching")
|
|
Connect._state = "reconnecting"
|
|
eq(WirelessIcon.anim(), "searching", "reconnecting: searching")
|
|
Connect._state = "online"
|
|
eq(WirelessIcon.anim(), "3bars", "online: three bars")
|
|
Connect._state = "error"
|
|
eq(WirelessIcon.anim(), "error", "error: error")
|
|
-- pokefirered/src/link_rfu_3.c:448
|
|
local bars = { [0] = 1, [4] = 1, [5] = 2, [10] = 3, [15] = 4, [24] = 4, [25] = 3, [30] = 2, [35] = 1 }
|
|
for f, frame in pairs(bars) do
|
|
eq(WirelessIcon.frameFor("3bars", f), frame, "3 bars at frame " .. f)
|
|
end
|
|
eq(WirelessIcon.frameFor("searching", 0), 1, "searching starts on frame 1")
|
|
eq(WirelessIcon.frameFor("searching", 10), 5, "then frame 5")
|
|
eq(WirelessIcon.frameFor("error", 0), 6, "error starts on frame 6")
|
|
eq(WirelessIcon.frameFor("error", 10), 1, "then frame 1")
|
|
Connect._state = "online"
|
|
WirelessIcon.frame()
|
|
WirelessIcon.update(10 / 60)
|
|
eq(WirelessIcon.frame(), 3, "update(dt) advances the 60 Hz counter")
|
|
check(WirelessIcon.onLinkMap("FR_VIRIDIAN_CITY_POKEMON_CENTER_2F"), "shown on a Pokemon Center 2F")
|
|
check(WirelessIcon.onLinkMap("FR_UNION_ROOM"), "and in the Union Room")
|
|
check(not WirelessIcon.onLinkMap("FR_PALLET_TOWN"), "not out in the field")
|
|
eq(WirelessIcon.X, 231, "sprite centre x 231")
|
|
eq(WirelessIcon.Y, 8, "sprite centre y 8")
|
|
|
|
reset()
|
|
if failed == 0 then
|
|
print("[pass] link adapter")
|
|
os.exit(0)
|
|
end
|
|
print("[fail] link adapter: " .. failed)
|
|
os.exit(1)
|