mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-24 01:45:58 +02:00
- skater.read: reskate.skater.get() (position, heading, speed, vertical speed, airborne, air time) and position, heading, speed and on_ground on their own; nil without a skater. From the trainer's telemetry. The heading is radians, -pi to pi: 0 faces +Z, pi/2 faces +X, and a higher heading is turned to the skater's left (checked in game). - skater.teleport: reskate.skater.teleport(at, heading?) through teleport_local_skater_heading; false and why when the host has turned teleporting off (as the console's tp), there is no skater, or it is asked more than twice a second. Positions must be finite and within 100 km. - world.raycast: reskate.world.raycast(from, to) is where the line first meets the world's collision (local_surface_hit), or nil; 64 a tick, 1000 m at most. - The host passes the game to plugins through Environment, so the tests fake a skater and flat ground. reskate-test has a skater module (skipped without a skater; the harness counts skips) and ptest tp; reskate-hostile has teleport-spam and raycast-flood. Checked in game: teleporting, the two-a-second cap and the raycast cap. ptest skater's heading check failed there (0 to 2 pi from the trainer) and is fixed, not yet rerun in game.
118 lines
3.9 KiB
Lua
118 lines
3.9 KiB
Lua
-- The test runner: each file in checks/ is a module of checks, and `ptest` runs them. A check
|
|
-- passes when its function returns without an error, and is skipped when it returns h.skip(why)
|
|
-- (it needs something there is not now, such as a skater); a later check passes when it calls
|
|
-- done(true) in time. Only failures and the summary are printed, to stay under the 50 lines a second a plugin has.
|
|
local harness = {}
|
|
local modules, order = {}, {}
|
|
local running = nil
|
|
|
|
function harness.module(name)
|
|
if not modules[name] then
|
|
modules[name] = {}
|
|
table.insert(order, name)
|
|
end
|
|
local list = modules[name]
|
|
local m = {}
|
|
function m.check(title, fn)
|
|
table.insert(list, {title = title, fn = fn})
|
|
end
|
|
-- fn(done): call done(ok, why) within `within` ms of the run starting.
|
|
function m.later(title, within, fn)
|
|
table.insert(list, {title = title, fn = fn, within = within})
|
|
end
|
|
return m
|
|
end
|
|
|
|
-- What the call raised; an error when it was allowed.
|
|
function harness.refused(fn, ...)
|
|
local ok, err = pcall(fn, ...)
|
|
if ok then error("was allowed", 2) end
|
|
return tostring(err)
|
|
end
|
|
|
|
local skip_mark = {}
|
|
function harness.skip(why)
|
|
return {[skip_mark] = true, why = why}
|
|
end
|
|
|
|
function harness.names()
|
|
return table.clone(order)
|
|
end
|
|
|
|
function harness.has(name)
|
|
return modules[name] ~= nil
|
|
end
|
|
|
|
local function finish(run)
|
|
running = nil
|
|
local shown = 0
|
|
for _, line in run.failures do
|
|
shown += 1
|
|
if shown > 20 then
|
|
reskate.console.print("FAIL ... and " .. (#run.failures - 20) .. " more")
|
|
break
|
|
end
|
|
reskate.console.print("FAIL " .. line)
|
|
end
|
|
local parts = {}
|
|
for _, name in run.names do
|
|
local tally = run.per[name]
|
|
table.insert(parts, name .. " " .. tally.passed .. "/" .. tally.total)
|
|
end
|
|
local skipped = run.skipped > 0 and (", " .. run.skipped .. " skipped (" .. table.concat(run.skips, "; ") .. ")") or ""
|
|
reskate.console.print("ptest: " .. run.passed .. " passed, " .. run.failed .. " failed" .. skipped .. " (" .. table.concat(parts, ", ") .. ")")
|
|
end
|
|
|
|
local function record(run, name, title, ok, why)
|
|
local tally = run.per[name]
|
|
if ok then
|
|
run.passed += 1
|
|
tally.passed += 1
|
|
else
|
|
run.failed += 1
|
|
table.insert(run.failures, name .. ": " .. title .. ": " .. tostring(why or "failed"))
|
|
end
|
|
end
|
|
|
|
function harness.run(names)
|
|
if running then
|
|
reskate.console.print("ptest: a run is still going on")
|
|
return
|
|
end
|
|
local run = {names = names, passed = 0, failed = 0, skipped = 0, pending = 0, failures = {}, skips = {}, per = {}}
|
|
running = run
|
|
for _, name in names do
|
|
run.per[name] = {passed = 0, total = #modules[name]}
|
|
end
|
|
for _, name in names do
|
|
for _, item in modules[name] do
|
|
if item.within then
|
|
run.pending += 1
|
|
local finished = false
|
|
local function done(ok, why)
|
|
if finished then return end
|
|
finished = true
|
|
record(run, name, item.title, ok, why)
|
|
run.pending -= 1
|
|
if run.pending == 0 then finish(run) end
|
|
end
|
|
reskate.time.after(item.within, function() done(false, "did not finish within " .. item.within .. " ms") end)
|
|
local ok, err = pcall(item.fn, done)
|
|
if not ok then done(false, err) end
|
|
else
|
|
local ok, err = pcall(item.fn)
|
|
if ok and type(err) == "table" and err[skip_mark] then
|
|
run.skipped += 1
|
|
run.per[name].total -= 1
|
|
table.insert(run.skips, name .. ": " .. item.title .. ": " .. err.why)
|
|
else
|
|
record(run, name, item.title, ok, err)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if run.pending == 0 then finish(run) end
|
|
end
|
|
|
|
return harness
|