Skip to content

Commit 12dd62a

Browse files
committed
Fix SnapshotTool annotation test hanging on its snapshot dialog
Since the fix of #58, SnapshotTool defers its action with a zero-delay timer. In test_annotation_tools, this action ran during the processEvents() call of guidata's exec_dialog, before the automatic closing of dialogs was scheduled, so the modal ResizeDialog opened by save_snapshot was never closed and the test hung. The test only passed under PyQt5 on Linux when run after another test, whose pending closing timer closed the dialog; it hung when run on its own, on Windows, and with PyQt6 or PySide6. In unattended mode, replace the snapshot action by a recorder, run the deferred actions within the test, and check that the action was called once with the plot and an oriented rectangle. Assisted-by: Claude Opus 5.5
1 parent b62c67d commit 12dd62a

2 files changed

Lines changed: 21 additions & 0 deletions

File tree

‎doc/release_notes/release_2.12.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424
⚙️ Dependencies:
2525

2626
* Python 3.10 or later is now required, and wheels are no longer built for Python 3.9. Python 3.9 users must keep an earlier compatible release (PlotPy 2.11.x) (closes [Issue #74](https://github.com/PlotPyStack/PlotPy/issues/74))
27+
28+
🔧 Other changes:
29+
30+
* **Test suite** — The `SnapshotTool` annotation test no longer hangs when run on its own, on Windows or with Qt 6: it waited indefinitely for the snapshot dialog to be closed

‎plotpy/tests/unit/test_annotation_tools.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66
import pytest
77
from guidata.qthelpers import exec_dialog, execenv, qt_app_context
8+
from qtpy import QtWidgets as QW
89

910
from plotpy.interfaces.items import IBasePlotItem, IShapeItemType
1011
from plotpy.tests import vistools as ptv
@@ -97,13 +98,29 @@ def _test_annotation_tools(tool_classes: tuple[type[InteractiveTool], ...]):
9798
for tool_class in tool_classes:
9899
tool = win.manager.get_tool(tool_class)
99100
assert tool is not None
101+
snapshot_calls: list[tuple] | None = None
102+
if execenv.unattended and isinstance(tool, SnapshotTool):
103+
# The snapshot action opens modal dialogs (destination size, options,
104+
# file name) which are never closed in unattended mode
105+
snapshot_calls = []
106+
tool.action_func = lambda *args, calls=snapshot_calls: calls.append(
107+
args
108+
)
100109
tool.activate()
101110
x_path = np.linspace(0, 0.5, 100)
102111
y_path = np.linspace(0, 0.5, 100)
103112
with execenv.context(accept_dialogs=True):
104113
drag_mouse(win, x_path, y_path)
114+
# Run the actions deferred by a zero-delay timer (e.g. the snapshot
115+
# action) within this test rather than in a later event loop
116+
QW.QApplication.processEvents()
105117
if hasattr(tool_class, "SWITCH_TO_DEFAULT_TOOL"):
106118
assert win.manager.get_default_tool() == default_tool
119+
if snapshot_calls is not None:
120+
assert len(snapshot_calls) == 1
121+
snap_plot, p0, p1 = snapshot_calls[0]
122+
assert snap_plot is plot
123+
assert p0.x() < p1.x() and p0.y() < p1.y()
107124
plot.unselect_all()
108125
shape_items: list[IBasePlotItem] = plot.get_items(item_type=IShapeItemType)
109126
plot.select_some_items(shape_items)

0 commit comments

Comments
 (0)