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.
Files changed (407) hide show
  1. {symtest_cli-1.4.0/src/symtest_cli.egg-info → symtest_cli-1.4.1}/PKG-INFO +1 -1
  2. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/design.md +1 -18
  3. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/design_en.md +1 -20
  4. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/user_manual.md +13 -6
  5. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/user_manual_en.md +8 -1
  6. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/__init__.py +1 -1
  7. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/cli.py +16 -0
  8. symtest_cli-1.4.1/src/symtest/commands/migrate.py +160 -0
  9. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/config_schema.py +44 -2
  10. symtest_cli-1.4.1/src/symtest/config/normalize.py +43 -0
  11. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/base_runner.py +0 -19
  12. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/config_loader.py +28 -26
  13. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/sequence.py +11 -23
  14. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/single.py +2 -13
  15. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/parallel_runner.py +0 -2
  16. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/process_worker.py +4 -4
  17. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/test_case.py +88 -20
  18. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/csv_comparator.py +3 -1
  19. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/h5_comparator.py +4 -2
  20. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/numeric_compare.py +43 -40
  21. symtest_cli-1.4.1/src/symtest/reporting/__init__.py +17 -0
  22. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/reporting/diagnosis.py +65 -2
  23. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/controllers/case_controller.py +15 -5
  24. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/steps_editor.py +5 -5
  25. {symtest_cli-1.4.0 → symtest_cli-1.4.1/src/symtest_cli.egg-info}/PKG-INFO +1 -1
  26. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/SOURCES.txt +2 -1
  27. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
  28. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
  29. {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
  30. symtest_cli-1.4.1/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
  31. {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
  32. {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
  33. {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
  34. {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
  35. {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
  36. symtest_cli-1.4.1/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
  37. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-7.4.4.pyc +0 -0
  38. symtest_cli-1.4.1/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
  39. {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
  40. {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
  41. {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
  42. {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
  43. {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
  44. {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
  45. {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
  46. {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
  47. {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
  48. {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
  49. {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
  50. {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
  51. {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
  52. symtest_cli-1.4.1/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-9.0.3.pyc +0 -0
  53. {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
  54. symtest_cli-1.4.1/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  55. {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
  56. symtest_cli-1.4.1/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
  57. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_sequence.py +8 -8
  58. {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
  59. {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
  60. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-7.4.4.pyc +0 -0
  61. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
  62. {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
  63. symtest_cli-1.4.1/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
  64. {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
  65. {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
  66. {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
  67. {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
  68. {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
  69. {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
  70. symtest_cli-1.4.1/tests/unit/__pycache__/test_migrate.cpython-312-pytest-7.4.4.pyc +0 -0
  71. symtest_cli-1.4.1/tests/unit/__pycache__/test_migrate.cpython-312-pytest-9.0.3.pyc +0 -0
  72. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-312-pytest-7.4.4.pyc +0 -0
  73. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-312-pytest-9.0.3.pyc +0 -0
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {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
  81. symtest_cli-1.4.1/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-9.0.3.pyc +0 -0
  82. {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
  83. symtest_cli-1.4.1/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
  84. {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
  85. symtest_cli-1.4.1/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
  86. {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
  87. {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
  88. {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
  89. symtest_cli-1.4.1/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
  90. {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
  91. {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
  92. {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
  93. {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
  94. {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
  95. {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
  96. {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
  97. {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
  98. {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
  99. symtest_cli-1.4.1/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
  100. {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
  101. {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
  102. {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
  103. {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
  104. {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
  105. {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
  106. {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
  107. {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
  108. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_architecture_guard.py +5 -5
  109. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_case_env.py +4 -4
  110. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_config_loader.py +3 -3
  111. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_config_loader_new_features.py +13 -10
  112. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_execution_new_features.py +31 -27
  113. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_model_v2.py +9 -9
  114. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_orchestration_accept.py +11 -3
  115. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_process_worker.py +6 -6
  116. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_sequence_state.py +3 -3
  117. symtest_cli-1.4.1/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-7.4.4.pyc +0 -0
  118. symtest_cli-1.4.1/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-9.0.3.pyc +0 -0
  119. {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
  120. {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
  121. {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
  122. {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
  123. {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
  124. {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
  125. {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
  126. {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
  127. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_error_analysis.py +24 -2
  128. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_numeric_compare.py +5 -3
  129. {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
  130. symtest_cli-1.4.1/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  131. {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
  132. {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
  133. {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
  134. {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
  135. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_migrate.py +199 -4
  136. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
  137. symtest_cli-1.4.1/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
  138. {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
  139. {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
  140. {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
  141. {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
  142. {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
  143. {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
  144. {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
  145. {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
  146. {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
  147. {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
  148. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/conftest.py +3 -3
  149. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_case_controller.py +18 -12
  150. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_tui_run_test.py +3 -3
  151. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_widgets_data.py +2 -2
  152. {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
  153. {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
  154. {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
  155. {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
  156. symtest_cli-1.4.0/docs/design_1_4.md +0 -305
  157. symtest_cli-1.4.0/src/symtest/commands/migrate.py +0 -63
  158. symtest_cli-1.4.0/src/symtest/reporting/__init__.py +0 -10
  159. symtest_cli-1.4.0/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
  160. symtest_cli-1.4.0/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
  161. symtest_cli-1.4.0/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
  162. symtest_cli-1.4.0/tests/integration/parallel/__pycache__/test_depends_on.cpython-312-pytest-9.0.3.pyc +0 -0
  163. symtest_cli-1.4.0/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  164. symtest_cli-1.4.0/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
  165. symtest_cli-1.4.0/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
  166. symtest_cli-1.4.0/tests/unit/__pycache__/test_migrate.cpython-312-pytest-7.4.4.pyc +0 -0
  167. symtest_cli-1.4.0/tests/unit/core/__pycache__/test_case_env.cpython-312-pytest-9.0.3.pyc +0 -0
  168. symtest_cli-1.4.0/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
  169. symtest_cli-1.4.0/tests/unit/core/__pycache__/test_config_loader_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
  170. symtest_cli-1.4.0/tests/unit/core/__pycache__/test_execution_new_features.cpython-312-pytest-9.0.3.pyc +0 -0
  171. symtest_cli-1.4.0/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
  172. symtest_cli-1.4.0/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-7.4.4.pyc +0 -0
  173. symtest_cli-1.4.0/tests/unit/file_comparator/__pycache__/test_error_analysis.cpython-312-pytest-9.0.3.pyc +0 -0
  174. symtest_cli-1.4.0/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  175. symtest_cli-1.4.0/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
  176. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/LICENSE +0 -0
  177. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/MANIFEST.in +0 -0
  178. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/README.md +0 -0
  179. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/docs/tui_and_config_split_design.md +0 -0
  180. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/pyproject.toml +0 -0
  181. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/setup.cfg +0 -0
  182. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/setup.py +0 -0
  183. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/commands/__init__.py +0 -0
  184. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/commands/compare.py +0 -0
  185. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/__init__.py +0 -0
  186. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/config_io.py +0 -0
  187. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/import_expander.py +0 -0
  188. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/inheritance_expander.py +0 -0
  189. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/config/migrate.py +0 -0
  190. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/__init__.py +0 -0
  191. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/__init__.py +0 -0
  192. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/executor.py +0 -0
  193. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/execution/result.py +0 -0
  194. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/history_store.py +0 -0
  195. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/last_run_store.py +0 -0
  196. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/__init__.py +0 -0
  197. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/orchestration/accept.py +0 -0
  198. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/result.py +0 -0
  199. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/sequence_state.py +0 -0
  200. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/setup.py +0 -0
  201. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/types.py +0 -0
  202. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/__init__.py +0 -0
  203. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/assertions.py +0 -0
  204. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/result.py +0 -0
  205. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/core/validation/validator.py +0 -0
  206. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/__init__.py +0 -0
  207. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/base_comparator.py +0 -0
  208. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/binary_comparator.py +0 -0
  209. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/factory.py +0 -0
  210. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/json_comparator.py +0 -0
  211. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/result.py +0 -0
  212. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/script_comparator.py +0 -0
  213. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/text_comparator.py +0 -0
  214. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/file_comparator/xml_comparator.py +0 -0
  215. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/logging_config.py +0 -0
  216. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/__init__.py +0 -0
  217. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/config_runner.py +0 -0
  218. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/json_runner.py +0 -0
  219. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_config_runner.py +0 -0
  220. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_json_runner.py +0 -0
  221. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/parallel_yaml_runner.py +0 -0
  222. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/runners/yaml_runner.py +0 -0
  223. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/__init__.py +0 -0
  224. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/app.py +0 -0
  225. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/controllers/__init__.py +0 -0
  226. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/__init__.py +0 -0
  227. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/case_editor.py +0 -0
  228. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/screens/case_list.py +0 -0
  229. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/__init__.py +0 -0
  230. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/case_table.py +0 -0
  231. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/expected_editor.py +0 -0
  232. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/tui/widgets/search_bar.py +0 -0
  233. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/__init__.py +0 -0
  234. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/junit_xml_writer.py +0 -0
  235. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/path_resolver.py +0 -0
  236. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest/utils/report_generator.py +0 -0
  237. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/dependency_links.txt +0 -0
  238. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/entry_points.txt +0 -0
  239. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/requires.txt +0 -0
  240. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/src/symtest_cli.egg-info/top_level.txt +0 -0
  241. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/README.md +0 -0
  242. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__init__.py +0 -0
  243. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  244. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  245. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/__init__.cpython-39.pyc +0 -0
  246. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  247. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc +0 -0
  248. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-312.pyc +0 -0
  249. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-313.pyc +0 -0
  250. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/run_all.cpython-39.pyc +0 -0
  251. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
  252. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/conftest.py +0 -0
  253. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/h5_filter_demo.py +0 -0
  254. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/manual_report_example.py +0 -0
  255. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/demos/perf_parallel.py +0 -0
  256. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__init__.py +0 -0
  257. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-312.pyc +0 -0
  258. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-313.pyc +0 -0
  259. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/__pycache__/__init__.cpython-39.pyc +0 -0
  260. {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
  261. {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
  262. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/e2e/test_user_flows.py +0 -0
  263. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/circular_config_a.json +0 -0
  264. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/circular_config_b.json +0 -0
  265. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/hourglass_stdout_mismatch.txt +0 -0
  266. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/hourglass_stdout_sample.txt +0 -0
  267. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_base.json +0 -0
  268. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_chain.json +0 -0
  269. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/inheritance_circular.json +0 -0
  270. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/main_config.json +0 -0
  271. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/circular_config_a.json +0 -0
  272. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/circular_config_b.json +0 -0
  273. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_base.json +0 -0
  274. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_chain.json +0 -0
  275. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/inheritance_circular.json +0 -0
  276. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/main_config.json +0 -0
  277. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/missing_fields.json +0 -0
  278. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/setup_main.json +0 -0
  279. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/setup_sub.json +0 -0
  280. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_golden_file.json +0 -0
  281. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_simple.json +0 -0
  282. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_simple.yaml +0 -0
  283. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/skill_test_cases_steps.json +0 -0
  284. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/sub_json_tests.yaml +0 -0
  285. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/sub_text_tests.json +0 -0
  286. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases.json +0 -0
  287. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases.yaml +0 -0
  288. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_cases1.json +0 -0
  289. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_with_setup.json +0 -0
  290. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/migration/v1/test_with_setup.yaml +0 -0
  291. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/missing_fields.json +0 -0
  292. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/mock_fail_script.py +0 -0
  293. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/mock_pass_script.py +0 -0
  294. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/setup_main.json +0 -0
  295. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/setup_sub.json +0 -0
  296. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/sub_json_tests.yaml +0 -0
  297. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/sub_text_tests.json +0 -0
  298. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases.json +0 -0
  299. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases.yaml +0 -0
  300. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_cases1.json +0 -0
  301. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_with_setup.json +0 -0
  302. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/fixtures/test_with_setup.yaml +0 -0
  303. {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
  304. {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
  305. {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
  306. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/__pycache__/test_sequence.cpython-313-pytest-9.0.2.pyc +0 -0
  307. {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
  308. {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
  309. {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
  310. {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
  311. {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
  312. {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
  313. {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
  314. {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
  315. {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
  316. {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
  317. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_binary_compare.py +0 -0
  318. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_csv_compare.py +0 -0
  319. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_h5_compare.py +0 -0
  320. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_json_compare.py +0 -0
  321. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_text_compare.py +0 -0
  322. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/file_compare/test_xml_compare.py +0 -0
  323. {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
  324. {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
  325. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/test_depends_on.py +0 -0
  326. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/parallel/test_parallel_runner.py +0 -0
  327. {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
  328. {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
  329. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/path_handling/test_spaces_in_paths.py +0 -0
  330. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_history_feature.py +0 -0
  331. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_new_cli_features.py +0 -0
  332. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/integration/test_real_command_execution.py +0 -0
  333. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/run_all.py +0 -0
  334. {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
  335. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.2.pyc +0 -0
  336. {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
  337. {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
  338. {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
  339. {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
  340. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/__pycache__/test_result.cpython-313-pytest-9.0.2.pyc +0 -0
  341. {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
  342. {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
  343. {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
  344. {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
  345. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/commands/test_compare_command.py +0 -0
  346. {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
  347. {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
  348. {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
  349. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/__pycache__/test_execution.cpython-312.pyc +0 -0
  350. {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
  351. {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
  352. {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
  353. {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
  354. {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
  355. {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
  356. {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
  357. {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
  358. {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
  359. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_assertions.py +0 -0
  360. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_execution.py +0 -0
  361. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_history_store.py +0 -0
  362. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_last_run_store.py +0 -0
  363. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_setup.py +0 -0
  364. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_validation_error.py +0 -0
  365. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/core/test_xfail.py +0 -0
  366. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/__pycache__/__init__.cpython-312.pyc +0 -0
  367. {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
  368. {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
  369. {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
  370. {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
  371. {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
  372. {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
  373. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_hourglass_tangent_comparator.py +0 -0
  374. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_numeric_parity.py +0 -0
  375. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_script_comparator.py +0 -0
  376. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/file_comparator/test_workspace_plugins.py +0 -0
  377. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312.pyc +0 -0
  378. {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
  379. {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
  380. {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
  381. {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
  382. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_json_yaml_runner.py +0 -0
  383. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_test_case_filter.py +0 -0
  384. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/runners/test_test_case_tag_filter.py +0 -0
  385. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_binary_comparator.py +0 -0
  386. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_cli.py +0 -0
  387. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_config_io.py +0 -0
  388. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_h5_comparator.py +0 -0
  389. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_import_expander.py +0 -0
  390. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_inheritance_expander.py +0 -0
  391. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_result.py +0 -0
  392. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/test_run_all.py +0 -0
  393. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__init__.py +0 -0
  394. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/__init__.cpython-312.pyc +0 -0
  395. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/__init__.cpython-313.pyc +0 -0
  396. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  397. {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
  398. {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
  399. {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
  400. {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
  401. {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
  402. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_app.py +0 -0
  403. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/tui/test_search_functions.py +0 -0
  404. {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
  405. {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
  406. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/test_junit_xml_writer.py +0 -0
  407. {symtest_cli-1.4.0 → symtest_cli-1.4.1}/tests/unit/utils/test_report_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: symtest-cli
3
- Version: 1.4.0
3
+ Version: 1.4.1
4
4
  Summary: Regression testing for CLI applications, with multi-step workflows and numerical file comparison.
5
5
  Home-page: https://github.com/ozil111/symtest-cli
6
6
  Author: Xiaotong Wang
@@ -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. Pre-ratification evolution history lives in
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` | 均方根绝对误差(RMSE) |
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.
@@ -16,7 +16,7 @@ runtime. Library users can enable console logging explicitly::
16
16
  setup_console_logging(level=logging.DEBUG)
17
17
  """
18
18
 
19
- __version__ = "1.4.0"
19
+ __version__ = "1.4.1"
20
20
  __author__ = "Xiaotong Wang"
21
21
  __email__ = "xiaotongwang98@gmail.com"
22
22
 
@@ -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[]) — 二选一. Case-level env applies to all 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, TestCaseStep
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
- Required-field validation is controlled by the explicit *strict* flag
110
- (1.4 原则 6:拆除隐式 TUI 后门). Runners use the default ``strict=True``;
111
- the TUI passes ``strict=False`` explicitly and owns the relaxed form
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[TestCaseStep] = []
142
+ steps: List[TestStep] = []
139
143
  for step in step_configs:
140
- if strict:
141
- step_required = ["command", "args", "expected"]
142
- if not all(field in step for field in step_required):
143
- raise ValueError(
144
- f"Step in test case '{case.get('name', 'unnamed')}' "
145
- f"is missing required fields"
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(TestCaseStep(
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 strict:
179
- missing = [f for f in ("name", "execution", "expected") if f not in case]
180
- missing += [f for f in ("command", "args") if f not in execution]
181
- if missing:
182
- raise ValueError(
183
- f"Test case {case.get('name', 'unnamed')} "
184
- f"is missing required fields"
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,