piker/tests/ui/test_harness.py

127 lines
3.9 KiB
Python

from pathlib import Path
from types import SimpleNamespace
from PyQt6.QtCore import QSettings
from PyQt6.QtWidgets import (
QApplication,
QWidget,
)
import pyqtgraph as pg
import pytest
from pytestqt.qtbot import QtBot
from piker import config
@pytest.fixture(scope='module')
def module_qapp(
qapp: QApplication,
) -> QApplication:
'''
Pin pytest-qt's session application for this test module.
'''
return qapp
def test_ui_state_is_process_isolated_and_restored(
isolated_ui_state: SimpleNamespace,
module_qapp: QApplication,
pytestconfig: pytest.Config,
qtbot: QtBot,
) -> None:
'''
Prove imports and one UI test cannot reach user config state.
Previously `qapp_args` assigned `XDG_CONFIG_HOME` only after
Piker was imported, so `piker.config` cached real user paths
before Qt setup. This test verifies every cached path starts
below the process-owned temporary XDG root, then redirects those
globals and writes both a file and `QSettings` key. The fixture's
exact-state teardown proves those mutations disappear while its
process sentinels survive for the next test.
'''
state: SimpleNamespace = isolated_ui_state
assert module_qapp is state.qapp
assert QApplication.instance() is state.qapp
if pytestconfig.getoption('headless'):
assert state.qapp.platformName() == 'offscreen'
assert config._click_config_dir.is_relative_to(
state.process_config_home,
)
assert config._config_dir.is_relative_to(
state.process_config_home,
)
assert config._watchlists_data_path.is_relative_to(
state.process_config_home,
)
widget: QWidget = QWidget()
qtbot.addWidget(widget)
widget.show()
owned_dir: Path = state.test_config_dir
config._click_config_dir = owned_dir
config._config_dir = owned_dir
config._watchlists_data_path = owned_dir / 'watchlists.json'
(owned_dir / 'owned').write_text('remove-me', encoding='utf-8')
settings: QSettings = QSettings('pikers', 'piker')
settings.setFallbacksEnabled(False)
settings_path: Path = Path(settings.fileName())
assert settings_path.is_relative_to(state.process_config_home)
assert settings.value(state.preexisting_setting) == 'keep-me'
settings.setValue(state.owned_setting, 'remove-me')
settings.sync()
def test_ui_state_reuses_qapp_without_previous_test_leaks(
isolated_ui_state: SimpleNamespace,
module_qapp: QApplication,
qtbot: QtBot,
) -> None:
'''
Prove repeated tests reuse Qt without inheriting mutable state.
A session `QApplication` can retain widgets, settings and
PyQtGraph's `ViewBox.AllViews` entries from an earlier test. This
test runs after the mutation case, verifies the same application
and preserved process sentinel but no test-owned key, then
creates a real named `PlotWidget` and mutates Qt/PyQtGraph
options. Normal pytest-qt widget closure plus the isolation
fixture's registry assertions prove the second test also leaves
no state behind.
'''
state: SimpleNamespace = isolated_ui_state
assert module_qapp is state.qapp
assert QApplication.instance() is state.qapp
settings: QSettings = QSettings('pikers', 'piker')
settings.setFallbacksEnabled(False)
assert settings.value(state.preexisting_setting) == 'keep-me'
assert not settings.contains(state.owned_setting)
assert state.preexisting_config.read_text(
encoding='utf-8',
) == 'keep-me'
assert list(state.test_config_dir.iterdir()) == []
plot: pg.PlotWidget = pg.PlotWidget(name='pytest-owned-view')
qtbot.addWidget(plot)
plot.show()
assert pg.ViewBox.NamedViews['pytest-owned-view'] is (
plot.plotItem.vb
)
pg.setConfigOption(
'antialias',
not pg.getConfigOption('antialias'),
)
state.qapp.setQuitOnLastWindowClosed(
not state.qapp.quitOnLastWindowClosed(),
)