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,53 @@
1
+ """`dstf author` (§4.7 "Launch"; brief §3.8): the project checked for a
2
+ config or spec error, the shell mounted, the project opened on a thread
3
+ once the server runs, served on 127.0.0.1 until Stop server. The page
4
+ appears before a result is read."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ from nicegui import app, ui
11
+
12
+ from ..session import check_project
13
+ from .shell import Shell
14
+
15
+
16
+ def run_app(
17
+ config_path: Path,
18
+ *,
19
+ filter: str | None = None,
20
+ package: str | None = None,
21
+ port: int | None = None,
22
+ work_dir: Path | None = None,
23
+ backend: str | None = None,
24
+ show: bool = True,
25
+ ) -> None:
26
+ # Before anything is served: a config or spec error is one `Config
27
+ # error: …` line and exit 2, as from every other command (`cli.main`
28
+ # prints it). The open itself still runs on a thread, below — it scans
29
+ # the library and reads the results, which this does not.
30
+ check_project(config_path, backend=backend, work_dir=work_dir)
31
+ shell = Shell(
32
+ config_path,
33
+ work_dir=work_dir,
34
+ filter=filter,
35
+ package=package,
36
+ show_browser=show,
37
+ )
38
+ shell.mount()
39
+
40
+ def start() -> None:
41
+ shell.start()
42
+ shell.open_async(backend)
43
+
44
+ app.on_startup(start)
45
+ app.on_shutdown(shell.shutdown)
46
+ ui.run(
47
+ shell.index,
48
+ host="127.0.0.1",
49
+ port=port or 8080,
50
+ show=show,
51
+ reload=False,
52
+ title="DSTF",
53
+ )
@@ -0,0 +1,190 @@
1
+ """What both drawers of the Test page draw (the owner's ruling Q7: one
2
+ control set on every list): the filter box with its `.*` and `Aa` toggles
3
+ beside it and the sentence under it, and an icon button opening a menu of
4
+ choices (`role=menuitemradio`, the current one checked). The drawers own
5
+ their state (`TabState`); these draw it and report a change."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable, Sequence
10
+ from dataclasses import dataclass
11
+ from functools import partial
12
+ from typing import Any
13
+
14
+ from nicegui import ui
15
+
16
+ #: A menu's keyboard (Quasar's QMenu has none): ArrowDown / ArrowUp move
17
+ #: the focus to the next / previous choice, wrapping; Home / End to the
18
+ #: first / last. From outside the choices ArrowDown goes to the first and
19
+ #: ArrowUp to the last. Tab closes it; the focus returns to its button (the
20
+ #: menu pattern: its choices are out of the tab order, `tabindex="-1"`).
21
+ #: The choices are the menu's radio items, or its plain items that are not
22
+ #: disabled (the tree card's Move to…: a refused place is no choice).
23
+ #: ``__MENU_ID__`` is the menu's element id (`menu_keys`).
24
+ _MENU_KEYS = """(e) => {
25
+ if (e.key === 'Tab') { getElement(__MENU_ID__)?.hide(); return; }
26
+ const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
27
+ if (!keys.includes(e.key)) return;
28
+ const items = [...e.currentTarget.querySelectorAll(
29
+ '[role="menuitemradio"], [role="menuitem"]:not([aria-disabled="true"])')];
30
+ if (!items.length) return;
31
+ e.preventDefault();
32
+ const at = items.indexOf(document.activeElement);
33
+ const last = items.length - 1;
34
+ let to;
35
+ if (e.key === 'Home') to = 0;
36
+ else if (e.key === 'End') to = last;
37
+ else if (at < 0) to = e.key === 'ArrowDown' ? 0 : last;
38
+ else if (e.key === 'ArrowDown') to = at === last ? 0 : at + 1;
39
+ else to = at === 0 ? last : at - 1;
40
+ items[to].focus();
41
+ }"""
42
+
43
+
44
+ #: A view control's tooltip, above it: below, it covers what opens there.
45
+ TIP_ABOVE = 'anchor="top middle" self="bottom middle"'
46
+
47
+
48
+ def menu_keys(menu: ui.menu) -> str:
49
+ """`_MENU_KEYS` for ``menu``."""
50
+ return _MENU_KEYS.replace("__MENU_ID__", str(menu.id))
51
+
52
+
53
+ def _pressed(button: ui.button, on: bool) -> None:
54
+ button.props(f'aria-pressed="{str(on).lower()}"')
55
+
56
+
57
+ @dataclass
58
+ class FilterBox:
59
+ needle: ui.input
60
+ regex: ui.button
61
+ case: ui.button
62
+ why: ui.label
63
+
64
+ def say(self, text: str | None) -> None:
65
+ """The sentence under the box (a bad pattern, a timeout), or none."""
66
+ self.why.set_text(text or "")
67
+ self.why.set_visibility(bool(text))
68
+
69
+
70
+ def filter_box(
71
+ needle: str,
72
+ regex: bool,
73
+ case: bool,
74
+ on_change: Callable[[str, bool, bool], None],
75
+ placeholder: str,
76
+ marker: str,
77
+ ) -> FilterBox:
78
+ """The input, with `.*` "Regular expression" and `Aa` "Match case" as
79
+ toggles (`aria-pressed`) beside it, then the sentence line, hidden.
80
+ Markers: ``<marker>needle``, ``<marker>regex``, ``<marker>case``,
81
+ ``<marker>filter:why``. ``on_change(needle, regex, case)`` on any of
82
+ them."""
83
+ state: dict[str, Any] = {"needle": needle, "regex": regex, "case": case}
84
+
85
+ def changed() -> None:
86
+ on_change(state["needle"], state["regex"], state["case"])
87
+
88
+ def typed(e: Any) -> None:
89
+ state["needle"] = e.value or ""
90
+ changed()
91
+
92
+ def flip(key: str, button: ui.button) -> None:
93
+ state[key] = not state[key]
94
+ _pressed(button, state[key])
95
+ changed()
96
+
97
+ buttons: dict[str, ui.button] = {}
98
+ with ui.row().classes("items-center no-wrap gap-1 w-full"):
99
+ box = (
100
+ ui.input(placeholder=placeholder, value=needle, on_change=typed)
101
+ .props("dense outlined clearable debounce=250")
102
+ .classes("col")
103
+ .mark(f"{marker}needle")
104
+ )
105
+ for key, text, tip in (
106
+ ("regex", ".*", "Regular expression"),
107
+ ("case", "Aa", "Match case"),
108
+ ):
109
+ button = (
110
+ ui.button(text)
111
+ .props("flat dense no-caps")
112
+ .classes("dstf-mono")
113
+ .mark(f"{marker}{key}")
114
+ )
115
+ button.on_click(partial(flip, key, button))
116
+ _pressed(button, state[key])
117
+ with button:
118
+ ui.tooltip(tip)
119
+ buttons[key] = button
120
+ why = ui.label().classes("dstf-why").mark(f"{marker}filter:why")
121
+ why.set_visibility(False)
122
+ return FilterBox(box, buttons["regex"], buttons["case"], why)
123
+
124
+
125
+ @dataclass
126
+ class MenuButton:
127
+ button: ui.button
128
+ items: dict[str, ui.menu_item]
129
+ value: str
130
+
131
+ def set(self, value: str) -> None:
132
+ """Check ``value``'s item and no other."""
133
+ self.value = value
134
+ for key, item in self.items.items():
135
+ on = key == value
136
+ item.props(f'aria-checked="{str(on).lower()}"')
137
+ if on:
138
+ item.classes(add="dstf-menu-checked")
139
+ else:
140
+ item.classes(remove="dstf-menu-checked")
141
+
142
+
143
+ def menu_button(
144
+ icon: str,
145
+ tip: str,
146
+ options: Sequence[tuple[str, str]],
147
+ value: str,
148
+ on_change: Callable[[str], None],
149
+ marker: str,
150
+ ) -> MenuButton:
151
+ """An icon button, named and tooltipped ``tip``, opening a menu of
152
+ ``options`` (``(value, label)``), each a `menuitemradio` with the
153
+ current one checked, and the arrow keys moving among them. Markers:
154
+ ``<marker>`` on the button, ``<marker>:<value>`` on each item."""
155
+ button = ui.button(icon=icon).props("flat dense round").mark(marker)
156
+ # On the props dict: its name is the tip (the icon's ligature names
157
+ # nothing), and a props string would break on a quote.
158
+ button.props["aria-label"] = tip
159
+ button.props["aria-haspopup"] = "menu"
160
+ items: dict[str, ui.menu_item] = {}
161
+
162
+ def choose(key: str) -> None:
163
+ menu.set(key)
164
+ on_change(key)
165
+
166
+ with button:
167
+ # Above the button: below it (Quasar's default) the tooltip covered
168
+ # the open menu's first choice and its check.
169
+ ui.tooltip(tip).props(TIP_ABOVE)
170
+ with ui.menu().props('role="menu"') as element:
171
+ element.on("keydown", js_handler=menu_keys(element))
172
+ for key, label in options:
173
+ item = (
174
+ ui.menu_item(on_click=partial(choose, key))
175
+ .props('role="menuitemradio" tabindex="-1"')
176
+ .mark(f"{marker}:{key}")
177
+ )
178
+ # `ui.menu_item` has no `set_text`: the text is a label in
179
+ # an item section, the check a hidden icon beside it.
180
+ with item:
181
+ with ui.item_section().props("side"):
182
+ ui.icon("check").classes("dstf-menu-check").props(
183
+ 'aria-hidden="true"'
184
+ )
185
+ with ui.item_section():
186
+ ui.item_label(label)
187
+ items[key] = item
188
+ menu = MenuButton(button, items, value)
189
+ menu.set(value)
190
+ return menu