Skip to content

Commit

Permalink
Fail tests if there are any widgets left open
Browse files Browse the repository at this point in the history
Qt widgets that are not cleaned up after a test can affect the following
tests. This change makes it so that tests that do not clean up after
themselves will error.
  • Loading branch information
matrss committed Mar 21, 2024
1 parent b420380 commit a4ad334
Show file tree
Hide file tree
Showing 19 changed files with 60 additions and 44 deletions.
2 changes: 1 addition & 1 deletion tests/_test_msui/test_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Test_Editor:
save_file_name = fs.path.join(ROOT_DIR, "testeditor_save.json")

@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
with mock.patch("PyQt5.QtWidgets.QMessageBox.warning", return_value=QtWidgets.QMessageBox.Yes):
self.window = editor.EditorMainWindow()
self.save_file_name = self.save_file_name
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_kmloverlay_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
class Test_KmlOverlayDockWidget:

@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.view = mock.Mock()
self.view.map = mock.Mock(side_effect=lambda x, y: (x, y))
self.view.map.plot = mock.Mock(return_value=[mock.Mock()])
Expand Down
6 changes: 3 additions & 3 deletions tests/_test_msui/test_linearview.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

class Test_MSS_LV_Options_Dialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = tv.MSUI_LV_Options_Dialog(settings=_DEFAULT_SETTINGS_LINEARVIEW)
self.window.show()
QtTest.QTest.qWaitForWindowExposed(self.window)
Expand All @@ -54,7 +54,7 @@ def test_get(self):

class Test_MSSLinearViewWindow:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
initial_waypoints = [ft.Waypoint(40., 25., 300), ft.Waypoint(60., -10., 400), ft.Waypoint(40., 10, 300)]

waypoints_model = ft.WaypointsTableModel("")
Expand Down Expand Up @@ -86,7 +86,7 @@ def test_options(self, mockdlg):

class Test_LinearViewWMS:
@pytest.fixture(autouse=True)
def setup(self, qapp, mswms_server):
def setup(self, qtbot, mswms_server):
self.url = mswms_server
self.tempdir = tempfile.mkdtemp()
if not os.path.exists(self.tempdir):
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_mpl_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

class Test_MapCanvas:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
kwargs = {'resolution': 'l', 'area_thresh': 1000.0, 'ax': plt.gca(), 'llcrnrlon': -15.0, 'llcrnrlat': 35.0,
'urcrnrlon': 30.0, 'urcrnrlat': 65.0, 'epsg': '4326'}
self.map = MapCanvas(**kwargs)
Expand Down
4 changes: 2 additions & 2 deletions tests/_test_msui/test_mscolab.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

class Test_Mscolab_connect_window:
@pytest.fixture(autouse=True)
def setup(self, qapp, mscolab_server):
def setup(self, qtbot, mscolab_server):
self.url = mscolab_server
self.userdata = 'UV10@uv10', 'UV10', 'uv10'
self.operation_name = "europe"
Expand Down Expand Up @@ -258,7 +258,7 @@ class Test_Mscolab:
}

@pytest.fixture(autouse=True)
def setup(self, qapp, mscolab_app, mscolab_server):
def setup(self, qtbot, mscolab_app, mscolab_server):
self.app = mscolab_app
self.url = mscolab_server
self.userdata = 'UV10@uv10', 'UV10', 'uv10'
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_mscolab_merge_waypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

class Test_Mscolab_Merge_Waypoints:
@pytest.fixture(autouse=True)
def setup(self, qapp, mscolab_app, mscolab_server):
def setup(self, qtbot, mscolab_app, mscolab_server):
self.app = mscolab_app
self.url = mscolab_server
self.window = msui.MSUIMainWindow(mscolab_data_dir=mscolab_settings.MSCOLAB_DATA_DIR)
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_mss.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from mslib.msui import mss


def test_mss_rename_message(qapp):
def test_mss_rename_message(qtbot):
main_window = mss.MSSMainWindow()
main_window.show()
QtTest.QTest.mouseClick(main_window.pushButton, QtCore.Qt.LeftButton)
8 changes: 4 additions & 4 deletions tests/_test_msui/test_msui.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_main():

class Test_MSS_TutorialMode:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qapp, qtbot):
qapp.setApplicationDisplayName("MSUI")
self.main_window = msui_mw.MSUIMainWindow(tutorial_mode=True)
self.main_window.create_new_flight_track()
Expand Down Expand Up @@ -95,7 +95,7 @@ def test_tutorial_dir(self):

