Add syntax subcommand to print .shout file format reference
Gives users a quick built-in reference for the file format, directives, wildcards, exit codes, and CLI options without needing external docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c62c1d3161
commit
0b0a66b6d4
183
src/main.rs
183
src/main.rs
|
|
@ -50,6 +50,7 @@ fn print_usage() {
|
|||
eprintln!(" test [options] [files...] Run .shout test files");
|
||||
eprintln!(" version Print the version");
|
||||
eprintln!(" example Print an example .shout file");
|
||||
eprintln!(" syntax Print the .shout file format reference");
|
||||
eprintln!(" help [command] display help for command");
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +90,7 @@ fn parse_args() -> Option<(&'static str, TestOpts)> {
|
|||
match args[1].as_str() {
|
||||
"test" => print_test_help(),
|
||||
"example" => print_example_help(),
|
||||
"syntax" => print_syntax_help(),
|
||||
"version" => print_version_help(),
|
||||
"help" => print_help_help(),
|
||||
other => {
|
||||
|
|
@ -112,6 +114,10 @@ fn parse_args() -> Option<(&'static str, TestOpts)> {
|
|||
print_example();
|
||||
process::exit(0);
|
||||
}
|
||||
"syntax" => {
|
||||
print_syntax();
|
||||
process::exit(0);
|
||||
}
|
||||
"test" => {}
|
||||
other => {
|
||||
eprintln!("Unknown command: {other}");
|
||||
|
|
@ -552,6 +558,183 @@ $ true
|
|||
);
|
||||
}
|
||||
|
||||
fn print_syntax_help() {
|
||||
eprintln!("Usage: shout syntax");
|
||||
eprintln!();
|
||||
eprintln!("Print the .shout file format reference");
|
||||
}
|
||||
|
||||
fn print_syntax() {
|
||||
println!(
|
||||
r##"SHOUT FILE FORMAT
|
||||
=================
|
||||
|
||||
Each .shout file describes a shell session: commands to run and their
|
||||
expected output. Each file runs in a fresh temp directory with a single
|
||||
/bin/sh session. State carries between commands within a file.
|
||||
|
||||
COMMANDS AND OUTPUT
|
||||
-------------------
|
||||
|
||||
Lines starting with "$ " are commands. Lines between commands are expected
|
||||
output (stdout and stderr are merged).
|
||||
|
||||
$ echo hello
|
||||
hello
|
||||
|
||||
$ ls missing
|
||||
ls: missing: No such file or directory
|
||||
|
||||
WILDCARDS
|
||||
---------
|
||||
|
||||
"..." matches any characters for the rest of the line (inline wildcard):
|
||||
|
||||
$ date
|
||||
...
|
||||
|
||||
$ brew --version
|
||||
Homebrew ...
|
||||
|
||||
"..." on its own line matches zero or more entire lines (multi-line wildcard):
|
||||
|
||||
$ echo "one"; echo "two"; echo "three"
|
||||
one
|
||||
...
|
||||
three
|
||||
|
||||
EXIT CODES
|
||||
----------
|
||||
|
||||
"[N]" on the last line of expected output asserts exit code N:
|
||||
|
||||
$ false
|
||||
[1]
|
||||
|
||||
$ sh -c "exit 42"
|
||||
[42]
|
||||
|
||||
"[*]" asserts any non-zero exit code:
|
||||
|
||||
$ false
|
||||
[*]
|
||||
|
||||
"[0]" explicitly asserts exit code 0 (the default when no code is given):
|
||||
|
||||
$ true
|
||||
[0]
|
||||
|
||||
COMMENTS
|
||||
--------
|
||||
|
||||
Lines starting with "#" are comments (not executed, no output expected):
|
||||
|
||||
# this is a comment
|
||||
$ echo hello
|
||||
hello
|
||||
|
||||
$ echo hi # comments after commands are also stripped
|
||||
hi
|
||||
|
||||
Use "\#" in expected output to match a literal line starting with "#":
|
||||
|
||||
$ echo "# heading"
|
||||
\# heading
|
||||
|
||||
DIRECTIVES
|
||||
----------
|
||||
|
||||
Directives appear before the first command.
|
||||
|
||||
@env KEY=VALUE
|
||||
|
||||
Set an environment variable for the session.
|
||||
|
||||
@env DATABASE_URL=sqlite::memory:
|
||||
@env DEBUG=1
|
||||
|
||||
@setup <path>
|
||||
|
||||
Prepend commands (and @env/@teardown/@def) from another file. The setup
|
||||
file uses a plain format: each line is a command (no "$ " prefix needed),
|
||||
"#" lines are comments, blank lines are ignored. Setup files cannot
|
||||
reference other setup files (no nesting).
|
||||
|
||||
@setup shared.shout
|
||||
|
||||
If a setup command fails (non-zero exit), the test aborts with an error.
|
||||
|
||||
User file @env overrides setup file @env for the same key.
|
||||
User file @def overrides setup file @def for the same name.
|
||||
|
||||
@teardown <command>
|
||||
|
||||
Run a cleanup command after all test commands, regardless of pass/fail.
|
||||
Teardown failures produce warnings but don't affect test results.
|
||||
Can appear in both .shout files and setup files.
|
||||
|
||||
@teardown rm -rf "$SHOUT_DIR/tmp"
|
||||
@teardown docker rm -f test-container
|
||||
|
||||
@def <name> <body>
|
||||
|
||||
Define a macro. If a command matches <name> exactly, <body> is
|
||||
substituted before execution. Use "\" at end of line to continue the
|
||||
body onto the next line. Allowed in both .shout files and setup files.
|
||||
|
||||
@def start-server \
|
||||
PORT=$PORT node server.js &
|
||||
|
||||
$ start-server
|
||||
$ curl localhost:$PORT
|
||||
OK
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
---------------------
|
||||
|
||||
These are set automatically before running commands:
|
||||
|
||||
HOME temp directory for this test file
|
||||
SHOUT_DIR same temp directory
|
||||
SHOUT_SOURCE_DIR directory containing the .shout file
|
||||
SHOUT_PROJECT_DIR directory where shout was invoked
|
||||
PORT auto-assigned (from 5400 or --port-from), increments per file
|
||||
PATH prepended with --path dirs, if any
|
||||
|
||||
SETUP FILE FORMAT
|
||||
-----------------
|
||||
|
||||
Setup files (referenced by @setup) use a simpler format:
|
||||
|
||||
- Each line is a shell command (no "$ " prefix)
|
||||
- "#" lines are comments, blank lines are ignored
|
||||
- @env, @teardown, and @def directives are supported
|
||||
- @setup is not allowed (no nesting)
|
||||
|
||||
Example setup file:
|
||||
|
||||
# setup.shout
|
||||
@env DB_URL=sqlite:test.db
|
||||
@teardown rm -f "$SHOUT_PROJECT_DIR/test.db"
|
||||
npm install --silent
|
||||
|
||||
CLI OPTIONS
|
||||
-----------
|
||||
|
||||
shout test [options] [files...]
|
||||
|
||||
-u, --update Rewrite .shout files 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 (default: 10s)
|
||||
-t, --filter <pattern> Only run files matching <pattern>
|
||||
-v, --verbose Print each command as it runs
|
||||
--port-from <n> Auto-assign $PORT starting from <n> (default: 5400)
|
||||
--parallel Run files in parallel"##
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (_, opts) = parse_args().unwrap();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user