dynamic-systems-testing-framework 1.0.1__py3-none-any.whl

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 (185) hide show
  1. dstf/__init__.py +41 -0
  2. dstf/__main__.py +10 -0
  3. dstf/authoring/__init__.py +13 -0
  4. dstf/authoring/builtin/__init__.py +32 -0
  5. dstf/authoring/builtin/accept.py +205 -0
  6. dstf/authoring/builtin/edit_criteria.py +479 -0
  7. dstf/authoring/builtin/edit_family.py +228 -0
  8. dstf/authoring/builtin/edit_on_plot.py +1354 -0
  9. dstf/authoring/builtin/edit_tree.py +1399 -0
  10. dstf/authoring/builtin/enqueue.py +144 -0
  11. dstf/authoring/capabilities.py +463 -0
  12. dstf/authoring/errors.py +49 -0
  13. dstf/authoring/family_rows.py +526 -0
  14. dstf/authoring/forms.py +631 -0
  15. dstf/authoring/layout.py +104 -0
  16. dstf/authoring/live_rows.py +337 -0
  17. dstf/authoring/live_runs.py +385 -0
  18. dstf/authoring/needle.py +127 -0
  19. dstf/authoring/picker_rows.py +92 -0
  20. dstf/authoring/queue_rows.py +354 -0
  21. dstf/authoring/session.py +3016 -0
  22. dstf/authoring/shell/__init__.py +9 -0
  23. dstf/authoring/shell/actions.py +702 -0
  24. dstf/authoring/shell/app.py +53 -0
  25. dstf/authoring/shell/drawer_controls.py +190 -0
  26. dstf/authoring/shell/family_fold.py +468 -0
  27. dstf/authoring/shell/figure_hook.py +249 -0
  28. dstf/authoring/shell/grips.js +1000 -0
  29. dstf/authoring/shell/header.py +783 -0
  30. dstf/authoring/shell/live_view.py +93 -0
  31. dstf/authoring/shell/picker.py +267 -0
  32. dstf/authoring/shell/placing.py +122 -0
  33. dstf/authoring/shell/plot_card.py +1171 -0
  34. dstf/authoring/shell/queue_panel.py +433 -0
  35. dstf/authoring/shell/rail.py +593 -0
  36. dstf/authoring/shell/shell.py +528 -0
  37. dstf/authoring/shell/stats_card.py +501 -0
  38. dstf/authoring/shell/strip.py +204 -0
  39. dstf/authoring/shell/style.py +721 -0
  40. dstf/authoring/shell/suite.py +277 -0
  41. dstf/authoring/shell/suite_grid.js +414 -0
  42. dstf/authoring/shell/suite_grid.py +373 -0
  43. dstf/authoring/shell/tab_theme.py +110 -0
  44. dstf/authoring/shell/tabstate.py +146 -0
  45. dstf/authoring/shell/test_list.py +179 -0
  46. dstf/authoring/shell/test_page.py +1404 -0
  47. dstf/authoring/shell/tree_card.py +1171 -0
  48. dstf/authoring/shell/variables_drawer.py +589 -0
  49. dstf/authoring/shell/widgets.py +188 -0
  50. dstf/authoring/situation.py +287 -0
  51. dstf/authoring/spec_document.py +215 -0
  52. dstf/authoring/spec_preview.py +83 -0
  53. dstf/authoring/suite_rows.py +99 -0
  54. dstf/authoring/tree_move.py +516 -0
  55. dstf/authoring/tree_pointer.py +33 -0
  56. dstf/authoring/tree_text.py +61 -0
  57. dstf/authoring/typed_number.js +64 -0
  58. dstf/authoring/typed_number.py +76 -0
  59. dstf/authoring/unscorable.py +141 -0
  60. dstf/authoring/variable_rows.py +216 -0
  61. dstf/backends/__init__.py +283 -0
  62. dstf/backends/base.py +1117 -0
  63. dstf/backends/common/__init__.py +1 -0
  64. dstf/backends/common/json_driver.py +484 -0
  65. dstf/backends/common/live_mat.py +369 -0
  66. dstf/backends/common/mat_reader.py +632 -0
  67. dstf/backends/common/proc_kill.py +238 -0
  68. dstf/backends/common/proc_output.py +20 -0
  69. dstf/backends/common/result_files.py +109 -0
  70. dstf/backends/common/result_variables.py +329 -0
  71. dstf/backends/common/statistics.py +39 -0
  72. dstf/backends/cross_backend.py +193 -0
  73. dstf/backends/dymola/__init__.py +5 -0
  74. dstf/backends/dymola/backend.py +776 -0
  75. dstf/backends/dymola/interface_loader.py +241 -0
  76. dstf/backends/dymola/log_parser.py +396 -0
  77. dstf/backends/dymola/persistent_backend.py +949 -0
  78. dstf/backends/fmpy/__init__.py +11 -0
  79. dstf/backends/fmpy/backend.py +443 -0
  80. dstf/backends/julia/__init__.py +27 -0
  81. dstf/backends/julia/backend.py +156 -0
  82. dstf/backends/julia/persistent_backend.py +541 -0
  83. dstf/backends/julia/run_persistent.jl +167 -0
  84. dstf/backends/julia/run_test.jl +146 -0
  85. dstf/backends/openmodelica/__init__.py +21 -0
  86. dstf/backends/openmodelica/backend.py +368 -0
  87. dstf/backends/openmodelica/init_xml.py +76 -0
  88. dstf/backends/openmodelica/log_parser.py +119 -0
  89. dstf/backends/openmodelica/mos_generator.py +228 -0
  90. dstf/backends/openmodelica/persistent_backend.py +1045 -0
  91. dstf/backends/openmodelica/session_loader.py +63 -0
  92. dstf/backends/persistent.py +1811 -0
  93. dstf/backends/pool.py +602 -0
  94. dstf/backends/progress.py +395 -0
  95. dstf/backends/python/__init__.py +11 -0
  96. dstf/backends/python/backend.py +141 -0
  97. dstf/backends/python/run_test.py +141 -0
  98. dstf/backends/run_metadata.py +103 -0
  99. dstf/backends/types.py +310 -0
  100. dstf/cli/__init__.py +12 -0
  101. dstf/cli/author_cmd.py +61 -0
  102. dstf/cli/baseline_cmds.py +341 -0
  103. dstf/cli/common.py +231 -0
  104. dstf/cli/diag_cmds.py +350 -0
  105. dstf/cli/main.py +561 -0
  106. dstf/cli/review.py +398 -0
  107. dstf/cli/run_cmds.py +589 -0
  108. dstf/cli/spec_cmds.py +123 -0
  109. dstf/comparison/__init__.py +13 -0
  110. dstf/comparison/algorithms.py +2127 -0
  111. dstf/comparison/builtins.py +291 -0
  112. dstf/comparison/comparator.py +1189 -0
  113. dstf/comparison/geometry.py +164 -0
  114. dstf/comparison/handles.py +1194 -0
  115. dstf/comparison/metric_tree.py +332 -0
  116. dstf/comparison/mode_schema.py +176 -0
  117. dstf/comparison/modes.py +788 -0
  118. dstf/comparison/registry.py +230 -0
  119. dstf/comparison/seeds.py +281 -0
  120. dstf/comparison/sides.py +158 -0
  121. dstf/comparison/snap.py +62 -0
  122. dstf/comparison/strict_keys.py +246 -0
  123. dstf/comparison/summaries.py +354 -0
  124. dstf/comparison/tree_eval.py +868 -0
  125. dstf/comparison/tree_spec.py +678 -0
  126. dstf/comparison/types.py +194 -0
  127. dstf/comparison/validator.py +232 -0
  128. dstf/config.py +815 -0
  129. dstf/discovery/__init__.py +0 -0
  130. dstf/discovery/json_recognizer.py +702 -0
  131. dstf/discovery/library_state.py +248 -0
  132. dstf/discovery/mo_parser.py +794 -0
  133. dstf/discovery/patch_apply.py +308 -0
  134. dstf/discovery/patch_envelope.py +63 -0
  135. dstf/discovery/plots.py +318 -0
  136. dstf/discovery/recognizer.py +103 -0
  137. dstf/discovery/scan_cache.py +154 -0
  138. dstf/discovery/spec_parser.py +538 -0
  139. dstf/discovery/spec_shape.py +122 -0
  140. dstf/discovery/spec_update.py +493 -0
  141. dstf/discovery/test_registry.py +488 -0
  142. dstf/discovery/unit_tests.py +95 -0
  143. dstf/figures/__init__.py +14 -0
  144. dstf/figures/assemble.py +48 -0
  145. dstf/figures/decimate.py +150 -0
  146. dstf/figures/families.py +289 -0
  147. dstf/figures/palette.py +170 -0
  148. dstf/figures/plotly.py +1413 -0
  149. dstf/figures/view.py +233 -0
  150. dstf/pipeline.py +435 -0
  151. dstf/plugins.py +104 -0
  152. dstf/reporting/__init__.py +0 -0
  153. dstf/reporting/console_report.py +187 -0
  154. dstf/reporting/context_builder.py +979 -0
  155. dstf/reporting/dashboard_render.py +534 -0
  156. dstf/reporting/junit_report.py +192 -0
  157. dstf/reporting/overlay_loader.py +708 -0
  158. dstf/reporting/report_suite.py +1059 -0
  159. dstf/reporting/rows.py +458 -0
  160. dstf/reporting/schema_export.py +413 -0
  161. dstf/reporting/stats_rows.py +321 -0
  162. dstf/reporting/status.py +388 -0
  163. dstf/reporting/templates/axes.js +61 -0
  164. dstf/reporting/templates/dashboard.html +284 -0
  165. dstf/reporting/templates/dashboard.js +604 -0
  166. dstf/reporting/templates/filters.js +100 -0
  167. dstf/reporting/templates/interactive.html +420 -0
  168. dstf/reporting/templates/report.js +116 -0
  169. dstf/reporting/templates/vendor/plotly.min.js +8 -0
  170. dstf/reporting/theme.py +324 -0
  171. dstf/reporting/tree_rows.py +73 -0
  172. dstf/reporting/verdicts.py +124 -0
  173. dstf/storage/__init__.py +0 -0
  174. dstf/storage/accept.py +216 -0
  175. dstf/storage/baseline.py +43 -0
  176. dstf/storage/batch_manifest.py +204 -0
  177. dstf/storage/ref_index.py +295 -0
  178. dstf/storage/reference_store.py +661 -0
  179. dstf/storage/roles.py +576 -0
  180. dstf/storage/run_stats.py +85 -0
  181. dynamic_systems_testing_framework-1.0.1.dist-info/METADATA +266 -0
  182. dynamic_systems_testing_framework-1.0.1.dist-info/RECORD +185 -0
  183. dynamic_systems_testing_framework-1.0.1.dist-info/WHEEL +4 -0
  184. dynamic_systems_testing_framework-1.0.1.dist-info/entry_points.txt +2 -0
  185. dynamic_systems_testing_framework-1.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,204 @@