class Test_MSS_AboutDialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = msui_mw.MSUI_AboutDialog()
yield
self.window.hide()
Expand All @@ -109,7 +109,7 @@ def test_milestone_url(self):

class Test_MSS_ShortcutDialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.main_window = msui_mw.MSUIMainWindow()
self.main_window.show()
self.shortcuts = msui_mw.MSUI_ShortcutsDialog()
Expand Down Expand Up @@ -169,7 +169,7 @@ class Test_MSSSideViewWindow:
}

@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.sample_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'../',
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_multiple_flightpath_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

class Test_MultipleFlightpathControlWidget:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = msui.MSUIMainWindow()
self.window.create_new_flight_track()

Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_remotesensing.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Test_RemoteSensingControlWidget:
Tests about RemoteSensingControlWidget
"""
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.view = Mock()
self.map = qt.TopViewPlotter()
self.map.init_map()
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_satellite_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

class Test_SatelliteDockWidget:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.view = mock.Mock()
self.window = sd.SatelliteControlWidget(view=self.view)
self.window.show()
Expand Down
6 changes: 3 additions & 3 deletions tests/_test_msui/test_sideview.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

class Test_MSS_SV_OptionsDialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = tv.MSUI_SV_OptionsDialog(settings=_DEFAULT_SETTINGS_SIDEVIEW)
self.window.show()
QtTest.QTest.qWaitForWindowExposed(self.window)
Expand Down Expand Up @@ -73,7 +73,7 @@ def test_setColour(self, mockdlg):

class Test_MSSSideViewWindow:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
initial_waypoints = [ft.Waypoint(40., 25., 300), ft.Waypoint(60., -10., 400), ft.Waypoint(40., 10, 300)]

waypoints_model = ft.WaypointsTableModel("")
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_y_axes(self):

class Test_SideViewWMS:
@pytest.fixture(autouse=True)
def setup(self, qapp, mswms_server):
def setup(self, qtbot, mswms_server):
self.url = mswms_server
self.tempdir = tempfile.mkdtemp()
if not os.path.exists(self.tempdir):
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_suffix.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

class Test_SuffixChange:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = tv.MSUI_SV_OptionsDialog(settings=_DEFAULT_SETTINGS_SIDEVIEW)
self.window.show()
QtTest.QTest.qWaitForWindowExposed(self.window)
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_tableview.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

class Test_TableView:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
# Create an initital flight track.
initial_waypoints = [ft.Waypoint(flightlevel=0, location="EDMO", comments="take off OP"),
ft.Waypoint(48.10, 10.27, 200),
Expand Down
12 changes: 4 additions & 8 deletions tests/_test_msui/test_topview.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

class Test_MSS_TV_MapAppearanceDialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.window = tv.MSUI_TV_MapAppearanceDialog(settings=_DEFAULT_SETTINGS_TOPVIEW)
self.window.show()
QtTest.QTest.qWaitForWindowExposed(self.window)
Expand All @@ -55,7 +55,7 @@ def test_get(self):

class Test_MSSTopViewWindow:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
mainwindow = MSUIMainWindow()
initial_waypoints = [ft.Waypoint(40., 25., 0), ft.Waypoint(60., -10., 0), ft.Waypoint(40., 10, 0)]
waypoints_model = ft.WaypointsTableModel("")
Expand Down Expand Up @@ -199,7 +199,7 @@ def test_map_options(self):

class Test_TopViewWMS:
@pytest.fixture(autouse=True)
def setup(self, qapp, mswms_server):
def setup(self, qtbot, mswms_server):
self.url = mswms_server
self.tempdir = tempfile.mkdtemp()
if not os.path.exists(self.tempdir):
Expand Down Expand Up @@ -241,11 +241,7 @@ def test_server_getmap(self, qtbot):


class Test_MSUITopViewWindow:
@pytest.fixture(autouse=True)
def setup(self, qapp):
pass

def test_kwargs_update_does_not_harm(self):
def test_kwargs_update_does_not_harm(self, qtbot):
initial_waypoints = [ft.Waypoint(40., 25., 0), ft.Waypoint(60., -10., 0), ft.Waypoint(40., 10, 0)]
waypoints_model = ft.WaypointsTableModel("")
waypoints_model.insertRows(0, rows=len(initial_waypoints), waypoints=initial_waypoints)
Expand Down
4 changes: 2 additions & 2 deletions tests/_test_msui/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(self, args=None, **named_args):
@mock.patch("mslib.utils.qt.Worker.start", Worker.run)
class Test_MSS_ShortcutDialog:
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.updater = Updater()
self.status = ""
self.update_available = False
Expand Down Expand Up @@ -128,7 +128,7 @@ def test_exception(self):
@mock.patch("subprocess.Popen", new=SubprocessSameMock)
@mock.patch("subprocess.run", new=SubprocessSameMock)
@mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Yes)
def test_ui(self, mock):
def test_ui(self, mock, qtbot):
ui = UpdaterUI()
ui.updater.on_update_available.emit("", "")
assert ui.statusLabel.text() == "Update successful. Please restart MSS."
Expand Down
2 changes: 1 addition & 1 deletion tests/_test_msui/test_wms_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
class Test_WMSCapabilities:

@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.capabilities = mock.Mock()
self.capabilities.capabilities_document = u"Hölla die Waldfee".encode("utf-8")
self.capabilities.provider = mock.Mock()
Expand Down
6 changes: 3 additions & 3 deletions tests/_test_msui/test_wms_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def query_server(self, qtbot, url):

class Test_HSecWMSControlWidget(WMSControlWidgetSetup):
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self._setup("hsec")
yield
self._teardown()
Expand Down Expand Up @@ -409,7 +409,7 @@ def test_server_no_thread(self, mockthread, qtbot):

class Test_VSecWMSControlWidget(WMSControlWidgetSetup):
@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self._setup("vsec")
yield
self._teardown()
Expand Down Expand Up @@ -495,7 +495,7 @@ class TestWMSControlWidgetSetupSimple:
<Extent name="ELEVATION" default="900.0"> 500.0,600.0,700.0,900.0 </Extent>"""

