A powerful testing tool that allows you to execute and record commands inside a Docker container, replay them, and validate whether your results match expected outputs. CLT helps automate testing of command-line applications with consistent environments.
- Record interactive command sessions in reproducible test files
- Replay recorded sessions to verify consistent behavior
- Intelligent output comparison with regex pattern matching
- Reusable test blocks for modular testing
- GitHub Action support for CI/CD integration
- Cross-platform compatibility via Docker containers
Installation is straightforward. Simply clone the repository and start using it:
git clone https://github.com/manticoresoftware/clt.git
cd clt
./clt help
CLT provides prebuilt binaries for Linux environments (both amd64 and arm64), which are automatically detected based on your platform. While binaries for macOS and Windows are not currently available, they can be built if required.
NOTE: CLT requires Docker to be installed on your machine as all tests run in a container environment.
-
Start recording a test session with the
record
command:./clt record centos:7
This starts a recording session using the specified Docker image. You'll see the path to the recording file displayed:
Recording data to file: ./tests/centos:7_20230714_161043.rec
-
Perform your commands in the interactive shell. All inputs and outputs will be recorded.
-
When finished, press
^D
(Ctrl+D) once to stop recording and save your test.
To validate and replay a recorded test:
./clt test -t ./tests/centos:7_20230714_161043.rec -d centos:7
echo $?
-t
specifies the test file to replay-d
enables debug output that shows diff information if results don't match
The exit code will be 0 if the test passed (outputs match) or 1 if there are differences.
After recording, you can refine your test to handle dynamic outputs:
./clt refine -t ./tests/centos:7_20230714_161043.rec centos:7
This will open your configured editor to modify test outputs and add patterns where needed.
CLT test files (.rec
) use a special syntax to differentiate between commands, outputs, and other sections.
Note: CLT uses en dashes (–
) in its section markers, not regular hyphens (-
). The code samples below show the correct syntax with en dashes.
Section | Syntax | Description |
---|---|---|
Input |
––– input ––– |
Marks the beginning of a command input section |
Output |
––– output ––– |
Marks the beginning of a command output section |
Output with Checker |
––– output: checker-name ––– |
Like output but uses a custom checker program |
Block |
––– block: path/to/block ––– |
Includes a reusable test block from a .recb file |
Comment |
––– comment ––– |
Marks a comment section (ignored during test execution) |
Note: The duration
section (––– duration: 123ms (45.67%) –––
) is automatically generated by the test system and should not be added manually to .rec
files. It appears in the resulting .rep
files after test execution.
A test file typically consists of repeating input and output sections:
––– input –––
echo "Hello, world!"
––– output –––
Hello, world!
Each input section contains commands exactly as they were typed, and each output section contains the expected output that should be matched during test replay.
For outputs that change between runs (timestamps, PIDs, etc.), CLT supports regex pattern matching.
Place regex patterns between #!/
and /!#
markers in your test output:
#!/[0-9]+/!# # Matches any number
CLT provides predefined patterns in the .clt/patterns
file. Use them with the syntax %{PATTERN_NAME}
:
%{SEMVER} # Matches semantic version numbers
You can define custom patterns by adding them to your project's .clt/patterns
file using the format:
PATTERN_NAME REGEX_PATTERN
Example .clt/patterns
file:
SEMVER [0-9]+\.[0-9]+\.[0-9]+
YEAR [0-9]{4}
IPADDR [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
COMMITDATE [a-z0-9]{7}@[0-9]{6}
Note: CLT first loads the global patterns from $PROJECT_DIR/.clt/patterns
and then appends your project-specific patterns from .clt/patterns
for extended functionality.
Create modular test components by extracting flows into .recb
files placed in the same directory as your test files:
––– block: login-sequence –––
This will include the contents of the login-sequence.recb
file located in the same directory as your test. You can also organize blocks in subdirectories using relative paths:
––– block: auth/admin-login –––
The path is always relative to the location of the .rec
file that's including the block. Blocks can be nested, allowing you to compose complex test scenarios from reusable components.
For advanced output validation beyond regex patterns, CLT supports custom checker programs. These are specified in the output statement:
––– output: custom-checker –––
- Place your checker executable in the
.clt/checkers/
directory - The checker will receive two arguments:
- First argument: Path to a temp file containing expected output (from .rec file)
- Second argument: Path to a temp file containing actual output (from replay)
- The checker should return:
- Exit code 0 if outputs match (test passes)
- Non-zero exit code if there are differences (test fails)
- Any stdout/stderr output from the checker will be displayed when there are differences
Checkers can be written in any language, but must be executable. Here's an example of a simple bash checker that verifies if a specific string exists in the output:
#!/bin/bash
# simple-contains-checker.sh - A checker that verifies if output contains a specific string
EXPECTED_FILE="$1" # Path to expected output from .rec file
ACTUAL_FILE="$2" # Path to actual output from test run
# Get the string to search for from the first line of expected file
SEARCH_STRING=$(head -n 1 "$EXPECTED_FILE")
# Check if the actual output contains this string
if grep -q "$SEARCH_STRING" "$ACTUAL_FILE"; then
echo "✓ Found expected string: $SEARCH_STRING"
exit 0 # Success
else
echo "✗ Missing expected string: $SEARCH_STRING"
exit 1 # Failure
fi
This provides great flexibility for validating complex outputs without needing regex patterns.
CLT's behavior can be customized through several environment variables:
Variable | Description | Default |
---|---|---|
CLT_DEBUG |
Enable debug mode to show detailed output | 0 |
CLT_DIFF_INLINE |
Display differences in-line instead of side-by-side | 0 |
CLT_EDITOR |
Custom editor for refining tests | Auto-detected (nano or vim ) |
CLT_PROMPTS |
Array of additional prompts to detect (e.g., CLT_PROMPTS=("mysql> ") ) |
("clt> ") |
CLT_NO_COLOR |
Disable colored output | Not set |
DEFAULT_DELAY |
Default delay in ms between each command in the test | 5 |
RUN_ARGS |
Additional arguments to pass to docker run |
Not set |
CLT provides a ready-to-use GitHub action to run tests in your CI/CD pipeline. You can customize the action with several parameters:
Parameter | Description | Default |
---|---|---|
image |
Docker image to run the tests in | Required |
test_prefix |
Filter tests by prefix in tests folder | tests/ |
run_args |
Additional arguments to pass to docker run |
'' |
init_code |
Code to execute before running tests | '' |
timeout |
Allowed timeout in minutes for tests | 5 |
artifact |
Artifact to download for local built docker image | Not set |
repository |
Repository to checkout | Not set |
ref |
Branch name or ref to checkout | Default branch |
name: CLT tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
clt:
name: Run CLT tests
strategy:
fail-fast: false
matrix:
image: [ "ubuntu:bionic", "ubuntu:focal", "ubuntu:jammy", "debian:buster", "debian:bullseye", "debian:bookworm" ]
runs-on: ubuntu-22.04
steps:
- uses: manticoresoftware/clt@v3
with:
image: ${{ matrix.image }}
test_prefix: test/clt-tests/
run_args: -e TELEMETRY=0
timeout: 10
init_code: |
apt-get update
apt-get install -y curl
CLT uses a specific directory structure for its key files:
.clt/
├── checkers/ # Custom output checker executables
└── patterns # Pattern definitions for regex matching
tests/
├── *.rec # Test recording files
└── *.recb # Reusable test blocks
The .clt/
directory contains CLT configuration and special-purpose files, while the actual test files and reusable blocks are stored in the tests directory or other directories specified for your tests.
CLT uses several file extensions:
Extension | Description |
---|---|
.rec |
Original record of inputs and outputs, may include links to block files |
.recb |
Reusable block file that can be included in .rec files |
.rep |
Replay file containing results of replaying a .rec file |
- Use
^D
(Ctrl+D) only once to close the CLT environment; for other exits, use theexit
command - Avoid using
^C
,^V
,^Z
, and other control characters as they may not function correctly - Reverse search (
^R
) is currently unsupported - Complete tests in the
clt>
shell and press^D
to terminate the session - Use a simple terminal like iTerm; VS Code terminal may cause unusual behavior
- Each command in the
.rec
test must have an output that contains a newline
Only the following keystrokes are fully supported:
- Standard input characters
- Left and right arrow keys
- Backspace and delete keys
- CTRL+a (beginning of line) and CTRL+e (end of line)
To build the rec
and cmp
tools for both aarch64 and amd64 Linux:
./bin/cross-build
git add .
git commit -m 'Update binaries'
During test execution, CLT:
- Finds
.rec
files and compiles them into a ready-to-use version on the fly - Executes each command in sequence, generating a
.rep
file - Uses the
cmp
tool to compare the results with the compiled version of the.rec
files
If you need to work with additional command prompts (e.g., mysql>
prompt), configure them as follows:
CLT_PROMPTS=("mysql> ")
This helps CLT correctly detect command completion in different interactive shells.