cli-test-framework 0.8.1__tar.gz → 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_test_framework-0.8.1/src/cli_test_framework.egg-info → cli_test_framework-0.9.1}/PKG-INFO +20 -2
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/README.md +19 -1
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/docs/user_manual.md +178 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/setup.py +1 -1
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/cli.py +29 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/base_runner.py +12 -4
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/config_loader.py +37 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/parallel_runner.py +4 -7
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/test_case.py +2 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/config_runner.py +8 -7
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/json_runner.py +2 -7
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/parallel_config_runner.py +8 -7
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/parallel_json_runner.py +2 -6
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/parallel_yaml_runner.py +2 -6
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/yaml_runner.py +2 -7
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1/src/cli_test_framework.egg-info}/PKG-INFO +20 -2
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework.egg-info/SOURCES.txt +5 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.9.1/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.9.1/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.9.1/tests/unit/core/test_config_loader.py +136 -0
- cli_test_framework-0.9.1/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.9.1/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.9.1/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/test_json_yaml_runner.py +50 -0
- cli_test_framework-0.9.1/tests/unit/runners/test_test_case_tag_filter.py +254 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/test_cli.py +47 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.8.1/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
- cli_test_framework-0.8.1/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/MANIFEST.in +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/docs/design.md +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/docs/design_en.md +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/docs/user_manual_en.md +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/pyproject.toml +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/setup.cfg +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/commands/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/commands/compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/assertions.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/execution.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/history_store.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/process_worker.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/setup.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/types.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/base_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/binary_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/csv_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/factory.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/h5_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/json_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/result.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/text_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/file_comparator/xml_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/logging_config.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/utils/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/utils/junit_xml_writer.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/utils/path_resolver.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/utils/report_generator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework.egg-info/dependency_links.txt +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework.egg-info/entry_points.txt +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework.egg-info/requires.txt +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework.egg-info/top_level.txt +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/README.md +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/__init__.cpython-39.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/run_all.cpython-312.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/run_all.cpython-39.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/conftest.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/demos/h5_filter_demo.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/demos/manual_report_example.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/demos/perf_parallel.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__init__.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__pycache__/__init__.cpython-312.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__pycache__/__init__.cpython-39.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/__pycache__/test_user_flows.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/e2e/test_user_flows.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/fixtures/test_cases.json +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/fixtures/test_cases.yaml +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/fixtures/test_cases1.json +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/fixtures/test_with_setup.json +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/fixtures/test_with_setup.yaml +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_binary_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_csv_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_h5_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_json_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_text_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/file_compare/test_xml_compare.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/parallel/test_parallel_runner.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/path_handling/test_spaces_in_paths.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/test_history_feature.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/test_real_command_execution.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/integration/test_sequence.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/run_all.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/test_report.txt +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/commands/test_compare_command.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-9.0.3.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/__pycache__/test_setup.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/test_assertions.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/test_execution.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/test_history_store.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/test_process_worker.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/core/test_setup.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/runners/test_test_case_filter.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/test_h5_comparator.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/test_run_all.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-7.4.4.pyc +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/test_junit_xml_writer.py +0 -0
- {cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/tests/unit/utils/test_report_generator.py +0 -0
{cli_test_framework-0.8.1/src/cli_test_framework.egg-info → cli_test_framework-0.9.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-test-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.
|
|
5
5
|
Home-page: https://github.com/ozil111/cli-test-framework
|
|
6
6
|
Author: Xiaotong Wang
|
|
@@ -56,7 +56,7 @@ CLI Test Framework was built for that workflow.
|
|
|
56
56
|
- **Sequence Steps** — Multi-step execution within a single test case, fail-fast
|
|
57
57
|
- **Setup Module** — Auto-configure environment variables before tests, auto-cleanup after
|
|
58
58
|
- **File Comparison** — Text / JSON / CSV / XML / HDF5 / Binary, with CLI and embedded assertion support
|
|
59
|
-
- **Filtered Execution** — Run specific test cases by name
|
|
59
|
+
- **Filtered Execution** — Run specific test cases by name or tag
|
|
60
60
|
- **JUnit XML Output** — `--junit-xml` for GitLab CI / Jenkins / CircleCI test report panels
|
|
61
61
|
|
|
62
62
|
## Quick Start
|
|
@@ -76,6 +76,7 @@ pip install cli-test-framework
|
|
|
76
76
|
"name": "hello",
|
|
77
77
|
"command": "echo",
|
|
78
78
|
"args": ["Hello World"],
|
|
79
|
+
"tags": ["smoke"],
|
|
79
80
|
"expected": {
|
|
80
81
|
"return_code": 0,
|
|
81
82
|
"output_contains": ["Hello World"]
|
|
@@ -145,6 +146,19 @@ Multiple files and mixed assertion types coexist naturally:
|
|
|
145
146
|
cli-test run test_cases.json --parallel --workers 4
|
|
146
147
|
```
|
|
147
148
|
|
|
149
|
+
### Run Specific Cases
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# By name
|
|
153
|
+
cli-test run test_cases.json -t test_1 -t test_2
|
|
154
|
+
|
|
155
|
+
# By tag
|
|
156
|
+
cli-test run test_cases.json --tag smoke
|
|
157
|
+
|
|
158
|
+
# Combine name and tag (AND logic)
|
|
159
|
+
cli-test run test_cases.json -t test_1 --tag smoke
|
|
160
|
+
```
|
|
161
|
+
|
|
148
162
|
### JUnit XML (CI Integration)
|
|
149
163
|
|
|
150
164
|
```bash
|
|
@@ -184,6 +198,10 @@ compare-files result1.h5 result2.h5 --h5-table-regex "output_.*" --h5-rtol 1e-5
|
|
|
184
198
|
|
|
185
199
|
## Changelog
|
|
186
200
|
|
|
201
|
+
### 0.9.0
|
|
202
|
+
|
|
203
|
+
- **Tag filtering**: New `tags` field in test case definitions for categorization. Use `--tag` CLI option or `test_case_tag_filter` Python API to batch-filter test cases. Acts as OR within tags, AND with name filtering.
|
|
204
|
+
|
|
187
205
|
### 0.8.0
|
|
188
206
|
|
|
189
207
|
- **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. Supports `passed` / `failed` / `timeout` / `error` status mapping. Zero extra dependencies — built with `xml.etree.ElementTree`.
|
|
@@ -20,7 +20,7 @@ CLI Test Framework was built for that workflow.
|
|
|
20
20
|
- **Sequence Steps** — Multi-step execution within a single test case, fail-fast
|
|
21
21
|
- **Setup Module** — Auto-configure environment variables before tests, auto-cleanup after
|
|
22
22
|
- **File Comparison** — Text / JSON / CSV / XML / HDF5 / Binary, with CLI and embedded assertion support
|
|
23
|
-
- **Filtered Execution** — Run specific test cases by name
|
|
23
|
+
- **Filtered Execution** — Run specific test cases by name or tag
|
|
24
24
|
- **JUnit XML Output** — `--junit-xml` for GitLab CI / Jenkins / CircleCI test report panels
|
|
25
25
|
|
|
26
26
|
## Quick Start
|
|
@@ -40,6 +40,7 @@ pip install cli-test-framework
|
|
|
40
40
|
"name": "hello",
|
|
41
41
|
"command": "echo",
|
|
42
42
|
"args": ["Hello World"],
|
|
43
|
+
"tags": ["smoke"],
|
|
43
44
|
"expected": {
|
|
44
45
|
"return_code": 0,
|
|
45
46
|
"output_contains": ["Hello World"]
|
|
@@ -109,6 +110,19 @@ Multiple files and mixed assertion types coexist naturally:
|
|
|
109
110
|
cli-test run test_cases.json --parallel --workers 4
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
### Run Specific Cases
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# By name
|
|
117
|
+
cli-test run test_cases.json -t test_1 -t test_2
|
|
118
|
+
|
|
119
|
+
# By tag
|
|
120
|
+
cli-test run test_cases.json --tag smoke
|
|
121
|
+
|
|
122
|
+
# Combine name and tag (AND logic)
|
|
123
|
+
cli-test run test_cases.json -t test_1 --tag smoke
|
|
124
|
+
```
|
|
125
|
+
|
|
112
126
|
### JUnit XML (CI Integration)
|
|
113
127
|
|
|
114
128
|
```bash
|
|
@@ -148,6 +162,10 @@ compare-files result1.h5 result2.h5 --h5-table-regex "output_.*" --h5-rtol 1e-5
|
|
|
148
162
|
|
|
149
163
|
## Changelog
|
|
150
164
|
|
|
165
|
+
### 0.9.0
|
|
166
|
+
|
|
167
|
+
- **Tag filtering**: New `tags` field in test case definitions for categorization. Use `--tag` CLI option or `test_case_tag_filter` Python API to batch-filter test cases. Acts as OR within tags, AND with name filtering.
|
|
168
|
+
|
|
151
169
|
### 0.8.0
|
|
152
170
|
|
|
153
171
|
- **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. Supports `passed` / `failed` / `timeout` / `error` status mapping. Zero extra dependencies — built with `xml.etree.ElementTree`.
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
- [安装](#安装)
|
|
6
6
|
- [测试用例定义](#测试用例定义)
|
|
7
7
|
- [运行测试](#运行测试)
|
|
8
|
+
- [占位符(变量替换)](#占位符变量替换)
|
|
9
|
+
- [标签过滤](#标签过滤)
|
|
8
10
|
- [Setup 模块](#setup-模块)
|
|
9
11
|
- [并行测试](#并行测试)
|
|
10
12
|
- [顺序步骤测试](#顺序步骤测试)
|
|
@@ -100,6 +102,7 @@ test_cases:
|
|
|
100
102
|
| `args` | 否 | 命令参数列表 |
|
|
101
103
|
| `description` | 否 | 测试用例描述 |
|
|
102
104
|
| `timeout` | 否 | 超时秒数,默认 3600,设 `null` 无限制 |
|
|
105
|
+
| `tags` | 否 | 标签列表,用于批量过滤(如 `["smoke", "fast"]`) |
|
|
103
106
|
| `resources` | 否 | 资源配置,见[资源感知调度](#资源感知调度) |
|
|
104
107
|
| `expected.return_code` | 否 | 期望返回码 |
|
|
105
108
|
| `expected.output_contains` | 否 | 输出需包含的字符串列表 |
|
|
@@ -162,6 +165,13 @@ cli-test run test_cases.json --parallel --execution-mode process
|
|
|
162
165
|
# 只运行指定用例
|
|
163
166
|
cli-test run test_cases.json -t test_name_1 -t test_name_2
|
|
164
167
|
|
|
168
|
+
# 按标签过滤
|
|
169
|
+
cli-test run test_cases.json --tag smoke
|
|
170
|
+
cli-test run test_cases.json --tag smoke --tag regression
|
|
171
|
+
|
|
172
|
+
# 同时按名称和标签过滤(AND 关系)
|
|
173
|
+
cli-test run test_cases.json -t test_name_1 --tag smoke
|
|
174
|
+
|
|
165
175
|
# 详细输出
|
|
166
176
|
cli-test run test_cases.json --verbose
|
|
167
177
|
|
|
@@ -191,6 +201,7 @@ runner = JSONRunner(
|
|
|
191
201
|
config_file="test_cases.json",
|
|
192
202
|
workspace="/path/to/project", # 可选,默认项目根目录
|
|
193
203
|
test_case_filter=["test_1"], # 可选,只运行指定用例
|
|
204
|
+
test_case_tag_filter=["smoke"], # 可选,只运行包含指定标签的用例
|
|
194
205
|
history_dir="./hist", # 可选,启用历史记录与回归检测
|
|
195
206
|
regression_threshold=2.0, # 可选,回归阈值倍数,默认 1.5
|
|
196
207
|
)
|
|
@@ -235,6 +246,173 @@ for detail in runner.results["details"]:
|
|
|
235
246
|
print(detail["name"], detail["status"], detail.get("message", ""))
|
|
236
247
|
```
|
|
237
248
|
|
|
249
|
+
## 占位符(变量替换)
|
|
250
|
+
|
|
251
|
+
当同一个配置文件需要在不同环境下使用不同参数(如求解器路径、模型文件路径等)时,可以用 `{变量名}` 占位符编写配置,运行时通过 `--var` 或 `variables` 参数传入实际值。
|
|
252
|
+
|
|
253
|
+
### 编写含占位符的配置
|
|
254
|
+
|
|
255
|
+
JSON:
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"test_cases": [
|
|
260
|
+
{
|
|
261
|
+
"name": "求解器测试",
|
|
262
|
+
"command": "{solver}",
|
|
263
|
+
"args": ["--input", "{model}", "--output", "{output}"],
|
|
264
|
+
"expected": { "return_code": 0 }
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
YAML:
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
test_cases:
|
|
274
|
+
- name: 求解器测试
|
|
275
|
+
command: "{solver}"
|
|
276
|
+
args: ["--input", "{model}", "--output", "{output}"]
|
|
277
|
+
expected:
|
|
278
|
+
return_code: 0
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
占位符 `{变量名}` 可出现在配置文件的任意字符串值中,包括 `command`、`args`、`name`、`expected.output_contains` 等。支持同一个字符串中使用多个占位符,如 `"{solver} --input {model}"`。
|
|
282
|
+
|
|
283
|
+
> **安全设计**:只有 `variables` 字典中存在的 key 才会被替换。`{xxx}` 若无匹配不会报错,而是原样保留。因此 `expected.output_matches` 中的正则模式(如 `{2,}`、`\d{4}`)不受影响。
|
|
284
|
+
|
|
285
|
+
### 用法
|
|
286
|
+
|
|
287
|
+
#### CLI
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# 单个变量
|
|
291
|
+
cli-test run test_cases.json --var solver=/opt/solver/bin/solver.exe
|
|
292
|
+
|
|
293
|
+
# 多个变量
|
|
294
|
+
cli-test run test_cases.json --var solver=/opt/solver/bin/solver.exe --var model=./data/model.dat
|
|
295
|
+
|
|
296
|
+
# 与并行模式、标签过滤等组合使用
|
|
297
|
+
cli-test run test_cases.json --var solver=solver.exe --parallel --workers 4 --tag smoke
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
`--var` 格式为 `KEY=VALUE`,可多次使用。等号分隔,key 和 value 两侧的空格会被自动去除。
|
|
301
|
+
|
|
302
|
+
#### Python API
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
from cli_test_framework.runners import JSONRunner, YAMLRunner, ParallelJSONRunner, ParallelYAMLRunner
|
|
306
|
+
|
|
307
|
+
# 顺序运行
|
|
308
|
+
runner = JSONRunner(
|
|
309
|
+
config_file="test_cases.json",
|
|
310
|
+
variables={
|
|
311
|
+
"solver": "/opt/solver/bin/solver.exe",
|
|
312
|
+
"model": "./data/model.dat",
|
|
313
|
+
"output": "./results/output.dat",
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
success = runner.run_tests()
|
|
317
|
+
|
|
318
|
+
# 并行运行
|
|
319
|
+
runner = ParallelJSONRunner(
|
|
320
|
+
config_file="test_cases.json",
|
|
321
|
+
variables={"solver": "/opt/solver/bin/solver.exe"},
|
|
322
|
+
)
|
|
323
|
+
success = runner.run_tests()
|
|
324
|
+
|
|
325
|
+
# YAML 同样支持
|
|
326
|
+
runner = YAMLRunner(
|
|
327
|
+
config_file="test_cases.yaml",
|
|
328
|
+
variables={"solver": "/opt/solver/bin/solver.exe"},
|
|
329
|
+
)
|
|
330
|
+
success = runner.run_tests()
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 适用场景
|
|
334
|
+
|
|
335
|
+
| 场景 | 示例 |
|
|
336
|
+
|---|---|
|
|
337
|
+
| 不同求解器版本测试 | `--var solver=v1.0/solver.exe` ↔ `--var solver=v2.0/solver.exe` |
|
|
338
|
+
| 不同输入数据 | `--var model=case_1.dat` ↔ `--var model=case_2.dat` |
|
|
339
|
+
| CI/CD 环境适配 | 本地 `/opt/solver.exe`,CI `/runner/solver.exe` |
|
|
340
|
+
| 多平台路径 | Windows `--var solver=C:\solver.exe`,Linux `--var solver=/opt/solver.exe` |
|
|
341
|
+
|
|
342
|
+
## 标签过滤
|
|
343
|
+
|
|
344
|
+
通过标签(tags)可以对测试用例进行分类,并在运行时按标签批量过滤。标签过滤与名称过滤可同时使用,满足 AND 关系(两个条件都必须满足)。
|
|
345
|
+
|
|
346
|
+
### 在测试用例中定义标签
|
|
347
|
+
|
|
348
|
+
JSON:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"test_cases": [
|
|
353
|
+
{
|
|
354
|
+
"name": "快速测试",
|
|
355
|
+
"command": "echo",
|
|
356
|
+
"args": ["hello"],
|
|
357
|
+
"tags": ["smoke", "fast"],
|
|
358
|
+
"expected": { "return_code": 0 }
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"name": "完整回归测试",
|
|
362
|
+
"command": "python",
|
|
363
|
+
"args": ["long_test.py"],
|
|
364
|
+
"tags": ["regression", "slow"],
|
|
365
|
+
"expected": { "return_code": 0 }
|
|
366
|
+
}
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
YAML:
|
|
372
|
+
|
|
373
|
+
```yaml
|
|
374
|
+
test_cases:
|
|
375
|
+
- name: 快速测试
|
|
376
|
+
command: echo
|
|
377
|
+
args: ["hello"]
|
|
378
|
+
tags: ["smoke", "fast"]
|
|
379
|
+
expected:
|
|
380
|
+
return_code: 0
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
`tags` 是可选字段,不指定则默认为空列表。每个用例可以有多个标签。
|
|
384
|
+
|
|
385
|
+
### 运行时过滤
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
# 只运行带 "smoke" 标签的用例
|
|
389
|
+
cli-test run test_cases.json --tag smoke
|
|
390
|
+
|
|
391
|
+
# 运行带 "smoke" 或 "regression" 标签的用例(OR 关系)
|
|
392
|
+
cli-test run test_cases.json --tag smoke --tag regression
|
|
393
|
+
|
|
394
|
+
# 同时按名称和标签过滤(AND 关系)
|
|
395
|
+
cli-test run test_cases.json -t alpha --tag fast
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Python API
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
runner = JSONRunner(
|
|
402
|
+
config_file="test_cases.json",
|
|
403
|
+
test_case_tag_filter=["smoke"], # 只运行含 smoke 标签的用例
|
|
404
|
+
)
|
|
405
|
+
success = runner.run_tests()
|
|
406
|
+
|
|
407
|
+
# 结合名称过滤
|
|
408
|
+
runner = JSONRunner(
|
|
409
|
+
config_file="test_cases.json",
|
|
410
|
+
test_case_filter=["alpha", "beta"],
|
|
411
|
+
test_case_tag_filter=["fast"],
|
|
412
|
+
)
|
|
413
|
+
success = runner.run_tests()
|
|
414
|
+
```
|
|
415
|
+
|
|
238
416
|
## Setup 模块
|
|
239
417
|
|
|
240
418
|
Setup 模块在测试前执行初始化、测试后执行清理。
|
|
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="cli-test-framework",
|
|
11
|
-
version="0.
|
|
11
|
+
version="0.9.1",
|
|
12
12
|
author="Xiaotong Wang",
|
|
13
13
|
author_email="xiaotongwang98@gmail.com",
|
|
14
14
|
description="A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.",
|
|
@@ -22,6 +22,18 @@ from .utils.junit_xml_writer import write_junit_xml
|
|
|
22
22
|
logger = logging.getLogger("cli_test_framework.cli")
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
def _parse_vars(var_list):
|
|
26
|
+
"""Parse ``['solver=/path', 'model=./m.dat']`` → ``{'solver': '/path', ...}``."""
|
|
27
|
+
variables = {}
|
|
28
|
+
for item in var_list or []:
|
|
29
|
+
if '=' not in item:
|
|
30
|
+
logger.warning("Ignoring invalid --var '%s' (expected KEY=VALUE)", item)
|
|
31
|
+
continue
|
|
32
|
+
key, _, value = item.partition('=')
|
|
33
|
+
variables[key.strip()] = value.strip()
|
|
34
|
+
return variables
|
|
35
|
+
|
|
36
|
+
|
|
25
37
|
def create_parser():
|
|
26
38
|
"""Create and configure the argument parser"""
|
|
27
39
|
parser = argparse.ArgumentParser(
|
|
@@ -51,6 +63,8 @@ Examples:
|
|
|
51
63
|
help='Output format for test results')
|
|
52
64
|
run_parser.add_argument('--test-case', '-t', action='append', default=None,
|
|
53
65
|
help='Run only specified test case(s) by name (can be used multiple times)')
|
|
66
|
+
run_parser.add_argument('--tag', action='append', default=None,
|
|
67
|
+
help='Run only test cases with matching tag(s) (can be used multiple times)')
|
|
54
68
|
run_parser.add_argument('--history-dir',
|
|
55
69
|
help='Directory for .symtest runtime history (enables smart scheduling & regression detection)')
|
|
56
70
|
run_parser.add_argument('--regression-threshold', type=float, default=1.5,
|
|
@@ -59,6 +73,11 @@ Examples:
|
|
|
59
73
|
run_parser.add_argument('--debug', action='store_true', help='Enable debug mode')
|
|
60
74
|
run_parser.add_argument('--junit-xml', dest='junit_xml',
|
|
61
75
|
help='Write JUnit XML report to the specified file path')
|
|
76
|
+
run_parser.add_argument('--var', action='append', default=[],
|
|
77
|
+
metavar='KEY=VALUE',
|
|
78
|
+
help='Set a variable for config placeholder substitution, '
|
|
79
|
+
'e.g. --var solver=/path/to/solver '
|
|
80
|
+
'(can be used multiple times)')
|
|
62
81
|
|
|
63
82
|
# ---- Compare command ----
|
|
64
83
|
compare_parser = subparsers.add_parser('compare', help='Compare two files')
|
|
@@ -134,6 +153,8 @@ def run_tests(args):
|
|
|
134
153
|
# construct Namespace objects without the newer arguments.
|
|
135
154
|
history_dir = getattr(args, 'history_dir', None)
|
|
136
155
|
regression_threshold = getattr(args, 'regression_threshold', 1.5)
|
|
156
|
+
var_list = getattr(args, 'var', [])
|
|
157
|
+
variables = _parse_vars(var_list)
|
|
137
158
|
|
|
138
159
|
try:
|
|
139
160
|
if args.parallel:
|
|
@@ -145,8 +166,10 @@ def run_tests(args):
|
|
|
145
166
|
max_workers=args.workers,
|
|
146
167
|
execution_mode=args.execution_mode,
|
|
147
168
|
test_case_filter=args.test_case,
|
|
169
|
+
test_case_tag_filter=args.tag,
|
|
148
170
|
history_dir=history_dir,
|
|
149
171
|
regression_threshold=regression_threshold,
|
|
172
|
+
variables=variables,
|
|
150
173
|
)
|
|
151
174
|
elif file_ext in ['.yaml', '.yml']:
|
|
152
175
|
runner = ParallelYAMLRunner(
|
|
@@ -155,8 +178,10 @@ def run_tests(args):
|
|
|
155
178
|
max_workers=args.workers,
|
|
156
179
|
execution_mode=args.execution_mode,
|
|
157
180
|
test_case_filter=args.test_case,
|
|
181
|
+
test_case_tag_filter=args.tag,
|
|
158
182
|
history_dir=history_dir,
|
|
159
183
|
regression_threshold=regression_threshold,
|
|
184
|
+
variables=variables,
|
|
160
185
|
)
|
|
161
186
|
else:
|
|
162
187
|
logger.error("Unsupported configuration file format for parallel mode: %s", file_ext)
|
|
@@ -168,16 +193,20 @@ def run_tests(args):
|
|
|
168
193
|
config_file=str(config_file),
|
|
169
194
|
workspace=args.workspace,
|
|
170
195
|
test_case_filter=args.test_case,
|
|
196
|
+
test_case_tag_filter=args.tag,
|
|
171
197
|
history_dir=history_dir,
|
|
172
198
|
regression_threshold=regression_threshold,
|
|
199
|
+
variables=variables,
|
|
173
200
|
)
|
|
174
201
|
elif file_ext in ['.yaml', '.yml']:
|
|
175
202
|
runner = YAMLRunner(
|
|
176
203
|
config_file=str(config_file),
|
|
177
204
|
workspace=args.workspace,
|
|
178
205
|
test_case_filter=args.test_case,
|
|
206
|
+
test_case_tag_filter=args.tag,
|
|
179
207
|
history_dir=history_dir,
|
|
180
208
|
regression_threshold=regression_threshold,
|
|
209
|
+
variables=variables,
|
|
181
210
|
)
|
|
182
211
|
else:
|
|
183
212
|
logger.error("Unsupported configuration file format: %s", file_ext)
|
{cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/base_runner.py
RENAMED
|
@@ -14,6 +14,7 @@ logger = logging.getLogger("cli_test_framework.core.base_runner")
|
|
|
14
14
|
class BaseRunner(ABC):
|
|
15
15
|
def __init__(self, config_file: str, workspace: Optional[str] = None,
|
|
16
16
|
test_case_filter: Optional[List[str]] = None,
|
|
17
|
+
test_case_tag_filter: Optional[List[str]] = None,
|
|
17
18
|
history_dir: Optional[str] = None,
|
|
18
19
|
regression_threshold: float = 1.5):
|
|
19
20
|
if workspace:
|
|
@@ -27,6 +28,7 @@ class BaseRunner(ABC):
|
|
|
27
28
|
self.config_path = self.workspace / config_path
|
|
28
29
|
self.test_cases: List[TestCase] = []
|
|
29
30
|
self.test_case_filter: Optional[List[str]] = test_case_filter
|
|
31
|
+
self.test_case_tag_filter: Optional[List[str]] = test_case_tag_filter
|
|
30
32
|
if history_dir:
|
|
31
33
|
self.history_dir = str((self.workspace / history_dir).resolve())
|
|
32
34
|
else:
|
|
@@ -63,16 +65,22 @@ class BaseRunner(ABC):
|
|
|
63
65
|
# pass
|
|
64
66
|
|
|
65
67
|
def _apply_test_case_filter(self) -> None:
|
|
66
|
-
"""根据 test_case_filter 过滤测试用例"""
|
|
67
|
-
if self.test_case_filter:
|
|
68
|
+
"""根据 test_case_filter 和 test_case_tag_filter 过滤测试用例"""
|
|
69
|
+
if self.test_case_filter or self.test_case_tag_filter:
|
|
68
70
|
original_count = len(self.test_cases)
|
|
69
|
-
self.test_cases = [
|
|
71
|
+
self.test_cases = [
|
|
72
|
+
tc for tc in self.test_cases
|
|
73
|
+
if (not self.test_case_filter or tc.name in self.test_case_filter)
|
|
74
|
+
and (not self.test_case_tag_filter
|
|
75
|
+
or set(tc.tags or []) & set(self.test_case_tag_filter))
|
|
76
|
+
]
|
|
70
77
|
filtered_out = original_count - len(self.test_cases)
|
|
71
78
|
if filtered_out > 0:
|
|
72
79
|
logger.info("Filtered out %d test case(s). Running %d specified case(s).",
|
|
73
80
|
filtered_out, len(self.test_cases))
|
|
74
81
|
if not self.test_cases:
|
|
75
|
-
logger.warning("No matching test cases found for:
|
|
82
|
+
logger.warning("No matching test cases found for: names=%s, tags=%s",
|
|
83
|
+
self.test_case_filter, self.test_case_tag_filter)
|
|
76
84
|
|
|
77
85
|
def run_tests(self) -> bool:
|
|
78
86
|
"""Run all test cases and return whether all tests passed"""
|
{cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/config_loader.py
RENAMED
|
@@ -11,6 +11,7 @@ Backward-compatible: the runner classes still expose ``load_test_cases()`` and
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import logging
|
|
14
|
+
import re
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Any, Dict, List, Optional, Tuple
|
|
16
17
|
|
|
@@ -20,6 +21,40 @@ from ..utils.path_resolver import resolve_paths
|
|
|
20
21
|
|
|
21
22
|
logger = logging.getLogger("cli_test_framework.core.config_loader")
|
|
22
23
|
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Placeholder substitution
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
_PLACEHOLDER_RE = re.compile(r'\{(\w+)\}')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def substitute_placeholders(
|
|
32
|
+
config: Dict[str, Any],
|
|
33
|
+
variables: Optional[Dict[str, Any]] = None,
|
|
34
|
+
) -> Dict[str, Any]:
|
|
35
|
+
"""递归替换 config 中字符串值的 ``{placeholder}`` 占位符。
|
|
36
|
+
|
|
37
|
+
只替换 ``variables`` 中存在的 key,未匹配的 ``{xxx}`` 原样保留,
|
|
38
|
+
不会影响 ``expected.matches`` 等字段中的正则模式(如 ``{2}``)。
|
|
39
|
+
"""
|
|
40
|
+
if not variables:
|
|
41
|
+
return config
|
|
42
|
+
|
|
43
|
+
def _sub(value: Any) -> Any:
|
|
44
|
+
if isinstance(value, str):
|
|
45
|
+
return _PLACEHOLDER_RE.sub(
|
|
46
|
+
lambda m: str(variables[m.group(1)])
|
|
47
|
+
if m.group(1) in variables else m.group(0),
|
|
48
|
+
value,
|
|
49
|
+
)
|
|
50
|
+
if isinstance(value, list):
|
|
51
|
+
return [_sub(item) for item in value]
|
|
52
|
+
if isinstance(value, dict):
|
|
53
|
+
return {k: _sub(v) for k, v in value.items()}
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
return _sub(config)
|
|
57
|
+
|
|
23
58
|
|
|
24
59
|
# ---------------------------------------------------------------------------
|
|
25
60
|
# Test-case parsing (loaded dict → list[TestCase])
|
|
@@ -79,6 +114,7 @@ def parse_test_cases(
|
|
|
79
114
|
steps=steps,
|
|
80
115
|
description=case.get("description", ""),
|
|
81
116
|
resources=case.get("resources"),
|
|
117
|
+
tags=case.get("tags", []),
|
|
82
118
|
))
|
|
83
119
|
else:
|
|
84
120
|
# ── Single-command mode (backward-compatible) ──
|
|
@@ -100,6 +136,7 @@ def parse_test_cases(
|
|
|
100
136
|
description=case.get("description", ""),
|
|
101
137
|
timeout=case.get("timeout"),
|
|
102
138
|
resources=case.get("resources"),
|
|
139
|
+
tags=case.get("tags", []),
|
|
103
140
|
))
|
|
104
141
|
|
|
105
142
|
return cases
|
{cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/parallel_runner.py
RENAMED
|
@@ -81,9 +81,7 @@ class ParallelRunner(BaseRunner):
|
|
|
81
81
|
def __init__(self, config_file: str, workspace: Optional[str] = None,
|
|
82
82
|
max_workers: Optional[int] = None,
|
|
83
83
|
execution_mode: str = "thread",
|
|
84
|
-
|
|
85
|
-
history_dir: Optional[str] = None,
|
|
86
|
-
regression_threshold: float = 1.5):
|
|
84
|
+
**kwargs):
|
|
87
85
|
"""
|
|
88
86
|
初始化并行运行器
|
|
89
87
|
|
|
@@ -92,11 +90,10 @@ class ParallelRunner(BaseRunner):
|
|
|
92
90
|
workspace: 工作目录
|
|
93
91
|
max_workers: 最大并发数,默认为CPU核心数
|
|
94
92
|
execution_mode: 执行模式,'thread'(线程) 或 'process'(进程)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
regression_threshold: 回归检测阈值
|
|
93
|
+
**kwargs: 透传给 BaseRunner 的额外参数
|
|
94
|
+
(test_case_filter, test_case_tag_filter, history_dir, regression_threshold)
|
|
98
95
|
"""
|
|
99
|
-
super().__init__(config_file, workspace,
|
|
96
|
+
super().__init__(config_file, workspace, **kwargs)
|
|
100
97
|
self.max_workers = max_workers
|
|
101
98
|
self.execution_mode = execution_mode
|
|
102
99
|
self.lock = threading.Lock() # 用于线程安全的结果更新
|
{cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/core/test_case.py
RENAMED
|
@@ -21,6 +21,7 @@ class TestCase:
|
|
|
21
21
|
timeout: Optional[float] = None
|
|
22
22
|
resources: Optional[Dict[str, Any]] = None
|
|
23
23
|
steps: Optional[List[TestCaseStep]] = None
|
|
24
|
+
tags: List[str] = field(default_factory=list)
|
|
24
25
|
|
|
25
26
|
def to_dict(self) -> Dict[str, Any]:
|
|
26
27
|
"""Convert test case to dictionary format"""
|
|
@@ -31,6 +32,7 @@ class TestCase:
|
|
|
31
32
|
"expected": self.expected,
|
|
32
33
|
"timeout": self.timeout,
|
|
33
34
|
"resources": self.resources,
|
|
35
|
+
"tags": self.tags,
|
|
34
36
|
}
|
|
35
37
|
if self.steps is not None:
|
|
36
38
|
result["steps"] = [
|
|
@@ -10,7 +10,7 @@ import logging
|
|
|
10
10
|
from typing import Optional, Dict, Any, Callable, BinaryIO
|
|
11
11
|
|
|
12
12
|
from ..core.base_runner import BaseRunner
|
|
13
|
-
from ..core.config_loader import parse_test_cases
|
|
13
|
+
from ..core.config_loader import parse_test_cases, substitute_placeholders
|
|
14
14
|
from ..core.test_case import TestCase
|
|
15
15
|
from ..core.execution import execute_single_test_case
|
|
16
16
|
from ..core.types import TestCaseData
|
|
@@ -29,13 +29,12 @@ class ConfigRunner(BaseRunner):
|
|
|
29
29
|
|
|
30
30
|
def __init__(self, config_file: str = "test_cases.json",
|
|
31
31
|
workspace: Optional[str] = None,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
super().__init__(config_file, workspace, test_case_filter,
|
|
37
|
-
history_dir, regression_threshold)
|
|
32
|
+
config_loader: Optional[Callable[[BinaryIO], Dict[str, Any]]] = None,
|
|
33
|
+
variables: Optional[Dict[str, Any]] = None,
|
|
34
|
+
**kwargs):
|
|
35
|
+
super().__init__(config_file, workspace, **kwargs)
|
|
38
36
|
self._config_loader = config_loader
|
|
37
|
+
self._variables = variables or {}
|
|
39
38
|
# Backward-compatible attribute for tests that patch path_resolver
|
|
40
39
|
self.path_resolver = PathResolver(self.workspace)
|
|
41
40
|
|
|
@@ -49,6 +48,8 @@ class ConfigRunner(BaseRunner):
|
|
|
49
48
|
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
50
49
|
config = self._config_loader(f)
|
|
51
50
|
|
|
51
|
+
config = substitute_placeholders(config, self._variables)
|
|
52
|
+
|
|
52
53
|
self.load_setup_from_config(config)
|
|
53
54
|
self.test_cases = parse_test_cases(
|
|
54
55
|
config, self.workspace, self.path_resolver,
|
{cli_test_framework-0.8.1 → cli_test_framework-0.9.1}/src/cli_test_framework/runners/json_runner.py
RENAMED
|
@@ -12,15 +12,10 @@ class JSONRunner(ConfigRunner):
|
|
|
12
12
|
"""Sequential JSON test runner (backward-compatible thin wrapper)."""
|
|
13
13
|
|
|
14
14
|
def __init__(self, config_file="test_cases.json",
|
|
15
|
-
workspace: Optional[str] = None,
|
|
16
|
-
test_case_filter: Optional[list] = None,
|
|
17
|
-
history_dir: Optional[str] = None,
|
|
18
|
-
regression_threshold: float = 1.5):
|
|
15
|
+
workspace: Optional[str] = None, **kwargs):
|
|
19
16
|
super().__init__(
|
|
20
17
|
config_file=config_file,
|
|
21
18
|
workspace=workspace,
|
|
22
|
-
test_case_filter=test_case_filter,
|
|
23
|
-
history_dir=history_dir,
|
|
24
|
-
regression_threshold=regression_threshold,
|
|
25
19
|
config_loader=json.load,
|
|
20
|
+
**kwargs,
|
|
26
21
|
)
|
|
@@ -10,7 +10,7 @@ import logging
|
|
|
10
10
|
from typing import Optional, Dict, Any, Callable, BinaryIO
|
|
11
11
|
|
|
12
12
|
from ..core.parallel_runner import ParallelRunner, AtomicSemaphore
|
|
13
|
-
from ..core.config_loader import parse_test_cases, execute_sequence
|
|
13
|
+
from ..core.config_loader import parse_test_cases, execute_sequence, substitute_placeholders
|
|
14
14
|
from ..core.test_case import TestCase
|
|
15
15
|
from ..core.execution import execute_single_test_case
|
|
16
16
|
from ..core.types import TestCaseData
|
|
@@ -31,10 +31,9 @@ class ParallelConfigRunner(ParallelRunner):
|
|
|
31
31
|
workspace: Optional[str] = None,
|
|
32
32
|
max_workers: Optional[int] = None,
|
|
33
33
|
execution_mode: str = "thread",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
config_loader: Optional[Callable[[BinaryIO], Dict[str, Any]]] = None):
|
|
34
|
+
config_loader: Optional[Callable[[BinaryIO], Dict[str, Any]]] = None,
|
|
35
|
+
variables: Optional[Dict[str, Any]] = None,
|
|
36
|
+
**kwargs):
|
|
38
37
|
# Auto-detect physical CPU cores (reserve 2 for OS)
|
|
39
38
|
self.total_physical = os.cpu_count() or 4
|
|
40
39
|
self.safe_capacity = max(1, self.total_physical - 2)
|
|
@@ -43,9 +42,9 @@ class ParallelConfigRunner(ParallelRunner):
|
|
|
43
42
|
max_workers = self.total_physical
|
|
44
43
|
|
|
45
44
|
super().__init__(config_file, workspace, max_workers,
|
|
46
|
-
execution_mode,
|
|
47
|
-
history_dir, regression_threshold)
|
|
45
|
+
execution_mode, **kwargs)
|
|
48
46
|
self._config_loader = config_loader
|
|
47
|
+
self._variables = variables or {}
|
|
49
48
|
# Backward-compatible attribute for tests that patch path_resolver
|
|
50
49
|
self.path_resolver = PathResolver(self.workspace)
|
|
51
50
|
|
|
@@ -124,6 +123,8 @@ class ParallelConfigRunner(ParallelRunner):
|
|
|
124
123
|
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
125
124
|
config = self._config_loader(f)
|
|
126
125
|
|
|
126
|
+
config = substitute_placeholders(config, self._variables)
|
|
127
|
+
|
|
127
128
|
self.load_setup_from_config(config)
|
|
128
129
|
self.test_cases = parse_test_cases(
|
|
129
130
|
config, self.workspace, self.path_resolver,
|