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.
- dstf/__init__.py +41 -0
- dstf/__main__.py +10 -0
- dstf/authoring/__init__.py +13 -0
- dstf/authoring/builtin/__init__.py +32 -0
- dstf/authoring/builtin/accept.py +205 -0
- dstf/authoring/builtin/edit_criteria.py +479 -0
- dstf/authoring/builtin/edit_family.py +228 -0
- dstf/authoring/builtin/edit_on_plot.py +1354 -0
- dstf/authoring/builtin/edit_tree.py +1399 -0
- dstf/authoring/builtin/enqueue.py +144 -0
- dstf/authoring/capabilities.py +463 -0
- dstf/authoring/errors.py +49 -0
- dstf/authoring/family_rows.py +526 -0
- dstf/authoring/forms.py +631 -0
- dstf/authoring/layout.py +104 -0
- dstf/authoring/live_rows.py +337 -0
- dstf/authoring/live_runs.py +385 -0
- dstf/authoring/needle.py +127 -0
- dstf/authoring/picker_rows.py +92 -0
- dstf/authoring/queue_rows.py +354 -0
- dstf/authoring/session.py +3016 -0
- dstf/authoring/shell/__init__.py +9 -0
- dstf/authoring/shell/actions.py +702 -0
- dstf/authoring/shell/app.py +53 -0
- dstf/authoring/shell/drawer_controls.py +190 -0
- dstf/authoring/shell/family_fold.py +468 -0
- dstf/authoring/shell/figure_hook.py +249 -0
- dstf/authoring/shell/grips.js +1000 -0
- dstf/authoring/shell/header.py +783 -0
- dstf/authoring/shell/live_view.py +93 -0
- dstf/authoring/shell/picker.py +267 -0
- dstf/authoring/shell/placing.py +122 -0
- dstf/authoring/shell/plot_card.py +1171 -0
- dstf/authoring/shell/queue_panel.py +433 -0
- dstf/authoring/shell/rail.py +593 -0
- dstf/authoring/shell/shell.py +528 -0
- dstf/authoring/shell/stats_card.py +501 -0
- dstf/authoring/shell/strip.py +204 -0
- dstf/authoring/shell/style.py +721 -0
- dstf/authoring/shell/suite.py +277 -0
- dstf/authoring/shell/suite_grid.js +414 -0
- dstf/authoring/shell/suite_grid.py +373 -0
- dstf/authoring/shell/tab_theme.py +110 -0
- dstf/authoring/shell/tabstate.py +146 -0
- dstf/authoring/shell/test_list.py +179 -0
- dstf/authoring/shell/test_page.py +1404 -0
- dstf/authoring/shell/tree_card.py +1171 -0
- dstf/authoring/shell/variables_drawer.py +589 -0
- dstf/authoring/shell/widgets.py +188 -0
- dstf/authoring/situation.py +287 -0
- dstf/authoring/spec_document.py +215 -0
- dstf/authoring/spec_preview.py +83 -0
- dstf/authoring/suite_rows.py +99 -0
- dstf/authoring/tree_move.py +516 -0
- dstf/authoring/tree_pointer.py +33 -0
- dstf/authoring/tree_text.py +61 -0
- dstf/authoring/typed_number.js +64 -0
- dstf/authoring/typed_number.py +76 -0
- dstf/authoring/unscorable.py +141 -0
- dstf/authoring/variable_rows.py +216 -0
- dstf/backends/__init__.py +283 -0
- dstf/backends/base.py +1117 -0
- dstf/backends/common/__init__.py +1 -0
- dstf/backends/common/json_driver.py +484 -0
- dstf/backends/common/live_mat.py +369 -0
- dstf/backends/common/mat_reader.py +632 -0
- dstf/backends/common/proc_kill.py +238 -0
- dstf/backends/common/proc_output.py +20 -0
- dstf/backends/common/result_files.py +109 -0
- dstf/backends/common/result_variables.py +329 -0
- dstf/backends/common/statistics.py +39 -0
- dstf/backends/cross_backend.py +193 -0
- dstf/backends/dymola/__init__.py +5 -0
- dstf/backends/dymola/backend.py +776 -0
- dstf/backends/dymola/interface_loader.py +241 -0
- dstf/backends/dymola/log_parser.py +396 -0
- dstf/backends/dymola/persistent_backend.py +949 -0
- dstf/backends/fmpy/__init__.py +11 -0
- dstf/backends/fmpy/backend.py +443 -0
- dstf/backends/julia/__init__.py +27 -0
- dstf/backends/julia/backend.py +156 -0
- dstf/backends/julia/persistent_backend.py +541 -0
- dstf/backends/julia/run_persistent.jl +167 -0
- dstf/backends/julia/run_test.jl +146 -0
- dstf/backends/openmodelica/__init__.py +21 -0
- dstf/backends/openmodelica/backend.py +368 -0
- dstf/backends/openmodelica/init_xml.py +76 -0
- dstf/backends/openmodelica/log_parser.py +119 -0
- dstf/backends/openmodelica/mos_generator.py +228 -0
- dstf/backends/openmodelica/persistent_backend.py +1045 -0
- dstf/backends/openmodelica/session_loader.py +63 -0
- dstf/backends/persistent.py +1811 -0
- dstf/backends/pool.py +602 -0
- dstf/backends/progress.py +395 -0
- dstf/backends/python/__init__.py +11 -0
- dstf/backends/python/backend.py +141 -0
- dstf/backends/python/run_test.py +141 -0
- dstf/backends/run_metadata.py +103 -0
- dstf/backends/types.py +310 -0
- dstf/cli/__init__.py +12 -0
- dstf/cli/author_cmd.py +61 -0
- dstf/cli/baseline_cmds.py +341 -0
- dstf/cli/common.py +231 -0
- dstf/cli/diag_cmds.py +350 -0
- dstf/cli/main.py +561 -0
- dstf/cli/review.py +398 -0
- dstf/cli/run_cmds.py +589 -0
- dstf/cli/spec_cmds.py +123 -0
- dstf/comparison/__init__.py +13 -0
- dstf/comparison/algorithms.py +2127 -0
- dstf/comparison/builtins.py +291 -0
- dstf/comparison/comparator.py +1189 -0
- dstf/comparison/geometry.py +164 -0
- dstf/comparison/handles.py +1194 -0
- dstf/comparison/metric_tree.py +332 -0
- dstf/comparison/mode_schema.py +176 -0
- dstf/comparison/modes.py +788 -0
- dstf/comparison/registry.py +230 -0
- dstf/comparison/seeds.py +281 -0
- dstf/comparison/sides.py +158 -0
- dstf/comparison/snap.py +62 -0
- dstf/comparison/strict_keys.py +246 -0
- dstf/comparison/summaries.py +354 -0
- dstf/comparison/tree_eval.py +868 -0
- dstf/comparison/tree_spec.py +678 -0
- dstf/comparison/types.py +194 -0
- dstf/comparison/validator.py +232 -0
- dstf/config.py +815 -0
- dstf/discovery/__init__.py +0 -0
- dstf/discovery/json_recognizer.py +702 -0
- dstf/discovery/library_state.py +248 -0
- dstf/discovery/mo_parser.py +794 -0
- dstf/discovery/patch_apply.py +308 -0
- dstf/discovery/patch_envelope.py +63 -0
- dstf/discovery/plots.py +318 -0
- dstf/discovery/recognizer.py +103 -0
- dstf/discovery/scan_cache.py +154 -0
- dstf/discovery/spec_parser.py +538 -0
- dstf/discovery/spec_shape.py +122 -0
- dstf/discovery/spec_update.py +493 -0
- dstf/discovery/test_registry.py +488 -0
- dstf/discovery/unit_tests.py +95 -0
- dstf/figures/__init__.py +14 -0
- dstf/figures/assemble.py +48 -0
- dstf/figures/decimate.py +150 -0
- dstf/figures/families.py +289 -0
- dstf/figures/palette.py +170 -0
- dstf/figures/plotly.py +1413 -0
- dstf/figures/view.py +233 -0
- dstf/pipeline.py +435 -0
- dstf/plugins.py +104 -0
- dstf/reporting/__init__.py +0 -0
- dstf/reporting/console_report.py +187 -0
- dstf/reporting/context_builder.py +979 -0
- dstf/reporting/dashboard_render.py +534 -0
- dstf/reporting/junit_report.py +192 -0
- dstf/reporting/overlay_loader.py +708 -0
- dstf/reporting/report_suite.py +1059 -0
- dstf/reporting/rows.py +458 -0
- dstf/reporting/schema_export.py +413 -0
- dstf/reporting/stats_rows.py +321 -0
- dstf/reporting/status.py +388 -0
- dstf/reporting/templates/axes.js +61 -0
- dstf/reporting/templates/dashboard.html +284 -0
- dstf/reporting/templates/dashboard.js +604 -0
- dstf/reporting/templates/filters.js +100 -0
- dstf/reporting/templates/interactive.html +420 -0
- dstf/reporting/templates/report.js +116 -0
- dstf/reporting/templates/vendor/plotly.min.js +8 -0
- dstf/reporting/theme.py +324 -0
- dstf/reporting/tree_rows.py +73 -0
- dstf/reporting/verdicts.py +124 -0
- dstf/storage/__init__.py +0 -0
- dstf/storage/accept.py +216 -0
- dstf/storage/baseline.py +43 -0
- dstf/storage/batch_manifest.py +204 -0
- dstf/storage/ref_index.py +295 -0
- dstf/storage/reference_store.py +661 -0
- dstf/storage/roles.py +576 -0
- dstf/storage/run_stats.py +85 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/METADATA +266 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/RECORD +185 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/WHEEL +4 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/entry_points.txt +2 -0
- 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")
|