@pytest.fixture(autouse=True)
def setup(self, qapp):
def setup(self, qtbot):
self.view = HSecViewMockup()
self.window = wc.HSecWMSControlWidget(view=self.view)
self.window.show()
Expand Down
36 changes: 28 additions & 8 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import eventlet
import eventlet.wsgi

from PyQt5 import QtWidgets
from contextlib import contextmanager
from mslib.mscolab.conf import mscolab_settings
from mslib.mscolab.server import APP, initialize_managers
Expand All @@ -41,8 +40,8 @@
from tests.utils import is_url_response_ok


@pytest.fixture
def fail_if_open_message_boxes_left():
@contextmanager
def _fail_if_open_message_boxes_left():
# Mock every MessageBox widget in the test suite to avoid unwanted freezes on unhandled error popups etc.
with mock.patch("PyQt5.QtWidgets.QMessageBox.question") as q, \
mock.patch("PyQt5.QtWidgets.QMessageBox.information") as i, \
Expand All @@ -57,11 +56,22 @@ def fail_if_open_message_boxes_left():
pytest.fail(f"An unhandled message box popped up during your test!\n{summary}")


@pytest.fixture
def close_remaining_widgets():
@contextmanager
def _fail_if_any_widgets_left_open(qapp, qtbot):
yield

def assert_no_widgets_left_open():
widgets = set(qapp.topLevelWindows() + qapp.topLevelWidgets())
assert len(widgets) == 0, f"There are Qt widgets left open at the end of the test!\n{widgets=}"

qtbot.wait_until(assert_no_widgets_left_open)


@contextmanager
def _close_remaining_widgets(qapp):
yield
# Try to close all remaining widgets after each test
for qobject in set(QtWidgets.QApplication.topLevelWindows() + QtWidgets.QApplication.topLevelWidgets()):
for qobject in set(qapp.topLevelWindows() + qapp.topLevelWidgets()):
try:
qobject.destroy()
# Some objects deny permission, pass in that case
Expand All @@ -70,8 +80,18 @@ def close_remaining_widgets():


@pytest.fixture
def qapp(qapp, fail_if_open_message_boxes_left, close_remaining_widgets):
yield qapp
def qtbot(qapp, qtbot):
"""Override of the qtbot fixture from pytest-qt.
This fixture makes sure that every test using it will fail if there are either any
message boxes left open at the end or if any Qt widgets were not properly closed.
Afterwards it will destroy all remaining top-level widgets to make sure that they do
not affect the following tests.
"""
with _close_remaining_widgets(qapp), \
_fail_if_any_widgets_left_open(qapp, qtbot), \
_fail_if_open_message_boxes_left():
yield qtbot


@pytest.fixture(scope="session")
Expand Down

0 comments on commit a4ad334

Please sign in to comment.