chartflow 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.
- chartflow/__init__.py +28 -0
- chartflow/_verify_importexport.py +169 -0
- chartflow/edit/__init__.py +119 -0
- chartflow/edit/code_editor.py +1007 -0
- chartflow/edit/code_folder.py +523 -0
- chartflow/edit/completer.py +2652 -0
- chartflow/edit/context_analyzer.py +521 -0
- chartflow/edit/demo.py +469 -0
- chartflow/edit/line_number_area.py +548 -0
- chartflow/edit/minimap.py +581 -0
- chartflow/edit/python_editor.py +509 -0
- chartflow/edit/syntax_highlighter.py +291 -0
- chartflow/edit/test_editor.py +190 -0
- chartflow/flow/__init__.py +31 -0
- chartflow/flow/_demo.py +98 -0
- chartflow/flow/chart.py +1101 -0
- chartflow/flow/editor.py +375 -0
- chartflow/flow/flow.py +6 -0
- chartflow/flow/qchart.py +1250 -0
- chartflow/flow/test/__init__.py +0 -0
- chartflow/flow/test/test_chart.py +537 -0
- chartflow/flow/test_decorator_sync.py +102 -0
- chartflow/fsm/__init__.py +84 -0
- chartflow/fsm/decorators.py +395 -0
- chartflow/fsm/demo.py +381 -0
- chartflow/fsm/demo_pyqt6.py +881 -0
- chartflow/fsm/demo_tree.py +46 -0
- chartflow/fsm/logic.py +447 -0
- chartflow/fsm/meta.py +569 -0
- chartflow/fsm/scheduler.py +427 -0
- chartflow/fsm/stage.py +2079 -0
- chartflow/fsm/stdio.py +66 -0
- chartflow/fsm/test/__init__.py +7 -0
- chartflow/fsm/test/test_decorators.py +210 -0
- chartflow/fsm/test/test_events.py +202 -0
- chartflow/fsm/test/test_integration.py +596 -0
- chartflow/fsm/test/test_logic.py +371 -0
- chartflow/fsm/test/test_meta.py +192 -0
- chartflow/fsm/test/test_runtime_param.py +336 -0
- chartflow/fsm/test/test_scheduler.py +280 -0
- chartflow/fsm/test/test_stage.py +314 -0
- chartflow/fsm/test_behavior_events.py +263 -0
- chartflow/fsm/test_nested_parallel.py +80 -0
- chartflow/fsm/test_stage_spec.py +448 -0
- chartflow/fsm/utils.py +34 -0
- chartflow/node/__init__.py +66 -0
- chartflow/node/_node_base.py +727 -0
- chartflow/node/_node_interaction.py +488 -0
- chartflow/node/_node_interface.py +426 -0
- chartflow/node/_node_markers.py +648 -0
- chartflow/node/_node_ports.py +536 -0
- chartflow/node/_port_interface.py +111 -0
- chartflow/node/arrow_item.py +287 -0
- chartflow/node/color_utils.py +113 -0
- chartflow/node/connection_item.py +939 -0
- chartflow/node/demo.py +258 -0
- chartflow/node/demo_arrow_drag.py +46 -0
- chartflow/node/demo_decorator.py +63 -0
- chartflow/node/demo_ports.py +69 -0
- chartflow/node/enums.py +35 -0
- chartflow/node/example.py +84 -0
- chartflow/node/layout_utils.py +307 -0
- chartflow/node/main_window.py +196 -0
- chartflow/node/menu_bar.py +240 -0
- chartflow/node/node_canvas.py +1730 -0
- chartflow/node/node_item.py +754 -0
- chartflow/node/popmenu.py +472 -0
- chartflow/node/port_item.py +591 -0
- chartflow/node/qnode_editor.py +73 -0
- chartflow/node/selection_rect.py +53 -0
- chartflow/node/style_panel.py +615 -0
- chartflow/node/styles.py +63 -0
- chartflow/node/test_qnode.py +2336 -0
- chartflow/showcase.py +528 -0
- chartflow/theme.py +508 -0
- chartflow-0.1.dist-info/METADATA +23 -0
- chartflow-0.1.dist-info/RECORD +79 -0
- chartflow-0.1.dist-info/WHEEL +5 -0
- chartflow-0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""Tests for Logic class"""
|
|
2
|
+
|
|
3
|
+
from chartflow.fsm.meta import StageMachineMeta
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from chartflow.fsm.logic import Logic
|
|
8
|
+
from chartflow.fsm.scheduler import Scheduler
|
|
9
|
+
from chartflow.fsm.meta import clear_prototypes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture(autouse=True)
|
|
13
|
+
def reset_state():
|
|
14
|
+
"""Reset state before each test"""
|
|
15
|
+
Scheduler._instance = None
|
|
16
|
+
clear_prototypes()
|
|
17
|
+
Logic._Logic__names__ = {}
|
|
18
|
+
Logic._Logic__types__ = {}
|
|
19
|
+
StageMachineMeta.__ID_PROTOS__ = {}
|
|
20
|
+
yield
|
|
21
|
+
Scheduler._instance = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MockKnowledge:
|
|
25
|
+
"""Mock knowledge object"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TestLogicInit:
|
|
30
|
+
"""Test Logic initialization"""
|
|
31
|
+
|
|
32
|
+
def test_logic_auto_registers(self):
|
|
33
|
+
"""Test that Logic auto-registers with Scheduler"""
|
|
34
|
+
scheduler = Scheduler()
|
|
35
|
+
|
|
36
|
+
class TestLogic(Logic):
|
|
37
|
+
NAME = "TestLogic"
|
|
38
|
+
|
|
39
|
+
def my_state(self, inst):
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
logic = TestLogic()
|
|
43
|
+
|
|
44
|
+
assert logic in scheduler
|
|
45
|
+
|
|
46
|
+
def test_logic_tracks_instances(self):
|
|
47
|
+
"""Test that Logic tracks instances by name"""
|
|
48
|
+
|
|
49
|
+
class TestLogic(Logic):
|
|
50
|
+
NAME = "TestLogic"
|
|
51
|
+
|
|
52
|
+
def my_state(self, inst):
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
logic1 = TestLogic()
|
|
56
|
+
logic2 = TestLogic()
|
|
57
|
+
|
|
58
|
+
instances = Logic._Logic__names__.get("TestLogic", {})
|
|
59
|
+
assert logic1.uid in instances
|
|
60
|
+
assert logic2.uid in instances
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TestLogicLifecycle:
|
|
64
|
+
"""Test Logic lifecycle methods"""
|
|
65
|
+
|
|
66
|
+
def test_check_method(self):
|
|
67
|
+
"""Test _check method"""
|
|
68
|
+
|
|
69
|
+
class TestLogic(Logic):
|
|
70
|
+
NAME = "TestLogic"
|
|
71
|
+
|
|
72
|
+
def my_state(self, inst):
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
logic = TestLogic()
|
|
76
|
+
|
|
77
|
+
assert logic._check(False) is True
|
|
78
|
+
|
|
79
|
+
def test_enter_method(self):
|
|
80
|
+
"""Test _enter method calls onStart"""
|
|
81
|
+
|
|
82
|
+
class TestLogic(Logic):
|
|
83
|
+
NAME = "TestLogic"
|
|
84
|
+
|
|
85
|
+
def __init__(self):
|
|
86
|
+
self.started = False
|
|
87
|
+
super().__init__()
|
|
88
|
+
|
|
89
|
+
def my_state(self, inst):
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def onStart(self, it):
|
|
93
|
+
self.started = True
|
|
94
|
+
|
|
95
|
+
logic = TestLogic()
|
|
96
|
+
logic._enter()
|
|
97
|
+
|
|
98
|
+
assert logic.started
|
|
99
|
+
assert logic._isEntered
|
|
100
|
+
|
|
101
|
+
def test_step_method(self):
|
|
102
|
+
"""Test _step method calls handleStage and onStep"""
|
|
103
|
+
|
|
104
|
+
class TestLogic(Logic):
|
|
105
|
+
NAME = "TestLogic"
|
|
106
|
+
|
|
107
|
+
def __init__(self):
|
|
108
|
+
self.stepped = False
|
|
109
|
+
super().__init__()
|
|
110
|
+
|
|
111
|
+
def my_state(self, inst):
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
def onStep(self, it):
|
|
115
|
+
self.stepped = True
|
|
116
|
+
|
|
117
|
+
logic = TestLogic()
|
|
118
|
+
logic._current = "my_state"
|
|
119
|
+
logic._step()
|
|
120
|
+
|
|
121
|
+
assert logic.stepped
|
|
122
|
+
|
|
123
|
+
def test_exit_method(self):
|
|
124
|
+
"""Test _exit method calls onStop"""
|
|
125
|
+
|
|
126
|
+
class TestLogic(Logic):
|
|
127
|
+
NAME = "TestLogic"
|
|
128
|
+
|
|
129
|
+
def __init__(self):
|
|
130
|
+
self.stopped = False
|
|
131
|
+
super().__init__()
|
|
132
|
+
|
|
133
|
+
def my_state(self, inst):
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
def onStop(self, it):
|
|
137
|
+
self.stopped = True
|
|
138
|
+
|
|
139
|
+
logic = TestLogic()
|
|
140
|
+
logic._exit()
|
|
141
|
+
|
|
142
|
+
assert logic.stopped
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TestLogicProperties:
|
|
146
|
+
"""Test Logic properties"""
|
|
147
|
+
|
|
148
|
+
def test_alive_property(self):
|
|
149
|
+
"""Test alive property"""
|
|
150
|
+
|
|
151
|
+
class TestLogic(Logic):
|
|
152
|
+
NAME = "TestLogic"
|
|
153
|
+
|
|
154
|
+
def my_state(self, inst):
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
logic = TestLogic()
|
|
158
|
+
|
|
159
|
+
assert logic.alive is True
|
|
160
|
+
|
|
161
|
+
logic.alive = False
|
|
162
|
+
|
|
163
|
+
assert logic.alive is False
|
|
164
|
+
|
|
165
|
+
def test_enabled_property(self):
|
|
166
|
+
"""Test enabled property"""
|
|
167
|
+
|
|
168
|
+
class TestLogic(Logic):
|
|
169
|
+
NAME = "TestLogic"
|
|
170
|
+
|
|
171
|
+
def my_state(self, inst):
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
logic = TestLogic()
|
|
175
|
+
|
|
176
|
+
assert logic.enabled is True
|
|
177
|
+
|
|
178
|
+
logic.enabled = False
|
|
179
|
+
|
|
180
|
+
assert logic.enabled is False
|
|
181
|
+
|
|
182
|
+
def test_uid_property(self):
|
|
183
|
+
"""Test uid property"""
|
|
184
|
+
|
|
185
|
+
class TestLogic(Logic):
|
|
186
|
+
NAME = "TestLogic"
|
|
187
|
+
|
|
188
|
+
def my_state(self, inst):
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
logic1 = TestLogic()
|
|
192
|
+
logic2 = TestLogic()
|
|
193
|
+
|
|
194
|
+
assert logic1.uid == 0
|
|
195
|
+
assert logic2.uid == 1
|
|
196
|
+
|
|
197
|
+
def test_priority_property(self):
|
|
198
|
+
"""Test priority property"""
|
|
199
|
+
|
|
200
|
+
class TestLogic(Logic):
|
|
201
|
+
NAME = "TestLogic"
|
|
202
|
+
PRIORITY = 5
|
|
203
|
+
|
|
204
|
+
def my_state(self, inst):
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
logic = TestLogic()
|
|
208
|
+
|
|
209
|
+
assert logic.priority == 5
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TestLogicRef:
|
|
213
|
+
"""Test Logic reference methods"""
|
|
214
|
+
|
|
215
|
+
def test_ref_by_name(self):
|
|
216
|
+
"""Test ref() by base name returns list"""
|
|
217
|
+
|
|
218
|
+
class TestLogic(Logic):
|
|
219
|
+
NAME = "TestLogic"
|
|
220
|
+
|
|
221
|
+
def my_state(self, inst):
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
logic1 = TestLogic()
|
|
225
|
+
logic2 = TestLogic()
|
|
226
|
+
|
|
227
|
+
refs = logic1.ref("TestLogic")
|
|
228
|
+
|
|
229
|
+
assert isinstance(refs, list)
|
|
230
|
+
assert logic1 in refs
|
|
231
|
+
assert logic2 in refs
|
|
232
|
+
|
|
233
|
+
def test_ref_by_full_name(self):
|
|
234
|
+
"""Test ref() by full name returns single instance"""
|
|
235
|
+
|
|
236
|
+
class TestLogic(Logic):
|
|
237
|
+
NAME = "TestLogic"
|
|
238
|
+
|
|
239
|
+
def my_state(self, inst):
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
logic1 = TestLogic()
|
|
243
|
+
logic2 = TestLogic()
|
|
244
|
+
|
|
245
|
+
ref1 = logic1.ref("TestLogic0")
|
|
246
|
+
ref2 = logic2.ref("TestLogic1")
|
|
247
|
+
|
|
248
|
+
assert ref1 is logic1
|
|
249
|
+
assert ref2 is logic2
|
|
250
|
+
|
|
251
|
+
def test_ref_last(self):
|
|
252
|
+
"""Test refLast() returns previous instance"""
|
|
253
|
+
|
|
254
|
+
class TestLogic(Logic):
|
|
255
|
+
NAME = "TestLogic"
|
|
256
|
+
|
|
257
|
+
def my_state(self, inst):
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
logic1 = TestLogic()
|
|
261
|
+
logic2 = TestLogic()
|
|
262
|
+
|
|
263
|
+
assert logic2.refLast() is logic1
|
|
264
|
+
assert logic1.refLast() is None
|
|
265
|
+
|
|
266
|
+
def test_ref_next(self):
|
|
267
|
+
"""Test refNext() returns next instance"""
|
|
268
|
+
|
|
269
|
+
class TestLogic(Logic):
|
|
270
|
+
NAME = "TestLogic"
|
|
271
|
+
|
|
272
|
+
def my_state(self, inst):
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
logic1 = TestLogic()
|
|
276
|
+
logic2 = TestLogic()
|
|
277
|
+
|
|
278
|
+
assert logic1.refNext() is logic2
|
|
279
|
+
assert logic2.refNext() is None
|
|
280
|
+
|
|
281
|
+
def test_ref_adjacent(self):
|
|
282
|
+
"""Test refAdjacent() returns prev and next"""
|
|
283
|
+
|
|
284
|
+
class TestLogic(Logic):
|
|
285
|
+
NAME = "TestLogic"
|
|
286
|
+
|
|
287
|
+
def my_state(self, inst):
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
logic1 = TestLogic()
|
|
291
|
+
logic2 = TestLogic()
|
|
292
|
+
logic3 = TestLogic()
|
|
293
|
+
|
|
294
|
+
prev, next = logic2.refAdjacent()
|
|
295
|
+
|
|
296
|
+
assert prev is logic1
|
|
297
|
+
assert next is logic3
|
|
298
|
+
|
|
299
|
+
def test_ref_head(self):
|
|
300
|
+
"""Test refHead() returns first instance"""
|
|
301
|
+
|
|
302
|
+
class TestLogic(Logic):
|
|
303
|
+
NAME = "TestLogic"
|
|
304
|
+
|
|
305
|
+
def my_state(self, inst):
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
logic1 = TestLogic()
|
|
309
|
+
logic2 = TestLogic()
|
|
310
|
+
|
|
311
|
+
assert logic1.refHead() is logic1
|
|
312
|
+
|
|
313
|
+
def test_ref_end(self):
|
|
314
|
+
"""Test refEnd() returns last instance"""
|
|
315
|
+
|
|
316
|
+
class TestLogic(Logic):
|
|
317
|
+
NAME = "TestLogic"
|
|
318
|
+
|
|
319
|
+
def my_state(self, inst):
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
logic1 = TestLogic()
|
|
323
|
+
logic2 = TestLogic()
|
|
324
|
+
|
|
325
|
+
assert logic1.refEnd() is logic2
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class TestLogicKill:
|
|
329
|
+
"""Test Logic kill functionality"""
|
|
330
|
+
|
|
331
|
+
def test_kill_sets_alive_false(self):
|
|
332
|
+
"""Test kill() sets alive to False"""
|
|
333
|
+
|
|
334
|
+
class TestLogic(Logic):
|
|
335
|
+
NAME = "TestLogic"
|
|
336
|
+
|
|
337
|
+
def my_state(self, inst):
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
logic = TestLogic()
|
|
341
|
+
|
|
342
|
+
assert logic.alive is True
|
|
343
|
+
|
|
344
|
+
logic.kill()
|
|
345
|
+
|
|
346
|
+
assert logic.alive is False
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class TestLogicControl:
|
|
350
|
+
"""Test Logic control methods"""
|
|
351
|
+
|
|
352
|
+
def test_enable_disable(self):
|
|
353
|
+
"""Test enable() and disable() methods"""
|
|
354
|
+
|
|
355
|
+
class TestLogic(Logic):
|
|
356
|
+
NAME = "TestLogic"
|
|
357
|
+
|
|
358
|
+
def my_state(self, inst):
|
|
359
|
+
return None
|
|
360
|
+
|
|
361
|
+
logic = TestLogic()
|
|
362
|
+
|
|
363
|
+
assert logic.enabled is True
|
|
364
|
+
|
|
365
|
+
logic.disable()
|
|
366
|
+
|
|
367
|
+
assert logic.enabled is False
|
|
368
|
+
|
|
369
|
+
logic.enable()
|
|
370
|
+
|
|
371
|
+
assert logic.enabled is True
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Tests for StageMachineMeta metaclass"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from chartflow.fsm.meta import StageMachineMeta, get_prototype, list_prototypes, clear_prototypes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestStateDiscovery:
|
|
8
|
+
"""Test automatic state discovery"""
|
|
9
|
+
|
|
10
|
+
def test_basic_state_discovery(self):
|
|
11
|
+
"""Test that methods become states"""
|
|
12
|
+
clear_prototypes()
|
|
13
|
+
|
|
14
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
15
|
+
NAME = "TestStage"
|
|
16
|
+
|
|
17
|
+
def state_a(self, inst):
|
|
18
|
+
return "state_b"
|
|
19
|
+
|
|
20
|
+
def state_b(self, inst):
|
|
21
|
+
return "state_a"
|
|
22
|
+
|
|
23
|
+
assert "state_a" in TestStage.__states__
|
|
24
|
+
assert "state_b" in TestStage.__states__
|
|
25
|
+
|
|
26
|
+
def test_private_methods_not_states(self):
|
|
27
|
+
"""Test that _private methods are not states"""
|
|
28
|
+
clear_prototypes()
|
|
29
|
+
|
|
30
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
31
|
+
NAME = "TestStage"
|
|
32
|
+
|
|
33
|
+
def public_state(self, inst):
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def _private_method(self):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
assert "public_state" in TestStage.__states__
|
|
40
|
+
assert "_private_method" not in TestStage.__states__
|
|
41
|
+
|
|
42
|
+
def test_uppercase_methods_not_states(self):
|
|
43
|
+
"""Test that UPPERCASE methods are not states"""
|
|
44
|
+
clear_prototypes()
|
|
45
|
+
|
|
46
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
47
|
+
NAME = "TestStage"
|
|
48
|
+
|
|
49
|
+
def normal_state(self, inst):
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def CONSTANT(self):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
assert "normal_state" in TestStage.__states__
|
|
56
|
+
assert "CONSTANT" not in TestStage.__states__
|
|
57
|
+
|
|
58
|
+
def test_ignored_methods_not_states(self):
|
|
59
|
+
"""Test that ignored methods are not states"""
|
|
60
|
+
clear_prototypes()
|
|
61
|
+
|
|
62
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
63
|
+
NAME = "TestStage"
|
|
64
|
+
|
|
65
|
+
def normal_state(self, inst):
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def onLoading(self, it, k, *children):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
assert "normal_state" in TestStage.__states__
|
|
72
|
+
assert "onLoading" not in TestStage.__states__
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestClassRegistration:
|
|
76
|
+
"""Test automatic class registration"""
|
|
77
|
+
|
|
78
|
+
def test_class_registered(self):
|
|
79
|
+
"""Test that classes with NAME are registered"""
|
|
80
|
+
clear_prototypes()
|
|
81
|
+
|
|
82
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
83
|
+
NAME = "TestStage"
|
|
84
|
+
|
|
85
|
+
def state_a(self, inst):
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
proto = get_prototype("TestStage")
|
|
89
|
+
assert proto is TestStage
|
|
90
|
+
|
|
91
|
+
def test_base_classes_not_registered(self):
|
|
92
|
+
"""Test that base classes are not registered"""
|
|
93
|
+
clear_prototypes()
|
|
94
|
+
|
|
95
|
+
class Stage(metaclass=StageMachineMeta):
|
|
96
|
+
NAME = "Stage"
|
|
97
|
+
|
|
98
|
+
class Logic(Stage):
|
|
99
|
+
NAME = "Logic"
|
|
100
|
+
|
|
101
|
+
assert get_prototype("Stage") is None
|
|
102
|
+
assert get_prototype("Logic") is None
|
|
103
|
+
|
|
104
|
+
def test_name_validation(self):
|
|
105
|
+
"""Test that NAME ending with digit is rejected"""
|
|
106
|
+
clear_prototypes()
|
|
107
|
+
|
|
108
|
+
with pytest.raises(ValueError):
|
|
109
|
+
class BadStage(metaclass=StageMachineMeta):
|
|
110
|
+
NAME = "BadStage1"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class TestInheritance:
|
|
114
|
+
"""Test state and decorator inheritance"""
|
|
115
|
+
|
|
116
|
+
def test_states_inherited(self):
|
|
117
|
+
"""Test that states are inherited from parent"""
|
|
118
|
+
clear_prototypes()
|
|
119
|
+
|
|
120
|
+
class BaseStage(metaclass=StageMachineMeta):
|
|
121
|
+
NAME = "BaseStage"
|
|
122
|
+
|
|
123
|
+
def base_state(self, inst):
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
class DerivedStage(BaseStage):
|
|
127
|
+
NAME = "DerivedStage"
|
|
128
|
+
|
|
129
|
+
def derived_state(self, inst):
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
assert "base_state" in DerivedStage.__states__
|
|
133
|
+
assert "derived_state" in DerivedStage.__states__
|
|
134
|
+
|
|
135
|
+
def test_recursive_inherited(self):
|
|
136
|
+
"""Test that recursive markers are inherited"""
|
|
137
|
+
clear_prototypes()
|
|
138
|
+
|
|
139
|
+
class BaseStage(metaclass=StageMachineMeta):
|
|
140
|
+
NAME = "BaseStage"
|
|
141
|
+
|
|
142
|
+
def base_state(self, inst):
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
BaseStage.__recursive__ = {"base_state"}
|
|
146
|
+
|
|
147
|
+
class DerivedStage(BaseStage):
|
|
148
|
+
NAME = "DerivedStage"
|
|
149
|
+
|
|
150
|
+
def derived_state(self, inst):
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
assert "base_state" in DerivedStage.__recursive__
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class TestPrototypeFunctions:
|
|
157
|
+
"""Test prototype utility functions"""
|
|
158
|
+
|
|
159
|
+
def test_get_prototype(self):
|
|
160
|
+
"""Test get_prototype function"""
|
|
161
|
+
clear_prototypes()
|
|
162
|
+
|
|
163
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
164
|
+
NAME = "TestStage"
|
|
165
|
+
|
|
166
|
+
assert get_prototype("TestStage") is TestStage
|
|
167
|
+
assert get_prototype("NonExistent") is None
|
|
168
|
+
|
|
169
|
+
def test_list_prototypes(self):
|
|
170
|
+
"""Test list_prototypes function"""
|
|
171
|
+
clear_prototypes()
|
|
172
|
+
|
|
173
|
+
class TestStageAlpha(metaclass=StageMachineMeta):
|
|
174
|
+
NAME = "TestStageAlpha"
|
|
175
|
+
|
|
176
|
+
class TestStageBeta(metaclass=StageMachineMeta):
|
|
177
|
+
NAME = "TestStageBeta"
|
|
178
|
+
|
|
179
|
+
protos = list_prototypes()
|
|
180
|
+
assert "TestStageAlpha" in protos
|
|
181
|
+
assert "TestStageBeta" in protos
|
|
182
|
+
|
|
183
|
+
def test_clear_prototypes(self):
|
|
184
|
+
"""Test clear_prototypes function"""
|
|
185
|
+
clear_prototypes()
|
|
186
|
+
|
|
187
|
+
class TestStage(metaclass=StageMachineMeta):
|
|
188
|
+
NAME = "TestStage"
|
|
189
|
+
|
|
190
|
+
assert get_prototype("TestStage") is TestStage
|
|
191
|
+
clear_prototypes()
|
|
192
|
+
assert get_prototype("TestStage") is None
|