A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.
Project description
CLI Testing Framework
1. Overview
This is a lightweight and extensible automated testing framework that supports defining test cases via JSON/YAML formats, providing complete test execution, result verification, and report generation capabilities. The framework is designed to provide standardized test management for command-line tools and scripts, with enterprise-grade parallel execution support and advanced file comparison features.
2. Features
- 🚀 Parallel Test Execution: Support for multi-threading and multi-processing parallel testing with significant performance improvements
- 🔧 Setup Module System: Plugin-based architecture for pre-test setup tasks (environment variables, database initialization, service startup)
- 🏗️ Modular Architecture: Decoupled design of core components (runner/assertion/report/setup)
- 📄 Multi-Format Support: Native support for JSON/YAML test case formats
- 🧠 Intelligent Command Parsing: Smart handling of complex commands like
"python ./script.py" - 📁 Smart Path Resolution: Automatic handling of relative and absolute path conversions
- ✅ Rich Assertion Mechanism: Return code validation, output content matching, regex verification
- 🔌 Extensible Interfaces: Quickly implement new test format support by inheriting BaseRunner
- 🔒 Isolated Execution Environment: Independent sub-process execution ensures test isolation
- 📊 Comprehensive Reports: Detailed pass rate statistics and failure diagnostics
- 🔧 Thread-Safe Design: Robust concurrent execution with proper synchronization
- 📝 Advanced File Comparison: Support for comparing various file types (text, binary, JSON, HDF5) with detailed diff output
- 🎛️ Resource-Aware Scheduling: Per-test timeout and resource hints (CPU cores / estimated time / memory / priority) with automatic CPU core detection and semaphore-based scheduling to prevent resource conflicts and solver "runaway" scenarios
3. Quick Start
Environment Requirements
pip install cli-test-framework
Python >= 3.9
Sequential Execution
from src.runners.json_runner import JSONRunner
runner = JSONRunner(
config_file="path/to/test_cases.json",
workspace="/project/root"
)
success = runner.run_tests()
Parallel Execution
from src.runners.parallel_json_runner import ParallelJSONRunner
# Multi-threaded execution with resource-aware scheduling
runner = ParallelJSONRunner(
config_file="path/to/test_cases.json",
workspace="/project/root",
max_workers=4, # Maximum concurrent workers
execution_mode="thread" # "thread" (supports CPU scheduling) or "process"
)
success = runner.run_tests()
Resource-Aware Scheduling: When using execution_mode="thread", the framework automatically:
- Detects available CPU cores on your machine
- Manages CPU resource allocation using semaphore-based scheduling
- Prevents resource conflicts by queuing tasks that require more cores than available
- Injects environment variables to constrain solver threads (prevents "runaway" scenarios)
Setup Module Usage
from cli_test_framework import JSONRunner, EnvironmentSetup
# Using built-in environment variable setup
runner = JSONRunner("test_cases.json")
env_setup = EnvironmentSetup({
"TEST_ENV": "development",
"API_URL": "http://localhost:8080"
})
runner.setup_manager.add_setup(env_setup)
success = runner.run_tests()
File Comparison
# Compare two text files
compare-files file1.txt file2.txt
# Compare JSON files with key-based comparison
compare-files data1.json data2.json --json-compare-mode key-based --json-key-field id
# Compare HDF5 files with specific options
compare-files data1.h5 data2.h5 --h5-table table1,table2 --h5-rtol 1e-6
# Compare binary files with similarity check
compare-files binary1.bin binary2.bin --similarity
4. Test Case Format
JSON Format
{
"setup": {
"environment_variables": {
"TEST_ENV": "development",
"API_URL": "http://localhost:8080",
"DEBUG_MODE": "true"
}
},
"test_cases": [
{
"name": "Environment Variable Test",
"command": "python",
"args": ["-c", "import os; print(f'Environment: {os.environ.get(\"TEST_ENV\")}')"],
"expected": {
"return_code": 0,
"output_contains": ["Environment: development"]
}
},
{
"name": "Full_Car_Crash_Simulation",
"command": "radioss_solver",
"args": ["-i", "input.0000.rad"],
"timeout": 36000,
"resources": {
"cpu_cores": 4,
"estimated_time": 18000,
"min_memory_mb": 16000,
"priority": 10
},
"expected": {
"return_code": 0
}
},
{
"name": "File Comparison Test",
"command": "compare-files",
"args": ["file1.txt", "file2.txt", "--verbose"],
"expected": {
"return_code": 0,
"output_contains": ["Files are identical"],
"output_matches": [".*comparison completed.*"]
}
}
]
}
YAML Format
setup:
environment_variables:
TEST_ENV: "production"
DATABASE_URL: "sqlite:///test.db"
test_cases:
- name: Environment Test
command: python
args:
- "-c"
- "import os; print(f'DB: {os.environ.get(\"DATABASE_URL\")}')"
expected:
return_code: 0
output_contains:
- "DB: sqlite:///test.db"
- name: Directory Scan Test
command: ls
args:
- -l
- docs/
expected:
return_code: 0
output_matches: ".*\\.md$"
Resource-Aware Configuration
For simulation and long-running tasks (CAE/FEA), you can specify resource requirements to enable intelligent scheduling with automatic CPU core management:
{
"name": "Full_Car_Crash_Simulation",
"command": "radioss_solver",
"args": ["-i", "input.0000.rad"],
"timeout": 36000,
"resources": {
"cpu_cores": 4,
"estimated_time": 18000,
"min_memory_mb": 16000,
"priority": 10
},
"expected": {
"return_code": 0
}
}
Field Descriptions:
-
timeout(optional, float): Hard limit in seconds. If the test exceeds this time, it will be killed. Default: 3600 seconds (1 hour). Set tonullfor unlimited (not recommended).- Common values:
60(1 min),300(5 min),3600(1 hour),18000(5 hours),86400(24 hours)
- Common values:
-
resources.cpu_cores(optional, int): Number of CPU cores required by this task. The framework automatically detects available CPU cores and uses semaphore-based scheduling to manage resource allocation. Tasks that require more cores than available will wait until resources are freed. Default:1core.- How it works: The framework automatically detects your machine's CPU count (e.g., 16 cores), reserves 2 cores for the system, and creates a resource pool with the remaining cores (e.g., 14 cores). Tasks acquire cores from this pool before execution.
- Environment injection: When a task starts, the framework automatically sets
OMP_NUM_THREADS,MKL_NUM_THREADS, andNPROCenvironment variables to constrain solver threads, preventing "runaway" scenarios where solvers ignore Python's scheduling. - Example scenarios:
- Machine with 16 cores (14 available): 3 tasks requiring 4 cores each can run concurrently (3×4=12 cores used, 2 cores free)
- Machine with 8 cores (6 available): 1 task requiring 4 cores + 1 task requiring 2 cores can run concurrently
- Recommendations:
- Heavy simulations:
4-8cores - Medium tasks:
2-4cores - Lightweight scripts:
1core (default)
- Heavy simulations:
-
resources.estimated_time(optional, float): Estimated duration in seconds for LPT (Longest Processing Time) scheduling. Tasks with longer estimated times are scheduled first in parallel runs to improve throughput.- Example:
18000= 5 hours,3600= 1 hour,300= 5 minutes
- Example:
-
resources.min_memory_mb(optional, float): Estimated memory requirement in MB. Used for OOM (Out Of Memory) risk warnings. Currently informational only.- Example:
16000= 16 GB,8192= 8 GB,4096= 4 GB
- Example:
-
resources.priority(optional, int): Task priority (higher number = higher priority). Currently informational only. Recommended range: 0-10.10: Critical/blocking tasks (must run first)7-9: High priority (important business paths)4-6: Normal priority1-3: Low priority / exploratory tests0or unset: Default priority
Note:
- All time values (
timeout,estimated_time) are in seconds, not milliseconds. This matches Python'ssubprocess.run(timeout=...)API. - CPU core scheduling is only active in thread mode. Process mode will fall back to original behavior (process-level isolation provides some resource separation).
5. File Comparison Features
Supported File Types
- Text Files: Plain text, source code, markdown, etc.
- JSON Files: With exact or key-based comparison
- HDF5 Files: Structure and content comparison with numerical tolerance
- Binary Files: With optional similarity index calculation
Comparison Options
Text Comparison
compare-files file1.txt file2.txt \
--start-line 10 \
--end-line 20 \
--encoding utf-8
JSON Comparison
compare-files data1.json data2.json \
--json-compare-mode key-based \
--json-key-field id,name
HDF5 Comparison
New Feature: HDF5 group path expansion! By default, when you specify a group path in --h5-table, the comparator will automatically expand and compare all datasets and subgroups within that path.
# Compare specific tables/groups with auto-expansion (default behavior)
compare-files data1.h5 data2.h5 \
--h5-table group1/subgroupA \
--h5-rtol 1e-5 \
--h5-atol 1e-8
# Disable auto-expansion to compare only the specified path itself
compare-files data1.h5 data2.h5 \
--h5-table group1 \
--h5-no-expand-path
# Use regex patterns (also supports auto-expansion)
compare-files data1.h5 data2.h5 \
--h5-table-regex "group1/.*" \
--h5-structure-only
# Use comma-separated table names with regex (New in 0.3.7)
compare-files data1.h5 data2.h5 \
--h5-table-regex "table1,table2,table3" \
--h5-rtol 1e-6
Binary Comparison
compare-files binary1.bin binary2.bin \
--similarity \
--chunk-size 16384
Output Formats
- Text: Human-readable diff output
- JSON: Structured comparison results
- HTML: Visual diff with syntax highlighting
6. System Architecture
Enhanced Architecture Flow
graph TD
A[Test Cases] --> B{Execution Mode}
B -->|Sequential| C[JSONRunner/YAMLRunner]
B -->|Parallel| D[ParallelRunner]
D --> E[ThreadPoolExecutor/ProcessPoolExecutor]
C --> F[Command Parser]
E --> F
F --> G[Path Resolver]
G --> H[Sub-process Execution]
H --> I[Assertion Engine]
I --> J[Thread-Safe Result Collection]
J --> K[Report Generator]
L[File Comparator] --> M[Text Comparator]
L --> N[JSON Comparator]
L --> O[HDF5 Comparator]
L --> P[Binary Comparator]
Core Components
1. Intelligent Command Parser
# Handles complex commands like "python ./script.py"
command_parts = case["command"].split()
if len(command_parts) > 1:
actual_command = resolve_command(command_parts[0]) # "python"
script_parts = resolve_paths(command_parts[1:]) # "./script.py" -> full path
final_command = f"{actual_command} {' '.join(script_parts)}"
2. Enhanced Path Resolver
def resolve_command(self, command: str) -> str:
system_commands = {
'echo', 'ping', 'python', 'node', 'java', 'docker', ...
}
if command in system_commands or Path(command).is_absolute():
return command
return str(self.workspace / command)
3. Parallel Runner Base Class
class ParallelRunner(BaseRunner):
def __init__(self, max_workers=None, execution_mode="thread"):
self.max_workers = max_workers or os.cpu_count()
self.execution_mode = execution_mode
self._results_lock = threading.Lock()
self._print_lock = threading.Lock()
7. Advanced Usage
Performance Testing
# Quick performance test
python performance_test.py
# Unit tests for parallel functionality
python -m pytest tests/test_parallel_runner.py -v
Error Handling and Fallback
try:
runner = ParallelJSONRunner(config_file="test_cases.json")
success = runner.run_tests()
if not success:
# Check failed tests
for detail in runner.results["details"]:
if detail["status"] == "failed":
print(f"Failed test: {detail['name']}")
print(f"Error: {detail['message']}")
except Exception as e:
print(f"Execution error: {e}")
# Fallback to sequential execution
runner.run_tests_sequential()
Best Practices
-
Choose Appropriate Concurrency:
import os # For CPU-intensive tasks max_workers = os.cpu_count() # For I/O-intensive tasks max_workers = os.cpu_count() * 2
-
Resource-Aware Scheduling:
- For CAE/FEA simulations: Always specify
cpu_coresin your test configuration to prevent resource conflicts - Mixed workloads: Configure lightweight tasks with
cpu_cores: 1and heavy simulations with appropriate core counts - Example mixed configuration:
{ "test_cases": [ { "name": "Heavy Simulation", "command": "radioss_solver", "resources": { "cpu_cores": 4 } }, { "name": "Lightweight Script", "command": "python script.py", "resources": { "cpu_cores": 1 } } ] }
- Monitor resource usage: The framework prints resource acquisition/release logs to help you understand scheduling behavior
- For CAE/FEA simulations: Always specify
-
Test Case Design:
- ✅ Ensure test independence (no dependencies between tests)
- ✅ Avoid shared resource conflicts (different files/ports)
- ✅ Use relative paths (framework handles resolution automatically)
- ✅ Specify
cpu_coresfor CPU-intensive tasks to enable intelligent scheduling
-
Debugging:
# Enable verbose output for debugging runner = ParallelJSONRunner( config_file="test_cases.json", max_workers=1, # Set to 1 for easier debugging execution_mode="thread" )
8. Example Demonstrations
Input Example
{
"test_cases": [
{
"name": "Python Version Check",
"command": "python --version",
"args": [],
"expected": {
"output_matches": "Python 3\\.[89]\\.",
"return_code": 0
}
},
{
"name": "File Processing Test",
"command": "python ./process_file.py",
"args": ["input.txt", "--output", "result.txt"],
"expected": {
"return_code": 0,
"output_contains": ["Processing completed"]
}
}
]
}
Output Report
Test Results Summary:
Total Tests: 15
Passed: 15
Failed: 0
Performance Statistics:
Sequential execution time: 12.45 seconds
Parallel execution time: 3.21 seconds
Speedup ratio: 3.88x
Detailed Results:
✓ Python Version Check
✓ File Processing Test
✓ JSON Comparison Test
...
9. Troubleshooting
Common Issues
-
Process Mode Serialization Error
- Cause: Objects contain non-serializable attributes (like locks)
- Solution: Use independent process worker functions
-
Path Resolution Error
- Cause: System commands treated as relative paths
- Solution: Update
PathResolversystem command list
-
Performance Not Improved
- Cause: Test cases too short, parallel overhead exceeds benefits
- Solution: Increase test case count or use more complex tests
-
Command Not Found Error
- Cause: Complex commands like
"python ./script.py"not parsed correctly - Solution: Framework now automatically handles this (fixed in latest version)
- Cause: Complex commands like
Debug Tips
# Enable detailed logging
import logging
logging.basicConfig(level=logging.DEBUG)
# Check detailed results
import json
print(json.dumps(runner.results, indent=2, ensure_ascii=False))
10. Extension and Customization
Adding New Runners
class XMLRunner(BaseRunner):
def load_test_cases(self):
import xml.etree.ElementTree as ET
# Parse XML structure and convert to TestCase objects
class CustomParallelRunner(ParallelRunner):
def custom_preprocessing(self):
# Add custom logic before test execution
pass
Custom Assertions
class CustomAssertions(Assertions):
@staticmethod
def performance_threshold(execution_time, max_time):
if execution_time > max_time:
raise AssertionError(f"Execution too slow: {execution_time}s > {max_time}s")
11. Version Compatibility
- Python Version: 3.6+
- Dependencies: Standard library only (no external dependencies for core functionality)
- Backward Compatibility: Fully compatible with existing
JSONRunnercode - Platform Support: Windows, macOS, Linux
12. Performance Benchmarks
| Test Scenario | Sequential | Parallel (Thread) | Parallel (Process) | Speedup |
|---|---|---|---|---|
| 10 I/O tests | 5.2s | 1.4s | 2.1s | 3.7x |
| 20 CPU tests | 12.8s | 8.9s | 6.2s | 2.1x |
| Mixed tests | 8.5s | 2.3s | 3.1s | 3.7x |
13. Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
python -m pytest tests/ -v - Submit a pull request
14. License
This project is licensed under the MIT License - see the LICENSE file for details.
📚 Complete Documentation
For comprehensive documentation including detailed Setup Module guide, API reference, and advanced usage examples, see:
The user manual includes:
- 🔧 Setup Module: Complete guide for environment variables and custom plugins
- 🚀 Parallel Testing: Advanced parallel execution strategies
- 📁 File Comparison: Detailed comparison capabilities for all file types
- 🔌 API Reference: Full API documentation and examples
- 🛠️ Troubleshooting: Common issues and solutions
- 📝 Best Practices: Recommended patterns and configurations
🚀 Ready to supercharge your testing workflow with setup modules, parallel execution and advanced file comparison!
For detailed parallel testing guide, see: PARALLEL_TESTING_GUIDE.md
支持数据过滤(New in 0.3.6)
你可以通过 --h5-data-filter 选项只比较满足特定条件的数据。例如:
# 只比较大于 1e-6 的数据
compare-files data1.h5 data2.h5 --h5-data-filter '>1e-6'
# 只比较绝对值大于 1e-6 的数据
compare-files data1.h5 data2.h5 --h5-data-filter 'abs>1e-6'
# 只比较小于等于 0.01 的数据
compare-files data1.h5 data2.h5 --h5-data-filter '<=0.01'
支持的表达式包括:>, >=, <, <=, ==,以及 abs 前缀(绝对值过滤)。
版本更新日志
0.4.2 (Latest)
✨ New Features
- Resource-Aware CPU Scheduling: Automatic CPU core detection and semaphore-based scheduling to prevent resource conflicts
- Added
cpu_coresfield inresourcesconfiguration to specify CPU requirements per task - Automatic environment variable injection (
OMP_NUM_THREADS,MKL_NUM_THREADS,NPROC) to constrain solver threads - Prevents solver "runaway" scenarios where solvers ignore Python's scheduling
- Intelligent resource pool management: automatically reserves 2 cores for system use
- Added
- Enhanced execution engine: Support for custom environment variables in test execution
🔧 Improvements
- Better resource management: Tasks now wait for available CPU cores instead of overwhelming the system
- Automatic CPU detection: No manual configuration needed - framework detects available cores automatically
- Thread-safe resource allocation: Semaphore-based scheduling ensures thread-safe resource management
0.3.7
🐛 Bug Fixes
- Fixed H5 table regex matching:
--h5-table-regex=table1,table2now correctly matches bothtable1andtable2instead of treating the entire string as a single regex pattern - Enhanced regex pattern support: Multiple comma-separated table names are now supported in
--h5-table-regexparameter
✨ New Features
- Improved HDF5 comparison: Better handling of multiple table selection with regex patterns
- Enhanced debug output: More detailed logging for HDF5 table matching process
🔧 Improvements
- Backward compatibility: All existing functionality remains unchanged
- Better error handling: More informative error messages for regex pattern parsing
0.3.6
✨ New Features
- Data filtering for HDF5 files: Added
--h5-data-filteroption to compare only data meeting specific criteria - Enhanced HDF5 comparison: Support for absolute value filtering and various comparison operators
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cli_test_framework-0.5.0.tar.gz.
File metadata
- Download URL: cli_test_framework-0.5.0.tar.gz
- Upload date:
- Size: 115.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f36044c5b57fda1cdeb7b86a173282638a50dce4daaf31893f7983dff4bd1a2
|
|
| MD5 |
a655459bf45b6d73b7429f2c9662eb68
|
|
| BLAKE2b-256 |
414eb069d64b646401c851cddcb7ecbd2f63d1f99b5df909adae1f9523a672fc
|
File details
Details for the file cli_test_framework-0.5.0-py3-none-any.whl.
File metadata
- Download URL: cli_test_framework-0.5.0-py3-none-any.whl
- Upload date:
- Size: 63.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d7d1f914739c24b4a5a2c8fa87f52d5021d98ca38570f7f642c51b7fa582649
|
|
| MD5 |
fa443c81c1d9f6cd2800569eb00c5bc0
|
|
| BLAKE2b-256 |
c582021e2a41522cb0a52ea1080a59f78e4bbf11b7afe11cf193174f54874762
|