Skip to content

Commit 797ffcf

Browse files
committed
Merge remote-tracking branch 'Codra/develop_improve-test-coverage' into develop
2 parents f84224e + d9483a4 commit 797ffcf

17 files changed

+1188
-245
lines changed

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"python.testing.unittestEnabled": false,
2323
"python.testing.pytestEnabled": true,
2424
"python.testing.pytestPath": "pytest",
25-
"python.testing.pytestArgs": [],
25+
"python.testing.pytestArgs": [
26+
"plotpy"
27+
],
2628
"[python]": {
2729
"editor.defaultFormatter": "charliermarsh.ruff"
2830
},

plotpy/items/shape/range.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ def set_range(self, _min: float, _max: float, dosignal: bool = True) -> None:
246246
if dosignal:
247247
self.plot().SIG_RANGE_CHANGED.emit(self, self._min, self._max)
248248

249-
def move_shape(self, old_pos: QC.QPointF, new_pos: QC.QPointF) -> None:
249+
def move_shape(
250+
self, old_pos: tuple[float, float], new_pos: tuple[float, float]
251+
) -> None:
250252
"""Translate the shape such that old_pos becomes new_pos in axis coordinates
251253
252254
Args:

plotpy/plot/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,9 @@ def disable_unused_axes(self):
11071107
self.enableAxis(self.colormap_axis)
11081108

11091109
def get_items(
1110-
self, z_sorted: bool = False, item_type: itf.IBasePlotItem | None = None
1110+
self,
1111+
z_sorted: bool = False,
1112+
item_type: type[itf.IItemType | itf.IBasePlotItem] | None = None,
11111113
) -> list[itf.IBasePlotItem]:
11121114
"""Return widget's item list
11131115
(items are based on IBasePlotItem's interface)

plotpy/tests/features/test_colormap_manager.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,58 @@
1111

1212
# guitest: show
1313

14+
import pytest
1415
import qtpy.QtCore as QC
1516
import qtpy.QtGui as QG
1617
from guidata.env import execenv
1718
from guidata.qthelpers import exec_dialog, qt_app_context
1819

19-
from plotpy.mathutils.colormap import ALL_COLORMAPS
20+
from plotpy.mathutils.colormap import ALL_COLORMAPS, delete_cmap, get_cmap
2021
from plotpy.widgets.colormap.manager import ColorMapManager
2122
from plotpy.widgets.colormap.widget import EditableColormap
2223

2324

24-
def test_colormap_manager() -> None:
25+
@pytest.fixture
26+
def test_cmap(cmap_name="Kinda Viridis"):
27+
cmap = EditableColormap(
28+
[QG.QColor(QC.Qt.GlobalColor.blue), QG.QColor(QC.Qt.GlobalColor.yellow)],
29+
name=cmap_name,
30+
)
31+
yield cmap
32+
33+
delete_cmap(get_cmap(cmap_name))
34+
35+
36+
def test_colormap_manager(test_cmap: EditableColormap) -> None:
2537
"""Test the colormap manager widget."""
26-
with qt_app_context():
38+
with qt_app_context(exec_loop=False):
2739
red = QG.QColor(QC.Qt.GlobalColor.red)
28-
blue = QG.QColor(QC.Qt.GlobalColor.blue)
29-
yellow = QG.QColor(QC.Qt.GlobalColor.yellow)
30-
cmap = EditableColormap(blue, yellow, name="kinda_viridis")
31-
ALL_COLORMAPS["kinda_viridis"] = cmap
32-
dlg = ColorMapManager(None, active_colormap="YlGn")
33-
dlg.colormap_editor.colormap_widget.add_handle_at_relative_pos(0.5, red)
34-
dlg.get_colormap()
35-
dlg.colormap_editor.update_colormap_widget()
36-
dlg.colormap_editor.update_current_dataset()
37-
result = exec_dialog(dlg)
40+
cmap_editor = ColorMapManager(None, active_colormap="YlGn")
41+
cmap_editor.show()
42+
43+
with execenv.context(accept_dialogs=True):
44+
cmap_editor.save_colormap(test_cmap)
45+
cmap_editor.colormap_editor.colormap_widget.add_handle_at_relative_pos(0.5, red)
46+
cmap_editor.get_colormap()
47+
cmap_editor.colormap_editor.update_colormap_widget()
48+
cmap_editor.colormap_editor.update_current_dataset()
49+
50+
# set the colormap to last one
51+
with execenv.context(accept_dialogs=True):
52+
cmap_editor.remove_colormap()
53+
54+
result = exec_dialog(cmap_editor)
3855
execenv.print("Dialog result:", result)
39-
cmap = dlg.get_colormap()
56+
cmap = cmap_editor.get_colormap()
4057
execenv.print("Selected colormap:", None if cmap is None else cmap.name)
58+
delete_cmap(test_cmap)
4159

4260

4361
if __name__ == "__main__":
44-
test_colormap_manager()
62+
test_name = "Kinda Viridis"
63+
new_colormap = EditableColormap(
64+
[QG.QColor(QC.Qt.GlobalColor.blue), QG.QColor(QC.Qt.GlobalColor.yellow)],
65+
name=test_name,
66+
)
67+
test_colormap_manager(new_colormap)
68+
delete_cmap(get_cmap(test_name))

plotpy/tests/items/test_curves.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from plotpy.builder import make
1414
from plotpy.tests import vistools as ptv
15+
from plotpy.tools import CurveStatsTool
1516

1617

1718
def test_plot():
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import numpy as np
6+
import pytest
7+
from guidata.qthelpers import exec_dialog, execenv, qt_app_context
8+
9+
from plotpy.interfaces.items import IBasePlotItem, IShapeItemType
10+
from plotpy.tests import vistools as ptv
11+
from plotpy.tests.features.test_auto_curve_image import make_curve_image_legend
12+
from plotpy.tools import (
13+
AnnotatedCircleTool,
14+
AnnotatedEllipseTool,
15+
AnnotatedObliqueRectangleTool,
16+
AnnotatedPointTool,
17+
AnnotatedRectangleTool,
18+
AnnotatedSegmentTool,
19+
AverageCrossSectionTool,
20+
CircleTool,
21+
CrossSectionTool,
22+
EllipseTool,
23+
ImageStatsTool,
24+
InteractiveTool,
25+
LabelTool,
26+
ObliqueRectangleTool,
27+
PointTool,
28+
RectangleTool,
29+
SegmentTool,
30+
SelectTool,
31+
SnapshotTool,
32+
)
33+
34+
if TYPE_CHECKING:
35+
from plotpy.items.image.base import BaseImageItem
36+
from plotpy.plot.plotwidget import PlotWindow
37+
38+
from plotpy.tests.unit.utils import drag_mouse, undo_redo
39+
40+
TOOLS: tuple[type[InteractiveTool], ...] = (
41+
AnnotatedCircleTool,
42+
AnnotatedEllipseTool,
43+
AnnotatedObliqueRectangleTool,
44+
AnnotatedPointTool,
45+
AnnotatedRectangleTool,
46+
AnnotatedSegmentTool,
47+
AverageCrossSectionTool,
48+
CrossSectionTool,
49+
SnapshotTool,
50+
ImageStatsTool,
51+
CircleTool,
52+
EllipseTool,
53+
ObliqueRectangleTool,
54+
PointTool,
55+
RectangleTool,
56+
SegmentTool,
57+
LabelTool,
58+
)
59+
60+
61+
def create_window(tool_classes: tuple[type[InteractiveTool], ...]) -> PlotWindow:
62+
"""Create a window with the given tools. The plot contains a curve, an image and a
63+
legend.
64+
65+
Args:
66+
tool_classes: tools to add to the window.
67+
68+
Returns:
69+
PlotWindow: The window containing the tools.
70+
"""
71+
items = make_curve_image_legend()
72+
img: BaseImageItem = items[0]
73+
img.set_rotatable(True)
74+
img.set_movable(True)
75+
win = ptv.show_items(
76+
items, wintitle="Unit tests for RectangularActionTools", auto_tools=True
77+
)
78+
79+
for toolklass in tool_classes:
80+
_ = win.manager.add_tool(toolklass)
81+
82+
assert len(tool_classes) > 0
83+
84+
return win
85+
86+
87+
def _test_annotation_tools(tool_classes: tuple[type[InteractiveTool], ...]):
88+
"""Generic test for annotation tool. Simulates a mouse drag on the plot and checks
89+
that the tool is activated and deactivated correctly."""
90+
with qt_app_context(exec_loop=False) as qapp:
91+
win = create_window(tool_classes)
92+
win.show()
93+
plot = win.manager.get_plot()
94+
default_tool = win.manager.get_default_tool()
95+
for tool_class in tool_classes:
96+
tool = win.manager.get_tool(tool_class)
97+
assert tool is not None
98+
tool.activate()
99+
x_path = np.linspace(0, 0.5, 100)
100+
y_path = np.linspace(0, 0.5, 100)
101+
with execenv.context(accept_dialogs=True):
102+
drag_mouse(win, qapp, x_path, y_path)
103+
if hasattr(tool_class, "SWITCH_TO_DEFAULT_TOOL"):
104+
assert win.manager.get_default_tool() == default_tool
105+
plot.unselect_all()
106+
shape_items: list[IBasePlotItem] = plot.get_items(item_type=IShapeItemType)
107+
plot.select_some_items(shape_items)
108+
select_tool = win.manager.get_tool(SelectTool)
109+
assert select_tool is not None
110+
select_tool.activate()
111+
112+
drag_mouse(win, qapp, np.linspace(0.2, 0.5, 10), np.linspace(0.2, 0.5, 10))
113+
114+
undo_redo(qapp, win)
115+
116+
exec_dialog(win)
117+
118+
119+
@pytest.mark.parametrize("tool", TOOLS)
120+
def test_tool(tool: type[InteractiveTool]) -> None:
121+
"""Test a single tool."""
122+
_test_annotation_tools((tool,))
123+
124+
125+
def _test_all_tools():
126+
"""Test all tools."""
127+
_test_annotation_tools(TOOLS)
128+
129+
130+
if __name__ == "__main__":
131+
_test_all_tools()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import numpy as np
6+
import pytest
7+
from guidata.qthelpers import exec_dialog, qt_app_context
8+
9+
from plotpy.tests.unit.utils import (
10+
create_window,
11+
drag_mouse,
12+
)
13+
from plotpy.tools import (
14+
HCursorTool,
15+
HRangeTool,
16+
VCursorTool,
17+
XCursorTool,
18+
)
19+
20+
if TYPE_CHECKING:
21+
from plotpy.tools.cursor import BaseCursorTool
22+
23+
24+
@pytest.mark.parametrize(
25+
"cursor_tool", [HCursorTool, VCursorTool, XCursorTool, HRangeTool]
26+
)
27+
def test_cursor_tool(cursor_tool: type[BaseCursorTool]):
28+
"""Test the cursor tools. by simulating a mouse drag and checking if the tool
29+
shape is created.
30+
31+
Args:
32+
cursor_tool: Cursor tool class to test.
33+
"""
34+
with qt_app_context(exec_loop=True) as qapp:
35+
win, tool = create_window(cursor_tool)
36+
win.show()
37+
plot = win.manager.get_plot()
38+
39+
active_tool = win.manager.get_active_tool()
40+
assert isinstance(active_tool, cursor_tool)
41+
tool_shape_type = type(active_tool.create_shape())
42+
assert tool_shape_type not in (type(item) for item in plot.get_items())
43+
44+
drag_mouse(win, qapp, np.array([0.5, 0.6, 0.7]), np.array([0.5, 0.6, 0.7]))
45+
assert tool_shape_type in (type(item) for item in plot.get_items())
46+
47+
exec_dialog(win)
48+
49+
50+
if __name__ == "__main__":
51+
for tool in (HCursorTool, VCursorTool, XCursorTool, HRangeTool):
52+
test_cursor_tool(tool)

0 commit comments

Comments
 (0)