symtest-cli 1.4.1__tar.gz → 1.5.0__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.1/src/symtest_cli.egg-info → symtest_cli-1.5.0}/PKG-INFO +1 -1
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/docs/design.md +43 -10
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/docs/user_manual.md +164 -21
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/docs/user_manual_en.md +176 -22
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/__init__.py +1 -1
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/commands/compare.py +27 -15
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/config_schema.py +42 -8
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/validation/assertions.py +50 -2
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/validation/validator.py +48 -9
- symtest_cli-1.5.0/src/symtest/file_comparator/__init__.py +44 -0
- symtest_cli-1.5.0/src/symtest/file_comparator/base_comparator.py +128 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/binary_comparator.py +6 -6
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/csv_comparator.py +3 -4
- symtest_cli-1.5.0/src/symtest/file_comparator/extractor_comparator.py +199 -0
- symtest_cli-1.5.0/src/symtest/file_comparator/factory.py +341 -0
- symtest_cli-1.4.1/src/symtest/file_comparator/base_comparator.py → symtest_cli-1.5.0/src/symtest/file_comparator/file_comparator_base.py +57 -30
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/h5_comparator.py +10 -6
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/json_comparator.py +3 -4
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/result.py +66 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/script_comparator.py +22 -41
- symtest_cli-1.5.0/src/symtest/file_comparator/script_extract_comparator.py +160 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/text_comparator.py +2 -2
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/utils/report_generator.py +80 -17
- {symtest_cli-1.4.1 → symtest_cli-1.5.0/src/symtest_cli.egg-info}/PKG-INFO +1 -1
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest_cli.egg-info/SOURCES.txt +12 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_csv_compare.py +2 -2
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_text_compare.py +8 -1
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_assertions.py +20 -5
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_extractor_channels.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_extractor_channels.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_plugin_lifecycle.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_plugin_lifecycle.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_script_extract.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_script_extract.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-312-pytest-7.4.4.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-312-pytest-9.0.3.pyc +0 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/test_extractor_channels.py +253 -0
- symtest_cli-1.5.0/tests/unit/file_comparator/test_plugin_lifecycle.py +390 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_script_comparator.py +10 -9
- symtest_cli-1.5.0/tests/unit/file_comparator/test_script_extract.py +187 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_workspace_plugins.py +29 -48
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/test_report_generator.py +177 -0
- symtest_cli-1.4.1/src/symtest/file_comparator/__init__.py +0 -10
- symtest_cli-1.4.1/src/symtest/file_comparator/factory.py +0 -191
- 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.1/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-312-pytest-9.0.3.pyc +0 -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.1/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/LICENSE +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/MANIFEST.in +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/README.md +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/docs/design_en.md +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/docs/tui_and_config_split_design.md +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/pyproject.toml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/setup.cfg +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/setup.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/cli.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/commands/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/commands/migrate.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/config_io.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/import_expander.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/inheritance_expander.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/migrate.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/config/normalize.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/base_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/config_loader.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/execution/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/execution/executor.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/execution/result.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/history_store.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/last_run_store.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/orchestration/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/orchestration/accept.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/orchestration/sequence.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/orchestration/single.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/parallel_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/process_worker.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/result.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/sequence_state.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/setup.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/test_case.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/types.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/validation/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/core/validation/result.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/numeric_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/file_comparator/xml_comparator.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/logging_config.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/reporting/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/reporting/diagnosis.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/config_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/json_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/parallel_config_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/parallel_json_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/parallel_yaml_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/runners/yaml_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/app.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/controllers/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/controllers/case_controller.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/screens/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/screens/case_editor.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/screens/case_list.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/widgets/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/widgets/case_table.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/widgets/expected_editor.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/widgets/search_bar.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/tui/widgets/steps_editor.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/utils/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/utils/junit_xml_writer.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest/utils/path_resolver.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest_cli.egg-info/dependency_links.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest_cli.egg-info/entry_points.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest_cli.egg-info/requires.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/src/symtest_cli.egg-info/top_level.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/README.md +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/__init__.cpython-39.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/run_all.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/run_all.cpython-313.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/run_all.cpython-39.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/conftest.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/demos/h5_filter_demo.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/demos/manual_report_example.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/demos/perf_parallel.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/__init__.cpython-39.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/test_user_flows.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/__pycache__/test_user_flows.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/e2e/test_user_flows.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/circular_config_a.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/circular_config_b.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/hourglass_stdout_mismatch.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/hourglass_stdout_sample.txt +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/inheritance_base.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/inheritance_chain.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/inheritance_circular.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/main_config.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/circular_config_a.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/circular_config_b.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/inheritance_base.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/inheritance_chain.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/inheritance_circular.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/main_config.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/missing_fields.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/setup_main.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/setup_sub.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/skill_test_cases_golden_file.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/skill_test_cases_simple.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/skill_test_cases_simple.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/skill_test_cases_steps.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/sub_json_tests.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/sub_text_tests.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/test_cases.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/test_cases.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/test_cases1.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/test_with_setup.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/migration/v1/test_with_setup.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/missing_fields.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/mock_fail_script.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/mock_pass_script.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/setup_main.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/setup_sub.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/sub_json_tests.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/sub_text_tests.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/test_cases.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/test_cases.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/test_cases1.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/test_with_setup.json +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/fixtures/test_with_setup.yaml +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_history_feature.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_new_cli_features.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_new_cli_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_new_cli_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_real_command_execution.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/__pycache__/test_sequence.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_binary_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_h5_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_json_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/file_compare/test_xml_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/test_depends_on.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/parallel/test_parallel_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/path_handling/test_spaces_in_paths.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/test_history_feature.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/test_new_cli_features.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/test_real_command_execution.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/integration/test_sequence.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/run_all.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_binary_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_binary_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_binary_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_cli.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_config_io.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_config_io.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_h5_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_import_expander.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_inheritance_expander.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_inheritance_expander.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_inheritance_expander.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_migrate.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_migrate.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_result.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_result.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_result.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/__pycache__/test_run_all.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/commands/__pycache__/test_compare_command.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/commands/test_compare_command.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_architecture_guard.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_architecture_guard.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_assertions.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_execution_new_features.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_history_store.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_last_run_store.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_last_run_store.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_last_run_store.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_model_v2.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_model_v2.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_orchestration_accept.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_orchestration_accept.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_process_worker.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_sequence_state.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_sequence_state.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_setup.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_setup.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_validation_error.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_validation_error.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_validation_error.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_xfail.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_xfail.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/__pycache__/test_xfail.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_architecture_guard.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_case_env.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_config_loader.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_config_loader_new_features.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_execution.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_execution_new_features.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_history_store.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_last_run_store.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_model_v2.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_orchestration_accept.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_process_worker.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_sequence_state.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_setup.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_validation_error.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/core/test_xfail.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_hourglass_tangent_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_numeric_compare.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_numeric_compare.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_numeric_parity.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_numeric_parity.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_script_comparator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/__pycache__/test_workspace_plugins.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_error_analysis.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_hourglass_tangent_comparator.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_numeric_compare.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/file_comparator/test_numeric_parity.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-39-pytest-8.3.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/test_json_yaml_runner.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/test_test_case_filter.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/runners/test_test_case_tag_filter.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_binary_comparator.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_cli.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_config_io.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_h5_comparator.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_import_expander.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_inheritance_expander.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_migrate.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_result.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/test_run_all.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__init__.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/__init__.cpython-312.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/__init__.cpython-313.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_app.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_case_controller.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_search_functions.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_tui_run_test.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/__pycache__/test_widgets_data.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/conftest.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/test_app.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/test_case_controller.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/test_search_functions.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/test_tui_run_test.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/tui/test_widgets_data.py +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-7.4.4.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-9.0.3.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/__pycache__/test_report_generator.cpython-313-pytest-9.0.2.pyc +0 -0
- {symtest_cli-1.4.1 → symtest_cli-1.5.0}/tests/unit/utils/test_junit_xml_writer.py +0 -0
|
@@ -175,24 +175,57 @@ PathResolver 解析(系统命令直通、shell builtin 平台包装、复合
|
|
|
175
175
|
|
|
176
176
|
## 6. 文件比较子系统
|
|
177
177
|
|
|
178
|
-
### 6.1
|
|
178
|
+
### 6.1 三泳道比较器架构
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
所有比较器共享根契约 `ComparatorBase.compare(ctx) -> ComparisonResult`:
|
|
181
|
+
框架构建 `CompareContext`(workspace / actual / baseline / params / error_analysis);
|
|
182
|
+
`actual`/`baseline` 以及插件 `path_params` 声明的构造参数在**比较器构造之前**
|
|
183
|
+
按 workspace 统一解析,插件不得自行对 CWD resolve。配置单一事实源:
|
|
184
|
+
比较器配置只存在于构造器捕获的实例状态,`ctx` 仅承载调用级上下文。
|
|
185
|
+
三条泳道职责互斥:
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
| 泳道 | 基类 | 契约 | verdict 归属 | 内置实例 |
|
|
188
|
+
|---|---|---|---|---|
|
|
189
|
+
| 文件泳道 | `FileComparator` | `read_content` + `compare_content`(两文件模型,行列窗口、line N 偏移、chunk_size、similarity 均属本泳道) | 插件 | text / json / csv / xml / h5 / binary |
|
|
190
|
+
| 数据泳道 | `ExtractorComparator` | `extract(ctx) -> {channel: ChannelData}`;框架逐通道跑 `compare_numeric`,per-channel 容差(`channels` / `default_channel`),聚合 `ChannelResult` | **框架**(容差语义对 AI 消费端可预期) | script_extract、extractor 插件 |
|
|
191
|
+
| 自主泳道 | 直接继承根 | `compare(ctx)` 全权判定 | 插件 | script、hourglass 式分析插件 |
|
|
185
192
|
|
|
186
|
-
-
|
|
193
|
+
- 文本系共享 difflib 行级基底,json / csv / xml 为其结构化特化;h5 面向科学数据集;binary 流式分块 + LCS 相似度
|
|
194
|
+
- 统一返回 `ComparisonResult`(identical / differences / error / error_stats / command_output / channels),支持 text / json / html 渲染
|
|
195
|
+
- 数据泳道插件可通过 `ChannelData.extra_stats` 附带自定义误差指标,存放在独立命名空间(`ChannelResult.extra_stats`),不可覆盖框架规范指标;但 verdict 仍由框架容差持有——需要自定 verdict 的走自主泳道
|
|
196
|
+
|
|
197
|
+
### 6.2 通道协议(数据泳道)
|
|
198
|
+
|
|
199
|
+
- 通道判定:`identical = all(channels.passed)`;差异 position 带通道前缀(`channel S33`);`error_stats` 按通道名嵌套
|
|
200
|
+
- 内置 `script_extract` 类型:子进程执行用户脚本(零改动接入),脚本 stdout 输出约定 JSON:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{"channels": {"S11": {"expected": [...], "actual": [...], "extra_stats": {...}}}}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
非零退出、超时、畸形 JSON 一律判为比较 error(绝不静默通过);`actual`/`baseline` 按惯例以 baseline 在前的顺序追加为尾部 argv 槽位(可缺省)
|
|
207
|
+
- 结果粒度:一条 compareSpec = 一条断言;`ComparisonResult.channels` 携带通道级子结果,报告逐通道展示 pass/fail + stats;JSON 输出中通道 differences 按 per-channel 配额截断
|
|
208
|
+
|
|
209
|
+
### 6.3 工厂与插件发现
|
|
210
|
+
|
|
211
|
+
- `file_type` 取值:`text` / `json` / `csv` / `xml` / `h5` / `binary` / `script` / `script_extract`;工厂按类型分发,支持动态注册与全局 reset(测试用)
|
|
187
212
|
- 插件发现四处来源:内置 `*_comparator.py` 自动发现、`workspace/comparators/` 自动扫描、`--plugin-dir` CLI 参数、`CLITEST_PLUGIN_DIRS` 环境变量(供进程模式 worker 使用)
|
|
188
|
-
- 命名约定:`*_comparator.py` + `*Comparator`
|
|
213
|
+
- 命名约定:`*_comparator.py` + `*Comparator` 类名;可用类属性 `comparator_type` 显式指定类型名(如 `script_extract`);抽象基类自动跳过注册
|
|
214
|
+
- 配置传递与严格校验:`actual`/`baseline`/`type`/`options` 之外的键转发给比较器构造函数(kwargs);构造器参数是**严格**的——未声明的键(拼写错误)在构造时大声失败并给出支持参数清单。`options` 是框架持有的插件配置命名空间(并入构造参数,显式顶层键优先),核心 schema 不为单个插件增加专属字段;`channels`/`default_channel` 属数据泳道框架结构
|
|
189
215
|
|
|
190
|
-
### 6.
|
|
216
|
+
### 6.4 script 比较协议(自主泳道对外契约)
|
|
191
217
|
|
|
192
|
-
- 子进程方式执行 `<interpreter> <script> <
|
|
218
|
+
- 子进程方式执行 `<interpreter> <script> <baseline> <actual>`(文件槽位可缺省)
|
|
193
219
|
- 默认 exit code 0 → 通过;可选 `pass_pattern` / `fail_pattern` 正则匹配 stdout 细化判定
|
|
194
220
|
- 超时可配
|
|
195
221
|
|
|
222
|
+
### 6.5 插件公共 API
|
|
223
|
+
|
|
224
|
+
`symtest.file_comparator` 包导出:`ComparatorBase` / `CompareContext` /
|
|
225
|
+
`FileComparator` / `ExtractorComparator` / `ChannelData` / `ChannelResult` /
|
|
226
|
+
`compare_numeric` / `NumericComparisonStats` / `parse_data_filter`。
|
|
227
|
+
数据泳道插件只需依赖 `ExtractorComparator` + `ChannelData`,数值判定全部复用框架核心。
|
|
228
|
+
|
|
196
229
|
## 7. TUI 子系统
|
|
197
230
|
|
|
198
231
|
基于 Textual 的终端交互界面:App + Controller(load / create / update /
|
|
@@ -210,7 +243,7 @@ TUI 与 runner 共用同一解析器;宽松的展示形态在 TUI 侧自行处
|
|
|
210
243
|
| 新配置格式 | `BaseRunner` | 支持新的测试定义格式(如 XML、TOML) |
|
|
211
244
|
| 自定义 Setup | `BaseSetup` | 数据库初始化、服务启停等 |
|
|
212
245
|
| 自定义断言 | 扩展 `Assertions` | 特定业务校验逻辑 |
|
|
213
|
-
| 新比较器 | `
|
|
246
|
+
| 新比较器 | `FileComparator` / `ExtractorComparator` / `ComparatorBase` | 按泳道选择基类(文件格式 / 数据提取+通道 / 全权分析),放入 `comparators/` 目录自动发现 |
|
|
214
247
|
| 新 Runner | `ParallelRunner` / `BaseRunner` | 自定义并行调度策略 |
|
|
215
248
|
| TUI 扩展 | `CaseController` / Widgets | 扩展终端管理界面 |
|
|
216
249
|
|
|
@@ -1972,11 +1972,31 @@ class MySetup(BaseSetup):
|
|
|
1972
1972
|
|
|
1973
1973
|
### 自定义文件比较器
|
|
1974
1974
|
|
|
1975
|
-
|
|
1975
|
+
#### 三泳道架构(v2)
|
|
1976
1976
|
|
|
1977
|
-
|
|
1977
|
+
比较器插件体系围绕根契约 `ComparatorBase.compare(ctx) -> ComparisonResult` 组织,
|
|
1978
|
+
按判定权归属分为三条泳道:
|
|
1978
1979
|
|
|
1979
|
-
|
|
1980
|
+
| 泳道 | 基类 | 适用场景 | verdict 归属 |
|
|
1981
|
+
|---|---|---|---|
|
|
1982
|
+
| **文件泳道** | `FileComparator` | 比较两个同类文件(text/json/csv/xml/h5/binary 内置即此类) | 插件 |
|
|
1983
|
+
| **数据泳道** | `ExtractorComparator` | 从任意上游(文件/求解器输出/跨文件整理)提取数值数据,**复用框架数值核心**做通道化容差判定 | **框架** |
|
|
1984
|
+
| **自主泳道** | 直接继承 `ComparatorBase` | 独特判定逻辑(阈值组合、标签解析、渐近分析等) | 插件 |
|
|
1985
|
+
|
|
1986
|
+
选型决策:
|
|
1987
|
+
|
|
1988
|
+
- 数据本来就是 `(expected, actual)` 数值数组、只需 rtol/atol 判定 → **数据泳道**(可享受通道、per-channel 容差、统一 error_stats 与报告渲染)
|
|
1989
|
+
- 需要自定 verdict(比如"主分量紧、EAS 分量松"的组合判定、符号/渐近检查)→ **自主泳道**
|
|
1990
|
+
- 只是想让现成脚本快速接入 → 优先考虑内置 `script`(自定判定)或 `script_extract`(框架容差判定),零 Python 插件代码
|
|
1991
|
+
|
|
1992
|
+
**路径解析约定**:`actual`/`baseline` 以及插件用 `path_params` 类属性声明的路径参数,
|
|
1993
|
+
由框架在**比较器构造之前**统一按 workspace 解析为绝对路径——构造器捕获的状态
|
|
1994
|
+
(`self.script`、`self.cwd` 等)持有的已是解析后的绝对路径。插件内部
|
|
1995
|
+
**禁止**对 CWD 做 `Path.resolve()`——并行/process 模式下 CWD 不可靠。
|
|
1996
|
+
|
|
1997
|
+
#### 插件发现方式(三种泳道通用)
|
|
1998
|
+
|
|
1999
|
+
在 workspace 下创建 `comparators/` 目录,放入 `*_comparator.py` 文件,框架会在首次使用时自动发现并注册其中的 `*Comparator` 类。
|
|
1980
2000
|
|
|
1981
2001
|
```
|
|
1982
2002
|
your-workspace/
|
|
@@ -1993,26 +2013,148 @@ symtest run test_config.json --plugin-dir ./extra_plugins
|
|
|
1993
2013
|
|
|
1994
2014
|
插件也会通过环境变量 `CLITEST_PLUGIN_DIRS` 自动继承到 process 模式子进程。
|
|
1995
2015
|
|
|
1996
|
-
|
|
1997
|
-
-
|
|
2016
|
+
**命名约定**:
|
|
2017
|
+
- 文件名必须以 `_comparator.py` 结尾(如 `my_analysis_comparator.py`)
|
|
1998
2018
|
- 类名必须以 `Comparator` 结尾(如 `MyAnalysisComparator`)
|
|
1999
|
-
- 注册的 type 名 = 类名去掉 `Comparator` 再小写(如 `myanalysis
|
|
2000
|
-
-
|
|
2001
|
-
- 通过 `from symtest.file_comparator import ComparisonResult, Difference` 构造结构化结果
|
|
2002
|
-
- `extra_kwargs` 自动从 config `compareSpec` 透传
|
|
2019
|
+
- 注册的 type 名 = 类名去掉 `Comparator` 再小写(如 `myanalysis`);也可用类属性 `comparator_type = "my_analysis"` 显式指定
|
|
2020
|
+
- 抽象基类(未实现全部抽象方法)自动跳过注册
|
|
2003
2021
|
|
|
2004
|
-
|
|
2022
|
+
在配置中直接使用注册的类型名。比较器构造参数是**严格**的:`actual`/`baseline`/
|
|
2023
|
+
`type` 以外的键会透传给插件构造函数,但插件未声明的参数(如拼写错误
|
|
2024
|
+
`pass_threhsold`)会在构造时**大声失败**(错误信息包含比较器类型名与支持的
|
|
2025
|
+
参数清单),绝不静默回退到默认值。插件自有配置建议放入 `options` 命名空间
|
|
2026
|
+
(其条目并入构造参数,显式顶层键优先),避免向核心 schema 增加插件专属字段:
|
|
2005
2027
|
|
|
2006
2028
|
```json
|
|
2007
2029
|
{
|
|
2008
2030
|
"type": "myanalysis",
|
|
2009
2031
|
"actual": "optional_for_plugins",
|
|
2010
2032
|
"baseline": "optional_for_plugins",
|
|
2011
|
-
"param1": "value1"
|
|
2033
|
+
"options": {"param1": "value1", "param2": 42}
|
|
2034
|
+
}
|
|
2035
|
+
```
|
|
2036
|
+
|
|
2037
|
+
#### 数据泳道:通道提取器插件
|
|
2038
|
+
|
|
2039
|
+
适用场景:一个脚本/插件产出多通道数据(如 CSV 的不同列、不同物理量),
|
|
2040
|
+
每通道需要独立的容差与误差分析。插件只负责"提取",判定完全由框架完成。
|
|
2041
|
+
|
|
2042
|
+
```python
|
|
2043
|
+
# comparators/uel_stress_comparator.py
|
|
2044
|
+
import numpy as np
|
|
2045
|
+
from symtest.file_comparator import (
|
|
2046
|
+
ExtractorComparator, ChannelData, CompareContext,
|
|
2047
|
+
)
|
|
2048
|
+
|
|
2049
|
+
class UelStressComparator(ExtractorComparator):
|
|
2050
|
+
path_params = ("uel_csv", "ref_csv") # 由框架按 workspace 解析
|
|
2051
|
+
|
|
2052
|
+
def __init__(self, uel_csv="", ref_csv="", **kwargs):
|
|
2053
|
+
super().__init__(**kwargs)
|
|
2054
|
+
self.uel_csv = uel_csv
|
|
2055
|
+
self.ref_csv = ref_csv
|
|
2056
|
+
|
|
2057
|
+
def extract(self, ctx: CompareContext):
|
|
2058
|
+
uel = np.loadtxt(self.uel_csv, delimiter=",")
|
|
2059
|
+
ref = np.loadtxt(self.ref_csv, delimiter=",")
|
|
2060
|
+
return {
|
|
2061
|
+
"S11": ChannelData(expected=ref[:, 0], actual=uel[:, 0]),
|
|
2062
|
+
"S33": ChannelData(
|
|
2063
|
+
expected=ref[:, 2], actual=uel[:, 2],
|
|
2064
|
+
extra_stats={"hydrostatic_shift": float(uel[:, 2].mean())},
|
|
2065
|
+
),
|
|
2066
|
+
}
|
|
2067
|
+
```
|
|
2068
|
+
|
|
2069
|
+
配置(per-channel 容差按名路由,未声明的通道用 `default_channel`):
|
|
2070
|
+
|
|
2071
|
+
```json
|
|
2072
|
+
{
|
|
2073
|
+
"type": "uelstress",
|
|
2074
|
+
"uel_csv": "out/uel.csv",
|
|
2075
|
+
"ref_csv": "out/native.csv",
|
|
2076
|
+
"channels": {
|
|
2077
|
+
"S33": {"atol": 600.0, "data_filter": "abs>1e-12"}
|
|
2078
|
+
},
|
|
2079
|
+
"default_channel": {"rtol": 1e-5, "atol": 1e-8}
|
|
2080
|
+
}
|
|
2081
|
+
```
|
|
2082
|
+
|
|
2083
|
+
**判定与结果语义**:
|
|
2084
|
+
- 每通道独立跑 `compare_numeric`(对称容差 `|a-b| <= max(rtol*max(|a|,|b|), atol)`)
|
|
2085
|
+
- `identical = 所有通道全过`;一条 compareSpec 仍是一条断言
|
|
2086
|
+
- 差异 position 带通道前缀(`channel S33`);`error_stats` 按通道名嵌套
|
|
2087
|
+
- 报告逐通道展示 pass/fail、容差与统计;JSON 输出中通道差异按 per-channel 配额截断
|
|
2088
|
+
- 自定义误差指标通过 `ChannelData.extra_stats` 附带,存放在**独立命名空间**
|
|
2089
|
+
(`ChannelResult.extra_stats`),不可能覆盖框架规范指标(`max_abs_error`、
|
|
2090
|
+
`total` 等);报告逐通道分别渲染两个命名空间
|
|
2091
|
+
- verdict 不可由插件干预——需要自定 verdict 请走自主泳道
|
|
2092
|
+
|
|
2093
|
+
#### 数据泳道:内置 `script_extract` 类型(零改动脚本接入)
|
|
2094
|
+
|
|
2095
|
+
现成分析脚本无需包装成 Python 插件:脚本以子进程执行,向 stdout 打印约定
|
|
2096
|
+
JSON,各通道即进入上述框架判定管线。
|
|
2097
|
+
|
|
2098
|
+
```json
|
|
2099
|
+
{
|
|
2100
|
+
"type": "script_extract",
|
|
2101
|
+
"script": "extract_channels.py",
|
|
2102
|
+
"args": ["--frame", "10"],
|
|
2103
|
+
"cwd": "case/subdir",
|
|
2104
|
+
"channels": {"S33": {"atol": 600.0}},
|
|
2105
|
+
"default_channel": {"rtol": 1e-5, "atol": 1e-8},
|
|
2106
|
+
"timeout": 600
|
|
2107
|
+
}
|
|
2108
|
+
```
|
|
2109
|
+
|
|
2110
|
+
**stdout JSON 协议**:
|
|
2111
|
+
|
|
2112
|
+
```json
|
|
2113
|
+
{
|
|
2114
|
+
"channels": {
|
|
2115
|
+
"S11": {"expected": [1.0, 2.0], "actual": [1.0, 2.0]},
|
|
2116
|
+
"S33": {"expected": [...], "actual": [...], "extra_stats": {"asymmetry": 2e-13}}
|
|
2117
|
+
}
|
|
2012
2118
|
}
|
|
2013
2119
|
```
|
|
2014
2120
|
|
|
2015
|
-
|
|
2121
|
+
**错误语义**:非零退出码、超时、畸形 JSON、缺 `channels` 键或通道缺
|
|
2122
|
+
`expected`/`actual` 数组 → 一律判为比较 error(绝不静默通过)。
|
|
2123
|
+
`actual`/`baseline`(若配置)以 baseline 在前的顺序追加为脚本尾部参数,可缺省。
|
|
2124
|
+
|
|
2125
|
+
#### 自主泳道:全权判定插件
|
|
2126
|
+
|
|
2127
|
+
适用场景:独特判定逻辑。插件实现 `compare(ctx)` 并直接返回结果。
|
|
2128
|
+
|
|
2129
|
+
```python
|
|
2130
|
+
# comparators/my_analysis_comparator.py
|
|
2131
|
+
from symtest.file_comparator import ComparatorBase, CompareContext, ComparisonResult, Difference
|
|
2132
|
+
|
|
2133
|
+
class MyAnalysisComparator(ComparatorBase):
|
|
2134
|
+
path_params = ("script", "case_dir")
|
|
2135
|
+
|
|
2136
|
+
def __init__(self, script="", case_dir=None, pass_threshold=1e-6):
|
|
2137
|
+
super().__init__() # 参数严格:未声明/拼错的配置键在构造时报错
|
|
2138
|
+
self.script = script # 构造前已由框架按 workspace 解析
|
|
2139
|
+
self.case_dir = case_dir
|
|
2140
|
+
self.pass_threshold = pass_threshold
|
|
2141
|
+
|
|
2142
|
+
def compare(self, ctx: CompareContext) -> ComparisonResult:
|
|
2143
|
+
result = ComparisonResult(
|
|
2144
|
+
file1=ctx.baseline, # 无文件输入时保持 None,不伪造空串
|
|
2145
|
+
file2=ctx.actual,
|
|
2146
|
+
)
|
|
2147
|
+
# ... 执行分析、解析指标 ...
|
|
2148
|
+
result.identical = True # 或 False + differences
|
|
2149
|
+
result.error_stats = {"full_rel": 8e-8} # 自由 dict,报告通用渲染
|
|
2150
|
+
return result
|
|
2151
|
+
```
|
|
2152
|
+
|
|
2153
|
+
`ctx` 字段(仅调用级上下文):`workspace` / `actual` / `baseline`(可空,
|
|
2154
|
+
无文件输入时为 `None`)/ `params`(文件泳道窗口参数)/ `error_analysis`。
|
|
2155
|
+
比较器配置的唯一权威在构造器捕获的实例状态中——`ctx` 不携带配置副本。
|
|
2156
|
+
|
|
2157
|
+
#### 内置 `script` 类型比较器(自主泳道开箱即用)
|
|
2016
2158
|
|
|
2017
2159
|
适用于独立分析脚本快速接入,无需编写比较器类:
|
|
2018
2160
|
|
|
@@ -2035,8 +2177,8 @@ symtest run test_config.json --plugin-dir ./extra_plugins
|
|
|
2035
2177
|
| 参数 | 必填 | 默认值 | 说明 |
|
|
2036
2178
|
|---|---|---|---|
|
|
2037
2179
|
| `script` | 是 | — | 脚本路径(相对 workspace 或绝对) |
|
|
2038
|
-
| `actual` | 否 | — |
|
|
2039
|
-
| `baseline` | 否 | — |
|
|
2180
|
+
| `actual` | 否 | — | 传给脚本的文件参数(尾部第二位) |
|
|
2181
|
+
| `baseline` | 否 | — | 传给脚本的文件参数(尾部第一位) |
|
|
2040
2182
|
| `cwd` | 否 | — | 脚本工作目录 |
|
|
2041
2183
|
| `interpreter` | 否 | `sys.executable` | Python 解释器 |
|
|
2042
2184
|
| `pass_exit_code` | 否 | `0` | 判定为通过的退出码 |
|
|
@@ -2052,15 +2194,15 @@ symtest run test_config.json --plugin-dir ./extra_plugins
|
|
|
2052
2194
|
|
|
2053
2195
|
脚本的 stdout 和 stderr 会完整捕获到 `Comparator Output` 区块中,在报告渲染时限 20 行展示。
|
|
2054
2196
|
|
|
2055
|
-
####
|
|
2197
|
+
#### 手动注册(编程方式)
|
|
2056
2198
|
|
|
2057
2199
|
```python
|
|
2058
|
-
from symtest.file_comparator import ComparatorFactory
|
|
2059
|
-
from symtest.file_comparator.
|
|
2200
|
+
from symtest.file_comparator import ComparatorFactory, ComparatorBase, CompareContext
|
|
2201
|
+
from symtest.file_comparator.result import ComparisonResult
|
|
2060
2202
|
|
|
2061
|
-
class FooComparator(
|
|
2062
|
-
|
|
2063
|
-
|
|
2203
|
+
class FooComparator(ComparatorBase):
|
|
2204
|
+
def compare(self, ctx: CompareContext) -> ComparisonResult:
|
|
2205
|
+
...
|
|
2064
2206
|
|
|
2065
2207
|
ComparatorFactory.register_comparator("foo", FooComparator)
|
|
2066
2208
|
|
|
@@ -2070,7 +2212,7 @@ comparator = ComparatorFactory.create_comparator("foo")
|
|
|
2070
2212
|
|
|
2071
2213
|
#### 专用插件示例:hourglass 切线刚度分析
|
|
2072
2214
|
|
|
2073
|
-
`examples/plugins/hourglass_tangent_comparator.py`
|
|
2215
|
+
`examples/plugins/hourglass_tangent_comparator.py` 是一个完整的自主泳道工作区插件示例,展示了如何将专用的 `analyze_*_tangent.py` 分析脚本接入框架:
|
|
2074
2216
|
|
|
2075
2217
|
```json
|
|
2076
2218
|
{
|
|
@@ -2085,6 +2227,7 @@ comparator = ComparatorFactory.create_comparator("foo")
|
|
|
2085
2227
|
**特点**:
|
|
2086
2228
|
- 通过 subprocess 调分析脚本(**零改动** analyze 代码),捕获 stdout 后用正则解析 `RESULT:` 行和 `full_rel`/`aa_rel`/`hh_rel`/`asymmetry` 等数值指标
|
|
2087
2229
|
- 构造结构化 `ComparisonResult`:`identical` 基于 `full_rel < pass_threshold` 判定;`differences` 列出超限指标;`error_stats` 包含全部数值
|
|
2230
|
+
- `path_params` 声明 `script`/`case_dir`,路径由框架按 workspace 解析
|
|
2088
2231
|
- 脚本 stdout 进入 `Comparator Output` 区块
|
|
2089
2232
|
|
|
2090
2233
|
使用方法:将插件文件复制到 workspace 的 `comparators/` 目录下即可自动发现,无需改框架代码。
|
|
@@ -1970,11 +1970,35 @@ class MySetup(BaseSetup):
|
|
|
1970
1970
|
|
|
1971
1971
|
### Custom File Comparator
|
|
1972
1972
|
|
|
1973
|
-
|
|
1973
|
+
#### Three-Lane Architecture (v2)
|
|
1974
1974
|
|
|
1975
|
-
|
|
1975
|
+
The comparator plugin system is organized around the root contract
|
|
1976
|
+
`ComparatorBase.compare(ctx) -> ComparisonResult`, split by verdict ownership
|
|
1977
|
+
into three lanes:
|
|
1976
1978
|
|
|
1977
|
-
|
|
1979
|
+
| Lane | Base class | Use case | Verdict owned by |
|
|
1980
|
+
|---|---|---|---|
|
|
1981
|
+
| **File lane** | `FileComparator` | Comparing two same-kind files (built-in text/json/csv/xml/h5/binary) | Plugin |
|
|
1982
|
+
| **Data lane** | `ExtractorComparator` | Extracting numeric data from any upstream (files / solver output / cross-file aggregation), reusing the framework numeric core for per-channel tolerance verdicts | **Framework** |
|
|
1983
|
+
| **Autonomous lane** | extend `ComparatorBase` directly | Custom verdict logic (composite thresholds, label parsing, sign/asymptotic checks) | Plugin |
|
|
1984
|
+
|
|
1985
|
+
Selection guide:
|
|
1986
|
+
|
|
1987
|
+
- Data is already `(expected, actual)` numeric arrays needing only rtol/atol → **data lane** (channels, per-channel tolerances, unified error_stats and report rendering)
|
|
1988
|
+
- Custom verdict needed (e.g. "tight on main components, loose on EAS components", sign checks) → **autonomous lane**
|
|
1989
|
+
- Just want to wire an existing script in quickly → built-in `script` (custom verdict) or `script_extract` (framework tolerance verdict), zero plugin code
|
|
1990
|
+
|
|
1991
|
+
**Path resolution convention**: `actual`/`baseline` and any constructor params
|
|
1992
|
+
declared in the plugin's `path_params` class attribute are resolved against
|
|
1993
|
+
the workspace by the framework **before the comparator is constructed** —
|
|
1994
|
+
constructor-captured state (`self.script`, `self.cwd`, ...) already holds
|
|
1995
|
+
absolute paths. Plugins must
|
|
1996
|
+
**never** resolve paths against CWD — it is unreliable under parallel/process
|
|
1997
|
+
execution.
|
|
1998
|
+
|
|
1999
|
+
#### Plugin Discovery (common to all lanes)
|
|
2000
|
+
|
|
2001
|
+
Create a `comparators/` directory under your workspace and place `*_comparator.py` files inside. The framework auto-discovers and registers the `*Comparator` classes on first use.
|
|
1978
2002
|
|
|
1979
2003
|
```
|
|
1980
2004
|
your-workspace/
|
|
@@ -1991,26 +2015,155 @@ symtest run test_config.json --plugin-dir ./extra_plugins
|
|
|
1991
2015
|
|
|
1992
2016
|
Plugins are also automatically inherited by process-mode child processes via the `CLITEST_PLUGIN_DIRS` environment variable.
|
|
1993
2017
|
|
|
1994
|
-
**
|
|
1995
|
-
-
|
|
2018
|
+
**Naming conventions**:
|
|
2019
|
+
- File name must end with `_comparator.py` (e.g., `my_analysis_comparator.py`)
|
|
1996
2020
|
- Class name must end with `Comparator` (e.g., `MyAnalysisComparator`)
|
|
1997
|
-
- The registered type name = class name without `Comparator`, lowercased (e.g., `myanalysis`)
|
|
1998
|
-
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2021
|
+
- The registered type name = class name without `Comparator`, lowercased (e.g., `myanalysis`); a `comparator_type = "my_analysis"` class attribute overrides it
|
|
2022
|
+
- Abstract base classes (with unimplemented abstract methods) are skipped automatically
|
|
2023
|
+
|
|
2024
|
+
Use the registered type name directly in your config. Comparator constructor
|
|
2025
|
+
parameters are **strict**: keys other than `actual`/`baseline`/`type` are
|
|
2026
|
+
forwarded to the plugin constructor, but a parameter the plugin does not
|
|
2027
|
+
declare (e.g. a typo like `pass_threhsold`) **fails loudly** at construction
|
|
2028
|
+
(the error names the comparator type and its supported parameters) — it never
|
|
2029
|
+
silently falls back to defaults. Plugin-owned configuration should go into
|
|
2030
|
+
the `options` namespace (entries merged into constructor kwargs; explicit
|
|
2031
|
+
top-level keys take precedence), keeping the core schema generic:
|
|
2003
2032
|
|
|
2004
2033
|
```json
|
|
2005
2034
|
{
|
|
2006
2035
|
"type": "myanalysis",
|
|
2007
2036
|
"actual": "optional_for_plugins",
|
|
2008
2037
|
"baseline": "optional_for_plugins",
|
|
2009
|
-
"param1": "value1"
|
|
2038
|
+
"options": {"param1": "value1", "param2": 42}
|
|
2010
2039
|
}
|
|
2011
2040
|
```
|
|
2012
2041
|
|
|
2013
|
-
####
|
|
2042
|
+
#### Data Lane: Channel Extractor Plugin
|
|
2043
|
+
|
|
2044
|
+
Use case: one script/plugin produces multi-channel data (e.g. different CSV
|
|
2045
|
+
columns, different physical quantities), each channel needing independent
|
|
2046
|
+
tolerances and error analysis. The plugin only EXTRACTS; the framework
|
|
2047
|
+
owns the verdict entirely.
|
|
2048
|
+
|
|
2049
|
+
```python
|
|
2050
|
+
# comparators/uel_stress_comparator.py
|
|
2051
|
+
import numpy as np
|
|
2052
|
+
from symtest.file_comparator import (
|
|
2053
|
+
ExtractorComparator, ChannelData, CompareContext,
|
|
2054
|
+
)
|
|
2055
|
+
|
|
2056
|
+
class UelStressComparator(ExtractorComparator):
|
|
2057
|
+
path_params = ("uel_csv", "ref_csv") # workspace-resolved by framework
|
|
2058
|
+
|
|
2059
|
+
def __init__(self, uel_csv="", ref_csv="", **kwargs):
|
|
2060
|
+
super().__init__(**kwargs)
|
|
2061
|
+
self.uel_csv = uel_csv
|
|
2062
|
+
self.ref_csv = ref_csv
|
|
2063
|
+
|
|
2064
|
+
def extract(self, ctx: CompareContext):
|
|
2065
|
+
uel = np.loadtxt(self.uel_csv, delimiter=",")
|
|
2066
|
+
ref = np.loadtxt(self.ref_csv, delimiter=",")
|
|
2067
|
+
return {
|
|
2068
|
+
"S11": ChannelData(expected=ref[:, 0], actual=uel[:, 0]),
|
|
2069
|
+
"S33": ChannelData(
|
|
2070
|
+
expected=ref[:, 2], actual=uel[:, 2],
|
|
2071
|
+
extra_stats={"hydrostatic_shift": float(uel[:, 2].mean())},
|
|
2072
|
+
),
|
|
2073
|
+
}
|
|
2074
|
+
```
|
|
2075
|
+
|
|
2076
|
+
Config (per-channel tolerances routed by name; unlisted channels use `default_channel`):
|
|
2077
|
+
|
|
2078
|
+
```json
|
|
2079
|
+
{
|
|
2080
|
+
"type": "uelstress",
|
|
2081
|
+
"uel_csv": "out/uel.csv",
|
|
2082
|
+
"ref_csv": "out/native.csv",
|
|
2083
|
+
"channels": {
|
|
2084
|
+
"S33": {"atol": 600.0, "data_filter": "abs>1e-12"}
|
|
2085
|
+
},
|
|
2086
|
+
"default_channel": {"rtol": 1e-5, "atol": 1e-8}
|
|
2087
|
+
}
|
|
2088
|
+
```
|
|
2089
|
+
|
|
2090
|
+
**Verdict and result semantics**:
|
|
2091
|
+
- Each channel runs `compare_numeric` independently (symmetric tolerance `|a-b| <= max(rtol*max(|a|,|b|), atol)`)
|
|
2092
|
+
- `identical = all channels pass`; one compareSpec is still one assertion
|
|
2093
|
+
- Difference positions carry the channel prefix (`channel S33`); `error_stats` nests by channel name
|
|
2094
|
+
- The report shows per-channel pass/fail, tolerances and stats; channel differences are trimmed per channel in JSON output
|
|
2095
|
+
- Custom error metrics ride along via `ChannelData.extra_stats`, kept in a SEPARATE namespace (`ChannelResult.extra_stats`) that can never overwrite framework canonical metrics (`max_abs_error`, `total`, ...); the report renders both namespaces per channel
|
|
2096
|
+
- The verdict itself cannot be altered by the plugin — custom verdicts belong to the autonomous lane
|
|
2097
|
+
|
|
2098
|
+
#### Data Lane: Built-in `script_extract` Type (Zero-Modification Script Access)
|
|
2099
|
+
|
|
2100
|
+
Existing analysis scripts need no Python plugin wrapper: the script runs as a
|
|
2101
|
+
subprocess and prints a JSON channel payload on stdout; each channel then
|
|
2102
|
+
enters the framework-owned tolerance pipeline described above.
|
|
2103
|
+
|
|
2104
|
+
```json
|
|
2105
|
+
{
|
|
2106
|
+
"type": "script_extract",
|
|
2107
|
+
"script": "extract_channels.py",
|
|
2108
|
+
"args": ["--frame", "10"],
|
|
2109
|
+
"cwd": "case/subdir",
|
|
2110
|
+
"channels": {"S33": {"atol": 600.0}},
|
|
2111
|
+
"default_channel": {"rtol": 1e-5, "atol": 1e-8},
|
|
2112
|
+
"timeout": 600
|
|
2113
|
+
}
|
|
2114
|
+
```
|
|
2115
|
+
|
|
2116
|
+
**stdout JSON protocol**:
|
|
2117
|
+
|
|
2118
|
+
```json
|
|
2119
|
+
{
|
|
2120
|
+
"channels": {
|
|
2121
|
+
"S11": {"expected": [1.0, 2.0], "actual": [1.0, 2.0]},
|
|
2122
|
+
"S33": {"expected": [...], "actual": [...], "extra_stats": {"asymmetry": 2e-13}}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
```
|
|
2126
|
+
|
|
2127
|
+
**Error semantics**: non-zero exit code, timeout, malformed JSON, a missing
|
|
2128
|
+
`channels` key, or a channel missing `expected`/`actual` arrays → always a
|
|
2129
|
+
comparison error (never a silent pass). `actual`/`baseline` (if configured)
|
|
2130
|
+
are appended as trailing script arguments with baseline first; both optional.
|
|
2131
|
+
|
|
2132
|
+
#### Autonomous Lane: Full-Verdict Plugin
|
|
2133
|
+
|
|
2134
|
+
Use case: custom verdict logic. The plugin implements `compare(ctx)` and
|
|
2135
|
+
returns the result directly.
|
|
2136
|
+
|
|
2137
|
+
```python
|
|
2138
|
+
# comparators/my_analysis_comparator.py
|
|
2139
|
+
from symtest.file_comparator import ComparatorBase, CompareContext, ComparisonResult, Difference
|
|
2140
|
+
|
|
2141
|
+
class MyAnalysisComparator(ComparatorBase):
|
|
2142
|
+
path_params = ("script", "case_dir")
|
|
2143
|
+
|
|
2144
|
+
def __init__(self, script="", case_dir=None, pass_threshold=1e-6):
|
|
2145
|
+
super().__init__() # strict: undeclared/misspelled config keys fail loudly
|
|
2146
|
+
self.script = script # workspace-resolved by the framework before construction
|
|
2147
|
+
self.case_dir = case_dir
|
|
2148
|
+
self.pass_threshold = pass_threshold
|
|
2149
|
+
|
|
2150
|
+
def compare(self, ctx: CompareContext) -> ComparisonResult:
|
|
2151
|
+
result = ComparisonResult(
|
|
2152
|
+
file1=ctx.baseline, # stays None when there is no file input — no fake ""
|
|
2153
|
+
file2=ctx.actual,
|
|
2154
|
+
)
|
|
2155
|
+
# ... run analysis, parse metrics ...
|
|
2156
|
+
result.identical = True # or False + differences
|
|
2157
|
+
result.error_stats = {"full_rel": 8e-8} # free-form dict, generic rendering
|
|
2158
|
+
return result
|
|
2159
|
+
```
|
|
2160
|
+
|
|
2161
|
+
`ctx` fields (invocation-level context only): `workspace` / `actual` /
|
|
2162
|
+
`baseline` (`None` when absent) / `params` (file-lane window ranges) /
|
|
2163
|
+
`error_analysis`. The single authoritative copy of comparator configuration
|
|
2164
|
+
lives in constructor-captured state — `ctx` never duplicates it.
|
|
2165
|
+
|
|
2166
|
+
#### Built-in `script` Type Comparator (Autonomous Lane Out of the Box)
|
|
2014
2167
|
|
|
2015
2168
|
For quickly integrating standalone analysis scripts without writing a comparator class:
|
|
2016
2169
|
|
|
@@ -2033,8 +2186,8 @@ For quickly integrating standalone analysis scripts without writing a comparator
|
|
|
2033
2186
|
| Parameter | Required | Default | Description |
|
|
2034
2187
|
|---|---|---|---|
|
|
2035
2188
|
| `script` | Yes | — | Script path (relative to workspace or absolute) |
|
|
2036
|
-
| `actual` | No | — |
|
|
2037
|
-
| `baseline` | No | — |
|
|
2189
|
+
| `actual` | No | — | File argument passed to the script (trailing second slot) |
|
|
2190
|
+
| `baseline` | No | — | File argument passed to the script (trailing first slot) |
|
|
2038
2191
|
| `cwd` | No | — | Script working directory |
|
|
2039
2192
|
| `interpreter` | No | `sys.executable` | Python interpreter |
|
|
2040
2193
|
| `pass_exit_code` | No | `0` | Exit code considered a pass |
|
|
@@ -2050,15 +2203,15 @@ For quickly integrating standalone analysis scripts without writing a comparator
|
|
|
2050
2203
|
|
|
2051
2204
|
The script's stdout and stderr are fully captured in the `Comparator Output` section, displayed up to 20 lines in the rendered report.
|
|
2052
2205
|
|
|
2053
|
-
####
|
|
2206
|
+
#### Manual Registration (Programmatic)
|
|
2054
2207
|
|
|
2055
2208
|
```python
|
|
2056
|
-
from symtest.file_comparator import ComparatorFactory
|
|
2057
|
-
from symtest.file_comparator.
|
|
2209
|
+
from symtest.file_comparator import ComparatorFactory, ComparatorBase, CompareContext
|
|
2210
|
+
from symtest.file_comparator.result import ComparisonResult
|
|
2058
2211
|
|
|
2059
|
-
class FooComparator(
|
|
2060
|
-
|
|
2061
|
-
|
|
2212
|
+
class FooComparator(ComparatorBase):
|
|
2213
|
+
def compare(self, ctx: CompareContext) -> ComparisonResult:
|
|
2214
|
+
...
|
|
2062
2215
|
|
|
2063
2216
|
ComparatorFactory.register_comparator("foo", FooComparator)
|
|
2064
2217
|
|
|
@@ -2068,7 +2221,7 @@ comparator = ComparatorFactory.create_comparator("foo")
|
|
|
2068
2221
|
|
|
2069
2222
|
#### Specialized Plugin Example: Hourglass Tangent Stiffness Analysis
|
|
2070
2223
|
|
|
2071
|
-
`examples/plugins/hourglass_tangent_comparator.py` is a complete workspace plugin example demonstrating how to integrate a dedicated `analyze_*_tangent.py` analysis script into the framework:
|
|
2224
|
+
`examples/plugins/hourglass_tangent_comparator.py` is a complete autonomous-lane workspace plugin example demonstrating how to integrate a dedicated `analyze_*_tangent.py` analysis script into the framework:
|
|
2072
2225
|
|
|
2073
2226
|
```json
|
|
2074
2227
|
{
|
|
@@ -2083,6 +2236,7 @@ comparator = ComparatorFactory.create_comparator("foo")
|
|
|
2083
2236
|
**Features**:
|
|
2084
2237
|
- Calls the analysis script via subprocess (**zero changes** to analyze code), parses `RESULT:` lines and numerical metrics like `full_rel`/`aa_rel`/`hh_rel`/`asymmetry` from stdout using regex
|
|
2085
2238
|
- Constructs a structured `ComparisonResult`: `identical` determined by `full_rel < pass_threshold`; `differences` lists exceeded metrics; `error_stats` contains all numeric values
|
|
2239
|
+
- `path_params` declares `script`/`case_dir`, resolved against the workspace by the framework
|
|
2086
2240
|
- Script stdout goes into the `Comparator Output` section
|
|
2087
2241
|
|
|
2088
2242
|
Usage: copy the plugin file into your workspace's `comparators/` directory for auto-discovery — no framework code changes needed.
|