diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 140b0af..3afb410 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,6 +32,13 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] qt-lib: [pyqt5, pyqt6, pyside6] os: [ubuntu-latest, windows-latest, macos-latest] + include: + - python-version: "3.9" + qt-lib: pyqt61 + os: ubuntu-latest + - python-version: "3.9" + qt-lib: pyside60 + os: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/src/pytestqt/qt_compat.py b/src/pytestqt/qt_compat.py index 3b9aef7..e66f369 100644 --- a/src/pytestqt/qt_compat.py +++ b/src/pytestqt/qt_compat.py @@ -24,7 +24,7 @@ def _import(name): - """Think call so we can mock it during testing""" + """Thin call so we can mock it during testing""" return __import__(name) @@ -111,7 +111,14 @@ def _import_module(module_name): self._check_qt_api_version() - self.qInfo = QtCore.qInfo + # qInfo is not exposed in PySide6 < 6.8.2 (#232) + if hasattr(QtCore, "qInfo"): + self.qInfo = QtCore.qInfo + elif hasattr(QtCore, "QMessageLogger"): + self.qInfo = lambda msg: QtCore.QMessageLogger().info(msg) + else: + self.qInfo = None + self.qDebug = QtCore.qDebug self.qWarning = QtCore.qWarning self.qCritical = QtCore.qCritical diff --git a/tests/test_basics.py b/tests/test_basics.py index 6b577f5..49cde4b 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -309,20 +309,13 @@ def test_events(events_queue, fix, i): res.stdout.fnmatch_lines(["*3 passed in*"]) -def test_header(testdir): - testdir.makeconftest( - """ - from pytestqt import qt_compat - from pytestqt.qt_compat import qt_api - - def mock_get_versions(): - return qt_compat.VersionTuple('PyQtAPI', '1.0', '2.5', '3.5') - - assert hasattr(qt_api, 'get_versions') - qt_api.get_versions = mock_get_versions - """ +def test_header(testdir, monkeypatch): + monkeypatch.setattr( + qt_api, + "get_versions", + lambda: qt_compat.VersionTuple("PyQtAPI", "1.0", "2.5", "3.5"), ) - res = testdir.runpytest() + res = testdir.runpytest_inprocess() res.stdout.fnmatch_lines( ["*test session starts*", "PyQtAPI 1.0 -- Qt runtime 2.5 -- Qt compiled 3.5"] ) @@ -613,7 +606,6 @@ class Mock: qtcore = Mock() for method_name in ( "qInstallMessageHandler", - "qInfo", "qDebug", "qWarning", "qCritical", diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 8627f9b..9347b9d 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -5,11 +5,18 @@ from pytestqt.exceptions import capture_exceptions, format_captured_exceptions from pytestqt.qt_compat import qt_api + +def has_pyside6_exception_capture(): + return qt_api.pytest_qt_api == "pyside6" and tuple( + int(part) for part in qt_api.get_versions().qt_api_version.split(".") + ) >= (6, 5, 2) + + # PySide6 is automatically captures exceptions during the event loop, # and re-raises them when control gets back to Python, so the related # functionality does not work, nor is needed for the end user. exception_capture_pyside6 = pytest.mark.skipif( - qt_api.pytest_qt_api == "pyside6", + has_pyside6_exception_capture(), reason="pytest-qt capture not working/needed on PySide6", ) @@ -51,7 +58,7 @@ def test_exceptions(qtbot): ) result = testdir.runpytest() if raise_error: - if qt_api.pytest_qt_api == "pyside6": + if has_pyside6_exception_capture(): # PySide6 automatically captures exceptions during the event loop, # and re-raises them when control gets back to Python. # This results in the exception not being captured by diff --git a/tests/test_logging.py b/tests/test_logging.py index d5ad134..f1fafbd 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -5,6 +5,10 @@ from pytestqt.qt_compat import qt_api +# qInfo is not exposed by PySide6 < 6.8.2 (#225) +HAS_QINFO = qt_api.qInfo is not None + + @pytest.mark.parametrize("test_succeeds", [True, False]) @pytest.mark.parametrize("qt_log", [True, False]) def test_basic_logging(testdir, test_succeeds, qt_log): @@ -14,7 +18,7 @@ def test_basic_logging(testdir, test_succeeds, qt_log): :type testdir: _pytest.pytester.TmpTestdir """ testdir.makepyfile( - """ + f""" import sys from pytestqt.qt_compat import qt_api @@ -26,14 +30,13 @@ def print_msg(msg_type, context, message): qt_api.QtCore.qInstallMessageHandler(print_msg) def test_types(): - qt_api.qInfo('this is an INFO message') + if {HAS_QINFO}: + qt_api.qInfo('this is an INFO message') qt_api.qDebug('this is a DEBUG message') qt_api.qWarning('this is a WARNING message') qt_api.qCritical('this is a CRITICAL message') - assert {} - """.format( - test_succeeds - ) + assert {test_succeeds} + """ ) res = testdir.runpytest(*(["--no-qt-log"] if not qt_log else [])) if test_succeeds: @@ -44,7 +47,7 @@ def test_types(): res.stdout.fnmatch_lines( [ "*-- Captured Qt messages --*", - "*QtInfoMsg: this is an INFO message*", + *(["*QtInfoMsg: this is an INFO message*"] if HAS_QINFO else []), "*QtDebugMsg: this is a DEBUG message*", "*QtWarningMsg: this is a WARNING message*", "*QtCriticalMsg: this is a CRITICAL message*", @@ -54,7 +57,7 @@ def test_types(): res.stdout.fnmatch_lines( [ "*-- Captured stderr call --*", - "this is an INFO message*", + *(["this is an INFO message*"] if HAS_QINFO else []), "this is a DEBUG message*", "this is a WARNING message*", "this is a CRITICAL message*", @@ -66,17 +69,23 @@ def test_qtlog_fixture(qtlog): """ Test qtlog fixture. """ - qt_api.qInfo("this is an INFO message") + expected = [] + if HAS_QINFO: + qt_api.qInfo("this is an INFO message") + expected.append((qt_api.QtCore.QtMsgType.QtInfoMsg, "this is an INFO message")) + qt_api.qDebug("this is a DEBUG message") qt_api.qWarning("this is a WARNING message") qt_api.qCritical("this is a CRITICAL message") - records = [(m.type, m.message.strip()) for m in qtlog.records] - assert records == [ - (qt_api.QtCore.QtMsgType.QtInfoMsg, "this is an INFO message"), + + expected += [ (qt_api.QtCore.QtMsgType.QtDebugMsg, "this is a DEBUG message"), (qt_api.QtCore.QtMsgType.QtWarningMsg, "this is a WARNING message"), (qt_api.QtCore.QtMsgType.QtCriticalMsg, "this is a CRITICAL message"), ] + + records = [(m.type, m.message.strip()) for m in qtlog.records] + assert records == expected # `records` attribute is read-only with pytest.raises(AttributeError): qtlog.records = [] diff --git a/tox.ini b/tox.ini index 09abcb7..6f97c96 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,15 @@ [tox] -envlist = py{39,310,311,312,313}-{pyqt5,pyside6,pyqt6} +envlist = py{39,310,311,312,313}-{pyqt5,pyside6,pyqt6},py39-pyside60,py39-pyqt61 [testenv] deps= pytest pyside6: pyside6 + pyside60: pyside6<6.1 pyqt5: pyqt5 pyqt6: pyqt6 + pyqt61: pyqt6<6.2 + pyqt61: pyqt6-sip<13.6 commands= pytest --color=yes {posargs} setenv=