CLI Test Framework
A lightweight automated testing framework for command-line applications. Define test cases in JSON/YAML, run all validations with a single command.
Particularly suited for scientific computing — deep HDF5 support with regex table matching, data filtering, and tolerance-based comparison, making simulation result verification effortless.
Why this exists
This project was created from a real need: regression testing finite-element software.
In solver-style projects, checking the exit code is not enough. A test often needs to run multiple commands, generate numerical result files, compare HDF5/CSV outputs with tolerances, and make sure old cases do not become slower over time.
CLI Test Framework was built for that workflow.
Highlights
- Golden File Assertion —
compare_filesembedded in testexpected, compares output files against baselines with tolerance - Parallel Execution — Multi-thread / multi-process, 3-5x speedup
- Resource-Aware Scheduling — Automatic CPU core management, prevents solver thread runaway
- Sequence Steps — Multi-step execution within a single test case, fail-fast
- Setup Module — Auto-configure environment variables before tests, auto-cleanup after
- File Comparison — Text / JSON / CSV / XML / HDF5 / Binary, with CLI and embedded assertion support
- Filtered Execution — Run specific test cases by name or tag
- JUnit XML Output —
--junit-xmlfor GitLab CI / Jenkins / CircleCI test report panels
Quick Start
pip install cli-test-framework
30-Second Setup
- Create
test_cases.json:
{
"test_cases": [
{
"name": "hello",
"command": "echo",
"args": ["Hello World"],
"tags": ["smoke"],
"expected": {
"return_code": 0,
"output_contains": ["Hello World"]
}
}
]
}
- Run:
cli-test run test_cases.json
Golden File Comparison in Tests
Run a simulation, then compare its output file against a reference:
{
"test_cases": [
{
"name": "FEA displacement check",
"command": "my_solver",
"args": ["--input", "case1.dat", "--output", "out.h5"],
"expected": {
"return_code": 0,
"compare_files": [
{
"actual": "out.h5",
"baseline": "ref/golden.h5",
"rtol": 1e-5,
"atol": 1e-8,
"tables": ["NASTRAN/RESULT/NODAL/DISPLACEMENT"]
}
]
}
}
]
}
actual– file produced by the commandbaseline– reference file to compare againsttype– comparator type (auto-detected from extension if omitted:.h5→h5,.json→json,.csv→csv,.xml→xml,.txt→text)- all other keys are forwarded as comparator parameters (
rtol,atol,tables,table_regex,data_filter,encoding,structure_only,delimiter,compare_mode,key_field, etc.)
Multiple files and mixed assertion types coexist naturally:
{
"expected": {
"return_code": 0,
"output_contains": ["simulation finished"],
"compare_files": [
{"actual": "out.h5", "baseline": "ref/disp.h5", "rtol": 1e-5},
{"actual": "report.csv", "baseline": "ref/expected.csv", "rtol": 1e-6}
]
}
}
Parallel Execution
cli-test run test_cases.json --parallel --workers 4
Run Specific Cases
# By name
cli-test run test_cases.json -t test_1 -t test_2
# By tag
cli-test run test_cases.json --tag smoke
# Combine name and tag (AND logic)
cli-test run test_cases.json -t test_1 --tag smoke
JUnit XML (CI Integration)
cli-test run test_cases.json --junit-xml report.xml
In GitLab CI:
# .gitlab-ci.yml
artifacts:
reports:
junit: report.xml
Python API
from cli_test_framework.runners import JSONRunner, ParallelJSONRunner
# Sequential
runner = JSONRunner(config_file="test_cases.json")
success = runner.run_tests()
# Parallel
runner = ParallelJSONRunner(config_file="test_cases.json", max_workers=4, execution_mode="thread")
success = runner.run_tests()
File Comparison (Standalone CLI)
compare-files result1.h5 result2.h5 --h5-table-regex "output_.*" --h5-rtol 1e-5
📖 Full Documentation: docs/user_manual.md
Changelog
0.9.0
- Tag filtering: New
tagsfield in test case definitions for categorization. Use--tagCLI option ortest_case_tag_filterPython API to batch-filter test cases. Acts as OR within tags, AND with name filtering.
0.8.0
- JUnit XML output: New
--junit-xml <filepath>CLI option writes JUnit-format XML reports that integrate directly with GitLab CI, Jenkins, CircleCI, and other CI tools. Supportspassed/failed/timeout/errorstatus mapping. Zero extra dependencies — built withxml.etree.ElementTree.
0.7.0
- Unified logging system: All diagnostic output (executor, runner, scheduler, setup) now uses Python's standard
loggingmodule under thecli_test_frameworknamespace. Library users can suppress output entirely vialogging.getLogger("cli_test_framework").setLevel(logging.WARNING). Removes the previous ad-hocprint()+_print_lockpattern —loggingis inherently thread-safe. - Default handler: A
StreamHandlerat INFO level is installed on first import, so CLI behavior is unchanged. Use--verbose/--debugto enable DEBUG-level output. - Public API:
get_logger(name)exposed viacli_test_framework.get_loggerfor consistent logger creation in extensions.
0.6.0
- Golden file assertion:
compare_filesis now a first-class assertion in testexpected— compare output files against baselines directly in your test definitions, with full tolerance and parameter support. Thefile_comparatorsubsystem is now integrated into the assertion pipeline, completing the closed loop from command execution to result verification.
0.5.2
- Runtime history tracking (
--history-dir): persist per-case execution time in.symtest, enable smart scheduling & regression detection - Regression warning: alert when a case runs slower than historical average × threshold (
--regression-threshold, default 1.5) - Smart scheduling: parallel runner prioritizes historical
avg_durationover configestimated_timefor task ordering - Per-case duration now shown in test result output
0.5.1
- Run specific test cases by name (
-t/test_case_filter)
0.5.0
- Steps feature: sequential multi-command execution within a single test case, fail-fast
0.4.2
- Resource-aware scheduling: auto-detect CPU cores, semaphore-based core allocation
- Auto-inject
OMP_NUM_THREADS/MKL_NUM_THREADS/NPROCto prevent solver thread runaway - Per-test
timeoutsupport to prevent hanging
0.4.1
- Multi-thread / multi-process parallel execution, 3-5x speedup
Contributing
Before submitting a PR, please make sure all tests pass:
python tests\run_all.py
License
MIT
Release files for cli-test-framework 0.9.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cli_test_framework-0.9.2.tar.gz | 306.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cli_test_framework-0.9.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 383.5 kB
Release files / cli_test_framework-0.9.2.tar.gz
| Download URL | cli_test_framework-0.9.2.tar.gz |
|---|---|
| Size | 306.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e03d3436488497bb9412c0c12486b3050f48569226f6746d92507e2bef4b42e8
|
|
BLAKE2b-256 checksum How to use checksums |
5e4c564a8de168054ed2dcb7e8421cfce39b605f6431cf5e2d6c419fe2f03443
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / cli_test_framework-0.9.2-py3-none-any.whl
| Download URL | cli_test_framework-0.9.2-py3-none-any.whl |
|---|---|
| Size | 76.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2188d1e091d58d5f2c7f5c0d5263fcba1c1e5b7d192953f054b5b81e0d1a1465
|
|
BLAKE2b-256 checksum How to use checksums |
03a8ee5e3f45edc2c7234f4da851a3330a68d98881affd93e2d1acc31a5bf5dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|