Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/actions/linux/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ runs:
- name: Install RasCAL2
shell: bash -l {0}
run: pip install .
- name:
- name: Pytest
shell: bash -l {0}
run: xvfb-run pytest -s tests/ ${{ inputs.pytest-options }} --cov=rascal2 --cov-report=term
- name: Pytest System Tests
shell: bash -l {0}
run: xvfb-run pytest -s tests/ ${{ inputs.pytest-options }} --cov=rascal2 --cov-report=term
3 changes: 3 additions & 0 deletions .github/actions/windows-mac/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ runs:
- name: Run Pytest
shell: bash -l {0}
run: pytest -s tests/ ${{ inputs.pytest-options }} --cov=rascal2 --cov-report=term
- name: Run Pytest System Tests
shell: bash -l {0}
run: pytest -s tests/ ${{ inputs.pytest-options }} --cov=rascal2 --cov-report=term --run-system-tests
37 changes: 37 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import tempfile
from pathlib import Path
from unittest.mock import patch
Expand Down Expand Up @@ -54,3 +55,39 @@ def teardown_mock_setting():
target.stop()

request.addfinalizer(teardown_mock_setting)


def pytest_addoption(parser):
parser.addoption("--run-system-tests", action="store_true", default=False, help="Run GUI system tests offscreen")
parser.addoption("--run-system-tests-show", action="store_true", default=False, help="Run GUI system tests")
parser.addoption("--run-unit-tests", action="store_true", default=False, help="Run unit tests")


def pytest_configure(config):
config.addinivalue_line("markers", "system: GUI system tests")
config.addinivalue_line("markers", "unit: unit tests")


allowed_markers = []
skipped_tests = []


def pytest_collection_modifyitems(config, items):
if config.getoption("--run-system-tests"):
allowed_markers.append(pytest.mark.system.mark)
os.environ["QT_QPA_PLATFORM"] = "minimal"
if config.getoption("--run-system-tests-show"):
allowed_markers.append(pytest.mark.system.mark)
os.environ["QT_QPA_PLATFORM"] = ""
if config.getoption("--run-unit-tests") or len(allowed_markers) == 0:
allowed_markers.append(pytest.mark.unit.mark)
for item in items:
if "gui_system" in item.nodeid:
item.add_marker(pytest.mark.system)
else:
item.add_marker(pytest.mark.unit)
if any(mark in allowed_markers for mark in item.own_markers):
pass
else:
item.add_marker(pytest.mark.skip(reason="Test not selected"))
skipped_tests.append(item.nodeid)
56 changes: 56 additions & 0 deletions tests/system/gui_system_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os
import sys
import unittest
from collections.abc import Callable

from PyQt6.QtTest import QTest
from PyQt6.QtWidgets import QApplication

from rascal2.ui.view import MainWindowView

SHOW_DELAY = 10 # Can be increased to watch tests
SHORT_DELAY = 100


def wait_until(
test_func: Callable[[], bool], delay=0.1, max_retry=100, message: str = "wait_until reached max retries"
):
"""Repeat test_func every delay seconds until it becomes true. Raises RuntimeError if max_retry is reached."""
for _ in range(max_retry):
if test_func():
return True
QTest.qWait(int(delay * 1000))
raise RuntimeError(message)


class GuiSystemBase(unittest.TestCase):
app: QApplication

def setUp(self) -> None:
self.start_processes_old = os.getenv("START_PROCESSES")
os.environ["START_PROCESSES"] = "False"
self.no_exceptions = True

sys.excepthook = self.exception_hook
self.main_window = MainWindowView()
self.main_window.show()
QTest.qWait(SHORT_DELAY)

def tearDown(self) -> None:
if not self.no_exceptions:
raise Exception("An exception occurred in a PyQt slot")
sys.excepthook = sys.__excepthook__

self.main_window.close()
wait_until(
lambda: not self.main_window.isVisible(),
delay=0.05,
max_retry=60,
message="Main window did not close within 3 seconds",
)
del self.main_window
os.environ["START_PROCESSES"] = self.start_processes_old

def exception_hook(self, exc_type, exc_value, exc_traceback):
self.no_exceptions = False
sys.__excepthook__(exc_type, exc_value, exc_traceback)
21 changes: 21 additions & 0 deletions tests/system/gui_system_loading_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from PyQt6.QtTest import QTest

from rascal2.dialogs.startup_dialog import LoadDialog
from tests.system.gui_system_base import SHORT_DELAY, GuiSystemBase


class TestGuiSystemLoading(GuiSystemBase):
def setUp(self) -> None:
super().setUp()

def tearDown(self) -> None:
super().tearDown()

def test_load(self):
QTest.qWait(SHORT_DELAY)
self.main_window.startup_dlg.import_project_button.click()
load_dialog = self.main_window.findChild(LoadDialog)
load_dialog.tabs.setCurrentIndex(2)
load_dialog.example_list_widget.itemClicked.emit(load_dialog.example_list_widget.item(0))
QTest.qWait(SHORT_DELAY)
assert self.main_window.presenter.model.project.name == "DSPC Standard Layers"
14 changes: 14 additions & 0 deletions tests/system/gui_system_main_window_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from tests.system.gui_system_base import GuiSystemBase


class TestGuiSystemMainWindow(GuiSystemBase):
def setUp(self) -> None:
super().setUp()

def tearDown(self) -> None:
super().tearDown()

def test_main_window(self):
self.main_window.presenter.create_project("project", ".")
names = [win.windowTitle() for win in self.main_window.mdi.subWindowList()]
assert names == ["Fitting Controls", "Terminal", "Project", "Plots"]
Loading