diff options
author | Lembrun <amadeusk7@free.fr> | 2021-03-11 19:11:06 +0100 |
---|---|---|
committer | Lembrun <amadeusk7@free.fr> | 2021-03-11 19:11:06 +0100 |
commit | 3707234c3ae95c9ee7cc0721b668a5e96a9cfa6f (patch) | |
tree | 60c027d9fa59c1c24537fcd4284458e0c1751006 /tests | |
parent | ef5ba1a0360b39f9eff027fbdc57f363597c3c3b (diff) | |
parent | 40054ac944a988e02ca8f3de414581f1e77688d8 (diff) | |
download | qutebrowser-3707234c3ae95c9ee7cc0721b668a5e96a9cfa6f.tar.gz qutebrowser-3707234c3ae95c9ee7cc0721b668a5e96a9cfa6f.zip |
Merge branch 'master' into pathlib-/unit/commands
Diffstat (limited to 'tests')
-rw-r--r-- | tests/end2end/test_invocations.py | 2 | ||||
-rw-r--r-- | tests/unit/browser/test_pdfjs.py | 2 | ||||
-rw-r--r-- | tests/unit/browser/test_qutescheme.py | 13 | ||||
-rw-r--r-- | tests/unit/config/test_qtargs.py | 16 | ||||
-rw-r--r-- | tests/unit/config/test_qtargs_locale_workaround.py | 11 | ||||
-rw-r--r-- | tests/unit/utils/test_jinja.py | 6 | ||||
-rw-r--r-- | tests/unit/utils/test_resources.py | 146 | ||||
-rw-r--r-- | tests/unit/utils/test_utils.py | 222 | ||||
-rw-r--r-- | tests/unit/utils/test_version.py | 76 |
9 files changed, 341 insertions, 153 deletions
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index f3d74d1f0..38e40f9b7 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -657,7 +657,7 @@ def test_dark_mode(webengine_versions, quteproc_new, request, quteproc_new.start(args) ver = webengine_versions.webengine - minor_version = f'{ver.majorVersion()}.{ver.minorVersion()}' + minor_version = str(ver.strip_patch()) expected = colors.get(minor_version, colors[None]) quteproc_new.open_path(f'data/darkmode/{filename}.html') diff --git a/tests/unit/browser/test_pdfjs.py b/tests/unit/browser/test_pdfjs.py index 788209d6f..86b875be5 100644 --- a/tests/unit/browser/test_pdfjs.py +++ b/tests/unit/browser/test_pdfjs.py @@ -77,7 +77,7 @@ class TestResources: @pytest.fixture def read_file_mock(self, mocker): - return mocker.patch.object(pdfjs.utils, 'read_file_binary', autospec=True) + return mocker.patch.object(pdfjs.resources, 'read_file_binary', autospec=True) def test_get_pdfjs_res_system(self, read_system_mock): read_system_mock.return_value = (b'content', 'path') diff --git a/tests/unit/browser/test_qutescheme.py b/tests/unit/browser/test_qutescheme.py index 213df4e0c..2ae939596 100644 --- a/tests/unit/browser/test_qutescheme.py +++ b/tests/unit/browser/test_qutescheme.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import QUrl, QUrlQuery import pytest from qutebrowser.browser import qutescheme, pdfjs, downloads -from qutebrowser.utils import utils +from qutebrowser.utils import resources class TestJavascriptHandler: @@ -43,15 +43,15 @@ class TestJavascriptHandler: @pytest.fixture(autouse=True) def patch_read_file(self, monkeypatch): - """Patch utils.read_file to return few fake JS files.""" + """Patch resources.read_file to return few fake JS files.""" def _read_file(path): - """Faked utils.read_file.""" + """Faked resources.read_file.""" for filename, content in self.js_files: if path == os.path.join('javascript', filename): return content raise OSError("File not found {}!".format(path)) - monkeypatch.setattr(utils, 'read_file', _read_file) + monkeypatch.setattr(resources, 'read_file', _read_file) @pytest.mark.parametrize("filename, content", js_files) def test_qutejavascript(self, filename, content): @@ -165,8 +165,9 @@ class TestHelpHandler: assert path == name return data - monkeypatch.setattr(qutescheme.utils, 'read_file', _read_file) - monkeypatch.setattr(qutescheme.utils, 'read_file_binary', _read_file_binary) + monkeypatch.setattr(qutescheme.resources, 'read_file', _read_file) + monkeypatch.setattr(qutescheme.resources, + 'read_file_binary', _read_file_binary) return _patch def test_unknown_file_type(self, data_patcher): diff --git a/tests/unit/config/test_qtargs.py b/tests/unit/config/test_qtargs.py index e7dbd5d95..695649213 100644 --- a/tests/unit/config/test_qtargs.py +++ b/tests/unit/config/test_qtargs.py @@ -530,6 +530,22 @@ class TestWebEngineArgs: for arg in expected: assert arg in args + @pytest.mark.linux + def test_locale_workaround(self, config_stub, monkeypatch, version_patcher, + parser): + class FakeLocale: + + def bcp47Name(self): + return 'de-CH' + + monkeypatch.setattr(qtargs.objects, 'backend', usertypes.Backend.QtWebEngine) + monkeypatch.setattr(qtargs, 'QLocale', FakeLocale) + version_patcher('5.15.3') + config_stub.val.qt.workarounds.locale = True + parsed = parser.parse_args([]) + args = qtargs.qt_args(parsed) + assert '--lang=de' in args + class TestEnvVars: diff --git a/tests/unit/config/test_qtargs_locale_workaround.py b/tests/unit/config/test_qtargs_locale_workaround.py index 977118198..7e313377b 100644 --- a/tests/unit/config/test_qtargs_locale_workaround.py +++ b/tests/unit/config/test_qtargs_locale_workaround.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <https://www.gnu.org/licenses/>. +import os import pathlib import pytest @@ -403,6 +404,7 @@ def qtwe_version(): ("zh_XX.UTF-8", "zh-CN"), # locale not available on my system ("zu_ZA.UTF-8", "en-US"), ]) +@pytest.mark.linux def test_lang_workaround_all_locales(lang, expected, qtwe_version): locale_name = QLocale(lang).bcp47Name() print(locale_name) @@ -444,3 +446,12 @@ def test_non_linux(qtwe_version): def test_disabled(qtwe_version, config_stub): config_stub.val.qt.workarounds.locale = False assert qtargs._get_lang_override(qtwe_version, "de-CH") is None + + +@pytest.mark.fake_os('linux') +def test_no_locales_available(qtwe_version, monkeypatch, caplog): + monkeypatch.setattr(qtargs.QLibraryInfo, 'location', lambda _path: '/doesnotexist') + assert qtargs._get_lang_override(qtwe_version, "de-CH") is None + assert caplog.messages == [ + f"{os.sep}doesnotexist{os.sep}qtwebengine_locales not found, skipping " + "workaround!"] diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 5555560bf..0ef03725c 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -33,7 +33,7 @@ from qutebrowser.config import configexc @pytest.fixture(autouse=True) def patch_read_file(monkeypatch): - """pytest fixture to patch utils.read_file.""" + """pytest fixture to patch resources.read_file.""" def _read_file(path): """A read_file which returns a simple template if the path is right.""" if path == os.path.join('html', 'test.html'): @@ -55,8 +55,8 @@ def patch_read_file(monkeypatch): else: raise OSError("Invalid path {}!".format(path)) - monkeypatch.setattr(jinja.utils, 'read_file', _read_file) - monkeypatch.setattr(jinja.utils, 'read_file_binary', _read_file_binary) + monkeypatch.setattr(jinja.resources, 'read_file', _read_file) + monkeypatch.setattr(jinja.resources, 'read_file_binary', _read_file_binary) def test_simple_template(): diff --git a/tests/unit/utils/test_resources.py b/tests/unit/utils/test_resources.py new file mode 100644 index 000000000..d8af64cb9 --- /dev/null +++ b/tests/unit/utils/test_resources.py @@ -0,0 +1,146 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>. + +"""Tests for qutebrowser.utils.resources.""" + +import sys +import os.path +import zipfile +import pytest +import qutebrowser +from qutebrowser.utils import utils, resources + + +@pytest.fixture(params=[True, False]) +def freezer(request, monkeypatch): + if request.param and not getattr(sys, 'frozen', False): + monkeypatch.setattr(sys, 'frozen', True, raising=False) + monkeypatch.setattr(sys, 'executable', qutebrowser.__file__) + elif not request.param and getattr(sys, 'frozen', False): + # Want to test unfrozen tests, but we are frozen + pytest.skip("Can't run with sys.frozen = True!") + + +@pytest.mark.usefixtures('freezer') +class TestReadFile: + + @pytest.fixture + def package_path(self, tmp_path): + return tmp_path / 'qutebrowser' + + @pytest.fixture + def html_path(self, package_path): + path = package_path / 'html' + path.mkdir(parents=True) + + for filename in ['test1.html', 'test2.html', 'README', 'unrelatedhtml']: + (path / filename).touch() + + subdir = path / 'subdir' + subdir.mkdir() + (subdir / 'subdir-file.html').touch() + + return path + + @pytest.fixture + def html_zip(self, tmp_path, html_path): + if not hasattr(zipfile, 'Path'): + pytest.skip("Needs zipfile.Path") + + zip_path = tmp_path / 'qutebrowser.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + for path in html_path.rglob('*'): + zf.write(path, path.relative_to(tmp_path)) + + assert sorted(zf.namelist()) == [ + 'qutebrowser/html/README', + 'qutebrowser/html/subdir/', + 'qutebrowser/html/subdir/subdir-file.html', + 'qutebrowser/html/test1.html', + 'qutebrowser/html/test2.html', + 'qutebrowser/html/unrelatedhtml', + ] + + yield zipfile.Path(zip_path) / 'qutebrowser' + + @pytest.fixture(params=['pathlib', 'zipfile']) + def resource_root(self, request): + """Resource files packaged either directly or via a zip.""" + if request.param == 'pathlib': + request.getfixturevalue('html_path') + return request.getfixturevalue('package_path') + elif request.param == 'zipfile': + return request.getfixturevalue('html_zip') + raise utils.Unreachable(request.param) + + def test_glob_resources(self, resource_root): + files = sorted(resources._glob(resource_root, 'html', '.html')) + assert files == ['html/test1.html', 'html/test2.html'] + + def test_glob_resources_subdir(self, resource_root): + files = sorted(resources._glob(resource_root, 'html/subdir', '.html')) + assert files == ['html/subdir/subdir-file.html'] + + def test_readfile(self): + """Read a test file.""" + content = resources.read_file(os.path.join('utils', 'testfile')) + assert content.splitlines()[0] == "Hello World!" + + @pytest.mark.parametrize('filename', ['javascript/scroll.js', + 'html/error.html']) + def test_read_cached_file(self, mocker, filename): + resources.preload() + m = mocker.patch('qutebrowser.utils.resources.importlib_resources.files') + resources.read_file(filename) + m.assert_not_called() + + def test_readfile_binary(self): + """Read a test file in binary mode.""" + content = resources.read_file_binary(os.path.join('utils', 'testfile')) + assert content.splitlines()[0] == b"Hello World!" + + @pytest.mark.parametrize('name', ['read_file', 'read_file_binary']) + @pytest.mark.parametrize('fake_exception', [KeyError, FileNotFoundError, None]) + def test_not_found(self, name, fake_exception, monkeypatch): + """Test behavior when a resources file wasn't found. + + With fake_exception, we emulate the rather odd error handling of certain Python + versions: https://bugs.python.org/issue43063 + """ + class BrokenFileFake: + + def __init__(self, exc): + self.exc = exc + + def read_bytes(self): + raise self.exc("File does not exist") + + def read_text(self, encoding): + raise self.exc("File does not exist") + + def __truediv__(self, _other): + return self + + if fake_exception is not None: + monkeypatch.setattr(resources.importlib_resources, 'files', + lambda _pkg: BrokenFileFake(fake_exception)) + + meth = getattr(resources, name) + with pytest.raises(FileNotFoundError): + meth('doesnotexist') diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index 4cf60943c..b43638cb3 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -28,7 +28,7 @@ import functools import re import shlex import math -import zipfile +import operator from PyQt5.QtCore import QUrl, QRect from PyQt5.QtGui import QClipboard @@ -40,22 +40,117 @@ import yaml import qutebrowser import qutebrowser.utils # for test_qualname from qutebrowser.utils import utils, version, usertypes +from qutebrowser.utils.utils import VersionNumber class TestVersionNumber: - @pytest.mark.parametrize('args, expected', [ - ([5, 15, 2], 'VersionNumber(5, 15, 2)'), - ([5, 15], 'VersionNumber(5, 15)'), - ([5], 'VersionNumber(5)'), + @pytest.mark.parametrize('num, expected', [ + (VersionNumber(5, 15, 2), 'VersionNumber(5, 15, 2)'), + (VersionNumber(5, 15), 'VersionNumber(5, 15)'), + (VersionNumber(5), 'VersionNumber(5)'), ]) - def test_repr(self, args, expected): - num = utils.VersionNumber(*args) + def test_repr(self, num, expected): assert repr(num) == expected + @pytest.mark.parametrize('num, expected', [ + (VersionNumber(5, 15, 2), '5.15.2'), + (VersionNumber(5, 15), '5.15'), + (VersionNumber(5), '5'), + ]) + def test_str(self, num, expected): + assert str(num) == expected + def test_not_normalized(self): with pytest.raises(ValueError, match='Refusing to construct'): - utils.VersionNumber(5, 15, 0) + VersionNumber(5, 15, 0) + + @pytest.mark.parametrize('num, expected', [ + (VersionNumber(5, 15, 2), VersionNumber(5, 15)), + (VersionNumber(5, 15), VersionNumber(5, 15)), + (VersionNumber(6), VersionNumber(6)), + ]) + def test_strip_patch(self, num, expected): + assert num.strip_patch() == expected + + @pytest.mark.parametrize('s, expected', [ + ('1x6.2', VersionNumber(1)), + ('6', VersionNumber(6)), + ('5.15', VersionNumber(5, 15)), + ('5.15.3', VersionNumber(5, 15, 3)), + ('5.15.3.dev1234', VersionNumber(5, 15, 3)), + ]) + def test_parse_valid(self, s, expected): + assert VersionNumber.parse(s) == expected + + @pytest.mark.parametrize('s, message', [ + ('foo6', "Failed to parse foo6"), + ('.6', "Failed to parse .6"), + ('0x6.2', "Can't construct a null version"), + ]) + def test_parse_invalid(self, s, message): + with pytest.raises(ValueError, match=message): + VersionNumber.parse(s) + + @pytest.mark.parametrize('lhs, op, rhs, outcome', [ + # == + (VersionNumber(6), operator.eq, VersionNumber(6), True), + (VersionNumber(6), operator.eq, object(), False), + + # != + (VersionNumber(6), operator.ne, VersionNumber(5), True), + (VersionNumber(6), operator.ne, object(), True), + + # >= + (VersionNumber(5, 14), operator.ge, VersionNumber(5, 13, 5), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(5, 14, 2), False), + (VersionNumber(5, 14, 3), operator.ge, VersionNumber(5, 14, 2), True), + (VersionNumber(5, 14, 3), operator.ge, VersionNumber(5, 14, 3), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(5, 13), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(5, 14), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(5, 15), False), + (VersionNumber(5, 14), operator.ge, VersionNumber(4), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(5), True), + (VersionNumber(5, 14), operator.ge, VersionNumber(6), False), + + # > + (VersionNumber(5, 14), operator.gt, VersionNumber(5, 13, 5), True), + (VersionNumber(5, 14), operator.gt, VersionNumber(5, 14, 2), False), + (VersionNumber(5, 14, 3), operator.gt, VersionNumber(5, 14, 2), True), + (VersionNumber(5, 14, 3), operator.gt, VersionNumber(5, 14, 3), False), + (VersionNumber(5, 14), operator.gt, VersionNumber(5, 13), True), + (VersionNumber(5, 14), operator.gt, VersionNumber(5, 14), False), + (VersionNumber(5, 14), operator.gt, VersionNumber(5, 15), False), + (VersionNumber(5, 14), operator.gt, VersionNumber(4), True), + (VersionNumber(5, 14), operator.gt, VersionNumber(5), True), + (VersionNumber(5, 14), operator.gt, VersionNumber(6), False), + + # <= + (VersionNumber(5, 14), operator.le, VersionNumber(5, 13, 5), False), + (VersionNumber(5, 14), operator.le, VersionNumber(5, 14, 2), True), + (VersionNumber(5, 14, 3), operator.le, VersionNumber(5, 14, 2), False), + (VersionNumber(5, 14, 3), operator.le, VersionNumber(5, 14, 3), True), + (VersionNumber(5, 14), operator.le, VersionNumber(5, 13), False), + (VersionNumber(5, 14), operator.le, VersionNumber(5, 14), True), + (VersionNumber(5, 14), operator.le, VersionNumber(5, 15), True), + (VersionNumber(5, 14), operator.le, VersionNumber(4), False), + (VersionNumber(5, 14), operator.le, VersionNumber(5), False), + (VersionNumber(5, 14), operator.le, VersionNumber(6), True), + + # < + (VersionNumber(5, 14), operator.lt, VersionNumber(5, 13, 5), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(5, 14, 2), True), + (VersionNumber(5, 14, 3), operator.lt, VersionNumber(5, 14, 2), False), + (VersionNumber(5, 14, 3), operator.lt, VersionNumber(5, 14, 3), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(5, 13), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(5, 14), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(5, 15), True), + (VersionNumber(5, 14), operator.lt, VersionNumber(4), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(5), False), + (VersionNumber(5, 14), operator.lt, VersionNumber(6), True), + ]) + def test_comparisons(self, lhs, op, rhs, outcome): + assert op(lhs, rhs) == outcome ELLIPSIS = '\u2026' @@ -132,115 +227,6 @@ def freezer(request, monkeypatch): pytest.skip("Can't run with sys.frozen = True!") -@pytest.mark.usefixtures('freezer') -class TestReadFile: - - @pytest.fixture - def package_path(self, tmp_path): - return tmp_path / 'qutebrowser' - - @pytest.fixture - def html_path(self, package_path): - path = package_path / 'html' - path.mkdir(parents=True) - - for filename in ['test1.html', 'test2.html', 'README', 'unrelatedhtml']: - (path / filename).touch() - - subdir = path / 'subdir' - subdir.mkdir() - (subdir / 'subdir-file.html').touch() - - return path - - @pytest.fixture - def html_zip(self, tmp_path, html_path): - if not hasattr(zipfile, 'Path'): - pytest.skip("Needs zipfile.Path") - - zip_path = tmp_path / 'qutebrowser.zip' - with zipfile.ZipFile(zip_path, 'w') as zf: - for path in html_path.rglob('*'): - zf.write(path, path.relative_to(tmp_path)) - - assert sorted(zf.namelist()) == [ - 'qutebrowser/html/README', - 'qutebrowser/html/subdir/', - 'qutebrowser/html/subdir/subdir-file.html', - 'qutebrowser/html/test1.html', - 'qutebrowser/html/test2.html', - 'qutebrowser/html/unrelatedhtml', - ] - - yield zipfile.Path(zip_path) / 'qutebrowser' - - @pytest.fixture(params=['pathlib', 'zipfile']) - def resource_root(self, request): - """Resource files packaged either directly or via a zip.""" - if request.param == 'pathlib': - request.getfixturevalue('html_path') - return request.getfixturevalue('package_path') - elif request.param == 'zipfile': - return request.getfixturevalue('html_zip') - raise utils.Unreachable(request.param) - - def test_glob_resources(self, resource_root): - files = sorted(utils._glob_resources(resource_root, 'html', '.html')) - assert files == ['html/test1.html', 'html/test2.html'] - - def test_glob_resources_subdir(self, resource_root): - files = sorted(utils._glob_resources(resource_root, 'html/subdir', '.html')) - assert files == ['html/subdir/subdir-file.html'] - - def test_readfile(self): - """Read a test file.""" - content = utils.read_file(os.path.join('utils', 'testfile')) - assert content.splitlines()[0] == "Hello World!" - - @pytest.mark.parametrize('filename', ['javascript/scroll.js', - 'html/error.html']) - def test_read_cached_file(self, mocker, filename): - utils.preload_resources() - m = mocker.patch('qutebrowser.utils.utils.importlib_resources.files') - utils.read_file(filename) - m.assert_not_called() - - def test_readfile_binary(self): - """Read a test file in binary mode.""" - content = utils.read_file_binary(os.path.join('utils', 'testfile')) - assert content.splitlines()[0] == b"Hello World!" - - @pytest.mark.parametrize('name', ['read_file', 'read_file_binary']) - @pytest.mark.parametrize('fake_exception', [KeyError, FileNotFoundError, None]) - def test_not_found(self, name, fake_exception, monkeypatch): - """Test behavior when a resources file wasn't found. - - With fake_exception, we emulate the rather odd error handling of certain Python - versions: https://bugs.python.org/issue43063 - """ - class BrokenFileFake: - - def __init__(self, exc): - self.exc = exc - - def read_bytes(self): - raise self.exc("File does not exist") - - def read_text(self, encoding): - raise self.exc("File does not exist") - - def __truediv__(self, _other): - return self - - if fake_exception is not None: - monkeypatch.setattr(utils.importlib_resources, 'files', - lambda _pkg: BrokenFileFake(fake_exception)) - - meth = getattr(utils, name) - with pytest.raises(FileNotFoundError): - meth('doesnotexist') - - @pytest.mark.parametrize('seconds, out', [ (-1, '-0:01'), (0, '0:00'), @@ -784,7 +770,7 @@ class TestOpenFile: info = version.DistributionInfo( id='org.kde.Platform', parsed=version.Distribution.kde_flatpak, - version=utils.parse_version('5.12'), + version=VersionNumber.parse('5.12'), pretty='Unknown') monkeypatch.setattr(version, 'distribution', lambda: info) diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index f846c91ac..879f84a1f 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -357,14 +357,14 @@ class TestGitStr: @pytest.fixture def commit_file_mock(self, mocker): - """Fixture providing a mock for utils.read_file for git-commit-id. + """Fixture providing a mock for resources.read_file for git-commit-id. On fixture teardown, it makes sure it got called with git-commit-id as argument. """ mocker.patch('qutebrowser.utils.version.subprocess', side_effect=AssertionError) - m = mocker.patch('qutebrowser.utils.version.utils.read_file') + m = mocker.patch('qutebrowser.utils.version.resources.read_file') yield m m.assert_called_with('git-commit-id') @@ -413,7 +413,7 @@ class TestGitStr: """Test with things raising OSError.""" m = mocker.patch('qutebrowser.utils.version.os') m.path.join.side_effect = OSError - mocker.patch('qutebrowser.utils.version.utils.read_file', + mocker.patch('qutebrowser.utils.version.resources.read_file', side_effect=OSError) with caplog.at_level(logging.ERROR, 'misc'): assert version._git_str() is None @@ -956,10 +956,13 @@ class TestWebEngineVersions: ('5.14.2', '77.0.3865.129'), ('5.15.1', '80.0.3987.163'), ('5.15.2', '83.0.4103.122'), + ('5.15.3', '87.0.4280.144'), + ('5.15.4', '87.0.4280.144'), + ('5.15.5', '87.0.4280.144'), ]) def test_from_pyqt(self, qt_version, chromium_version): expected = version.WebEngineVersions( - webengine=utils.parse_version(qt_version), + webengine=utils.VersionNumber.parse(qt_version), chromium=chromium_version, source='PyQt', ) @@ -982,15 +985,9 @@ class TestWebEngineVersions: versions = version.WebEngineVersions.from_pyqt(pyqt_webengine_version) - if pyqt_webengine_version == '5.15.3': - # Transient situation - we expect to get QtWebEngine 5.15.3 soon, - # so this will line up again. - assert versions.chromium == '87.0.4280.144' - pytest.xfail("Transient situation") - else: - from qutebrowser.browser.webengine import webenginesettings - webenginesettings.init_user_agent() - expected = webenginesettings.parsed_user_agent.upstream_browser_version + from qutebrowser.browser.webengine import webenginesettings + webenginesettings.init_user_agent() + expected = webenginesettings.parsed_user_agent.upstream_browser_version assert versions.chromium == expected @@ -1079,18 +1076,34 @@ class TestChromiumVersion: import_fake.patch() @pytest.fixture - def patch_importlib_no_package(self, monkeypatch): - """Simulate importlib not finding PyQtWebEngine-Qt.""" - try: - import importlib.metadata as importlib_metadata - except ImportError: - importlib_metadata = pytest.importorskip("importlib_metadata") - - def _fake_version(name): - assert name == 'PyQtWebEngine-Qt' - raise importlib_metadata.PackageNotFoundError(name) + def importlib_patcher(self, monkeypatch): + """Patch the importlib module.""" + def _patch(*, qt, qt5): + try: + import importlib.metadata as importlib_metadata + except ImportError: + importlib_metadata = pytest.importorskip("importlib_metadata") + + def _fake_version(name): + if name == 'PyQtWebEngine-Qt': + outcome = qt + elif name == 'PyQtWebEngine-Qt5': + outcome = qt5 + else: + raise utils.Unreachable(outcome) + + if outcome is None: + raise importlib_metadata.PackageNotFoundError(name) + return outcome + + monkeypatch.setattr(importlib_metadata, 'version', _fake_version) + + return _patch - monkeypatch.setattr(importlib_metadata, 'version', _fake_version) + @pytest.fixture + def patch_importlib_no_package(self, importlib_patcher): + """Simulate importlib not finding PyQtWebEngine-Qt[5].""" + importlib_patcher(qt=None, qt5=None) @pytest.mark.parametrize('patches, sources', [ (['elf_fail'], ['importlib', 'PyQt', 'Qt']), @@ -1114,6 +1127,21 @@ class TestChromiumVersion: versions = version.qtwebengine_versions(avoid_init=True) assert versions.source in sources + @pytest.mark.parametrize('qt, qt5, expected', [ + (None, '5.15.4', utils.VersionNumber(5, 15, 4)), + ('5.15.3', None, utils.VersionNumber(5, 15, 3)), + ('5.15.3', '5.15.4', utils.VersionNumber(5, 15, 4)), # -Qt5 takes precedence + ]) + def test_importlib(self, qt, qt5, expected, patch_elf_fail, importlib_patcher): + """Test the importlib version logic with different Qt packages. + + With PyQtWebEngine 5.15.4, PyQtWebEngine-Qt was renamed to PyQtWebEngine-Qt5. + """ + importlib_patcher(qt=qt, qt5=qt5) + versions = version.qtwebengine_versions(avoid_init=True) + assert versions.source == 'importlib' + assert versions.webengine == expected + @dataclasses.dataclass class VersionParams: |