Skip to content

Commit 1e2e7a8

Browse files
committed
Tests: added command line options to "PythonQwt-tests-py3" script
--mode unattended Run all tests in unattended mode --mode screenshots Run all tests to update screenshots
1 parent c71d998 commit 1e2e7a8

22 files changed

+220
-155
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ from qwt import tests
5555
tests.run()
5656
```
5757

58-
or from the command line:
58+
or from the command line (script name depends on Python major version number):
5959

6060
```bash
61-
PythonQwt-tests
61+
PythonQwt-py3
62+
```
63+
64+
Tests may also be executed in unattended mode:
65+
66+
```bash
67+
PythonQwt-tests-py3 --mode unattended
6268
```
6369

6470
## Overview

doc/examples/index.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,26 @@ Examples
66
The test launcher
77
-----------------
88

9-
A lot of examples are available in the `qwt.test` module ::
9+
A lot of examples are available in the ``qwt.test`` module ::
1010

1111
from qwt import tests
1212
tests.run()
1313

14-
The two lines above execute the `PythonQwt` test launcher:
14+
The two lines above execute the ``PythonQwt`` test launcher:
1515

1616
.. image:: /../qwt/tests/data/testlauncher.png
1717

18+
GUI-based test launcher can be executed from the command line thanks to the
19+
``PythonQwt-py3`` test script (or ``PythonQwt-py2`` for Python 2).
20+
21+
Unit tests may be executed from the commande line thanks to the console-based script
22+
``PythonQwt-tests-py3``: ``PythonQwt-tests-py3 --mode unattended``.
1823

1924
Tests
2025
-----
2126

27+
28+
2229
Here are some examples from the `qwt.test` module:
2330

2431
.. toctree::

qwt/tests/__init__.py

Lines changed: 127 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,31 @@
1616
import sys
1717
import subprocess
1818
import platform
19-
from qtpy.QtWidgets import (
20-
QApplication,
21-
QWidget,
22-
QMainWindow,
23-
QVBoxLayout,
24-
QFormLayout,
25-
QCheckBox,
26-
QGroupBox,
27-
QGridLayout,
28-
QToolButton,
29-
QStyle,
30-
QToolBar,
31-
QAction,
32-
QMessageBox,
33-
)
34-
from qtpy.QtGui import QIcon, QPixmap
35-
from qtpy.QtCore import Qt, QSize, QTimer
36-
from qtpy import PYQT5
19+
import argparse
20+
import inspect
21+
22+
from qtpy import QtWidgets as QW
23+
from qtpy import QtGui as QG
24+
from qtpy import QtCore as QC
25+
from qtpy import PYQT5, PYSIDE2
26+
3727
from qwt import QwtPlot
3828

29+
if PYSIDE2:
30+
import PySide2
3931

40-
TEST_PATH = osp.abspath(osp.dirname(__file__))
32+
PYTHON_QT_API = "PySide2 v" + PySide2.__version__
33+
elif PYQT5:
34+
from PyQt5.QtCore import PYQT_VERSION_STR
4135

36+
PYTHON_QT_API = "PyQt5 v" + PYQT_VERSION_STR
37+
else:
38+
from PyQt4.QtCore import PYQT_VERSION_STR
4239

43-
def run_test(fname, wait=False):
44-
"""Run test"""
45-
os.environ["PYTHONPATH"] = os.pathsep.join(sys.path)
46-
args = " ".join([sys.executable, '"' + fname + '"'])
47-
if os.environ.get("TEST_UNATTENDED") is not None:
48-
print(args)
49-
if wait:
50-
subprocess.call(args, shell=True)
51-
else:
52-
subprocess.Popen(args, shell=True)
40+
PYTHON_QT_API = "PyQt4 v" + PYQT_VERSION_STR
41+
42+
43+
TEST_PATH = osp.abspath(osp.dirname(__file__))
5344

5445

5546
def get_tests(package):
@@ -74,15 +65,24 @@ def get_tests(package):
7465
return tests
7566

7667

77-
def run_all_tests(wait):
68+
def run_test(fname, wait=True):
69+
"""Run test"""
70+
os.environ["PYTHONPATH"] = os.pathsep.join(sys.path)
71+
args = " ".join([sys.executable, '"' + fname + '"'])
72+
if TestEnvironment().unattended:
73+
print(" " + args)
74+
(subprocess.call if wait else subprocess.Popen)(args, shell=True)
75+
76+
77+
def run_all_tests(wait=True):
7878
"""Run all PythonQwt tests"""
7979
import qwt
8080

8181
for fname in get_tests(qwt):
8282
run_test(fname, wait=wait)
8383

8484

85-
class TestLauncher(QMainWindow):
85+
class TestLauncher(QW.QMainWindow):
8686
"""PythonQwt Test Launcher main window"""
8787

8888
ROWS = 5
@@ -94,8 +94,8 @@ def __init__(self, parent=None):
9494
self.setObjectName("TestLauncher")
9595
self.setWindowIcon(self.get_std_icon("FileDialogListView"))
9696
self.setWindowTitle("PythonQwt %s - Test Launcher" % __version__)
97-
self.setCentralWidget(QWidget())
98-
self.grid_layout = QGridLayout()
97+
self.setCentralWidget(QW.QWidget())
98+
self.grid_layout = QW.QGridLayout()
9999
self.centralWidget().setLayout(self.grid_layout)
100100
self.test_nb = None
101101
self.fill_layout()
@@ -104,31 +104,31 @@ def __init__(self, parent=None):
104104

105105
def get_std_icon(self, name):
106106
"""Return Qt standard icon"""
107-
return self.style().standardIcon(getattr(QStyle, "SP_" + name))
107+
return self.style().standardIcon(getattr(QW.QStyle, "SP_" + name))
108108

109109
def fill_layout(self):
110110
"""Fill grid layout"""
111111
import qwt
112112

113113
for fname in get_tests(qwt):
114114
self.add_test(fname)
115-
toolbar = QToolBar(self)
116-
all_act = QAction(self.get_std_icon("DialogYesButton"), "", self)
115+
toolbar = QW.QToolBar(self)
116+
all_act = QW.QAction(self.get_std_icon("DialogYesButton"), "", self)
117117
all_act.setIconText("Run all tests")
118118
all_act.triggered.connect(lambda checked: run_all_tests(wait=False))
119-
folder_act = QAction(self.get_std_icon("DirOpenIcon"), "", self)
119+
folder_act = QW.QAction(self.get_std_icon("DirOpenIcon"), "", self)
120120
folder_act.setIconText("Open tests folder")
121121
open_test_folder = lambda checked: os.startfile(TEST_PATH)
122122
folder_act.triggered.connect(open_test_folder)
123-
about_act = QAction(self.get_std_icon("FileDialogInfoView"), "", self)
123+
about_act = QW.QAction(self.get_std_icon("FileDialogInfoView"), "", self)
124124
about_act.setIconText("About")
125125
about_act.triggered.connect(self.about)
126126
for action in (all_act, folder_act, None, about_act):
127127
if action is None:
128128
toolbar.addSeparator()
129129
else:
130130
toolbar.addAction(action)
131-
toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
131+
toolbar.setToolButtonStyle(QC.Qt.ToolButtonTextBesideIcon)
132132
self.addToolBar(toolbar)
133133

134134
def add_test(self, fname):
@@ -139,78 +139,65 @@ def add_test(self, fname):
139139
row = (self.test_nb - 1) % self.ROWS
140140
column = (self.test_nb - 1) // self.ROWS
141141
bname = osp.basename(fname)
142-
button = QToolButton(self)
143-
button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
142+
button = QW.QToolButton(self)
143+
button.setToolButtonStyle(QC.Qt.ToolButtonTextUnderIcon)
144144
shot = osp.join(TEST_PATH, "data", bname.replace(".py", ".png"))
145145
if osp.isfile(shot):
146-
button.setIcon(QIcon(shot))
146+
button.setIcon(QG.QIcon(shot))
147147
else:
148148
button.setIcon(self.get_std_icon("DialogYesButton"))
149149
button.setText(bname)
150150
button.setToolTip(fname)
151-
button.setIconSize(QSize(130, 80))
151+
button.setIconSize(QC.QSize(130, 80))
152152
button.clicked.connect(lambda checked=None, fname=fname: run_test(fname))
153153
self.grid_layout.addWidget(button, row, column)
154154

155155
def about(self):
156156
"""About test launcher"""
157157
from qtpy.QtCore import __version__ as qt_version
158158

159-
QMessageBox.about(
159+
QW.QMessageBox.about(
160160
self,
161161
"About " + self.windowTitle(),
162162
"""<b>%s</b><p>Developped by Pierre Raybaut
163163
<br>Copyright &copy; 2020 Pierre Raybaut
164-
<p>Python %s, Qt %s on %s"""
164+
<p>Python %s, Qt %s, %s on %s"""
165165
% (
166166
self.windowTitle(),
167167
platform.python_version(),
168168
qt_version,
169+
PYTHON_QT_API,
169170
platform.system(),
170171
),
171172
)
172173

173174

174-
def run(wait=True):
175-
"""Run PythonQwt tests or test launcher (requires `guidata`)"""
176-
app = QApplication([])
177-
launcher = TestLauncher()
178-
launcher.show()
179-
unattended = os.environ.get("TEST_UNATTENDED") is not None
180-
if unattended:
181-
QTimer.singleShot(100, lambda: take_screenshot(launcher))
182-
app.exec_()
183-
launcher.close()
184-
if unattended:
185-
run_all_tests(wait=wait)
186-
187-
188-
class TestOptions(QGroupBox):
175+
class TestOptions(QW.QGroupBox):
189176
"""Test options groupbox"""
190177

191178
def __init__(self, parent=None):
192179
super(TestOptions, self).__init__("Test options", parent)
193-
self.setLayout(QFormLayout())
180+
self.setLayout(QW.QFormLayout())
194181
self.hide()
195182

196183
def add_checkbox(self, title, label, slot):
197184
"""Add new checkbox to option panel"""
198-
widget = QCheckBox(label, self)
185+
widget = QW.QCheckBox(label, self)
199186
widget.stateChanged.connect(slot)
200187
self.layout().addRow(title, widget)
201188
self.show()
202189
return widget
203190

204191

205-
class TestCentralWidget(QWidget):
192+
class TestCentralWidget(QW.QWidget):
206193
"""Test central widget"""
207194

208195
def __init__(self, widget_name, parent=None):
209196
super(TestCentralWidget, self).__init__(parent)
210197
self.widget_name = widget_name
211198
self.plots = None
212199
self.widget_of_interest = self.parent()
213-
self.setLayout(QVBoxLayout())
200+
self.setLayout(QW.QVBoxLayout())
214201
self.options = TestOptions(self)
215202
self.add_widget(self.options)
216203

@@ -238,24 +225,29 @@ def take_screenshot(widget):
238225
if PYQT5:
239226
pixmap = widget.grab()
240227
else:
241-
pixmap = QPixmap.grabWidget(widget)
228+
pixmap = QG.QPixmap.grabWidget(widget)
242229
bname = (widget.objectName().lower() + ".png").replace("window", "")
243230
bname = bname.replace("plot", "").replace("widget", "")
244231
pixmap.save(osp.join(TEST_PATH, "data", bname))
245-
QTimer.singleShot(0, QApplication.instance().quit)
232+
QC.QTimer.singleShot(0, QW.QApplication.instance().quit)
246233

247234

248-
def test_widget(widget_class, size=None, title=None, options=True, timeout=1000):
235+
def test_widget(widget_class, size=None, title=None, options=True):
249236
"""Test widget"""
250237
widget_name = widget_class.__name__
251-
app = QApplication([])
252-
window = widget = widget_class()
238+
app = QW.QApplication([])
239+
test_env = TestEnvironment()
240+
if inspect.signature(widget_class).parameters.get("unattended") is None:
241+
widget = widget_class()
242+
else:
243+
widget = widget_class(unattended=test_env.unattended)
244+
window = widget
253245
if options:
254-
if isinstance(widget, QMainWindow):
246+
if isinstance(widget, QW.QMainWindow):
255247
widget = window.centralWidget()
256248
widget.setParent(None)
257249
else:
258-
window = QMainWindow()
250+
window = QW.QMainWindow()
259251
central_widget = TestCentralWidget(widget_name, parent=window)
260252
central_widget.add_widget(widget)
261253
window.setCentralWidget(central_widget)
@@ -273,11 +265,73 @@ def test_widget(widget_class, size=None, title=None, options=True, timeout=1000)
273265
window.resize(width, height)
274266

275267
window.show()
276-
if os.environ.get("TEST_UNATTENDED") is not None:
277-
QTimer.singleShot(timeout, lambda: take_screenshot(widget_of_interest))
268+
if test_env.screenshots:
269+
QC.QTimer.singleShot(1000, lambda: take_screenshot(widget_of_interest))
270+
elif test_env.unattended:
271+
QC.QTimer.singleShot(0, QW.QApplication.instance().quit)
278272
app.exec_()
279273
return app
280274

281275

276+
class TestEnvironment(object):
277+
UNATTENDED_ARG = "unattended"
278+
SCREENSHOTS_ARG = "screenshots"
279+
UNATTENDED_ENV = "PYTHONQWT_UNATTENDED_TESTS"
280+
SCREENSHOTS_ENV = "PYTHONQWT_TAKE_SCREENSHOTS"
281+
282+
def __init__(self):
283+
self.parse_args()
284+
285+
@property
286+
def unattended(self):
287+
return os.environ.get(self.UNATTENDED_ENV) is not None
288+
289+
@property
290+
def screenshots(self):
291+
return os.environ.get(self.SCREENSHOTS_ENV) is not None
292+
293+
def parse_args(self):
294+
"""Parse command line arguments"""
295+
parser = argparse.ArgumentParser(description="Run PythonQwt tests")
296+
parser.add_argument(
297+
"--mode",
298+
choices=[self.UNATTENDED_ARG, self.SCREENSHOTS_ARG],
299+
required=False,
300+
)
301+
args = parser.parse_args()
302+
if args.mode is not None:
303+
self.set_env_from_args(args)
304+
305+
def set_env_from_args(self, args):
306+
"""Set appropriate environment variables"""
307+
for name in (self.UNATTENDED_ENV, self.SCREENSHOTS_ENV):
308+
if name in os.environ:
309+
os.environ.pop(name)
310+
if args.mode == self.UNATTENDED_ARG:
311+
os.environ[self.UNATTENDED_ENV] = "1"
312+
if args.mode == self.SCREENSHOTS_ARG:
313+
os.environ[self.SCREENSHOTS_ENV] = os.environ[self.UNATTENDED_ENV] = "1"
314+
315+
316+
def run(wait=True):
317+
"""Run PythonQwt tests or test launcher"""
318+
app = QW.QApplication([])
319+
launcher = TestLauncher()
320+
launcher.show()
321+
test_env = TestEnvironment()
322+
if test_env.screenshots:
323+
print("Running PythonQwt tests and taking screenshots automatically:")
324+
print("-------------------------------------------------------------")
325+
QC.QTimer.singleShot(100, lambda: take_screenshot(launcher))
326+
elif test_env.unattended:
327+
print("Running PythonQwt tests in unattended mode:")
328+
print("-------------------------------------------")
329+
QC.QTimer.singleShot(0, QW.QApplication.instance().quit)
330+
app.exec_()
331+
launcher.close()
332+
if test_env.unattended:
333+
run_all_tests(wait=wait)
334+
335+
282336
if __name__ == "__main__":
283337
run()

0 commit comments

Comments
 (0)