symtest-cli 1.4.0__tar.gz → 1.4.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.
- {symtest_cli-1.4.0/src/symtest_cli.egg-info → symtest_cli-1.4.1}/PKG-INFO +1 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/design.md +1 -18
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/design_en.md +1 -20
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/user_manual.md +13 -6
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/user_manual_en.md +8 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/__init__.py +1 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/cli.py +16 -0
- symtest_cli-1.4.1/src/symtest/commands/migrate.py +160 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/config_schema.py +44 -2
- symtest_cli-1.4.1/src/symtest/config/normalize.py +43 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/base_runner.py +0 -19
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/config_loader.py +28 -26
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/sequence.py +11 -23
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/single.py +2 -13
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/parallel_runner.py +0 -2
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/process_worker.py +4 -4
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/test_case.py +88 -20
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/csv_comparator.py +3 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/h5_comparator.py +4 -2
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/numeric_compare.py +43 -40
- symtest_cli-1.4.1/src/symtest/reporting/__init__.py +17 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/reporting/diagnosis.py +65 -2
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/controllers/case_controller.py +15 -5
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/steps_editor.py +5 -5
- {symtest_cli-1.4.0 → symtest_cli-1.4.1/src/symtest_cli.egg-info}/PKG-INFO +1 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/SOURCES.txt +2 -1
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_new_cli_features.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_new_cli_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_sequence.py +8 -8
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_binary_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_binary_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_config_io.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_inheritance_expander.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_inheritance_expander.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/__pycache__/test_migrate.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/__pycache__/test_migrate.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_architecture_guard.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_architecture_guard.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_last_run_store.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_last_run_store.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_model_v2.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_model_v2.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_orchestration_accept.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_orchestration_accept.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_sequence_state.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_sequence_state.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_validation_error.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_validation_error.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_xfail.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_xfail.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_architecture_guard.py +5 -5
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_case_env.py +4 -4
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_config_loader.py +3 -3
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_config_loader_new_features.py +13 -10
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_execution_new_features.py +31 -27
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_model_v2.py +9 -9
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_orchestration_accept.py +11 -3
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_process_worker.py +6 -6
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_sequence_state.py +3 -3
- symtest_cli-1.4.1/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_numeric_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_numeric_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_numeric_parity.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_numeric_parity.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_error_analysis.py +24 -2
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_numeric_compare.py +5 -3
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_migrate.py +199 -4
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.1/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/conftest.py +3 -3
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_case_controller.py +18 -12
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_tui_run_test.py +3 -3
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_widgets_data.py +2 -2
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/docs/design_1_4.md +0 -305
- symtest_cli-1.4.0/src/symtest/commands/migrate.py +0 -63
- symtest_cli-1.4.0/src/symtest/reporting/__init__.py +0 -10
- symtest_cli-1.4.0/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/__pycache__/test_migrate.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.4.0/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/LICENSE +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/MANIFEST.in +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/README.md +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/tui_and_config_split_design.md +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/pyproject.toml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/setup.cfg +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/setup.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/commands/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/commands/compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/config_io.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/import_expander.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/inheritance_expander.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/migrate.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/executor.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/result.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/history_store.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/last_run_store.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/accept.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/result.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/sequence_state.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/setup.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/types.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/assertions.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/result.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/validator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/base_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/binary_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/factory.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/json_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/result.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/script_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/text_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/xml_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/logging_config.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/config_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/json_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_config_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_json_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_yaml_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/yaml_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/app.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/controllers/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/case_editor.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/case_list.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/case_table.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/expected_editor.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/search_bar.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/junit_xml_writer.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/path_resolver.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/report_generator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/dependency_links.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/entry_points.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/requires.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/top_level.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/README.md +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-39.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-313.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-39.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/conftest.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/h5_filter_demo.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/manual_report_example.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/perf_parallel.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-39.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/test_user_flows.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/test_user_flows.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/test_user_flows.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/circular_config_a.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/circular_config_b.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/hourglass_stdout_mismatch.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/hourglass_stdout_sample.txt +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_base.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_chain.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_circular.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/main_config.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/circular_config_a.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/circular_config_b.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_base.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_chain.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_circular.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/main_config.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/missing_fields.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/setup_main.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/setup_sub.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_golden_file.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_simple.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_simple.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_steps.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/sub_json_tests.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/sub_text_tests.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases1.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_with_setup.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_with_setup.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/missing_fields.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/mock_fail_script.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/mock_pass_script.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/setup_main.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/setup_sub.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/sub_json_tests.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/sub_text_tests.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases1.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_with_setup.json +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_with_setup.yaml +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_history_feature.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_new_cli_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_real_command_execution.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_sequence.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_binary_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_csv_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_h5_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_json_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_text_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_xml_compare.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/test_depends_on.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/test_parallel_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/path_handling/test_spaces_in_paths.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_history_feature.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_new_cli_features.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_real_command_execution.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/run_all.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_binary_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_config_io.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_h5_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_import_expander.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_inheritance_expander.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_run_all.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/commands/test_compare_command.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_assertions.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_config_loader.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution_new_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_history_store.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_last_run_store.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_process_worker.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_setup.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_setup.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_validation_error.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_xfail.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_assertions.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_execution.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_history_store.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_last_run_store.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_setup.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_validation_error.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_xfail.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_hourglass_tangent_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_numeric_parity.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_script_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_workspace_plugins.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_json_yaml_runner.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_test_case_filter.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_test_case_tag_filter.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_binary_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_cli.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_config_io.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_h5_comparator.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_import_expander.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_inheritance_expander.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_result.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_run_all.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__init__.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_app.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_case_controller.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_search_functions.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/test_widgets_data.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_app.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_search_functions.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/test_junit_xml_writer.py +0 -0
- {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/test_report_generator.py +0 -0
|
@@ -155,14 +155,11 @@ PathResolver 解析(系统命令直通、shell builtin 平台包装、复合
|
|
|
155
155
|
→ 超时 kill 整个进程组
|
|
156
156
|
→ 返回附带 `next_action_hint` 的结构化结果。
|
|
157
157
|
|
|
158
|
-
当前实现的上述行为仍聚合在一处;1.4 将按 §10 宪法重排为
|
|
159
|
-
Executor / Validator / Orchestration 三段(迁移明细见 docs/design_1_4.md)。
|
|
160
|
-
|
|
161
158
|
### 4.6 断言与文件比较集成
|
|
162
159
|
|
|
163
160
|
- `compare_files` 是一等断言,经 ComparatorFactory 按类型分发(详见 §6)
|
|
164
161
|
- 所有断言可选;未声明的字段不做校验
|
|
165
|
-
- `--error-analysis` 为 CSV/H5 数值比较提供流式误差统计:`total_numeric_cells` / `mismatched_cells` / `max_abs_error` / `max_rel_error` / `mean_abs_error` / `rms_abs_error`
|
|
162
|
+
- `--error-analysis` 为 CSV/H5 数值比较提供流式误差统计:`total_numeric_cells` / `mismatched_cells` / `max_abs_error` / `max_rel_error` / `mean_abs_error` / `rms_abs_error`;幅值统计(max/mean/rms)覆盖全体参与比较的数值单元格(含通过格),mean/rms 以 `total_numeric_cells` 为分母。`--error-analysis-all` 额外对通过用例输出统计,除此之外两者行为一致
|
|
166
163
|
|
|
167
164
|
## 5. 运行时状态持久化(`.symtest/`)
|
|
168
165
|
|
|
@@ -252,7 +249,6 @@ TUI 与 runner 共用同一解析器;宽松的展示形态在 TUI 侧自行处
|
|
|
252
249
|
> **地位与效力**:本节自 Symtest 1.4 Phase 0 评审定稿,是本项目的核心架构契约,
|
|
253
250
|
> 对所有后续 feature 具有最高约束力——任何功能需求先对照本宪法确定归属,再写代码。
|
|
254
251
|
> 修订宪法必须在变更说明中显式指出所放宽/违反的条款及理由。
|
|
255
|
-
> 定稿前的演进推导见开发阶段文档 docs/design_1_4.md。
|
|
256
252
|
|
|
257
253
|
### 10.1 数据流主线
|
|
258
254
|
|
|
@@ -373,16 +369,3 @@ Reporter 的合法输入只能是 Result 类型;`next_action_hint` 属于 resu
|
|
|
373
369
|
`reporter`;
|
|
374
370
|
- guard 测试纳入常规回归套件(`python tests\run_all.py`),违规即失败;
|
|
375
371
|
- 冲突裁决次序:guard 测试 > 本节文字 > 个人偏好。
|
|
376
|
-
|
|
377
|
-
### 10.6 现状差距与收敛路径
|
|
378
|
-
|
|
379
|
-
定稿时刻(1.4 Phase 0)现行实现与本宪法的已知偏差如下,将在 1.4 对应 Phase 内
|
|
380
|
-
逐项收敛(迁移明细见 docs/design_1_4.md):
|
|
381
|
-
|
|
382
|
-
| 现状 | 违反 | 收敛 |
|
|
383
|
-
|---|---|---|
|
|
384
|
-
| `execution.py::validate_result` 住在执行层 | 原则 2 | Phase 2 迁入 `validation/validator.py` |
|
|
385
|
-
| `next_action_hint` 在 execution 层构造 | 原则 5 | Phase 2 迁入 `reporting/diagnosis.py` |
|
|
386
|
-
| retry 循环在 executor 内部 | 原则 2 / 4 | Phase 2 上移编排层 |
|
|
387
|
-
| Validator 侧 update_baseline 写文件 | 原则 3 | Phase 2 改为 runner 独立 accept 步骤 |
|
|
388
|
-
| `parse_test_cases` TUI mode 后门 | 原则 6 | Phase 2 拆除,单一解析器 |
|
|
@@ -164,10 +164,6 @@ stdout / stderr / returncode / duration) → assertions (return_code / contains
|
|
|
164
164
|
matches / compare_files) → retries on failure up to `retry_count` → kills the
|
|
165
165
|
whole process group on timeout → structured result with `next_action_hint`.
|
|
166
166
|
|
|
167
|
-
Today these behaviors are aggregated in one place; 1.4 rearranges them into
|
|
168
|
-
Executor / Validator / Orchestration per the §10 Constitution (migration details
|
|
169
|
-
in docs/design_1_4.md).
|
|
170
|
-
|
|
171
167
|
### 4.6 Assertions and File Comparison Integration
|
|
172
168
|
|
|
173
169
|
- `compare_files` is a first-class assertion dispatched via ComparatorFactory (see §6)
|
|
@@ -266,8 +262,7 @@ TUI side itself (§10 Principle 6).
|
|
|
266
262
|
> project's core architectural contract with supreme authority over all future
|
|
267
263
|
> features — resolve ownership against this constitution before writing any code.
|
|
268
264
|
> Amending it requires explicitly naming the clause being relaxed/waived and the
|
|
269
|
-
> rationale in the change description.
|
|
270
|
-
> the development-phase document docs/design_1_4.md.
|
|
265
|
+
> rationale in the change description.
|
|
271
266
|
|
|
272
267
|
### 10.1 Data Flow Spine
|
|
273
268
|
|
|
@@ -398,17 +393,3 @@ architecture guard tests enforced by CI:
|
|
|
398
393
|
- Guard tests are part of the regular regression suite
|
|
399
394
|
(`python tests\run_all.py`); violations fail the build;
|
|
400
395
|
- Conflict resolution order: guard tests > this text > personal preference.
|
|
401
|
-
|
|
402
|
-
### 10.6 Current Gap to This Constitution
|
|
403
|
-
|
|
404
|
-
Known deviations of the current implementation from this constitution as of
|
|
405
|
-
ratification (1.4 Phase 0). Each item converges during the corresponding 1.4
|
|
406
|
-
phase (migration details in docs/design_1_4.md):
|
|
407
|
-
|
|
408
|
-
| Current state | Violated | Convergence |
|
|
409
|
-
|---|---|---|
|
|
410
|
-
| `execution.py::validate_result` lives in the execution layer | Principle 2 | Phase 2: move to `validation/validator.py` |
|
|
411
|
-
| `next_action_hint` built in the execution layer | Principle 5 | Phase 2: move to `reporting/diagnosis.py` |
|
|
412
|
-
| Retry loop inside the executor | Principles 2 / 4 | Phase 2: lift to orchestration |
|
|
413
|
-
| update_baseline writes files inside validation | Principle 3 | Phase 2: runner-side independent accept step |
|
|
414
|
-
| `parse_test_cases` TUI-mode back door | Principle 6 | Phase 2: remove; single parser |
|
|
@@ -48,7 +48,7 @@ HDF5 文件比较依赖 `h5py`(已随框架安装)。如需在无 HDF5 环
|
|
|
48
48
|
|
|
49
49
|
## 测试用例定义
|
|
50
50
|
|
|
51
|
-
> **Schema v2(1.4)**:配置采用分层 DSL——执行相关字段(`command`、`args`、`timeout`、`retry_count`、`env`、`steps`)位于 `execution` 块,调度相关字段(`depends_on`、`resources`)位于 `scheduling` 块,`expected` 保持在顶层。旧的扁平布局已移除,可使用 `symtest migrate`
|
|
51
|
+
> **Schema v2(1.4)**:配置采用分层 DSL——执行相关字段(`command`、`args`、`timeout`、`retry_count`、`env`、`steps`)位于 `execution` 块,调度相关字段(`depends_on`、`resources`)位于 `scheduling` 块,`expected` 保持在顶层。旧的扁平布局已移除,可使用 `symtest migrate` 迁移。迁移会递归处理整棵 `import` 树:默认为每个文件生成 `<stem>.v2<ext>` 副本并自动重写父文件中的 import 路径;`--in-place` 则原地覆盖所有文件(与 `--output` 互斥)。
|
|
52
52
|
|
|
53
53
|
### JSON 格式
|
|
54
54
|
|
|
@@ -275,6 +275,13 @@ test_cases:
|
|
|
275
275
|
| `expected.output_matches` | 否 | 输出需匹配的正则表达式(单个字符串) |
|
|
276
276
|
| `expected.compare_files` | 否 | 文件比较断言列表,见下文 |
|
|
277
277
|
|
|
278
|
+
### execution 二选一(互斥)
|
|
279
|
+
|
|
280
|
+
`execution` 有两种形态,**二选一**且互斥,同时声明两种形态属于配置错误:
|
|
281
|
+
|
|
282
|
+
- **单命令形态**:`command` + `args`(`timeout` / `retry_count` / `env` 可选)
|
|
283
|
+
- **序列形态**:`steps`(列表,每个 step 为 `command + args + expected`);`command` / `args` 不得与 `steps` 并存
|
|
284
|
+
|
|
278
285
|
## Case 级环境变量(env)
|
|
279
286
|
|
|
280
287
|
通过 `execution` 内与 `command`、`steps` 同级的 `env` 字段,可为单个用例注入环境变量,仅在该用例(序列模式为所有 step)的子进程内生效,不影响其他用例。
|
|
@@ -1788,12 +1795,12 @@ CSV 比较按行列结构逐单元格比对;数值单元格在容差范围内
|
|
|
1788
1795
|
|---|---|
|
|
1789
1796
|
| `total_numeric_cells` | 参与数值比较的单元格总数 |
|
|
1790
1797
|
| `mismatched_cells` | 超出容差的单元格数 |
|
|
1791
|
-
| `max_abs_error` |
|
|
1792
|
-
| `max_rel_error` |
|
|
1793
|
-
| `mean_abs_error` |
|
|
1794
|
-
| `rms_abs_error` |
|
|
1798
|
+
| `max_abs_error` | 全体数值单元格中的最大绝对误差及其位置 |
|
|
1799
|
+
| `max_rel_error` | 全体数值单元格中的最大相对误差及其位置 |
|
|
1800
|
+
| `mean_abs_error` | 全体数值单元格的平均绝对误差(分母为 `total_numeric_cells`) |
|
|
1801
|
+
| `rms_abs_error` | 全体数值单元格的均方根绝对误差(RMSE,分母为 `total_numeric_cells`) |
|
|
1795
1802
|
|
|
1796
|
-
|
|
1803
|
+
统计是**流式**计算的,不依赖差异截断,幅值统计(max/mean/rms)覆盖**全体**参与比较的数值单元格(含在容差内通过的单元格),可用于观察通过格离容差的余量;非有限值(NaN/inf)单元格不参与幅值统计。两个参数的统计口径完全一致,区别仅在于:默认仅失败的比较输出统计,通过的比较不输出。
|
|
1797
1804
|
|
|
1798
1805
|
如需让**通过**的用例也输出统计信息(例如用于监控容差余量),可改用 `--error-analysis-all`(它会隐含启用 `--error-analysis`):
|
|
1799
1806
|
|
|
@@ -48,7 +48,7 @@ HDF5 file comparison depends on `h5py` (installed with the framework). If you ne
|
|
|
48
48
|
|
|
49
49
|
## Test Case Definition
|
|
50
50
|
|
|
51
|
-
> **Schema v2 (1.4)**: The config uses a layered DSL — execution-related fields (`command`, `args`, `timeout`, `retry_count`, `env`, `steps`) live in the `execution` block, scheduling-related fields (`depends_on`, `resources`) live in the `scheduling` block, and `expected` stays at the top level. The old flat layout was removed; use `symtest migrate` to migrate.
|
|
51
|
+
> **Schema v2 (1.4)**: The config uses a layered DSL — execution-related fields (`command`, `args`, `timeout`, `retry_count`, `env`, `steps`) live in the `execution` block, scheduling-related fields (`depends_on`, `resources`) live in the `scheduling` block, and `expected` stays at the top level. The old flat layout was removed; use `symtest migrate` to migrate. Migration recursively covers the whole `import` tree: by default each file gets a `<stem>.v2<ext>` copy with import paths rewritten automatically; `--in-place` overwrites all files in place (mutually exclusive with `--output`).
|
|
52
52
|
|
|
53
53
|
### JSON Format
|
|
54
54
|
|
|
@@ -275,6 +275,13 @@ test_cases:
|
|
|
275
275
|
| `expected.output_matches` | No | Regex pattern the output must match (single string) |
|
|
276
276
|
| `expected.compare_files` | No | File comparison assertions list, see below |
|
|
277
277
|
|
|
278
|
+
### execution — pick one form (mutually exclusive)
|
|
279
|
+
|
|
280
|
+
`execution` has two forms, **choose exactly one**; declaring both is a configuration error:
|
|
281
|
+
|
|
282
|
+
- **Single-command form**: `command` + `args` (with optional `timeout` / `retry_count` / `env`)
|
|
283
|
+
- **Steps form**: `steps` (a list; each step is `command + args + expected`); `command` / `args` must not be declared alongside `steps`
|
|
284
|
+
|
|
278
285
|
### File Comparison Assertions (compare_files)
|
|
279
286
|
|
|
280
287
|
Declare one or more file comparison rules in `expected.compare_files`. The framework automatically uses the corresponding comparator to diff the actual output file against the baseline after command execution. The test passes only when all comparisons pass; these coexist with `return_code`, `output_contains`, and other assertions.
|
|
@@ -15,6 +15,7 @@ import logging
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
17
|
from .logging_config import setup_console_logging
|
|
18
|
+
from .reporting.diagnosis import attach_next_action_hints
|
|
18
19
|
from .runners import JSONRunner, ParallelJSONRunner, ParallelYAMLRunner, YAMLRunner
|
|
19
20
|
from .utils.report_generator import ReportGenerator
|
|
20
21
|
from .utils.junit_xml_writer import write_junit_xml
|
|
@@ -207,6 +208,12 @@ Examples:
|
|
|
207
208
|
'--output', '-o',
|
|
208
209
|
help='Output path (default: <stem>.v2<ext>, e.g. old.json -> old.v2.json)',
|
|
209
210
|
)
|
|
211
|
+
migrate_parser.add_argument(
|
|
212
|
+
'--in-place', action='store_true',
|
|
213
|
+
help='Migrate the config and every file it imports recursively, '
|
|
214
|
+
'overwriting each file in place (originals are not kept; '
|
|
215
|
+
'mutually exclusive with --output)',
|
|
216
|
+
)
|
|
210
217
|
|
|
211
218
|
# ---- Compare command ----
|
|
212
219
|
compare_parser = subparsers.add_parser('compare', help='Compare two files')
|
|
@@ -396,6 +403,15 @@ def run_tests(args):
|
|
|
396
403
|
# Output results using ReportGenerator and honor --output-format
|
|
397
404
|
if hasattr(runner, 'results'):
|
|
398
405
|
results = runner.results
|
|
406
|
+
|
|
407
|
+
# ── Reporting 装配点(原则 5):next_action_hint 在报告输出前
|
|
408
|
+
# 按 failure_kind 填充,orchestration 只产出失败结论 ──
|
|
409
|
+
attach_next_action_hints(
|
|
410
|
+
results,
|
|
411
|
+
update_baseline=bool(getattr(args, 'update_baseline', False)),
|
|
412
|
+
config_path=str(config_file),
|
|
413
|
+
)
|
|
414
|
+
|
|
399
415
|
output_format = getattr(args, 'output_format', 'text')
|
|
400
416
|
|
|
401
417
|
if output_format == 'json':
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""``symtest migrate`` 子命令实现(1.4 Phase 4 迁移设计第一层)。
|
|
2
|
+
|
|
3
|
+
确定性转换:加载 v1 配置 → ``config.migrate.migrate_config`` 机械映射 →
|
|
4
|
+
按输出扩展名写出 JSON/YAML。
|
|
5
|
+
|
|
6
|
+
支持递归迁移整棵 ``import`` 树(两阶段:先把全部文件加载并迁移到内存,
|
|
7
|
+
任一失败则不写任何文件,全部成功后才统一写盘):
|
|
8
|
+
|
|
9
|
+
- 默认模式:每个文件写出 ``<stem>.v2<ext>`` 兄弟副本(原文件不动),
|
|
10
|
+
并把已迁移配置中的 import 路径重写为对应的 ``.v2`` 文件名,
|
|
11
|
+
迁移产物整棵树自洽、可直接 ``symtest validate``;
|
|
12
|
+
- ``--in-place``:整树原地覆盖(文件名/格式/import 路径全不变),
|
|
13
|
+
默认关闭,开启即接受覆盖风险;与 ``--output`` 互斥。
|
|
14
|
+
|
|
15
|
+
不做路径解析、不校验必填 —— 校验归 ``symtest validate``,人工判断项归
|
|
16
|
+
迁移复查 Skill(见 examples/skill-migration)。
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Dict, List, Tuple
|
|
24
|
+
|
|
25
|
+
from ..config.config_io import save_config
|
|
26
|
+
from ..config.import_expander import _load_raw_config
|
|
27
|
+
from ..config.migrate import migrate_config
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger("symtest.commands.migrate")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _resolve(path_str: str, workspace: Path) -> Path:
|
|
33
|
+
path = Path(path_str)
|
|
34
|
+
if not path.is_absolute():
|
|
35
|
+
path = workspace / path
|
|
36
|
+
return path.resolve()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _collect_import_tree(entry_path: Path) -> List[Path]:
|
|
40
|
+
"""DFS 后序收集入口文件及全部 import 子文件。
|
|
41
|
+
|
|
42
|
+
- processed 集合保证菱形 import(同一文件被多个父文件引用)只收集一次;
|
|
43
|
+
- 递归栈检测真循环 import 并抛 RuntimeError;
|
|
44
|
+
- 子文件缺失抛 FileNotFoundError;
|
|
45
|
+
- 返回顺序保证子文件先于引用它的父文件。
|
|
46
|
+
"""
|
|
47
|
+
ordered: List[Path] = []
|
|
48
|
+
processed: set = set()
|
|
49
|
+
|
|
50
|
+
def _walk(path: Path, stack: Tuple[str, ...]) -> None:
|
|
51
|
+
canonical = str(path.resolve())
|
|
52
|
+
if canonical in processed:
|
|
53
|
+
return
|
|
54
|
+
if canonical in stack:
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
f"Circular import detected: {canonical} "
|
|
57
|
+
f"(chain: {' -> '.join(stack + (canonical,))})"
|
|
58
|
+
)
|
|
59
|
+
config = _load_raw_config(path)
|
|
60
|
+
for item in config.get("test_cases", []) or []:
|
|
61
|
+
if isinstance(item, dict) and "import" in item:
|
|
62
|
+
sub_path = (path.parent / str(item["import"])).resolve()
|
|
63
|
+
if not sub_path.exists():
|
|
64
|
+
raise FileNotFoundError(
|
|
65
|
+
f"Imported config file not found: {sub_path} "
|
|
66
|
+
f"(referenced from {path})"
|
|
67
|
+
)
|
|
68
|
+
_walk(sub_path, stack + (canonical,))
|
|
69
|
+
processed.add(canonical)
|
|
70
|
+
ordered.append(path)
|
|
71
|
+
|
|
72
|
+
_walk(entry_path, ())
|
|
73
|
+
return ordered
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _v2_sibling(import_path: str) -> str:
|
|
77
|
+
"""'cases/sub.json' -> 'cases/sub.v2.json'(纯字符串变换,保留原分隔符)。"""
|
|
78
|
+
sep_idx = max(import_path.rfind("/"), import_path.rfind("\\"))
|
|
79
|
+
if sep_idx >= 0:
|
|
80
|
+
directory, filename = import_path[: sep_idx + 1], import_path[sep_idx + 1:]
|
|
81
|
+
else:
|
|
82
|
+
directory, filename = "", import_path
|
|
83
|
+
stem, dot, ext = filename.rpartition(".")
|
|
84
|
+
if not dot or not stem:
|
|
85
|
+
# 无扩展名(或 .gitignore 类隐藏名):直接追加后缀
|
|
86
|
+
return f"{directory}{filename}.v2"
|
|
87
|
+
return f"{directory}{stem}.v2.{ext}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _rewrite_import_paths(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
91
|
+
"""把 config 中 import 条目的路径改写为 .v2 兄弟名(就地修改并返回)。
|
|
92
|
+
|
|
93
|
+
import 条目中的其他字段(如 tags)原样保留。
|
|
94
|
+
"""
|
|
95
|
+
for item in config.get("test_cases", []) or []:
|
|
96
|
+
if isinstance(item, dict) and "import" in item:
|
|
97
|
+
item["import"] = _v2_sibling(str(item["import"]))
|
|
98
|
+
return config
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_migrate(args) -> bool:
|
|
102
|
+
"""CLI 壳:收集 import 树 → 整树 migrate_config → 按模式统一写盘。
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
True 成功;输入不存在/加载失败/循环 import/输出格式不支持返回 False。
|
|
107
|
+
"""
|
|
108
|
+
workspace = Path(getattr(args, "workspace", None) or Path.cwd()).resolve()
|
|
109
|
+
|
|
110
|
+
input_path = _resolve(args.config_file, workspace)
|
|
111
|
+
if not input_path.exists():
|
|
112
|
+
logger.error("Configuration file not found: %s", input_path)
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
in_place = getattr(args, "in_place", False)
|
|
116
|
+
output_arg = getattr(args, "output", None)
|
|
117
|
+
if in_place and output_arg:
|
|
118
|
+
logger.error("--output cannot be combined with --in-place")
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
files = _collect_import_tree(input_path)
|
|
123
|
+
# 第一阶段:整树加载并迁移到内存,任一失败则不写任何文件
|
|
124
|
+
pending: List[Tuple[Path, Dict[str, Any]]] = [
|
|
125
|
+
(f, migrate_config(_load_raw_config(f))) for f in files
|
|
126
|
+
]
|
|
127
|
+
except Exception as exc:
|
|
128
|
+
logger.error("Migration failed: %s", exc)
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
if output_arg and not in_place:
|
|
132
|
+
entry_output = _resolve(output_arg, workspace)
|
|
133
|
+
else:
|
|
134
|
+
# 默认输出 <stem>.v2<原扩展名>,如 old.json → old.v2.json
|
|
135
|
+
entry_output = input_path.parent / f"{input_path.stem}.v2{input_path.suffix}"
|
|
136
|
+
|
|
137
|
+
written: List[Path] = []
|
|
138
|
+
try:
|
|
139
|
+
# 第二阶段:统一写盘
|
|
140
|
+
for src, migrated in pending:
|
|
141
|
+
if in_place:
|
|
142
|
+
target = src
|
|
143
|
+
else:
|
|
144
|
+
# import 路径指向 .v2 子副本(--output 仅移动入口文件位置,
|
|
145
|
+
# 隐含假设输出与入口同目录,否则需手工核对相对路径)
|
|
146
|
+
_rewrite_import_paths(migrated)
|
|
147
|
+
if src == input_path:
|
|
148
|
+
target = entry_output
|
|
149
|
+
else:
|
|
150
|
+
target = src.parent / f"{src.stem}.v2{src.suffix}"
|
|
151
|
+
save_config(migrated, target)
|
|
152
|
+
written.append(target)
|
|
153
|
+
except ValueError as exc:
|
|
154
|
+
logger.error("%s", exc)
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
for target in written:
|
|
158
|
+
logger.info("Migrated config written to: %s", target)
|
|
159
|
+
print(str(target))
|
|
160
|
+
return True
|
|
@@ -164,11 +164,24 @@ CONFIG_SCHEMA: Dict[str, Any] = {
|
|
|
164
164
|
},
|
|
165
165
|
"executionSpec": {
|
|
166
166
|
"type": "object",
|
|
167
|
-
"additionalProperties": False,
|
|
168
167
|
"description": (
|
|
169
168
|
"Execution semantics (v2): single-command shorthand "
|
|
170
169
|
"(command/args/timeout/retry_count/env) OR the full steps form "
|
|
171
|
-
"(steps[]) —
|
|
170
|
+
"(steps[]) — 二选一,由 oneOf 强制互斥. Case-level env applies "
|
|
171
|
+
"to all steps. Declaring both forms is a schema violation."
|
|
172
|
+
),
|
|
173
|
+
"oneOf": [
|
|
174
|
+
{"$ref": "#/$defs/commandExecution"},
|
|
175
|
+
{"$ref": "#/$defs/stepsExecution"},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
"commandExecution": {
|
|
179
|
+
"type": "object",
|
|
180
|
+
"required": ["command", "args"],
|
|
181
|
+
"additionalProperties": False,
|
|
182
|
+
"description": (
|
|
183
|
+
"Single-command shorthand form: requires command + args; "
|
|
184
|
+
"the steps form must not be declared alongside."
|
|
172
185
|
),
|
|
173
186
|
"properties": {
|
|
174
187
|
"command": {
|
|
@@ -201,12 +214,41 @@ CONFIG_SCHEMA: Dict[str, Any] = {
|
|
|
201
214
|
"and scheduler-injected environment variables."
|
|
202
215
|
),
|
|
203
216
|
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
"stepsExecution": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"required": ["steps"],
|
|
222
|
+
"additionalProperties": False,
|
|
223
|
+
"description": (
|
|
224
|
+
"Full steps form: requires steps; command/args must not be "
|
|
225
|
+
"declared alongside."
|
|
226
|
+
),
|
|
227
|
+
"properties": {
|
|
204
228
|
"steps": {
|
|
205
229
|
"type": "array",
|
|
206
230
|
"minItems": 1,
|
|
207
231
|
"items": {"$ref": "#/$defs/step"},
|
|
208
232
|
"description": "Steps run in order with fail-fast semantics.",
|
|
209
233
|
},
|
|
234
|
+
"timeout": {
|
|
235
|
+
"type": ["number", "null"],
|
|
236
|
+
"description": "Timeout in seconds (default 3600); null = no limit.",
|
|
237
|
+
},
|
|
238
|
+
"retry_count": {
|
|
239
|
+
"type": "integer",
|
|
240
|
+
"minimum": 0,
|
|
241
|
+
"description": "Retries after the first failure; passing after retry marks the result flaky.",
|
|
242
|
+
},
|
|
243
|
+
"env": {
|
|
244
|
+
"type": "object",
|
|
245
|
+
"additionalProperties": {"type": ["string", "number", "boolean"]},
|
|
246
|
+
"description": (
|
|
247
|
+
"Case-level environment variables injected into every "
|
|
248
|
+
"step (subprocess). Overrides setup-level and "
|
|
249
|
+
"scheduler-injected environment variables."
|
|
250
|
+
),
|
|
251
|
+
},
|
|
210
252
|
},
|
|
211
253
|
},
|
|
212
254
|
"schedulingSpec": {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Draft 配置规范化(宽松形态归一,1.4 原则 6 的 DSL 层配套)。
|
|
2
|
+
|
|
3
|
+
core parser 全系统唯一且只接受 canonical TestCase;半成品/草稿配置
|
|
4
|
+
(TUI 编辑中间态、v1 迁移输出等)先经 ``normalize_draft_config`` 补齐
|
|
5
|
+
必填字段缺省值,再调用严格 ``core.config_loader.parse_test_cases``。
|
|
6
|
+
|
|
7
|
+
本模块属于 ``config`` 层(DSL 规范化),不在 ``core`` 内 —— core 不提供
|
|
8
|
+
任何宽松解析形态。
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import copy
|
|
13
|
+
from typing import Any, Dict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def normalize_draft_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
17
|
+
"""Normalize half-finished draft cases into canonical config dicts.
|
|
18
|
+
|
|
19
|
+
为缺字段的 case 补齐 canonical 必填项的缺省值(深拷贝,不改动入参):
|
|
20
|
+
|
|
21
|
+
- case 级:``name`` / ``execution`` / ``expected``;
|
|
22
|
+
- 单命令形态:``execution.command`` / ``execution.args``;
|
|
23
|
+
- steps 形态:每个 step 的 ``command`` / ``args`` / ``expected``。
|
|
24
|
+
|
|
25
|
+
``import`` 引用项原样跳过(由 import 展开管线处理)。
|
|
26
|
+
"""
|
|
27
|
+
normalized = copy.deepcopy(config)
|
|
28
|
+
for case in normalized.get("test_cases", []):
|
|
29
|
+
if not isinstance(case, dict) or "import" in case:
|
|
30
|
+
continue
|
|
31
|
+
case.setdefault("name", "")
|
|
32
|
+
execution = case.setdefault("execution", {})
|
|
33
|
+
if "steps" in execution:
|
|
34
|
+
for step in execution.get("steps", []):
|
|
35
|
+
if isinstance(step, dict):
|
|
36
|
+
step.setdefault("command", "")
|
|
37
|
+
step.setdefault("args", [])
|
|
38
|
+
step.setdefault("expected", {})
|
|
39
|
+
else:
|
|
40
|
+
execution.setdefault("command", "")
|
|
41
|
+
execution.setdefault("args", [])
|
|
42
|
+
case.setdefault("expected", {})
|
|
43
|
+
return normalized
|
|
@@ -176,7 +176,6 @@ class BaseRunner(ABC):
|
|
|
176
176
|
"description": case.description or None,
|
|
177
177
|
"tags": case.tags or [],
|
|
178
178
|
}
|
|
179
|
-
self._fill_hint_command(skip_result, case.name)
|
|
180
179
|
self.results["details"].append(skip_result)
|
|
181
180
|
logger.warning("⊘ Test %d skipped: %s", i, case.name)
|
|
182
181
|
logger.warning(" Reason: %s", skip_result["message"])
|
|
@@ -191,7 +190,6 @@ class BaseRunner(ABC):
|
|
|
191
190
|
result["expected"] = case.expected if case.expected else None
|
|
192
191
|
result["description"] = case.description or None
|
|
193
192
|
result["tags"] = case.tags or []
|
|
194
|
-
self._fill_hint_command(result, case.name)
|
|
195
193
|
|
|
196
194
|
self.results["details"].append(result)
|
|
197
195
|
duration = result.get("duration", 0)
|
|
@@ -298,23 +296,6 @@ class BaseRunner(ABC):
|
|
|
298
296
|
|
|
299
297
|
return result
|
|
300
298
|
|
|
301
|
-
def _fill_hint_command(self, result: Dict[str, Any], case_name: str) -> None:
|
|
302
|
-
"""Fill in the concrete CLI command inside ``next_action_hint``.
|
|
303
|
-
|
|
304
|
-
The execution layer attaches the hint with ``command=None`` because it
|
|
305
|
-
does not know the config file path; the runner does.
|
|
306
|
-
"""
|
|
307
|
-
hint = result.get("next_action_hint")
|
|
308
|
-
if not hint or hint.get("command"):
|
|
309
|
-
return
|
|
310
|
-
config = str(self.config_path)
|
|
311
|
-
if hint.get("action") == "update_baseline":
|
|
312
|
-
hint["command"] = (
|
|
313
|
-
f'symtest run "{config}" --update-baseline -t "{case_name}"'
|
|
314
|
-
)
|
|
315
|
-
else:
|
|
316
|
-
hint["command"] = f'symtest run "{config}" -t "{case_name}"'
|
|
317
|
-
|
|
318
299
|
def _apply_xfail_status(self, result: Dict[str, Any], case: "TestCase") -> None:
|
|
319
300
|
"""Apply xfail (expected failure) status mapping to a test result.
|
|
320
301
|
|
|
@@ -17,7 +17,7 @@ import re
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Any, Dict, List, Optional, Tuple
|
|
19
19
|
|
|
20
|
-
from .test_case import TestCase,
|
|
20
|
+
from .test_case import TestCase, TestStep
|
|
21
21
|
from ..utils.path_resolver import resolve_paths
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger("symtest.core.config_loader")
|
|
@@ -94,8 +94,6 @@ def parse_test_cases(
|
|
|
94
94
|
config: Dict[str, Any],
|
|
95
95
|
workspace: Optional[Path] = None,
|
|
96
96
|
path_resolver: Any = None,
|
|
97
|
-
*,
|
|
98
|
-
strict: bool = True,
|
|
99
97
|
) -> List[TestCase]:
|
|
100
98
|
"""Parse ``config['test_cases']`` (Schema v2) into ``TestCase`` objects.
|
|
101
99
|
|
|
@@ -106,11 +104,9 @@ def parse_test_cases(
|
|
|
106
104
|
When *workspace* and *path_resolver* are provided (Runner mode),
|
|
107
105
|
command/args paths are resolved.
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
(missing fields get sensible defaults and raw values are kept as-is for
|
|
113
|
-
display purposes).
|
|
107
|
+
解析器全系统唯一且只接受 canonical TestCase:必填字段缺失即抛
|
|
108
|
+
``ValueError``(1.4 原则 6:无 TUI 宽松模式后门)。编辑半成品的
|
|
109
|
+
宽松形态由 TUI 侧自行 normalize 后再调用本函数。
|
|
114
110
|
"""
|
|
115
111
|
cases: List[TestCase] = []
|
|
116
112
|
resolve = workspace is not None and path_resolver is not None
|
|
@@ -122,8 +118,16 @@ def parse_test_cases(
|
|
|
122
118
|
|
|
123
119
|
# Normalize both modes to a single ``steps`` list. A single-command
|
|
124
120
|
# shorthand becomes a single-element list; a sequence case uses its
|
|
125
|
-
# ``execution.steps`` directly.
|
|
121
|
+
# ``execution.steps`` directly. Declaring both forms is ambiguous
|
|
122
|
+
# (Schema v2 oneOf 互斥) and is rejected outright instead of being
|
|
123
|
+
# silently resolved.
|
|
126
124
|
is_sequence = "steps" in execution
|
|
125
|
+
if is_sequence and ("command" in execution or "args" in execution):
|
|
126
|
+
raise ValueError(
|
|
127
|
+
f"Test case '{case.get('name', 'unnamed')}': 'execution' "
|
|
128
|
+
f"declares both 'steps' and 'command'/'args' — the two forms "
|
|
129
|
+
f"are mutually exclusive (choose one)"
|
|
130
|
+
)
|
|
127
131
|
if is_sequence:
|
|
128
132
|
step_configs: List[Dict[str, Any]] = list(execution.get("steps", []))
|
|
129
133
|
else:
|
|
@@ -135,15 +139,14 @@ def parse_test_cases(
|
|
|
135
139
|
"retry_count": execution.get("retry_count", 0),
|
|
136
140
|
}]
|
|
137
141
|
|
|
138
|
-
steps: List[
|
|
142
|
+
steps: List[TestStep] = []
|
|
139
143
|
for step in step_configs:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
)
|
|
144
|
+
step_required = ["command", "args", "expected"]
|
|
145
|
+
if not all(field in step for field in step_required):
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"Step in test case '{case.get('name', 'unnamed')}' "
|
|
148
|
+
f"is missing required fields"
|
|
149
|
+
)
|
|
147
150
|
if resolve:
|
|
148
151
|
executable, resolved_args = _split_and_resolve(
|
|
149
152
|
step["command"], step["args"], workspace, path_resolver
|
|
@@ -151,7 +154,7 @@ def parse_test_cases(
|
|
|
151
154
|
else:
|
|
152
155
|
executable = step.get("command", "")
|
|
153
156
|
resolved_args = step.get("args", [])
|
|
154
|
-
steps.append(
|
|
157
|
+
steps.append(TestStep.from_flat(
|
|
155
158
|
command=executable,
|
|
156
159
|
args=resolved_args,
|
|
157
160
|
expected=step["expected"] if "expected" in step else step.get("expected", {}),
|
|
@@ -175,14 +178,13 @@ def parse_test_cases(
|
|
|
175
178
|
))
|
|
176
179
|
else:
|
|
177
180
|
# ── Single-command mode: execution shorthand fields → steps[0] ──
|
|
178
|
-
if
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
)
|
|
181
|
+
missing = [f for f in ("name", "execution", "expected") if f not in case]
|
|
182
|
+
missing += [f for f in ("command", "args") if f not in execution]
|
|
183
|
+
if missing:
|
|
184
|
+
raise ValueError(
|
|
185
|
+
f"Test case {case.get('name', 'unnamed')} "
|
|
186
|
+
f"is missing required fields"
|
|
187
|
+
)
|
|
186
188
|
cases.append(TestCase(
|
|
187
189
|
name=case.get("name", ""),
|
|
188
190
|
steps=None,
|