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:
@@ -0,0 +1,82 @@
|
||||
import std/[json, strutils, sequtils]
|
||||
|
||||
type
|
||||
BotRoundResult* = object
|
||||
name*: string
|
||||
score*: int
|
||||
rank*: int
|
||||
survived*: bool
|
||||
|
||||
RoundResult* = object
|
||||
round*: int
|
||||
results*: seq[BotRoundResult]
|
||||
|
||||
BotResult* = object
|
||||
name*: string
|
||||
totalScore*: int
|
||||
rank*: int
|
||||
firstPlaces*: int
|
||||
survivalCount*: int
|
||||
|
||||
BattleResult* = object
|
||||
bots*: seq[string]
|
||||
rounds*: seq[RoundResult]
|
||||
results*: seq[BotResult]
|
||||
winners*: seq[string] # bots with rank == 1
|
||||
|
||||
proc parseServerOutput*(stdout: string): BattleResult =
|
||||
for line in stdout.splitLines():
|
||||
let s = line.strip()
|
||||
if s.len == 0 or s[0] != '{': continue
|
||||
let node = parseJson(s)
|
||||
case node["event"].getStr()
|
||||
of "game_started":
|
||||
result.bots = node["bots"].getElems().mapIt(it.getStr())
|
||||
of "round_ended":
|
||||
var rr = RoundResult(round: node["round"].getInt())
|
||||
for r in node["results"].getElems():
|
||||
rr.results.add BotRoundResult(
|
||||
name: r["name"].getStr(),
|
||||
score: r["score"].getInt(),
|
||||
rank: r["rank"].getInt(),
|
||||
survived: r["survived"].getBool()
|
||||
)
|
||||
result.rounds.add rr
|
||||
of "battle_ended":
|
||||
for r in node["results"].getElems():
|
||||
result.results.add BotResult(
|
||||
name: r["name"].getStr(),
|
||||
totalScore: r["totalScore"].getInt(),
|
||||
rank: r["rank"].getInt(),
|
||||
firstPlaces: r["firstPlaces"].getInt(),
|
||||
survivalCount: r["survivalCount"].getInt()
|
||||
)
|
||||
result.winners = result.results.filterIt(it.rank == 1).mapIt(it.name)
|
||||
else: discard
|
||||
|
||||
when isMainModule:
|
||||
const sample = """
|
||||
{"event":"game_started","bots":["Alpha","Beta"]}
|
||||
{"event":"round_ended","round":1,"results":[{"name":"Alpha","score":150,"rank":1,"survived":true},{"name":"Beta","score":80,"rank":2,"survived":false}]}
|
||||
{"event":"round_ended","round":2,"results":[{"name":"Beta","score":200,"rank":1,"survived":true},{"name":"Alpha","score":60,"rank":2,"survived":false}]}
|
||||
{"event":"battle_ended","results":[{"name":"Alpha","totalScore":500,"rank":1,"firstPlaces":3,"survivalCount":5},{"name":"Beta","totalScore":300,"rank":2,"firstPlaces":1,"survivalCount":2}]}
|
||||
|
||||
not json, should be ignored
|
||||
"""
|
||||
let br = parseServerOutput(sample)
|
||||
assert br.bots == @["Alpha", "Beta"]
|
||||
assert br.rounds.len == 2
|
||||
assert br.rounds[0].round == 1
|
||||
assert br.rounds[0].results[0].name == "Alpha"
|
||||
assert br.rounds[0].results[0].score == 150
|
||||
assert br.rounds[0].results[0].rank == 1
|
||||
assert br.rounds[0].results[0].survived == true
|
||||
assert br.rounds[1].results[0].name == "Beta"
|
||||
assert br.rounds[1].results[1].survived == false
|
||||
assert br.results.len == 2
|
||||
assert br.results[0].name == "Alpha"
|
||||
assert br.results[0].totalScore == 500
|
||||
assert br.results[0].firstPlaces == 3
|
||||
assert br.results[0].survivalCount == 5
|
||||
assert br.winners == @["Alpha"]
|
||||
echo "all assertions passed"
|
||||
Reference in New Issue
Block a user