dynamic-systems-testing-framework 1.0.1__tar.gz → 1.1.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.
Files changed (200) hide show
  1. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/.gitignore +5 -1
  2. dynamic_systems_testing_framework-1.1.0/PKG-INFO +102 -0
  3. dynamic_systems_testing_framework-1.1.0/README.md +56 -0
  4. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/pyproject.toml +14 -4
  5. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/edit_criteria.py +2 -2
  6. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/edit_on_plot.py +3 -3
  7. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/session.py +15 -4
  8. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/app.py +2 -2
  9. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/header.py +5 -0
  10. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/tree_card.py +1 -1
  11. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/situation.py +1 -1
  12. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/unscorable.py +1 -1
  13. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/__init__.py +109 -53
  14. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/base.py +331 -78
  15. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/json_driver.py +30 -9
  16. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/dymola/backend.py +133 -13
  17. dynamic_systems_testing_framework-1.1.0/src/dstf/backends/dymola/interface_loader.py +404 -0
  18. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/dymola/persistent_backend.py +9 -5
  19. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/fmpy/backend.py +90 -16
  20. dynamic_systems_testing_framework-1.1.0/src/dstf/backends/julia/backend.py +275 -0
  21. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/julia/persistent_backend.py +10 -25
  22. dynamic_systems_testing_framework-1.1.0/src/dstf/backends/locate.py +309 -0
  23. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/backend.py +99 -12
  24. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/session_loader.py +7 -1
  25. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/persistent.py +309 -258
  26. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/progress.py +29 -6
  27. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/python/backend.py +40 -16
  28. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/python/run_test.py +8 -1
  29. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/types.py +102 -25
  30. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/__init__.py +16 -0
  31. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/cli/author_cmd.py +12 -21
  32. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/baseline_cmds.py +303 -0
  33. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/cli/common.py +42 -35
  34. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/diag_cmds.py +428 -0
  35. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/main.py +85 -0
  36. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/parser.py +2004 -0
  37. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/cli/run_cmds.py +120 -51
  38. dynamic_systems_testing_framework-1.1.0/src/dstf/cli/spec_cmds.py +244 -0
  39. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/__init__.py +1 -1
  40. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/algorithms.py +1 -1
  41. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/builtins.py +81 -4
  42. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/comparator.py +60 -2
  43. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/handles.py +3 -3
  44. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/metric_tree.py +24 -20
  45. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/mode_schema.py +62 -14
  46. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/modes.py +181 -76
  47. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/registry.py +77 -59
  48. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/sides.py +18 -3
  49. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/strict_keys.py +4 -3
  50. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/summaries.py +2 -2
  51. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/tree_eval.py +37 -5
  52. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/tree_spec.py +17 -10
  53. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/types.py +17 -7
  54. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/validator.py +86 -44
  55. dynamic_systems_testing_framework-1.1.0/src/dstf/config.py +1058 -0
  56. dynamic_systems_testing_framework-1.1.0/src/dstf/config_shape.py +387 -0
  57. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/json_recognizer.py +85 -40
  58. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/library_state.py +7 -1
  59. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/mo_parser.py +52 -13
  60. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/patch_apply.py +1 -1
  61. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/patch_envelope.py +1 -1
  62. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/recognizer.py +61 -15
  63. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/scan_cache.py +3 -2
  64. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/spec_parser.py +34 -55
  65. dynamic_systems_testing_framework-1.1.0/src/dstf/discovery/spec_shape.py +381 -0
  66. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/spec_update.py +37 -9
  67. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/test_registry.py +46 -37
  68. dynamic_systems_testing_framework-1.1.0/src/dstf/json_types.py +107 -0
  69. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/pipeline.py +42 -27
  70. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/plugins.py +16 -9
  71. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/context_builder.py +5 -5
  72. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/dashboard_render.py +165 -36
  73. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/report_suite.py +7 -3
  74. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/schema_export.py +89 -81
  75. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/dashboard.html +5 -7
  76. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/dashboard.js +7 -4
  77. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/interactive.html +4 -4
  78. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/accept.py +14 -8
  79. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/reference_store.py +12 -7
  80. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/roles.py +1 -1
  81. dynamic_systems_testing_framework-1.0.1/PKG-INFO +0 -266
  82. dynamic_systems_testing_framework-1.0.1/README.md +0 -226
  83. dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/interface_loader.py +0 -241
  84. dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/backend.py +0 -156
  85. dynamic_systems_testing_framework-1.0.1/src/dstf/cli/__init__.py +0 -12
  86. dynamic_systems_testing_framework-1.0.1/src/dstf/cli/baseline_cmds.py +0 -341
  87. dynamic_systems_testing_framework-1.0.1/src/dstf/cli/diag_cmds.py +0 -350
  88. dynamic_systems_testing_framework-1.0.1/src/dstf/cli/main.py +0 -561
  89. dynamic_systems_testing_framework-1.0.1/src/dstf/cli/spec_cmds.py +0 -123
  90. dynamic_systems_testing_framework-1.0.1/src/dstf/config.py +0 -815
  91. dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/spec_shape.py +0 -122
  92. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/LICENSE +0 -0
  93. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/__init__.py +0 -0
  94. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/__main__.py +0 -0
  95. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/__init__.py +0 -0
  96. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/__init__.py +0 -0
  97. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/accept.py +0 -0
  98. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/edit_family.py +0 -0
  99. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/edit_tree.py +0 -0
  100. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/builtin/enqueue.py +0 -0
  101. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/capabilities.py +0 -0
  102. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/errors.py +0 -0
  103. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/family_rows.py +0 -0
  104. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/forms.py +0 -0
  105. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/layout.py +0 -0
  106. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/live_rows.py +0 -0
  107. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/live_runs.py +0 -0
  108. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/needle.py +0 -0
  109. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/picker_rows.py +0 -0
  110. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/queue_rows.py +0 -0
  111. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/__init__.py +0 -0
  112. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/actions.py +0 -0
  113. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/drawer_controls.py +0 -0
  114. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/family_fold.py +0 -0
  115. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/figure_hook.py +0 -0
  116. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/grips.js +0 -0
  117. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/live_view.py +0 -0
  118. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/picker.py +0 -0
  119. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/placing.py +0 -0
  120. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/plot_card.py +0 -0
  121. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/queue_panel.py +0 -0
  122. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/rail.py +0 -0
  123. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/shell.py +0 -0
  124. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/stats_card.py +0 -0
  125. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/strip.py +0 -0
  126. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/style.py +0 -0
  127. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/suite.py +0 -0
  128. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/suite_grid.js +0 -0
  129. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/suite_grid.py +0 -0
  130. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/tab_theme.py +0 -0
  131. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/tabstate.py +0 -0
  132. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/test_list.py +0 -0
  133. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/test_page.py +0 -0
  134. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/variables_drawer.py +0 -0
  135. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/shell/widgets.py +0 -0
  136. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/spec_document.py +0 -0
  137. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/spec_preview.py +0 -0
  138. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/suite_rows.py +0 -0
  139. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/tree_move.py +0 -0
  140. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/tree_pointer.py +0 -0
  141. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/tree_text.py +0 -0
  142. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/typed_number.js +0 -0
  143. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/typed_number.py +0 -0
  144. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/authoring/variable_rows.py +0 -0
  145. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/__init__.py +0 -0
  146. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/live_mat.py +0 -0
  147. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/mat_reader.py +0 -0
  148. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/proc_kill.py +0 -0
  149. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/proc_output.py +0 -0
  150. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/result_files.py +0 -0
  151. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/result_variables.py +0 -0
  152. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/common/statistics.py +0 -0
  153. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/cross_backend.py +0 -0
  154. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/dymola/__init__.py +0 -0
  155. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/dymola/log_parser.py +0 -0
  156. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/fmpy/__init__.py +0 -0
  157. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/julia/__init__.py +0 -0
  158. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/julia/run_persistent.jl +0 -0
  159. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/julia/run_test.jl +0 -0
  160. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/__init__.py +0 -0
  161. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/init_xml.py +0 -0
  162. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/log_parser.py +0 -0
  163. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/mos_generator.py +0 -0
  164. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/openmodelica/persistent_backend.py +0 -0
  165. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/pool.py +0 -0
  166. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/python/__init__.py +0 -0
  167. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/backends/run_metadata.py +0 -0
  168. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/cli/review.py +0 -0
  169. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/geometry.py +0 -0
  170. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/seeds.py +0 -0
  171. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/comparison/snap.py +0 -0
  172. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/__init__.py +0 -0
  173. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/plots.py +0 -0
  174. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/discovery/unit_tests.py +0 -0
  175. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/__init__.py +0 -0
  176. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/assemble.py +0 -0
  177. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/decimate.py +0 -0
  178. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/families.py +0 -0
  179. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/palette.py +0 -0
  180. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/plotly.py +0 -0
  181. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/figures/view.py +0 -0
  182. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/__init__.py +0 -0
  183. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/console_report.py +0 -0
  184. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/junit_report.py +0 -0
  185. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/overlay_loader.py +0 -0
  186. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/rows.py +0 -0
  187. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/stats_rows.py +0 -0
  188. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/status.py +0 -0
  189. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/axes.js +0 -0
  190. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/filters.js +0 -0
  191. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/report.js +0 -0
  192. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/templates/vendor/plotly.min.js +0 -0
  193. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/theme.py +0 -0
  194. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/tree_rows.py +0 -0
  195. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/reporting/verdicts.py +0 -0
  196. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/__init__.py +0 -0
  197. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/baseline.py +0 -0
  198. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/batch_manifest.py +0 -0
  199. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/ref_index.py +0 -0
  200. {dynamic_systems_testing_framework-1.0.1 → dynamic_systems_testing_framework-1.1.0}/src/dstf/storage/run_stats.py +0 -0
