Skip to content

Commit 1cd4378

Browse files
authored
Missing Plotting Windows Tests (#422)
* added test * hijacking PR for version bump: patch
1 parent 051e8a0 commit 1cd4378

File tree

4 files changed

+193
-3
lines changed

4 files changed

+193
-3
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# OMC3 Changelog
22

3+
#### 2023-06-16 - v0.11.1 - _jdilly_
4+
5+
- Fixed:
6+
- OptionalString: 'None' as input is converted to None.
7+
- Missing Kerberos config added to MANIFEST for packaging.
8+
- Plot Optics plots now correct error-column, e.g. for beta-beating.
9+
- Added warnings/errors for too few bpms in N-BPM/3-BPM methods.
10+
- Added navbar to sphinx documentation.
11+
12+
- Tests:
13+
- Added test for the classes in omc3.plotting.utils.windows
14+
315
#### 2023-06-05 - v0.11.0 - _jdilly_
416

517
- Added:

omc3/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
__title__ = "omc3"
1212
__description__ = "An accelerator physics tools package for the OMC team at CERN."
1313
__url__ = "https://github.com/pylhc/omc3"
14-
__version__ = "0.11.0"
14+
__version__ = "0.11.1"
1515
__author__ = "pylhc"
1616
__author_email__ = "[email protected]"
1717
__license__ = "MIT"

omc3/plotting/utils/windows.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
except ImportError as e:
3838
LOG.debug(f"Could not import QtPy: {str(e)}")
3939
QMainWindow, QApplication, QVBoxLayout = None, None, None
40+
FigureCanvas, NavigationToolbar = None, None # for mock in pytest
4041
QWidget, QTabWidget = object, object
4142
else:
4243
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
@@ -79,7 +80,7 @@ def __init__(self, *figures: Figure, title: str):
7980
*figures (Figure): Figures to be contained in the widget
8081
title (str): Name of the widget
8182
"""
82-
QWidget.__init__(self) # no super(), because will not work with tests
83+
super().__init__()
8384

8485
self.title: str = title
8586
self.figures: Tuple[Figure, ...] = figures
@@ -107,7 +108,7 @@ def __init__(self, title: str = None):
107108
Args:
108109
title (str): Title of the created Window
109110
"""
110-
QTabWidget.__init__(self) # no super(), because will not work with tests
111+
super().__init__()
111112

112113
self.title: str = title
113114
self.tabs: Dict[str, QWidget] = {}

tests/unit/test_plot_windows.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import pytest
2+
from matplotlib.figure import Figure
3+
4+
from omc3.plotting.utils.windows import PlotWidget, TabWidget, SimpleTabWindow, VerticalTabWindow
5+
6+
7+
@pytest.mark.basic
8+
def test_plot_widget(monkeypatch):
9+
# Preparation ---
10+
monkeypatch.setattr("omc3.plotting.utils.windows.QVBoxLayout", MockLayout)
11+
monkeypatch.setattr("omc3.plotting.utils.windows.FigureCanvas", MockFigureCanvas)
12+
monkeypatch.setattr("omc3.plotting.utils.windows.NavigationToolbar", MockNavigationToolbar)
13+
14+
class MockPlotWidget(PlotWidget, MockQWidget):
15+
pass
16+
17+
figures = (Figure(), Figure(), Figure())
18+
my_title = "Hello OMC!"
19+
20+
# Execution ---
21+
widget = MockPlotWidget(*figures, title=my_title)
22+
23+
# Assert ---
24+
assert widget.title == my_title
25+
assert isinstance(widget._layout, MockLayout)
26+
assert len(widget._layout.widgets) == len(figures) * 2
27+
for idx, w in enumerate(widget._layout.widgets):
28+
if idx % 2:
29+
assert isinstance(w, MockNavigationToolbar)
30+
else:
31+
assert isinstance(w, MockFigureCanvas)
32+
assert w.figure == figures[idx // 2]
33+
34+
35+
@pytest.mark.basic
36+
def test_tab_widget():
37+
# Preparation ---
38+
class MockTabWidget(TabWidget, MockQTabWidget):
39+
pass
40+
41+
tabs = [MockQWidget("tab1"), MockQWidget("tab2"), MockQWidget("tab3")]
42+
my_title = "Hello OMC!"
43+
44+
# Execution ---
45+
widget = MockTabWidget(title=my_title)
46+
for tab in tabs:
47+
widget.add_tab(tab)
48+
49+
# Assert ---
50+
assert widget.title == my_title
51+
assert len(widget.tabs) == len(tabs)
52+
assert len(widget.added_tabs) == len(tabs)
53+
for idx, (title, tab) in enumerate(widget.added_tabs.items()):
54+
assert tabs[idx] == widget.tabs[title]
55+
assert tabs[idx] == tab
56+
57+
58+
@pytest.mark.basic
59+
@pytest.mark.parametrize('WindowClass', (SimpleTabWindow, VerticalTabWindow))
60+
def test_tab_window(monkeypatch, WindowClass):
61+
# Preparation ---
62+
monkeypatch.setattr("omc3.plotting.utils.windows.QApplication", MockQApplication)
63+
monkeypatch.setattr("omc3.plotting.utils.windows.QMainWindow", MockQMainWindow)
64+
monkeypatch.setattr("omc3.plotting.utils.windows.QTabWidget", MockQTabWidget)
65+
66+
class MockTabWidget(TabWidget, MockQTabWidget):
67+
pass
68+
69+
monkeypatch.setattr("omc3.plotting.utils.windows.TabWidget", MockTabWidget)
70+
71+
72+
tabs = [MockQWidget("tab1"), MockQWidget("tab2"), MockQWidget("tab3")]
73+
my_title = "Hello OMC!"
74+
my_size = (800, 600)
75+
76+
# Execution ---
77+
window = WindowClass(title=my_title, size=my_size)
78+
for tab in tabs:
79+
window.add_tab(tab)
80+
81+
# Assert Main Window ---
82+
assert window.main_window.title == my_title
83+
assert window.main_window.size == my_size
84+
assert window.main_window.shown
85+
86+
# Assert Tab Widget ---
87+
assert isinstance(window.tabs_widget, TabWidget)
88+
assert len(window.tabs_widget.added_tabs) == len(tabs)
89+
for tab, tab_added in zip(tabs, window.tabs_widget.added_tabs.values()):
90+
assert tab == tab_added
91+
assert window.tabs_widget.position == (MockQTabWidget.West if (WindowClass == VerticalTabWindow) else 0)
92+
93+
# Assert App ---
94+
assert not window.app.executed
95+
window.show()
96+
assert window.app.executed
97+
98+
99+
# Mock Classes -----------------------------------------------------------------
100+
101+
class MockQWidget:
102+
103+
def __init__(self, title = None):
104+
self.layout = None
105+
self.title = title
106+
107+
def setLayout(self, layout):
108+
self.layout = layout
109+
110+
111+
class MockQTabWidget:
112+
West = 1
113+
Center = 2
114+
East = 3
115+
116+
def __init__(self):
117+
self.added_tabs = {}
118+
self.position = 0
119+
120+
def addTab(self, tab, tab_title):
121+
self.added_tabs[tab_title] = tab
122+
123+
def setTabPosition(self, position):
124+
self.position = position
125+
126+
127+
class MockLayout:
128+
129+
def __init__(self) -> None:
130+
self.widgets = []
131+
132+
133+
def addWidget(self, widget):
134+
self.widgets.append(widget)
135+
136+
137+
class MockFigureCanvas:
138+
139+
def __init__(self, figure):
140+
self.figure = figure
141+
142+
143+
class MockNavigationToolbar():
144+
145+
def __init__(self, canvas, parent=None):
146+
self.canvas = canvas
147+
self.parent = parent
148+
149+
150+
class MockQApplication:
151+
152+
def __init__(self, *args, **kwargs):
153+
self.executed = False
154+
155+
def exec(self):
156+
self.executed = True
157+
158+
159+
class MockQMainWindow:
160+
161+
def __init__(self):
162+
self.title = None
163+
self.central_widget = None
164+
self.size = None
165+
self.shown = False
166+
167+
def setWindowTitle(self, title):
168+
self.title = title
169+
170+
def setCentralWidget(self, widget):
171+
self.central_widget = widget
172+
173+
def resize(self, width, height):
174+
self.size = (width, height)
175+
176+
def show(self):
177+
self.shown = True

0 commit comments

Comments
 (0)