| name | cram |
|---|---|
| description | Guide for writing, reading, and debugging cram functional tests (.t files). Use this when working with cram test files, creating new .t tests, debugging test failures, understanding cram output diffs, or when you encounter exit code 80 or .err files from test runs. Also use when the user mentions cram, .t files, or transcript-style shell tests. |
Cram is a functional testing framework for command-line programs. Tests are
.t files that read like shell session transcripts — commands, expected output,
and documentation all in one file.
This is a comment — any unindented line is documentation.
$ command to execute
expected output line
output matched as glob (glob)
output matched as regex (re)
line without trailing newline (no-eol)
escaped unprintable chars \x1b[0m (esc)
[1]
$ multiline command \
> continued here with the > prefix
output from the multiline command
Indentation rules:
- 2-space indent +
$= shell command - 2-space indent +
>= continuation of previous command (shell PS2 prompt) - 2-space indent (no prefix) = expected output
- No indent = comment/documentation
Exit codes: A non-zero exit code appears as [N] on its own line as the
last expected output. Exit code 0 is implicit and never shown.
Cram tries literal match first, then falls back to the annotated mode:
| Suffix | Meaning |
|---|---|
| (none) | Exact literal match |
(re) |
Perl-compatible regex |
(glob) |
Glob pattern (*, ?; escape with \) |
(no-eol) |
Line has no trailing newline |
(esc) |
Line contains escaped unprintable characters |
Combine when needed: a line can end with (re) and use regex to match escaped
output, etc.
Exit code 80 means "skip this test." Cram marks it with s in output and
does not count it as a failure. Use this for conditional tests:
$ command -v some-tool > /dev/null 2>&1 || exit 80
$ some-tool --version
some-tool 1.2.3
Exit code 80 skips the entire .t file, not just the remaining commands.
If you only want to skip part of a test file, split it into separate .t files
and put the skip guard in the one that needs it.
Empty test files are also treated as skipped.
Each test runs in a fresh temporary directory. Key variables provided:
| Variable | Value |
|---|---|
$TESTDIR |
Directory containing the .t file |
$TESTFILE |
Basename of the current .t file |
$TESTSHELL |
Shell path (from --shell) |
$CRAMTMP |
Test runner's temporary directory |
$TMPDIR |
Same temp directory (also $TEMP, $TMP) |
Cram resets these environment variables before each test to ensure reproducibility:
| Variable | Reset to |
|---|---|
LANG, LC_ALL, LANGUAGE |
C |
TZ |
GMT |
COLUMNS |
80 |
CDPATH, GREP_OPTIONS |
(empty) |
Use --preserve-env / -E to skip these resets when testing
environment-sensitive behavior.
- Failed tests produce a unified diff showing expected vs actual output
- A
.errfile is written alongside the.tfile with the actual output - Use
cram -i(interactive) to merge actual output back into the.tfile — useful for bootstrapping new tests or updating after intentional changes
Bootstrapping expected output: Run the command manually until it produces
correct output, then run cram -i yourtest.t. Cram will prompt you to accept
the actual output as the new expected output — no manual transcription needed.
This is the standard way to create tests for existing working commands.
$ source "$TESTDIR/helpers.sh"
$ date +%Y
\d{4} (re)
$ echo "$HOME/some/path"
*/some/path (glob)
$ command -v jq > /dev/null 2>&1 || exit 80
$ echo '{"a":1}' | jq .a
1
- Commands that read stdin (like
ssh) can consume the test shell's stdin and break subsequent commands. Usessh -nor redirect stdin explicitly. - Daemon processes that inherit stdout without closing it will cause cram to
hang waiting for EOF. Redirect daemon output:
daemon > /dev/null 2>&1 & - zsh and COLUMNS:
COLUMNS=80cannot be reset when using--shell=zshbecause zsh treats it specially. - Trailing whitespace in expected output matters — a literal match includes it. If a diff shows no visible difference, check for trailing spaces.
For the full CLI options, .cramrc configuration, and additional edge cases,
read references/cram-reference.md in this skill directory.