7e1f1483a4
- Document exception handling, zero-value BotResult trap, shared adversary bots - Add offline parsing example using parseServerOutput - Skip tests gracefully when JARs missing (guard before suite blocks) - Fix blocking readLine in runner_process.nim: poll with 50ms sleep + atEnd check (was preventing timeout enforcement, now blocks correctly during battle) - Add test task to QBot.nimble and config.nims setup docs to AGENTS.md - Add debug logging to TestBattleRunner for bot identity tracking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
2.1 KiB
Nim
63 lines
2.1 KiB
Nim
## BattleRunner Java subprocess — capture stdout JSON-lines output.
|
|
|
|
import std/[osproc, os, strutils, streams, times, net]
|
|
|
|
const
|
|
DefaultRunnerJar = "/home/davide/Projects/tank-royale/runner/examples/lib/robocode-tankroyale-runner.jar"
|
|
ScriptDir = currentSourcePath().parentDir() / ".." / ".." / "tools" / "battle_runner"
|
|
|
|
proc runBattleRunner*(serverUrl: string, botDirs: seq[string], rounds: int,
|
|
timeout: int = 30000): string =
|
|
## Starts TestBattleRunner as a subprocess. Returns captured stdout on success.
|
|
## Raises IOError on non-zero exit or TimeoutError on timeout.
|
|
let jar = getEnv("TR_BATTLE_RUNNER", DefaultRunnerJar)
|
|
let classDir = getEnv("TR_BATTLE_RUNNER_DIR", ScriptDir)
|
|
|
|
let args = @[
|
|
"-cp", classDir & ":" & jar,
|
|
"TestBattleRunner",
|
|
"--server-url", serverUrl,
|
|
"--rounds", $rounds,
|
|
"--bot-dirs", botDirs.join(",")
|
|
]
|
|
|
|
let p = startProcess("java", args = args,
|
|
options = {poUsePath, poStdErrToStdOut})
|
|
defer:
|
|
p.close()
|
|
|
|
let deadline = epochTime() + timeout.float / 1000.0
|
|
var stdout = ""
|
|
var line: string
|
|
# ponytail: poll instead of blocking readLine; 50ms sleep is fine for battle timescales
|
|
while epochTime() < deadline:
|
|
if p.peekExitCode() != -1:
|
|
# Process finished; drain remaining output without blocking
|
|
while p.outputStream.atEnd == false:
|
|
discard p.outputStream.readLine(line)
|
|
stdout.add(line & "\n")
|
|
break
|
|
sleep(50)
|
|
|
|
if epochTime() >= deadline and p.peekExitCode() == -1:
|
|
p.terminate()
|
|
raise newException(TimeoutError,
|
|
"BattleRunner timed out after " & $timeout & "ms")
|
|
|
|
let code = p.waitForExit()
|
|
if code != 0:
|
|
raise newException(IOError,
|
|
"BattleRunner exited with code " & $code & "\nOutput:\n" & stdout)
|
|
|
|
result = stdout
|
|
|
|
when isMainModule:
|
|
echo "runner_process.nim — usage:"
|
|
echo " runBattleRunner(serverUrl, botDirs, rounds, timeout)"
|
|
echo ""
|
|
echo "Env vars:"
|
|
echo " TR_BATTLE_RUNNER JAR path (default: " & DefaultRunnerJar & ")"
|
|
echo " TR_BATTLE_RUNNER_DIR class dir (default: tools/battle_runner/)"
|
|
echo ""
|
|
echo "Cannot self-test without a running TR server. Start one first."
|