feat: add shared integration test framework for garages (#126)

Implements:
- BattleResult type and JSON-lines parser (#127)
- TR server lifecycle manager (#128)
- Bot compiler using nim c (#129)
- runBattle() orchestrator (#130)
- Example test in OscillatorBot_garage (#131)
- Framework usage guide (#132)
- TestBattleRunner.java for external server (#133)
- BattleRunner process lifecycle (#134)

Fix: runner_process.nim was redefining TimeoutError locally; now
uses std/net.TimeoutError consistently with server_manager.nim.
This commit is contained in:
2026-08-30 12:03:35 +02:00
parent a50571e6da
commit 57a1915b10
13 changed files with 654 additions and 2 deletions
@@ -0,0 +1,67 @@
## TR server lifecycle manager — lazy singleton for integration tests.
## Start with ensureServer(), query with getServerUrl().
import std/[osproc, net, os, strutils, times, streams, exitprocs]
const
DefaultJar = "/home/davide/Projects/tank-royale/server/build/libs/robocode-tankroyale-server-0.35.5-all.jar"
ReadySignal = "Starting server on port"
StartupTimeout = 15.0 # seconds
var
serverProc: Process = nil
serverPort: int = 0
proc findFreePort(): int =
let s = newSocket()
s.bindAddr(Port(0))
let (_, port) = s.getLocalAddr()
result = port.int
s.close()
proc ensureServer*() =
if serverProc != nil: return
let jar = getEnv("TR_SERVER_JAR", DefaultJar)
if not fileExists(jar):
raise newException(IOError, "TR server JAR not found: " & jar &
"\nSet TR_SERVER_JAR env var to override.")
serverPort = findFreePort()
serverProc = startProcess("java",
args = @["-jar", jar, "--port", $serverPort, "--enable-initial-position"],
options = {poUsePath, poStdErrToStdOut})
exitprocs.addExitProc(proc() =
if serverProc != nil:
serverProc.terminate()
serverProc.close()
)
# Wait for ready signal
let deadline = epochTime() + StartupTimeout
var ready = false
while epochTime() < deadline:
var line: string
discard serverProc.outputStream.readLine(line)
if ReadySignal in line:
ready = true
break
if not ready:
serverProc.terminate()
serverProc.close()
serverProc = nil
raise newException(TimeoutError, "TR server did not start within " & $StartupTimeout & "s")
proc getServerUrl*(): string =
if serverPort == 0:
raise newException(CatchableError, "Server not started — call ensureServer() first")
"ws://localhost:" & $serverPort
when isMainModule:
echo "Starting TR server..."
ensureServer()
echo "Server ready at: ", getServerUrl()
echo "Sleeping 2s then exiting (addExitProc will kill server)..."
os.sleep(2000)
echo "Done."