1
+ """The Test page's status strip (the owner's ruling Q2 A): a full-width
2
+ row directly under the test's row, inside the header that stays at the
3
+ top. It says the test's situation (`authoring.situation`) in a tone, and
4
+ fills the one next action: the shell's own Preview and save / Discard for
5
+ pending edits (quiet when the edits name a variable only a new run
6
+ records), its own Remove or Cancel for a test the simulation queue holds
7
+ (D150), and the TEST-placement capabilities, each drawn as its
8
+ declared `emphasis` for the situation says — filled, quiet, or in the ⋮.
9
+ The strip knows no capability by name."""
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Callable
14
+ from typing import TYPE_CHECKING
15
+
16
+ from nicegui import ui
17
+
18
+ from ...backends.pool import ItemState
19
+ from ..capabilities import Placement
20
+ from ..errors import user_text
21
+ from ..session import Selection
22
+ from ..situation import Situation, Strip
23
+ from .actions import ActionBar, log_unless_users
24
+ from .widgets import Action
25
+
26
+ if TYPE_CHECKING:
27
+ from ...backends.pool import QueueItem
28
+ from .test_page import TestPage
29
+
30
+ TONES = ("grey", "red", "amber")
31
+ DISCARD_TIP = "Discards this test's edits; the header's Discard drops every test's."
32
+
33
+
34
+ class StatusStrip:
35
+ def __init__(self, page: TestPage) -> None:
36
+ self.page = page
37
+ #: What the strip says now; the bar's emphasis reads it. None while
38
+ #: the page has no test (`forget`).
39
+ self.situation: Situation | None = None
40
+ with (
41
+ ui.row()
42
+ .classes("dstf-strip dstf-strip-grey items-center gap-3 w-full")
43
+ .mark("strip") as self.row
44
+ ):
45
+ self.text = ui.label().classes("dstf-strip-text").mark("strip-text")
46
+ # The sentence in full where it is one line of more (a failed
47
+ # run's message): on hover, and for a screen reader.
48
+ with self.text:
49
+ self.tip = ui.tooltip("").mark("strip-text:tip")
50
+ self.tip.set_visibility(False)
51
+ with ui.row().classes("dstf-strip-actions items-center gap-2"):
52
+ self.preview = (
53
+ ui.button("Preview and save", on_click=self._preview)
54
+ .props("unelevated dense no-caps color=primary")
55
+ .mark("strip-preview")
56
+ )
57
+ self.discard = Action(
58
+ self._discard,
59
+ props="flat dense no-caps",
60
+ marker="strip-discard",
61
+ why=False,
62
+ )
63
+ # The board's label, and the header's: the tooltip says
64
+ # which edits this one drops.
65
+ with self.discard.button:
66
+ ui.tooltip(DISCARD_TIP)
67
+ # The queue's two: a queued test's next action is Remove, a
68
+ # running one's Cancel — on a backend that cannot stop a
69
+ # test, the sentence saying so in its place.
70
+ self.remove = (
71
+ ui.button("Remove", on_click=self._remove)
72
+ .props("unelevated dense no-caps color=primary")
73
+ .mark("strip-remove")
74
+ )
75
+ self.cancel = (
76
+ ui.button("Cancel", on_click=self._cancel)
77
+ .props("unelevated dense no-caps color=negative")
78
+ .mark("strip-cancel")
79
+ )
80
+ self.cancel_why = (
81
+ ui.label().classes("dstf-why").mark("strip-cancel:why")
82
+ )
83
+ self.actions = ActionBar(
84
+ page.shell,
85
+ Placement.TEST,
86
+ lambda: Selection(tests=frozenset({page.model_id})),
87
+ marker="test-actions",
88
+ apply=page.apply,
89
+ # The strip is drawn INSIDE the header, before the page
90
+ # holds it: asked for when a refusal arrives.
91
+ stale=lambda e: page.header.stale(e),
92
+ emphasis=lambda cap: getattr(cap, "emphasis", {}).get(
93
+ self.situation
94
+ ),
95
+ )
96
+ #: One line under the strip (`Strip.note`): why a running test's
97
+ #: page shows no growing trace (D151). Hidden while there is none.
98
+ self.note = ui.label().classes("dstf-strip-note w-full").mark("strip-note")
99
+ self.note.set_visibility(False)
100
+ self._show_edit_buttons(False)
101
+ self._show_queue_buttons(None)
102
+
103
+ def _show_edit_buttons(self, editing: bool, *, filled: bool = True) -> None:
104
+ """Preview and save and Discard, shown while edits are pending;
105
+ Preview and save is the filled next action unless the edits name a
106
+ variable only a new run records — Queue is then the one filled (the
107
+ owner's O1), and Preview and save is quiet, still one click."""
108
+ self.preview.set_visibility(editing)
109
+ self.discard.container.set_visibility(editing)
110
+ if filled:
111
+ self.preview.props(remove="flat", add="unelevated color=primary")
112
+ else:
113
+ self.preview.props(remove="unelevated color=primary", add="flat")
114
+
115
+ def _tone(self, tone: str) -> None:
116
+ self.row.classes(
117
+ remove=" ".join(f"dstf-strip-{t}" for t in TONES),
118
+ add=f"dstf-strip-{tone}",
119
+ )
120
+
121
+ def paint(self, strip: Strip) -> None:
122
+ self._tone(strip.tone)
123
+ self.text.set_text(strip.sentence)
124
+ self.note.set_text(strip.note)
125
+ self.note.set_visibility(bool(strip.note))
126
+ self.tip.set_text(strip.tip)
127
+ self.tip.set_visibility(bool(strip.tip))
128
+ # Before the bar's refresh: its emphasis reads the situation.
129
+ self.situation = strip.situation
130
+ self._show_edit_buttons(
131
+ strip.situation in (Situation.EDITS, Situation.EDITS_NEW),
132
+ filled=strip.situation is not Situation.EDITS_NEW,
133
+ )
134
+ self.discard.set("Discard", self.page.shell.busy_reason())
135
+ self._show_queue_buttons(strip.situation)
136
+ self.actions.container.set_visibility(True)
137
+ self.actions.refresh()
138
+
139
+ def say(self, strip: Strip) -> None:
140
+ """The sentence alone: a running test's seconds — or how far its
141
+ result file has got — move once a second, and nothing else of the
142
+ strip does."""
143
+ self.text.set_text(strip.sentence)
144
+
145
+ def _show_queue_buttons(self, situation: Situation | None) -> None:
146
+ session = self.page.shell.session
147
+ why = session.cannot_cancel
148
+ running = situation is Situation.RUNNING
149
+ # A stop already asked of it: no second Cancel while it ends.
150
+ item = session.queue_item(self.page.model_id)
151
+ stopping = item is not None and item.stopping
152
+ # Queued, and the queue holds its item: a test whose click is
153
+ # still being read (`Session.checking`) has nothing to remove yet.
154
+ self.remove.set_visibility(
155
+ situation is Situation.QUEUED
156
+ and item is not None
157
+ and session.item_state(item) is ItemState.WAITING
158
+ )
159
+ self.cancel.set_visibility(running and why is None and not stopping)
160
+ self.cancel_why.set_text(why or "")
161
+ self.cancel_why.set_visibility(running and why is not None)
162
+
163
+ def forget(self) -> None:
164
+ """No test: no sentence, no buttons (the page's `_gone`)."""
165
+ self.situation = None
166
+ self.text.set_text("")
167
+ self.note.set_visibility(False)
168
+ self.tip.set_visibility(False)
169
+ self._tone("grey")
170
+ self._show_edit_buttons(False)
171
+ self._show_queue_buttons(None)
172
+ self.actions.clear()
173
+ self.actions.container.set_visibility(False)
174
+
175
+ def _preview(self) -> None:
176
+ self.page.header.drawer.show(self.page.model_id)
177
+
178
+ def _discard(self) -> None:
179
+ # THIS test's edits; the header's Discard is every test's.
180
+ try:
181
+ self.page.shell.session.discard(models={self.page.model_id})
182
+ except Exception as e:
183
+ log_unless_users(e, "discard")
184
+ ui.notify(user_text(e), type="warning")
185
+
186
+ def _remove(self) -> None:
187
+ self._queue_act(self.page.shell.session.remove, "has already started")
188
+
189
+ def _cancel(self) -> None:
190
+ self._queue_act(self.page.shell.session.cancel, "is no longer running")
191
+
192
+ def _queue_act(self, act: Callable[[QueueItem], bool], missed: str) -> None:
193
+ """Remove or Cancel this test's item; a refusal — the pool's, or the
194
+ item having moved on since the strip was painted — is said."""
195
+ session, model_id = self.page.shell.session, self.page.model_id
196
+ item = session.queue_item(model_id)
197
+ try:
198
+ done = item is not None and act(item)
199
+ except Exception as e:
200
+ log_unless_users(e, "queue")
201
+ ui.notify(user_text(e), type="warning")
202
+ return
203
+ if not done:
204
+ ui.notify(f"{model_id} {missed}", type="warning")