docs(test_framework): add comprehensive integration test guide and fix runner blocking issue
- 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>
This commit is contained in:
@@ -143,10 +143,56 @@ BotRoundResult = object
|
||||
survived*: bool
|
||||
```
|
||||
|
||||
### Compilation behavior
|
||||
|
||||
`compileBots` runs on every `runBattle()` call — no caching. Each test that calls `runBattle()` recompiles all bots. Compile timeout is **30s per bot**, hardcoded, independent of `runBattle()`'s `timeout` parameter.
|
||||
|
||||
### Exceptions
|
||||
|
||||
`runBattle()` raises:
|
||||
|
||||
| Exception | Cause |
|
||||
|---|---|
|
||||
| `OSError` | Bot compilation failed — compiler output included in message |
|
||||
| `IOError` | Battle runner exited non-zero |
|
||||
| `TimeoutError` | Any timeout: server startup (15s), compilation (30s/bot), battle runner (per `timeout` param) |
|
||||
|
||||
```nim
|
||||
try:
|
||||
let r = runBattle(@[myBotDir, sittingDuck], rounds = 3)
|
||||
check "MyBot" in r.winners
|
||||
except OSError as e:
|
||||
echo "Compile failed: ", e.msg
|
||||
except IOError as e:
|
||||
echo "Battle runner failed: ", e.msg
|
||||
except TimeoutError as e:
|
||||
echo "Timed out: ", e.msg
|
||||
```
|
||||
|
||||
### Zero-value `BotResult` trap
|
||||
|
||||
Searching `r.results` by name returns a zero-initialized `BotResult` if the name doesn't match — no error raised. Guard against it:
|
||||
|
||||
```nim
|
||||
var myBot: BotResult
|
||||
for b in r.results:
|
||||
if b.name == "MyBot": myBot = b
|
||||
check myBot.name == "MyBot" # catches name mismatch / missing bot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing adversary bots
|
||||
|
||||
### Shared adversary bots
|
||||
|
||||
Pre-built adversaries (SittingDuck, OscillatorBot) live at `common_libs/test_framework/adversaries/` — import them directly, no need to copy per garage:
|
||||
|
||||
```nim
|
||||
const sittingDuck = currentSourcePath().parentDir.parentDir.parentDir /
|
||||
"common_libs" / "test_framework" / "adversaries" / "SittingDuck"
|
||||
```
|
||||
|
||||
Minimal bot — does nothing, useful as a baseline target:
|
||||
|
||||
**`tests/bots/SittingDuck/src/SittingDuck.nim`**
|
||||
@@ -184,6 +230,37 @@ Place adversary bots under `tests/bots/<BotName>/src/`. The framework infers the
|
||||
|
||||
---
|
||||
|
||||
## Testing parsing offline
|
||||
|
||||
`parseServerOutput` is exported from `battle_result.nim` and can be imported directly. Use it for fast offline tests — no Java server needed:
|
||||
|
||||
```nim
|
||||
import test_framework/battle_result
|
||||
|
||||
const fixture = """
|
||||
{"event":"game_started","bots":["MyBot","Enemy"]}
|
||||
{"event":"round_ended","round":1,"results":[{"name":"MyBot","score":200,"rank":1,"survived":true},{"name":"Enemy","score":0,"rank":2,"survived":false}]}
|
||||
{"event":"battle_ended","results":[{"name":"MyBot","totalScore":200,"rank":1,"firstPlaces":1,"survivalCount":1},{"name":"Enemy","totalScore":0,"rank":2,"firstPlaces":0,"survivalCount":0}]}
|
||||
"""
|
||||
let r = parseServerOutput(fixture)
|
||||
assert r.winners == @["MyBot"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skipping when JARs are unavailable
|
||||
|
||||
```nim
|
||||
import std/os
|
||||
if not existsEnv("TR_SERVER_JAR") or not existsEnv("TR_BATTLE_RUNNER"):
|
||||
echo "Skipping integration tests: TR_SERVER_JAR / TR_BATTLE_RUNNER not set"
|
||||
quit(0)
|
||||
```
|
||||
|
||||
Put this at the top of your test file, before any `suite` blocks.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`TR server JAR not found`**
|
||||
|
||||
Reference in New Issue
Block a user