4.1 KiB
4.1 KiB
shout
Transcript-based shell integration test runner. Rust.
Commands
cargo test— run tests (integration tests intests/shout.rs)cargo build— build the binarycargo run -- test [files...]— run shout CLI-u, --update— rewrite.shoutfiles with actual output-k, --keep— keep temp directories after run--clean-env— start with empty environment--path <path>— prepend to$PATH(repeatable)--timeout <dur>— per-command timeout (default10s)-t, --filter <pattern>— only run files whose path contains<pattern>-v, --verbose— print each command as it runs--port-from <n>— auto-assign$PORTstarting from n (default5400)--parallel— run files in parallel
Architecture
src/main.rs— CLI entry point, arg parsing, file discovery,run_oneorchestrationsrc/parse.rs— parses.shoutfiles intoShoutFile(list ofCommand), alsoparse_setupsrc/run.rs— executes commands via/bin/sh, captures output with sentinelssrc/matching.rs— wildcard-aware output matching and diff generationsrc/format.rs— evaluates pass/fail, formats failures and summarysrc/update.rs— rewrites.shoutfiles with actual output (--updatemode)src/duration.rs— parses duration strings (10s,500ms,1m)web/index.html— web documentation page
.shout file format
$prefix = command to execute- Lines between commands = expected output (stdout+stderr merged)
...on its own line = multi-line wildcard (matches zero or more lines)...inline = matches any characters on that line[N]on last line of expected output = assert exit code N[*]= assert any non-zero exit code; default expects 0#at start of line = comment (not executed, no output expected)$#also works as comment (legacy syntax)\#in expected output = literal line starting with##after a command = comment (stripped);#in expected output is literal@env KEY=VALUEbefore first command = set environment variable@teardown <command>before first command = run command after all test commands- Runs regardless of pass/fail
- Teardown failures produce warnings but don't affect test results
- Can appear in both
.shoutfiles and setup files
@setup path.shoutbefore first command = prepend commands (and@env) from another file- Setup files use a plain format: each line is a command (no
$prefix),#lines are comments, blank lines ignored - Setup files can contain
@env,@teardown, and@defdirectives but not@setup(no nesting) - User file
@envoverrides setup file@env - Setup command failures abort the test with an error
- Setup files use a plain format: each line is a command (no
@def name bodybefore first command = define a macro- If a command matches
nameexactly,bodyis substituted before execution - Backslash
\at end of line continues the body onto the next line - Allowed in both
.shoutfiles and setup files - User file
@defoverrides setup file@defwith the same name
- If a command matches
- Each file runs in a fresh temp dir with a single
/bin/shsession $HOMEand$SHOUT_DIRare set to the temp dir automatically$SHOUT_SOURCE_DIRis set to the directory containing the.shoutfile$SHOUT_PROJECT_DIRis set tocwdwhereshoutwas invoked- stdout and stderr are merged (
exec 2>&1)
New feature checklist
src/parse.rs— update types (Directive,ShoutFile,Command) and both parsers (parse+parse_setup)src/main.rs— wire up the parsed result inrun_one(directive resolution, command merging, result handling)tests/*.shout— integration test file exercising the feature end-to-endCLAUDE.md— update.shout file formatsectionREADME.md— update Directives sectionweb/index.html— add or update a section on the website- Run
cargo testto verify
Style
- Rust 2024 edition
- No OOP — plain functions and structs/enums
- Integration tests in
tests/shout.rs, example.shoutfiles intests/ - Never use
unsafe - Keep dependencies to the bare minimum.
- If you must use a dependency, use the lightest-weight version.