@@ -6,8 +6,9 @@ dist/
6
6
  build/
7
7
  *.egg
8
8
 
9
- # Virtual environments
9
+ # Virtual environments (.venv-example/: the one docs/guides/first-run.md makes)
10
10
  .venv/
11
+ .venv-example/
11
12
  venv/
12
13
  .env
13
14
 
@@ -46,6 +47,9 @@ testing_output/
46
47
  # Generated reports (dashboard.html, interactive plots, comparison_data.json)
47
48
  reports/
48
49
 
50
+ # The documentation site, built (sphinx-build docs docs/_build/html)
51
+ docs/_build/
52
+
49
53
  # Reference-FMUs binaries (fetched by scripts/fetch_reference_fmus.py)
50
54
  examples/fmu/reference-fmus-binaries/
51
55
 
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.5
2
+ Name: dynamic-systems-testing-framework
3
+ Version: 1.1.0
4
+ Summary: Dynamic Systems Testing Framework — regression & unit testing for time-dependent system behavior, with plug-in backends: Modelica (Dymola, OpenModelica), FMU (FMPy), Julia (ModelingToolkit) and Python to start
5
+ Project-URL: Homepage, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework
6
+ Project-URL: Repository, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework
7
+ Project-URL: Documentation, https://dstf.readthedocs.io/
8
+ Project-URL: Issues, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/issues
9
+ Project-URL: Changelog, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/CHANGELOG.md
10
+ Author: Scott Greenwood
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: dymola,fmi,fmu,julia,modelica,modelingtoolkit,openmodelica,regression-testing,simulation,testing
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: Scientific/Engineering
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: jinja2>=3.1
24
+ Requires-Dist: numpy>=1.20
25
+ Requires-Dist: psutil>=5.9
26
+ Provides-Extra: author
27
+ Requires-Dist: nicegui>=3.16; extra == 'author'
28
+ Requires-Dist: regex>=2026.9.10; extra == 'author'
29
+ Provides-Extra: dev
30
+ Requires-Dist: mypy>=1.10; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.16; extra == 'dev'
35
+ Requires-Dist: scipy>=1.10; extra == 'dev'
36
+ Requires-Dist: types-regex>=2026.9.10; extra == 'dev'
37
+ Provides-Extra: docs
38
+ Requires-Dist: furo>=2024.8.6; extra == 'docs'
39
+ Requires-Dist: myst-parser>=4.0; extra == 'docs'
40
+ Requires-Dist: sphinx>=8.1; extra == 'docs'
41
+ Provides-Extra: fmpy
42
+ Requires-Dist: fmpy>=0.3; extra == 'fmpy'
43
+ Provides-Extra: om
44
+ Requires-Dist: ompython>=3.5; extra == 'om'
45
+ Description-Content-Type: text/markdown
46
+
47
+ # Dynamic Systems Testing Framework (DSTF)
48
+
49
+ [![quality](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml/badge.svg)](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml)
50
+
51
+ DSTF is regression and unit testing for the behaviour of time-dependent
52
+ systems: Modelica libraries, FMUs, Julia models, or anything Python can
53
+ compute. It finds a library's tests, simulates them with a backend — Dymola,
54
+ OpenModelica, FMPy, Julia or Python, or one a plug-in adds — and scores each
55
+ result against a stored baseline by the criteria you write. It reports a
56
+ verdict per test on the command line, as JUnit XML for CI, and as a dashboard
57
+ and a page per test in the browser.
58
+
59
+ DSTF is in beta: the command line and the testing.json keys may still change
60
+ between minor versions; the changelog says when.
61
+
62
+ ## Install
63
+
64
+ ```console
65
+ $ uv tool install dynamic-systems-testing-framework
66
+ ```
67
+
68
+ or `pipx install dynamic-systems-testing-framework`. The command is `dstf`.
69
+ The `author` extra adds `dstf author`, a browser tool for writing criteria
70
+ beside the plots they score; `fmpy` and `om` add what the FMPy and
71
+ OpenModelica backends drive.
72
+
73
+ ## A first run
74
+
75
+ The Python example library needs no simulator. From a clone of this
76
+ repository, in a Python environment with numpy and scipy:
77
+
78
+ ```console
79
+ $ git clone https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework.git
80
+ $ cd DynamicSystemsTestingFramework
81
+ $ python3 -m pip install numpy scipy
82
+ $ dstf --config examples/python/PythonTestingLib/Resources/ReferenceResults/testing.json run
83
+ ```
84
+
85
+ It runs three tests, compares each with its stored baseline, prints a verdict
86
+ per test and opens the dashboard:
87
+
88
+ ![The dashboard: one row per test with its status, worst NRMSE, criteria passed and times](https://raw.githubusercontent.com/ORNL-Modelica/DynamicSystemsTestingFramework/main/docs/img/dashboard.png)
89
+
90
+ ## Documentation
91
+
92
+ <https://dstf.readthedocs.io/> — a first run, setting up `testing.json` for
93
+ your library, adding tests, reading results, baselines, criteria, CI, a page
94
+ per simulator, writing a backend plug-in, and a reference of every command,
95
+ key and comparison mode.
96
+
97
+ ## Changelog and licence
98
+
99
+ What changed in each version is in
100
+ [CHANGELOG.md](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/CHANGELOG.md).
101
+ DSTF is released under the MIT licence:
102
+ [LICENSE](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/LICENSE).
@@ -0,0 +1,56 @@
1
+ # Dynamic Systems Testing Framework (DSTF)
2
+
3
+ [![quality](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml/badge.svg)](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml)
4
+
5
+ DSTF is regression and unit testing for the behaviour of time-dependent
6
+ systems: Modelica libraries, FMUs, Julia models, or anything Python can
7
+ compute. It finds a library's tests, simulates them with a backend — Dymola,
8
+ OpenModelica, FMPy, Julia or Python, or one a plug-in adds — and scores each
9
+ result against a stored baseline by the criteria you write. It reports a
10
+ verdict per test on the command line, as JUnit XML for CI, and as a dashboard
11
+ and a page per test in the browser.
12
+
13
+ DSTF is in beta: the command line and the testing.json keys may still change
14
+ between minor versions; the changelog says when.
15
+
16
+ ## Install
17
+
18
+ ```console
19
+ $ uv tool install dynamic-systems-testing-framework
20
+ ```
21
+
22
+ or `pipx install dynamic-systems-testing-framework`. The command is `dstf`.
23
+ The `author` extra adds `dstf author`, a browser tool for writing criteria
24
+ beside the plots they score; `fmpy` and `om` add what the FMPy and
25
+ OpenModelica backends drive.
26
+
27
+ ## A first run
28
+
29
+ The Python example library needs no simulator. From a clone of this
30
+ repository, in a Python environment with numpy and scipy:
31
+
32
+ ```console
33
+ $ git clone https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework.git
34
+ $ cd DynamicSystemsTestingFramework
35
+ $ python3 -m pip install numpy scipy
36
+ $ dstf --config examples/python/PythonTestingLib/Resources/ReferenceResults/testing.json run
37
+ ```
38
+
39
+ It runs three tests, compares each with its stored baseline, prints a verdict
40
+ per test and opens the dashboard:
41
+
42
+ ![The dashboard: one row per test with its status, worst NRMSE, criteria passed and times](https://raw.githubusercontent.com/ORNL-Modelica/DynamicSystemsTestingFramework/main/docs/img/dashboard.png)
43
+
44
+ ## Documentation
45
+
46
+ <https://dstf.readthedocs.io/> — a first run, setting up `testing.json` for
47
+ your library, adding tests, reading results, baselines, criteria, CI, a page
48
+ per simulator, writing a backend plug-in, and a reference of every command,
49
+ key and comparison mode.
50
+
51
+ ## Changelog and licence
52
+
53
+ What changed in each version is in
54
+ [CHANGELOG.md](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/CHANGELOG.md).
55
+ DSTF is released under the MIT licence:
56
+ [LICENSE](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/LICENSE).
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "dynamic-systems-testing-framework"
3
- version = "1.0.1"
4
- description = "Dynamic Systems Testing Framework — regression & unit testing for time-dependent system behavior across Modelica (Dymola, OpenModelica), FMU (FMPy), Julia (ModelingToolkit), and Python backends"
3
+ version = "1.1.0"
4
+ description = "Dynamic Systems Testing Framework — regression & unit testing for time-dependent system behavior, with plug-in backends: Modelica (Dymola, OpenModelica), FMU (FMPy), Julia (ModelingToolkit) and Python to start"
5
5
  # What the package index shows: the README as the project page (its one image
6
6
  # is an absolute URL for that), the license by its SPDX name with the file
7
7
  # beside it, and where the source and the docs are.
@@ -14,6 +14,7 @@ keywords = [
14
14
  "modelingtoolkit", "simulation", "regression-testing", "testing",
15
15
  ]
16
16
  classifiers = [
17
+ "Development Status :: 4 - Beta",
17
18
  "Intended Audience :: Science/Research",
18
19
  "Intended Audience :: Developers",
19
20
  "Operating System :: OS Independent",
@@ -59,12 +60,18 @@ fmpy = ["fmpy>=0.3"]
59
60
  # subprocess path instead.
60
61
  # Install with: uv sync --extra om --locked --inexact
61
62
  om = ["OMPython>=3.5"]
63
+ # The documentation site (docs/conf.py): Sphinx, Markdown pages through MyST,
64
+ # the Furo theme. Build it with:
65
+ # uv sync --extra docs --locked --inexact
66
+ # uv run sphinx-build -W --keep-going -b html docs docs/_build/html
67
+ docs = ["sphinx>=8.1", "myst-parser>=4.0", "furo>=2024.8.6"]
62
68
 
63
69
  [project.urls]
64
70
  Homepage = "https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework"
65
71
  Repository = "https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework"
66
- Documentation = "https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/docs/usage.md"
72
+ Documentation = "https://dstf.readthedocs.io/"
67
73
  Issues = "https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/issues"
74
+ Changelog = "https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/CHANGELOG.md"
68
75
 
69
76
  [project.scripts]
70
77
  dstf = "dstf.cli:main_entry"
@@ -90,7 +97,7 @@ markers = [
90
97
  "fmpy: requires fmpy package",
91
98
  "ompython: requires OMPython package (persistent-worker OpenModelica)",
92
99
  "reference_fmus: requires Reference-FMUs binaries at examples/fmu/reference-fmus-binaries",
93
- "julia: requires Julia 1.10+ with ModelingToolkit + OrdinaryDiffEq installed in examples/julia/JuliaMtkTestingLib",
100
+ "julia: requires Julia 1.11+ with ModelingToolkit + OrdinaryDiffEq installed in examples/julia/JuliaMtkTestingLib",
94
101
  "playwright: browser-driven reporter tests (requires playwright + chromium); until D97 this marker did not exist and -m 'not playwright' was a no-op filter",
95
102
  ]
96
103
  filterwarnings = [
@@ -151,6 +158,9 @@ files = [
151
158
  "src/dstf/plugins.py",
152
159
  "src/dstf/storage",
153
160
  "src/dstf/config.py",
161
+ "src/dstf/config_shape.py",
162
+ "src/dstf/json_types.py",
163
+ "src/dstf/backends/locate.py",
154
164
  "src/dstf/pipeline.py",
155
165
  "src/dstf/reporting/rows.py",
156
166
  ]
@@ -130,7 +130,7 @@ class EditCriteria:
130
130
  sel = s.selection
131
131
  model_id = next(iter(sel.tests)) if len(sel.tests) == 1 else None
132
132
  if model_id is None or sel.leaf_path is None:
133
- return "select one leaf"
133
+ return "select one criterion"
134
134
  # A compared test whose tree the Session will not materialize: its
135
135
  # leaves are on the plots, but every edit is refused — the Session's
136
136
  # reason, as the editor's why-line, not the view's leaf as if
@@ -154,7 +154,7 @@ class EditCriteria:
154
154
  # The panel is rebuilt after edits made elsewhere: Edit tree wrapping
155
155
  # the selected leaf leaves the selection's path naming a combinator.
156
156
  if leaf is None:
157
- ui.label("this leaf is not in the scored tree")
157
+ ui.label("this criterion is not in the scored tree")
158
158
  return
159
159
  #: A folded row's members as they are read now: the act refuses
160
160
  #: the edit if a path no longer holds that member (`act`).
@@ -151,9 +151,9 @@ def _leaf_at(s: SessionView, model_id: str, path: str) -> dict:
151
151
  except (KeyError, IndexError, TypeError, ValueError):
152
152
  node = None
153
153
  if not isinstance(node, dict):
154
- raise ValueError(f"{path} names no leaf of {model_id}")
154
+ raise ValueError(f"{path} names no criterion of {model_id}")
155
155
  if "combinator" in node:
156
- raise ValueError(f"{path} is a combinator, not a leaf")
156
+ raise ValueError(f"{path} is a group, not a criterion")
157
157
  return node
158
158
 
159
159
 
@@ -463,7 +463,7 @@ def dragged(
463
463
  paths = [p for p, _ in _leaf_paths(s, model_id, variable)]
464
464
  path = _leaf_path_of(handle, paths)
465
465
  if path is None:
466
- raise ValueError(f"{handle} names no leaf on {variable} of {model_id}")
466
+ raise ValueError(f"{handle} names no criterion on {variable} of {model_id}")
467
467
  if view is None:
468
468
  view = s.view(model_id, variable) # full resolution, not the figure
469
469
  leaf = next((lv for lv in view.leaves if lv.path == path), None)
@@ -26,7 +26,7 @@ from ..backends import BackendUnavailableError, dataset_kind_for, select_backend
26
26
  from ..backends.base import Backend
27
27
  from ..backends.common.result_variables import tracked_results
28
28
  from ..backends.pool import ItemState, QueueItem, Refused, StartingWorker
29
- from ..backends.progress import recorded_backend
29
+ from ..backends.progress import recorded_backend, refuse_unregistered
30
30
  from ..backends.types import (
31
31
  DatasetType,
32
32
  TestResult,
@@ -60,6 +60,7 @@ from ..discovery.spec_update import (
60
60
  implicit_tree_op,
61
61
  is_literal_variable,
62
62
  needs_materialization,
63
+ not_stored_notes,
63
64
  unknown_override_modes,
64
65
  validate_entry_keys,
65
66
  validate_entry_metric_tree,
@@ -375,6 +376,7 @@ def resolve_project(
375
376
  assert config.work_dir is not None # resolved in __post_init__
376
377
  recorded, warning = recorded_backend(config.work_dir)
377
378
  if recorded and recorded != config.backend_name:
379
+ refuse_unregistered(recorded, config.work_dir)
378
380
  config = Config(
379
381
  config_file=config_path, backend_name=recorded, work_dir=work_dir
380
382
  )
@@ -848,7 +850,7 @@ class Session:
848
850
  def rescore(self, model_id: str) -> TestComparison:
849
851
  """Score ``model_id`` against the spec with pending ops applied. A
850
852
  flat-override test first gets the same ``add /metrics`` op
851
- ``spec-update`` prepends, so the document the tool edits and the one
853
+ ``spec update`` prepends, so the document the tool edits and the one
852
854
  it saves never differ in shape — but only when that tree scores
853
855
  exactly what the flat path scored (Conventions #7); otherwise the
854
856
  test is scored as loaded and nothing is staged. That op does not
@@ -1086,6 +1088,15 @@ class Session:
1086
1088
  result = replace(result, variables=tracked_results(test, result))
1087
1089
  return tracked_names(scored, result)
1088
1090
 
1091
+ def not_stored_notes(self, model_id: str) -> list[str]:
1092
+ """A sentence per baseline the test's entry, as staged, names that
1093
+ this folder stores none of (:func:`~dstf.discovery.spec_update.
1094
+ not_stored_notes`): `stage` took it — a soft check of another
1095
+ folder — and the pending preview says it is not scored here, as
1096
+ ``spec update`` prints it."""
1097
+ project = self._require_project()
1098
+ return not_stored_notes(self.spec.entry(model_id), model_id, project.store)
1099
+
1089
1100
  def variable_texts(self, model_id: str) -> dict[str, VariableText]:
1090
1101
  """``{name: VariableText}`` for this test — both readings of every
1091
1102
  variable its page can show, from the ONE scope builder the report
@@ -1790,7 +1801,7 @@ class Session:
1790
1801
  return out
1791
1802
 
1792
1803
  def save(self) -> None:
1793
- """Apply exactly the pending ops through ``spec-update``'s
1804
+ """Apply exactly the pending ops through ``spec update``'s
1794
1805
  read-modify-write, then reload. Refuses when the file changed on
1795
1806
  disk since it was loaded, and when the edits leave a criterion that
1796
1807
  can never be scored (`UnscorableSaveError`: `stage` took it, for its
@@ -2265,7 +2276,7 @@ class Session:
2265
2276
  # class it reads results through — and silenced: the tool's
2266
2277
  # process never narrates a run to the console it was
2267
2278
  # started from.
2268
- backend = select_backend(project.config, persistent=True)
2279
+ backend = select_backend(project.config, persistent=True, to_run=True)
2269
2280
  backend.quiet = True
2270
2281
  self._pool = open_pool(
2271
2282
  project.config,
@@ -18,7 +18,7 @@ def run_app(
18
18
  *,
19
19
  filter: str | None = None,
20
20
  package: str | None = None,
21
- port: int | None = None,
21
+ port: int,
22
22
  work_dir: Path | None = None,
23
23
  backend: str | None = None,
24
24
  show: bool = True,
@@ -46,7 +46,7 @@ def run_app(
46
46
  ui.run(
47
47
  shell.index,
48
48
  host="127.0.0.1",
49
- port=port or 8080,
49
+ port=port,
50
50
  show=show,
51
51
  reload=False,
52
52
  title="DSTF",
@@ -758,6 +758,11 @@ class PendingDrawer:
758
758
  ]
759
759
  for text in dict.fromkeys(said):
760
760
  ui.label(text).classes("text-xs dstf-mono").mark("drawer-gesture")
761
+ # A baseline the entry names that this folder does not store: staged
762
+ # all the same — a soft check of another folder — and said here, in
763
+ # the line `spec update` prints for it.
764
+ for text in session.not_stored_notes(model_id):
765
+ ui.label(text).classes("text-xs text-secondary").mark("drawer-not-stored")
761
766
  # Never an empty box: edits that cancel out are not here at all, and
762
767
  # neither is a tree written out and not edited (a `rescore`, a value
763
768
  # typed and cleared on a flat test) — an entry put back as saved has
@@ -307,7 +307,7 @@ def leaf_readout(leaf: dict) -> str:
307
307
  to — which ALREADY carries the score ("NRMSE 1.531e-01 vs tolerance
308
308
  1.000e-02 → FAIL"), so the display beside it said the number twice (the
309
309
  first screenshot) — else the display alone. A sentinel's criterion says
310
- the reason itself ("scalar leaf not scored — …"), so the error is not a
310
+ the reason itself ("scalar criterion not scored — …"), so the error is not a
311
311
  branch of its own. The trailing verdict is not stripped: `tube` and
312
312
  `range` do not end with it ("… → FAIL (requires 0)"), so a strip would
313
313
  read the string wrong for half the modes."""
@@ -9,7 +9,7 @@ anything the last scoring said).
9
9
  There is no state for edits to the simulation settings (the owner's ruling
10
10
  Q2 A drew one): ``/simulation`` is not stageable — `patch_apply`'s default
11
11
  whitelist is ``/comparison``, ``/metrics`` and ``/plots``, and widening it
12
- changes ``spec-update``, a seam another repository relies on. It waits for
12
+ changes ``spec update``, a seam another repository relies on. It waits for
13
13
  a simulation-settings editor."""
14
14
 
15
15
  from __future__ import annotations
@@ -137,5 +137,5 @@ def refuse_bounds(metric: str, params: Mapping[str, object]) -> None:
137
137
  if lo == hi and metric in STRICT_BOUNDS:
138
138
  raise Refused(
139
139
  f"min_value {_num(lo)} equals max_value {_num(hi)}; "
140
- f"a {metric} leaf needs min_value < max_value"
140
+ f"a {metric} criterion needs min_value < max_value"
141
141
  )
@@ -27,7 +27,11 @@ __all__ = [
27
27
  "resolve_variable_patterns",
28
28
  "get_backend",
29
29
  "get_backend_class",
30
+ "backend_class",
31
+ "backend_family",
32
+ "backend_names",
30
33
  "select_backend",
34
+ "source_types",
31
35
  "dataset_kind_for",
32
36
  ]
33
37
 
@@ -117,12 +121,23 @@ def _validate_capabilities(cls: type[Backend], name: str) -> None:
117
121
 
118
122
 
119
123
  def register(name: str):
120
- """Class decorator that registers a Backend subclass.
124
+ """Register a backend class under a name: the class decorator a backend
125
+ plug-in is declared with.
121
126
 
122
- Validates capability honesty at decoration time — see
123
- :func:`_validate_capabilities`. A typo or stale flag becomes an
124
- ``ImportError`` (via ``TypeError``) at module-import time rather
125
- than a confusing ``NotImplementedError`` mid-run.
127
+ The name is what ``testing.json``'s ``backend`` and ``backends`` say, and
128
+ what ``--backend`` takes. An entry named ``"<name> 2025"`` is this
129
+ backend's too.
130
+
131
+ Args:
132
+ name: The backend's name.
133
+
134
+ Returns:
135
+ The decorator, which returns the class unchanged.
136
+
137
+ Raises:
138
+ TypeError: At decoration, when the class declares a
139
+ :class:`~dstf.backends.types.Capability` without overriding the
140
+ method it needs.
126
141
  """
127
142
 
128
143
  def decorator(cls: type[Backend]) -> type[Backend]:
@@ -150,55 +165,89 @@ def get_backend_class(config: Config) -> type[Backend]:
150
165
  unavailable-backend ``RuntimeError`` an optional dependency's absence
151
166
  signals (FmpyBackend.__init__ imports fmpy).
152
167
  """
153
- backend = config.backend
154
- if backend not in _REGISTRY:
155
- _import_builtin_backend(backend)
156
- cls = _REGISTRY.get(backend)
168
+ cls = backend_class(config.backend)
157
169
  if cls is None:
158
170
  available = ", ".join(sorted(_REGISTRY)) or "(none)"
159
171
  raise ValueError(
160
- f"Unsupported backend: {backend} "
172
+ f"Unsupported backend: {config.backend} "
161
173
  f"(from '{config.backend_name}'). Available: {available}"
162
174
  )
163
175
  return cls
164
176
 
165
177
 
178
+ def backend_class(name: str) -> type[Backend] | None:
179
+ """The backend registered under ``name`` — a built-in is imported on
180
+ first use — or None."""
181
+ if name not in _REGISTRY:
182
+ _import_builtin_backend(name)
183
+ return _REGISTRY.get(name)
184
+
185
+
186
+ def backend_names() -> list[str]:
187
+ """Every backend there is a class for: the built-ins, then what a
188
+ plug-in or a caller registered, in registration order. The order
189
+ ``dstf check`` lists them in, and the one a project that names no
190
+ backend is searched in (``Backend.source_types``)."""
191
+ return [*_BUILTINS, *(name for name in _REGISTRY if name not in _BUILTINS)]
192
+
193
+
194
+ def source_types() -> list[str]:
195
+ """Every ``source_type`` a project may say: the ones a registered
196
+ backend runs (``Backend.source_types``), in the backends' order — the
197
+ built-ins' ``modelica``, ``fmu``, ``julia``, ``python``, then a
198
+ plug-in's. ``Config`` refuses any other, and the published schema
199
+ lists these."""
200
+ found: dict[str, None] = {}
201
+ for name in backend_names():
202
+ cls = backend_class(name)
203
+ if cls is not None:
204
+ found.update(dict.fromkeys(sorted(cls.source_types)))
205
+ return list(found)
206
+
207
+
208
+ def backend_family(backend_name: str) -> str:
209
+ """The backend a named entry is of: ``"Dymola 2025"`` is a ``"Dymola"``,
210
+ ``"OpenModelica 1.23"`` an ``"OpenModelica"`` — the registered name it
211
+ starts with, the longest where two do. A name that starts with none is
212
+ its own (and no backend, as :func:`get_backend_class` then says)."""
213
+ for name in sorted(backend_names(), key=len, reverse=True):
214
+ if backend_name.startswith(name):
215
+ return name
216
+ return backend_name
217
+
218
+
166
219
  class BackendUnavailableError(RuntimeError):
167
- """The configured backend cannot be built on this machine.
168
-
169
- Not a bug: an unregistered backend name, a missing executable, or a
170
- persistent variant whose dependency probe failed. Callers decide what
171
- to do about it — the CLI prints the message and exits 1
172
- (:func:`cli.run_cmds._get_backend`), the authoring tool shows it on the
173
- job that asked for the run, and ``Session.open`` turns it into a
174
- ``ConfigError`` so ``dstf author`` refuses the project with one line.
220
+ """The project's backend cannot be built on this machine: no backend of
221
+ that name is registered, its program was not found, or what it needs is not
222
+ installed. ``str()`` says which, in words true wherever it is shown — the
223
+ command line and `dstf author`'s Queue alike.
224
+
225
+ ``batch_runs``: only the variant whose workers stay alive is refused;
226
+ the backend's per-test script mode would run. A caller that offers that
227
+ mode says so in its own words (the CLI's ``--batch``).
175
228
  """
176
229
 
230
+ def __init__(self, message: str, *, batch_runs: bool = False):
231
+ super().__init__(message)
232
+ self.batch_runs = batch_runs
177
233
 
178
- def select_backend(config, *, persistent: bool) -> Backend:
179
- """Instantiate the backend for *config*, persistent variant when asked.
180
234
 
181
- When *persistent* is True and the backend **declares**
182
- :attr:`Capability.PERSISTENT_WORKERS`, pre-flight the variant's
183
- dependency probe and instantiate it. When the probe raises
184
- :class:`RuntimeError`, raise :class:`BackendUnavailableError` naming
185
- both fixes (install the dep, or re-run with ``--batch``) — no silent
186
- fallback.
235
+ def select_backend(config, *, persistent: bool, to_run: bool = False) -> Backend:
236
+ """Build the backend a project runs with.
187
237
 
188
- The declaration is what gates, not the presence of the hook: a backend
189
- that ships an experimental ``persistent_backend_cls`` it isn't ready to
190
- have driven says so by leaving the flag off, and lands on the batch
191
- path. Registration-time validation (:func:`_validate_capabilities`)
192
- guarantees the converse — flag implies a real override — so a declaring
193
- backend always has a class to return.
238
+ Args:
239
+ config: The project's configuration.
240
+ persistent: Build the variant that keeps the simulator open between
241
+ tests, where the backend has one; ``--batch`` is False.
242
+ to_run: The backend will simulate, not only read results: one that was
243
+ not found on this machine is refused.
194
244
 
195
- Backend-agnostic: no string matching on backend names. New persistent
196
- backends plug in by declaring the capability and overriding
197
- ``persistent_backend_cls`` + ``preflight`` on their backend classes.
245
+ Returns:
246
+ The backend.
198
247
 
199
- This function itself reads ``config.backend`` only to phrase the
200
- persistent-failure message; :func:`get_backend_class`, which it calls
201
- first, requires the attribute.
248
+ Raises:
249
+ BackendUnavailableError: The backend is not registered, not found, or
250
+ cannot be used here; the message says why and what to do.
202
251
  """
203
252
  try:
204
253
  batch_cls = get_backend_class(config)
@@ -206,10 +255,17 @@ def select_backend(config, *, persistent: bool) -> Backend:
206
255
  # An unregistered backend name is a configuration problem, not a
207
256
  # bug; same class of failure as a missing executable.
208
257
  raise BackendUnavailableError(str(exc)) from exc
258
+ if to_run:
259
+ try:
260
+ batch_cls.require_found(config)
261
+ except RuntimeError as exc:
262
+ raise BackendUnavailableError(str(exc)) from exc
209
263
  if not persistent or Capability.PERSISTENT_WORKERS not in batch_cls.capabilities:
210
264
  # Batch-only backend (FMPy, Python today). Persistent flag is a
211
265
  # no-op rather than an error so a single CLI works across mixed
212
- # backends in a multi-library config.
266
+ # backends in a multi-library config. A `--batch` WRITTEN for such
267
+ # a backend is another matter, and the CLI's to refuse
268
+ # (`cli.run_cmds._refuse_batch_without_workers`).
213
269
  return _instantiate(batch_cls, config)
214
270
  pcls = batch_cls.persistent_backend_cls()
215
271
  if pcls is None:
@@ -220,12 +276,8 @@ def select_backend(config, *, persistent: bool) -> Backend:
220
276
  pcls.preflight(config)
221
277
  except RuntimeError as exc:
222
278
  raise BackendUnavailableError(
223
- f"Persistent-worker {config.backend} unavailable.\n"
224
- f"\n"
225
- f"{exc}\n"
226
- f"\n"
227
- f"Or re-run with `--batch` to use the per-test subprocess "
228
- f"backend instead."
279
+ f"Persistent-worker {config.backend} unavailable.\n\n{exc}",
280
+ batch_runs=True,
229
281
  ) from exc
230
282
  return _instantiate(pcls, config)
231
283
 
@@ -245,16 +297,20 @@ def _instantiate(cls: type[Backend], config) -> Backend:
245
297
  raise BackendUnavailableError(str(exc)) from exc
246
298
 
247
299
 
300
+ #: The built-in backends and the module each registers itself from. Their
301
+ #: order is the order :func:`backend_names` lists them in.
302
+ _BUILTINS = {
303
+ "Dymola": ".dymola",
304
+ "OpenModelica": ".openmodelica",
305
+ "FMPy": ".fmpy",
306
+ "Julia": ".julia",
307
+ "Python": ".python",
308
+ }
309
+
310
+
248
311
  def _import_builtin_backend(name: str) -> None:
249
312
  """Import a built-in backend module so it registers itself."""
250
- builtins = {
251
- "Dymola": ".dymola",
252
- "FMPy": ".fmpy",
253
- "OpenModelica": ".openmodelica",
254
- "Julia": ".julia",
255
- "Python": ".python",
256
- }
257
- module = builtins.get(name)
313
+ module = _BUILTINS.get(name)
258
314
  if module:
259
315
  import importlib
260
316