statable 3.2.0__tar.gz → 3.2.2__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.
- {statable-3.2.0 → statable-3.2.2}/PKG-INFO +1 -1
- {statable-3.2.0 → statable-3.2.2}/codegen/__init__.py +1 -1
- {statable-3.2.0 → statable-3.2.2}/pyproject.toml +1 -1
- {statable-3.2.0 → statable-3.2.2}/statable/__init__.py +1 -1
- {statable-3.2.0 → statable-3.2.2}/statable/cli.py +1 -1
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/PKG-INFO +1 -1
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/SOURCES.txt +2 -1
- {statable-3.2.0 → statable-3.2.2}/statable_gui/main_window.py +10 -2
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_0_s1_packaging.py +9 -9
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_0_s2_public_api.py +4 -4
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_0_s3_cli.py +4 -4
- statable-3.2.2/tests/test_v3_2_s2_signals.py +144 -0
- {statable-3.2.0 → statable-3.2.2}/README.md +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/c_code_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/code_merger.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/code_templates.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/config.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/enum_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/event_queue_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/interrupt_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/naming_convention.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/osal_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/role_function_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/sample_data.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/state_actions_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/struct_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/timer_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/transition_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/type_mapper.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/change_actions.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/change_applier.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/clipboard_manager.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/data/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/data/action_definitions.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/data/keywords.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/data/prompt_templates.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/data/validation_rules.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/base_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/cell_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/custom_type_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/event_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/flag_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/interrupt_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/queue_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/role_function_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/state_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/timer_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/transition_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/items/variable_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/logger.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/models.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/prompt_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/response_parser.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/response_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/validation_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/validate/validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/codegen/variable_generator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/setup.cfg +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/__main__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/global_defs.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/mermaid_gen.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/model.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/parser.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/sample_data.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/shared/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/shared/condition_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/shared/literal_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/shared/role_function_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/state_machine.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable/xml_io.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/dependency_links.txt +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/entry_points.txt +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/requires.txt +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable.egg-info/top_level.txt +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/action_edit_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/code_generation_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/code_generation_settings_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/common_widgets.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/condition_builder_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/condition_edit_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/condition_template_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/config.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/dialogs.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/event_definition_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/event_delivery_settings_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/event_queue_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/global_defs.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/global_defs_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/i18n/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/interrupt_handler_edit_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/layer_settings_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/condition_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/literal_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/literal_management_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/role_function_edit_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/libcntrl/role_function_library.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/literal_definition_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/logger.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/matrix_table.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/new_project_templates.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/new_project_wizard.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/preference_keys.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/preferences.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/role_function_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/sample_data.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/state_actions_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/symbol_picker.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/traceball.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/__init__.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/actions_tab.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/code_widget.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/coverage_analyzer.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/draft.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/edit_dialogs.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/overview_tab.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/palette_widget.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/relations_edit_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/relations_tab.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/system_global_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/transition_actions_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/transitions_tab.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/validation_dialog.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/statable_gui/widgets.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_readme_consistency.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p1.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_10.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_2.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_5.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_6.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_7.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_8.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p12_9.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p2.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p3.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p4a.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_2_p4b.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_3_p1.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_4_p1_merge.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p1.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p10.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p11.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p2.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p3.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p4.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p5.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p6.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p8.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_5_p9.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_6_p1.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_6_p2.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_6_p3.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_7_p3.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_7_p4.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_7_p5_validation.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_8_p1_ai_prompt.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_8_p2_response_parser.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_8_p3_response_validator.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v2_8_p4_gui_integration.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_0_g1_shared.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_0_s4_docs.py +0 -0
- {statable-3.2.0 → statable-3.2.2}/tests/test_v3_2_s1_wizard.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "statable"
|
|
9
|
-
version = "3.2.
|
|
9
|
+
version = "3.2.2"
|
|
10
10
|
description = "MISRA C:2012-aware state machine design and C code generation for embedded systems"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.10"
|
|
@@ -211,6 +211,8 @@ class MainWindow(QMainWindow):
|
|
|
211
211
|
|
|
212
212
|
self.tab_widget = QTabWidget()
|
|
213
213
|
self.tab_widget.setTabsClosable(True)
|
|
214
|
+
# [v3.2.1] Enable drag-and-drop tab reordering
|
|
215
|
+
self.tab_widget.setMovable(True)
|
|
214
216
|
self.tab_widget.tabCloseRequested.connect(
|
|
215
217
|
self.close_tab)
|
|
216
218
|
self.setCentralWidget(self.tab_widget)
|
|
@@ -332,7 +334,8 @@ class MainWindow(QMainWindow):
|
|
|
332
334
|
|
|
333
335
|
open_btn = QAction(self.tr("Open"), self)
|
|
334
336
|
open_btn.setToolTip(self.tr("Open project"))
|
|
335
|
-
|
|
337
|
+
# [v3.2.2 fix] lambda: avoid passing triggered(bool) as filepath
|
|
338
|
+
open_btn.triggered.connect(lambda: self.open_project())
|
|
336
339
|
toolbar.addAction(open_btn)
|
|
337
340
|
|
|
338
341
|
save_btn = QAction(self.tr("Save"), self)
|
|
@@ -384,7 +387,8 @@ class MainWindow(QMainWindow):
|
|
|
384
387
|
file_menu.addSeparator()
|
|
385
388
|
|
|
386
389
|
open_action = QAction(self.tr("Open Project..."), self)
|
|
387
|
-
|
|
390
|
+
# [v3.2.1 fix] lambda: avoid passing triggered(bool) as filepath
|
|
391
|
+
open_action.triggered.connect(lambda: self.open_project())
|
|
388
392
|
file_menu.addAction(open_action)
|
|
389
393
|
|
|
390
394
|
save_action = QAction(self.tr("Save Project..."), self)
|
|
@@ -812,6 +816,10 @@ class MainWindow(QMainWindow):
|
|
|
812
816
|
|
|
813
817
|
def open_project(self, filepath: str = None):
|
|
814
818
|
"""Load the whole project"""
|
|
819
|
+
# [v3.2.1 fix] Qt triggered signal passes bool; normalize to None
|
|
820
|
+
if isinstance(filepath, bool):
|
|
821
|
+
filepath = None
|
|
822
|
+
|
|
815
823
|
# [v2.3] Prompt to save before loading
|
|
816
824
|
if not self._maybe_save():
|
|
817
825
|
return
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Phase S-1 (v3.0) tests: packaging foundation.
|
|
4
4
|
|
|
5
5
|
Verifies:
|
|
6
|
-
- code/pyproject.toml exists and declares statable/3.2.
|
|
6
|
+
- code/pyproject.toml exists and declares statable/3.2.2
|
|
7
7
|
- statable-cli entry point is defined
|
|
8
8
|
- extras: gui / dev
|
|
9
|
-
- statable.__version__ == "3.2.
|
|
10
|
-
- codegen.__version__ == "3.2.
|
|
9
|
+
- statable.__version__ == "3.2.2"
|
|
10
|
+
- codegen.__version__ == "3.2.2"
|
|
11
11
|
"""
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
@@ -64,8 +64,8 @@ def main() -> int:
|
|
|
64
64
|
check("project.name == 'statable'",
|
|
65
65
|
project.get("name") == "statable",
|
|
66
66
|
f"got: {project.get('name')!r}")
|
|
67
|
-
check("project.version == '3.2.
|
|
68
|
-
project.get("version") == "3.2.
|
|
67
|
+
check("project.version == '3.2.2'",
|
|
68
|
+
project.get("version") == "3.2.2",
|
|
69
69
|
f"got: {project.get('version')!r}")
|
|
70
70
|
|
|
71
71
|
# --- T4: requires-python ---------------------------------------
|
|
@@ -101,16 +101,16 @@ def main() -> int:
|
|
|
101
101
|
# --- T8: runtime __version__ -----------------------------------
|
|
102
102
|
try:
|
|
103
103
|
import statable
|
|
104
|
-
check("statable.__version__ == '3.2.
|
|
105
|
-
getattr(statable, "__version__", None) == "3.2.
|
|
104
|
+
check("statable.__version__ == '3.2.2'",
|
|
105
|
+
getattr(statable, "__version__", None) == "3.2.2",
|
|
106
106
|
f"got: {getattr(statable, '__version__', None)!r}")
|
|
107
107
|
except Exception as e:
|
|
108
108
|
check("statable importable", False, repr(e))
|
|
109
109
|
|
|
110
110
|
try:
|
|
111
111
|
import codegen
|
|
112
|
-
check("codegen.__version__ == '3.2.
|
|
113
|
-
getattr(codegen, "__version__", None) == "3.2.
|
|
112
|
+
check("codegen.__version__ == '3.2.2'",
|
|
113
|
+
getattr(codegen, "__version__", None) == "3.2.2",
|
|
114
114
|
f"got: {getattr(codegen, '__version__', None)!r}")
|
|
115
115
|
except Exception as e:
|
|
116
116
|
check("codegen importable", False, repr(e))
|
|
@@ -47,8 +47,8 @@ def main() -> int:
|
|
|
47
47
|
_summary()
|
|
48
48
|
return 1
|
|
49
49
|
|
|
50
|
-
check("statable has __version__ == '3.2.
|
|
51
|
-
getattr(statable, "__version__", None) == "3.2.
|
|
50
|
+
check("statable has __version__ == '3.2.2'",
|
|
51
|
+
getattr(statable, "__version__", None) == "3.2.2",
|
|
52
52
|
f"got: {getattr(statable, '__version__', None)!r}")
|
|
53
53
|
check("'__version__' in statable.__all__",
|
|
54
54
|
"__version__" in statable.__all__,
|
|
@@ -69,8 +69,8 @@ def main() -> int:
|
|
|
69
69
|
_summary()
|
|
70
70
|
return 1
|
|
71
71
|
|
|
72
|
-
check("codegen has __version__ == '3.2.
|
|
73
|
-
getattr(codegen, "__version__", None) == "3.2.
|
|
72
|
+
check("codegen has __version__ == '3.2.2'",
|
|
73
|
+
getattr(codegen, "__version__", None) == "3.2.2",
|
|
74
74
|
f"got: {getattr(codegen, '__version__', None)!r}")
|
|
75
75
|
check("'validate' in codegen.__all__",
|
|
76
76
|
"validate" in codegen.__all__,
|
|
@@ -60,8 +60,8 @@ def main() -> int:
|
|
|
60
60
|
try:
|
|
61
61
|
from statable import cli
|
|
62
62
|
check("statable.cli importable", True)
|
|
63
|
-
check("CLI_VERSION == '3.2.
|
|
64
|
-
cli.CLI_VERSION == "3.2.
|
|
63
|
+
check("CLI_VERSION == '3.2.2'",
|
|
64
|
+
cli.CLI_VERSION == "3.2.2",
|
|
65
65
|
f"got: {cli.CLI_VERSION!r}")
|
|
66
66
|
except Exception as e:
|
|
67
67
|
check("statable.cli importable", False, repr(e))
|
|
@@ -91,8 +91,8 @@ def main() -> int:
|
|
|
91
91
|
# --- T3: version --------------------------------------------------
|
|
92
92
|
rc, out, _ = run_cli(["version"])
|
|
93
93
|
check("version: exit 0", rc == 0, f"rc={rc}")
|
|
94
|
-
check("version: prints 'statable-cli 3.2.
|
|
95
|
-
"statable-cli 3.2.
|
|
94
|
+
check("version: prints 'statable-cli 3.2.2'",
|
|
95
|
+
"statable-cli 3.2.2" in out,
|
|
96
96
|
f"out={out!r}")
|
|
97
97
|
|
|
98
98
|
# --- T4: validate on real XML (json) ------------------------------
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# tests/test_v3_2_s2_signals.py
|
|
2
|
+
"""Test Qt signal-slot safety (v3.2.2).
|
|
3
|
+
|
|
4
|
+
Regression test for the v3.2.0 bug where `open_project(filepath=None)`
|
|
5
|
+
received Qt's `triggered(bool)` argument, resulting in
|
|
6
|
+
`project_from_xml(False)` and a hang on `open(False, "rb")`.
|
|
7
|
+
|
|
8
|
+
Key insight: PySide6 auto-truncates args for slots with fixed signatures,
|
|
9
|
+
so most `triggered.connect(self.slot)` are safe. The bug only occurs
|
|
10
|
+
when the slot has an OPTIONAL parameter that receives the bool.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
passed = 0
|
|
25
|
+
failed = 0
|
|
26
|
+
|
|
27
|
+
def check(name, cond):
|
|
28
|
+
nonlocal passed, failed
|
|
29
|
+
if cond:
|
|
30
|
+
passed += 1
|
|
31
|
+
print(f"[PASS] {name}")
|
|
32
|
+
else:
|
|
33
|
+
failed += 1
|
|
34
|
+
print(f"[FAIL] {name}")
|
|
35
|
+
|
|
36
|
+
# ================================================================
|
|
37
|
+
# T1: open_project normalizes bool arguments
|
|
38
|
+
# ================================================================
|
|
39
|
+
print("=" * 70)
|
|
40
|
+
print(" T1: open_project(bool) normalization")
|
|
41
|
+
print("=" * 70)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
from PySide6.QtWidgets import QApplication
|
|
45
|
+
app = QApplication.instance() or QApplication(sys.argv)
|
|
46
|
+
|
|
47
|
+
from statable_gui.main_window import MainWindow
|
|
48
|
+
|
|
49
|
+
win = MainWindow()
|
|
50
|
+
|
|
51
|
+
import statable_gui.main_window as mw_mod
|
|
52
|
+
original_pfx = mw_mod.project_from_xml
|
|
53
|
+
called_with = []
|
|
54
|
+
|
|
55
|
+
def fake_pfx(path):
|
|
56
|
+
called_with.append(path)
|
|
57
|
+
raise RuntimeError("TEST_STOP")
|
|
58
|
+
|
|
59
|
+
mw_mod.project_from_xml = fake_pfx
|
|
60
|
+
|
|
61
|
+
from PySide6.QtWidgets import QFileDialog
|
|
62
|
+
original_dialog = QFileDialog.getOpenFileName
|
|
63
|
+
QFileDialog.getOpenFileName = staticmethod(lambda *a, **k: ("", ""))
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
win.open_project(False)
|
|
67
|
+
check("open_project(False) does not hang", True)
|
|
68
|
+
except Exception as e:
|
|
69
|
+
check(f"open_project(False) raised: {e}", False)
|
|
70
|
+
|
|
71
|
+
check("project_from_xml not called with False",
|
|
72
|
+
False not in called_with)
|
|
73
|
+
|
|
74
|
+
called_with.clear()
|
|
75
|
+
try:
|
|
76
|
+
win.open_project(True)
|
|
77
|
+
check("open_project(True) does not hang", True)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
check(f"open_project(True) raised: {e}", False)
|
|
80
|
+
check("project_from_xml not called with True",
|
|
81
|
+
True not in called_with)
|
|
82
|
+
|
|
83
|
+
mw_mod.project_from_xml = original_pfx
|
|
84
|
+
QFileDialog.getOpenFileName = original_dialog
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
import traceback
|
|
88
|
+
print(traceback.format_exc())
|
|
89
|
+
check(f"T1 setup failed: {e}", False)
|
|
90
|
+
|
|
91
|
+
# ================================================================
|
|
92
|
+
# T2: open_project is connected via lambda (the actual bug)
|
|
93
|
+
# ================================================================
|
|
94
|
+
print()
|
|
95
|
+
print("=" * 70)
|
|
96
|
+
print(" T2: open_project is lambda-wrapped")
|
|
97
|
+
print("=" * 70)
|
|
98
|
+
|
|
99
|
+
mw_path = Path(__file__).parent.parent / "statable_gui" / "main_window.py"
|
|
100
|
+
src = mw_path.read_text(encoding="utf-8")
|
|
101
|
+
|
|
102
|
+
# The bug: `triggered.connect(self.open_project)` without lambda
|
|
103
|
+
# would pass triggered(bool) as filepath.
|
|
104
|
+
# FIX: must use `triggered.connect(lambda: self.open_project())`
|
|
105
|
+
bug_pattern = re.compile(
|
|
106
|
+
r'triggered\.connect\(\s*self\.open_project\s*\)'
|
|
107
|
+
)
|
|
108
|
+
check("open_project not connected without lambda",
|
|
109
|
+
not bug_pattern.search(src))
|
|
110
|
+
|
|
111
|
+
fix_pattern = re.compile(
|
|
112
|
+
r'triggered\.connect\(\s*lambda\s*:\s*self\.open_project\(\)\s*\)'
|
|
113
|
+
)
|
|
114
|
+
check("open_project connected via lambda",
|
|
115
|
+
bool(fix_pattern.search(src)))
|
|
116
|
+
|
|
117
|
+
# ================================================================
|
|
118
|
+
# T3: open_project has bool guard in body
|
|
119
|
+
# ================================================================
|
|
120
|
+
print()
|
|
121
|
+
print("=" * 70)
|
|
122
|
+
print(" T3: open_project has bool normalization guard")
|
|
123
|
+
print("=" * 70)
|
|
124
|
+
|
|
125
|
+
check("open_project has isinstance(filepath, bool) guard",
|
|
126
|
+
"isinstance(filepath, bool)" in src)
|
|
127
|
+
|
|
128
|
+
# Verify that the signature accepts Optional[str]
|
|
129
|
+
check("open_project signature has filepath parameter",
|
|
130
|
+
re.search(r'def open_project\(self,\s*filepath', src) is not None)
|
|
131
|
+
|
|
132
|
+
# ================================================================
|
|
133
|
+
# Summary
|
|
134
|
+
# ================================================================
|
|
135
|
+
print()
|
|
136
|
+
print("=" * 70)
|
|
137
|
+
print(f" TOTAL: {passed + failed} PASSED: {passed} FAILED: {failed}")
|
|
138
|
+
print("=" * 70)
|
|
139
|
+
|
|
140
|
+
return 0 if failed == 0 else 1
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
sys.exit(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/coverage_analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/relations_edit_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/system_global_dialog.py
RENAMED
|
File without changes
|
{statable-3.2.0 → statable-3.2.2}/statable_gui/transition_editor_direct/transition_actions_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|