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!(" test [options] [files...] Run .shout test files");
|
||||||
eprintln!(" version Print the version");
|
eprintln!(" version Print the version");
|
||||||
eprintln!(" example Print an example .shout file");
|
eprintln!(" example Print an example .shout file");
|
||||||
|
eprintln!(" syntax Print the .shout file format reference");
|
||||||
eprintln!(" help [command] display help for command");
|
eprintln!(" help [command] display help for command");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +90,7 @@ fn parse_args() -> Option<(&'static str, TestOpts)> {
|
||||||
match args[1].as_str() {
|
match args[1].as_str() {
|
||||||
"test" => print_test_help(),
|
"test" => print_test_help(),
|
||||||
"example" => print_example_help(),
|
"example" => print_example_help(),
|
||||||
|
"syntax" => print_syntax_help(),
|
||||||
"version" => print_version_help(),
|
"version" => print_version_help(),
|
||||||
"help" => print_help_help(),
|
"help" => print_help_help(),
|
||||||
other => {
|
other => {
|
||||||
|
|
@ -112,6 +114,10 @@ fn parse_args() -> Option<(&'static str, TestOpts)> {
|
||||||
print_example();
|
print_example();
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
|
"syntax" => {
|
||||||
|
print_syntax();
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
"test" => {}
|
"test" => {}
|
||||||
other => {
|
other => {
|
||||||
eprintln!("Unknown command: {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() {
|
fn main() {
|
||||||
let (_, opts) = parse_args().unwrap();
|
let (_, opts) = parse_args().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user