Skip to content
Léon Fievet edited this page Jul 7, 2026 · 4 revisions

CLI

The atlas dotnet tool (NuGet package Pixnop.Atlas.Cli) runs the Atlas scenarios of a compiled test assembly without VSTest, through the same in-process xunit runner dotnet test would use. It builds nothing: point it at the compiled test assembly.

dotnet tool install -g Pixnop.Atlas.Cli

atlas run bin/Debug/net10.0/MyMod.Scenarios.dll              # run everything, sequentially
atlas run bin/Debug/net10.0/MyMod.Scenarios.dll --filter Chest
atlas run bin/Debug/net10.0/MyMod.Scenarios.dll --list       # discover only, no server boot
atlas run bin/Debug/net10.0/MyMod.Scenarios.dll --parallel   # multi-process, one class per worker

run needs VINTAGE_STORY set to the Vintage Story install directory, same as dotnet test (see Getting Started). The variable is validated up front, so a missing install fails fast at the CLI boundary instead of deep inside the first scenario's fixture.

atlas run

Boots the embedded server in-process and executes the assembly's [AtlasScenario] methods sequentially, exactly like dotnet test would: same server, same world lifecycle, one live server per process. Output is a per-scenario PASS/FAIL line with duration, plus a summary.

Exit codes (same contract in every mode):

Code Meaning
0 Every scenario passed and at least one ran
1 At least one failure or runner error, or nothing ran (an empty run counts as a failure, so a typo'd filter cannot go green in CI)
2 Environment or usage error (VINTAGE_STORY missing, bad arguments)

--filter <substring>

Runs only the scenarios whose display name contains the substring (ordinal, case-insensitive). Display names are the same fully qualified names dotnet test reports. Combines with every mode, including --list and --parallel.

--list

Prints the discovered scenarios and exits without booting anything; VINTAGE_STORY is not required. Useful to sanity-check a --filter before paying for a server boot.

Worker mode: --worker

atlas run <dll> --worker runs exactly like plain run (one process, sequential, same exit codes) but reports exclusively as line-delimited JSON events on stdout: run-start, class-start, test-pass/test-fail/test-skip, class-end, error, run-end, every line versioned with v: 1. All human and engine chatter (the embedded server logs to the console) is rerouted to stderr, and a fail-safe guarantees the stream always ends with a well-formed run-end even when the run crashes.

  • --classes <A,B> (worker mode only; a usage error without --worker) restricts the run to the given scenario classes, comma-separated fully qualified names, exact match.
  • atlas run <dll> --list --worker performs discovery only: one discovered event per scenario, no server boot, VINTAGE_STORY not required.

Worker mode is the seam the --parallel orchestrator drives, and any tool that wants machine-readable Atlas results can consume it too. The full protocol contract (transport, versioning rules, every event's fields, an example transcript) lives in the repo: docs/specs/2026-07-06-worker-protocol.md.

Parallel execution: --parallel [N]

atlas run <dll> --parallel orchestrates the assembly's scenario classes over N worker subprocesses. The orchestrator discovers the classes without booting anything, then drains a greedy per-class queue: each worker is one atlas run <dll> --worker --classes <class> subprocess (one live server per worker, one class per dispatch), and workers pull the next class as they free up. Results stream back over the worker protocol and print live, per test; the final summary adds per-class wall clocks and the measured speedup versus the sum of class times (what running the classes back to back would have cost).

Default N. Without an explicit count, N is min(cores / 2, class count), always at least 1. Half the cores because each embedded server wants roughly two cores before workers start slowing each other down; capped at the class count because a worker without a class to run is pure overhead.

Crash translation. A worker that dies without a well-formed run-end, exits nonzero without a failing scenario explaining it, or outlives its per-class timeout is translated into a synthesized failed class carrying a stderr tail for forensics, and the queue keeps draining. A crashed worker can fail its class, never shorten the test list.

  • --worker-timeout <seconds> (parallel mode only): kills a worker stuck on one class for more than the given time (the whole worker process tree) and reports the class as failed. Default 600 per class: a generous outer defense above the in-process per-scenario watchdog (TimeoutMs), for the day a worker wedges outside any scenario.
  • --trx <path> (parallel mode only): writes one aggregated VSTest-style TRX report covering every class, so CI artifact upload and TRX tooling keep working without dotnet test. See CI Recipes.

Flag combination rules: --parallel is incompatible with --worker (workers are what it spawns) and with --list (listing never spawns workers); --worker-timeout and --trx require --parallel; --classes requires --worker.

Option summary

Option Modes Meaning
--filter <substring> all Only scenarios whose display name contains the substring (ordinal, case-insensitive).
--list plain, worker Print the discovered scenarios and exit without booting anything.
--worker worker Report exclusively as JSONL v1 events on stdout; chatter goes to stderr.
--classes <A,B> worker Run only these scenario classes (fully qualified names, exact match).
--parallel [N] parallel Run the classes on N worker subprocesses; N defaults to min(cores / 2, class count).
--worker-timeout <s> parallel Kill a worker stuck on one class for more than <s> seconds (default 600) and fail the class.
--trx <path> parallel Write one aggregated VSTest-style TRX report covering every class.
-h, --help all Show usage.

Clone this wiki locally