diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec index bcbd67405..e9ccc1619 100644 --- a/misc/qutebrowser.spec +++ b/misc/qutebrowser.spec @@ -5,6 +5,7 @@ import os sys.path.insert(0, os.getcwd()) from scripts import setupcommon +from qutebrowser import utils block_cipher = None @@ -30,9 +31,9 @@ def get_data_files(): setupcommon.write_git_file() -if os.name == 'nt': +if utils.is_windows: icon = 'icons/qutebrowser.ico' -elif sys.platform == 'darwin': +elif utils.is_mac: icon = 'icons/qutebrowser.icns' else: icon = None diff --git a/pytest.ini b/pytest.ini index d47c173f7..b853c8ca8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -25,6 +25,7 @@ markers = this: Used to mark tests during development no_invalid_lines: Don't fail on unparseable lines in end2end tests issue2478: Tests which are broken on Windows with QtWebEngine, https://github.com/qutebrowser/qutebrowser/issues/2478 + fake_os: Fake utils.is_* to a fake operating system qt_log_level_fail = WARNING qt_log_ignore = ^SpellCheck: .* diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 5bb07f129..f1dcc19e8 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -20,7 +20,6 @@ """Command dispatcher for TabbedBrowser.""" import os -import sys import os.path import shlex import functools @@ -430,7 +429,7 @@ class CommandDispatcher: tab.printing.to_printer(diag.printer(), print_callback) diag = QPrintDialog(tab) - if sys.platform == 'darwin': + if utils.is_mac: # For some reason we get a segfault when using open() on macOS ret = diag.exec_() if ret == QDialog.Accepted: diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 9458e39d0..07e5c7c30 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -174,7 +174,7 @@ def transform_path(path): Returns None if the path is invalid on the current platform. """ - if sys.platform != "win32": + if not utils.is_windows: return path path = utils.expand_windows_drive(path) # Drive dependent working directories are not supported, e.g. diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 0bac53915..7b4ece0e8 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -28,7 +28,6 @@ Module attributes: """ import os -import sys import ctypes import ctypes.util @@ -207,7 +206,7 @@ def init(args): # WORKAROUND for # https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - if sys.platform == 'linux': + if utils.is_linux: ctypes.CDLL(ctypes.util.find_library("GL"), mode=ctypes.RTLD_GLOBAL) _init_profiles() diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py index 42de3a65d..beaa690ca 100644 --- a/qutebrowser/browser/webkit/network/networkmanager.py +++ b/qutebrowser/browser/webkit/network/networkmanager.py @@ -58,7 +58,7 @@ def _is_secure_cipher(cipher): # https://codereview.qt-project.org/#/c/75943/ return False # OpenSSL should already protect against this in a better way - elif cipher.keyExchangeMethod() == 'DH' and os.name == 'nt': + elif cipher.keyExchangeMethod() == 'DH' and utils.is_windows: # https://weakdh.org/ return False elif cipher.encryptionMethod().upper().startswith('RC4'): diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index cf2156431..9a9daad77 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -19,7 +19,6 @@ """Wrapper over our (QtWebKit) WebView.""" -import sys import functools import xml.etree.ElementTree @@ -223,11 +222,11 @@ class WebKitCaret(browsertab.AbstractCaret): def move_to_end_of_word(self, count=1): if not self.selection_enabled: act = [QWebPage.MoveToNextWord] - if sys.platform == 'win32': # pragma: no cover + if utils.is_windows: # pragma: no cover act.append(QWebPage.MoveToPreviousChar) else: act = [QWebPage.SelectNextWord] - if sys.platform == 'win32': # pragma: no cover + if utils.is_windows: # pragma: no cover act.append(QWebPage.SelectPreviousChar) for _ in range(count): for a in act: @@ -236,11 +235,11 @@ class WebKitCaret(browsertab.AbstractCaret): def move_to_next_word(self, count=1): if not self.selection_enabled: act = [QWebPage.MoveToNextWord] - if sys.platform != 'win32': # pragma: no branch + if not utils.is_windows: # pragma: no branch act.append(QWebPage.MoveToNextChar) else: act = [QWebPage.SelectNextWord] - if sys.platform != 'win32': # pragma: no branch + if not utils.is_windows: # pragma: no branch act.append(QWebPage.SelectNextChar) for _ in range(count): for a in act: diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index 5608995a4..4f1ff10c8 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -19,8 +19,6 @@ """The main browser widgets.""" -import sys - from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl from PyQt5.QtGui import QPalette from PyQt5.QtWidgets import QStyleFactory @@ -57,7 +55,7 @@ class WebView(QWebView): def __init__(self, *, win_id, tab_id, tab, private, parent=None): super().__init__(parent) - if sys.platform == 'darwin': + if utils.is_mac: # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948 # See https://github.com/qutebrowser/qutebrowser/issues/462 self.setStyle(QStyleFactory.create('Fusion')) diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index 5449f1598..10e509ce7 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -25,7 +25,7 @@ import tempfile from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QSocketNotifier -from qutebrowser.utils import message, log, objreg, standarddir +from qutebrowser.utils import message, log, objreg, standarddir, utils from qutebrowser.commands import runners from qutebrowser.config import config from qutebrowser.misc import guiprocess @@ -406,9 +406,9 @@ def run_async(tab, cmd, *args, win_id, env, verbose=False): window=win_id) commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser) - if os.name == 'posix': + if utils.is_posix: runner = _POSIXUserscriptRunner(tabbed_browser) - elif os.name == 'nt': # pragma: no cover + elif utils.is_windows: # pragma: no cover runner = _WindowsUserscriptRunner(tabbed_browser) else: # pragma: no cover raise UnsupportedError diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 393aa26f0..9a1b88942 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -40,7 +40,7 @@ from PyQt5.QtWidgets import QApplication, QDialog from qutebrowser.commands import cmdutils from qutebrowser.misc import earlyinit, crashdialog -from qutebrowser.utils import usertypes, standarddir, log, objreg, debug +from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils @attr.s @@ -312,7 +312,7 @@ class SignalHandler(QObject): self._orig_handlers[signal.SIGTERM] = signal.signal( signal.SIGTERM, self.interrupt) - if os.name == 'posix' and hasattr(signal, 'set_wakeup_fd'): + if utils.is_posix and hasattr(signal, 'set_wakeup_fd'): # pylint: disable=import-error,no-member,useless-suppression import fcntl read_fd, write_fd = os.pipe() diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py index 562cc84cc..e308ac8a0 100644 --- a/qutebrowser/misc/ipc.py +++ b/qutebrowser/misc/ipc.py @@ -30,7 +30,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket import qutebrowser -from qutebrowser.utils import log, usertypes, error, objreg, standarddir +from qutebrowser.utils import log, usertypes, error, objreg, standarddir, utils CONNECT_TIMEOUT = 100 # timeout for connecting/disconnecting @@ -51,7 +51,7 @@ def _get_socketname_windows(basedir): def _get_socketname(basedir): """Get a socketname to use.""" - if os.name == 'nt': # pragma: no cover + if utils.is_windows: # pragma: no cover return _get_socketname_windows(basedir) parts_to_hash = [getpass.getuser()] @@ -139,8 +139,6 @@ class IPCServer(QObject): _server: A QLocalServer to accept new connections. _socket: The QLocalSocket we're currently connected to. _socketname: The socketname to use. - _socketopts_ok: Set if using setSocketOptions is working with this - OS/Qt version. _atime_timer: Timer to update the atime of the socket regularly. Signals: @@ -169,7 +167,7 @@ class IPCServer(QObject): self._timer.setInterval(READ_TIMEOUT) self._timer.timeout.connect(self.on_timeout) - if os.name == 'nt': # pragma: no cover + if utils.is_windows: # pragma: no cover self._atime_timer = None else: self._atime_timer = usertypes.Timer(self, 'ipc-atime') @@ -182,8 +180,7 @@ class IPCServer(QObject): self._socket = None self._old_socket = None - self._socketopts_ok = os.name == 'nt' - if self._socketopts_ok: # pragma: no cover + if utils.is_windows: # pragma: no cover # If we use setSocketOptions on Unix with Qt < 5.4, we get a # NameError while listening... log.ipc.debug("Calling setSocketOptions") @@ -210,7 +207,7 @@ class IPCServer(QObject): raise AddressInUseError(self._server) else: raise ListenError(self._server) - if not self._socketopts_ok: # pragma: no cover + if not utils.is_windows: # pragma: no cover # If we use setSocketOptions on Unix with Qt < 5.4, we get a # NameError while listening. # (see b135569d5c6e68c735ea83f42e4baf51f7972281) diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 3049d5dfe..fa0208ea7 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -409,6 +409,8 @@ def qt_message_handler(msg_type, context, msg): # https://codereview.qt-project.org/176831 "QObject::disconnect: Unexpected null parameter", ] + # not using utils.is_mac here, because we can't be sure we can successfully + # import the utils module here. if sys.platform == 'darwin': suppressed_msgs += [ 'libpng warning: iCCP: known incorrect sRGB profile', diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index 468fba51e..d6ad7fc39 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -20,7 +20,6 @@ """Utilities to get and initialize data/config paths.""" import os -import sys import shutil import os.path import contextlib @@ -28,7 +27,7 @@ import contextlib from PyQt5.QtCore import QStandardPaths from PyQt5.QtWidgets import QApplication -from qutebrowser.utils import log, debug, usertypes, message +from qutebrowser.utils import log, debug, usertypes, message, utils # The cached locations _locations = {} @@ -69,7 +68,7 @@ def _init_config(args): typ = QStandardPaths.ConfigLocation overridden, path = _from_args(typ, args) if not overridden: - if os.name == 'nt': + if utils.is_windows: app_data_path = _writable_location( QStandardPaths.AppDataLocation) path = os.path.join(app_data_path, 'config') @@ -80,7 +79,7 @@ def _init_config(args): _locations[Location.auto_config] = path # Override the normal (non-auto) config on macOS - if sys.platform == 'darwin': + if utils.is_mac: overridden, path = _from_args(typ, args) if not overridden: # pragma: no branch path = os.path.expanduser('~/.' + APPNAME) @@ -104,7 +103,7 @@ def _init_data(args): typ = QStandardPaths.DataLocation overridden, path = _from_args(typ, args) if not overridden: - if os.name == 'nt': + if utils.is_windows: app_data_path = _writable_location(QStandardPaths.AppDataLocation) path = os.path.join(app_data_path, 'data') else: @@ -114,7 +113,7 @@ def _init_data(args): # system_data _locations.pop(Location.system_data, None) # Remove old state - if sys.platform.startswith('linux'): + if utils.is_linux: path = '/usr/share/' + APPNAME if os.path.exists(path): _locations[Location.system_data] = path @@ -139,7 +138,7 @@ def _init_cache(args): typ = QStandardPaths.CacheLocation overridden, path = _from_args(typ, args) if not overridden: - if os.name == 'nt': + if utils.is_windows: # Local, not Roaming! data_path = _writable_location(QStandardPaths.DataLocation) path = os.path.join(data_path, 'cache') @@ -172,7 +171,7 @@ def download(): def _init_runtime(args): """Initialize location for runtime data.""" - if sys.platform.startswith('linux'): + if utils.is_linux: typ = QStandardPaths.RuntimeLocation else: # RuntimeLocation is a weird path on macOS and Windows. @@ -312,9 +311,9 @@ def init(args): _init_dirs(args) _init_cachedir_tag() if args is not None and getattr(args, 'basedir', None) is None: - if sys.platform == 'darwin': # pragma: no cover + if utils.is_mac: # pragma: no cover _move_macos() - elif os.name == 'nt': # pragma: no cover + elif utils.is_windows: # pragma: no cover _move_windows() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index b60b1bc13..23c442008 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -19,6 +19,7 @@ """Other utilities which don't fit anywhere else.""" +import os import io import re import sys @@ -49,6 +50,11 @@ from qutebrowser.utils import qtutils, log, debug fake_clipboard = None log_clipboard = False +is_mac = sys.platform.startswith('darwin') +is_linux = sys.platform.startswith('linux') +is_windows = sys.platform.startswith('win') +is_posix = os.name == 'posix' + class ClipboardError(Exception): @@ -377,7 +383,7 @@ def keyevent_to_string(e): A name of the key (combination) as a string or None if only modifiers are pressed.. """ - if sys.platform == 'darwin': + if is_mac: # Qt swaps Ctrl/Meta on macOS, so we switch it back here so the user # can use it in the config as expected. See: # https://github.com/qutebrowser/qutebrowser/issues/110 diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 673590208..0f1b2233d 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -248,12 +248,12 @@ def _os_info(): """ lines = [] releaseinfo = None - if sys.platform == 'linux': + if utils.is_linux: osver = '' releaseinfo = _release_info() - elif sys.platform == 'win32': + elif utils.is_windows: osver = ', '.join(platform.win32_ver()) - elif sys.platform == 'darwin': + elif utils.is_mac: release, versioninfo, machine = platform.mac_ver() if all(not e for e in versioninfo): versioninfo = '' diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index d5c03ad02..a51c8f2b3 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -36,7 +36,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) import qutebrowser -from scripts import utils +from qutebrowser.utils import utils +from scripts import utils as scriptutils # from scripts.dev import update_3rdparty @@ -70,7 +71,7 @@ def call_tox(toxenv, *args, python=sys.executable): def run_asciidoc2html(args): """Common buildsteps used for all OS'.""" - utils.print_title("Running asciidoc2html.py") + scriptutils.print_title("Running asciidoc2html.py") if args.asciidoc is not None: a2h_args = ['--asciidoc'] + args.asciidoc else: @@ -127,7 +128,7 @@ def patch_mac_app(): def build_mac(): """Build macOS .dmg/.app.""" - utils.print_title("Cleaning up...") + scriptutils.print_title("Cleaning up...") for f in ['wc.dmg', 'template.dmg']: try: os.remove(f) @@ -135,20 +136,20 @@ def build_mac(): pass for d in ['dist', 'build']: shutil.rmtree(d, ignore_errors=True) - utils.print_title("Updating 3rdparty content") + scriptutils.print_title("Updating 3rdparty content") # Currently disabled because QtWebEngine has no pdfjs support # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) - utils.print_title("Building .app via pyinstaller") + scriptutils.print_title("Building .app via pyinstaller") call_tox('pyinstaller', '-r') - utils.print_title("Patching .app") + scriptutils.print_title("Patching .app") patch_mac_app() - utils.print_title("Building .dmg") + scriptutils.print_title("Building .dmg") subprocess.check_call(['make', '-f', 'scripts/dev/Makefile-dmg']) dmg_name = 'qutebrowser-{}.dmg'.format(qutebrowser.__version__) os.rename('qutebrowser.dmg', dmg_name) - utils.print_title("Running smoke test") + scriptutils.print_title("Running smoke test") try: with tempfile.TemporaryDirectory() as tmpdir: @@ -177,11 +178,11 @@ def patch_windows(out_dir): def build_windows(): """Build windows executables/setups.""" - utils.print_title("Updating 3rdparty content") + scriptutils.print_title("Updating 3rdparty content") # Currently disabled because QtWebEngine has no pdfjs support # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) - utils.print_title("Building Windows binaries") + scriptutils.print_title("Building Windows binaries") parts = str(sys.version_info.major), str(sys.version_info.minor) ver = ''.join(parts) python_x86 = r'C:\Python{}-32\python.exe'.format(ver) @@ -194,19 +195,19 @@ def build_windows(): artifacts = [] - utils.print_title("Running pyinstaller 32bit") + scriptutils.print_title("Running pyinstaller 32bit") _maybe_remove(out_32) call_tox('pyinstaller', '-r', python=python_x86) shutil.move(out_pyinstaller, out_32) patch_windows(out_32) - utils.print_title("Running pyinstaller 64bit") + scriptutils.print_title("Running pyinstaller 64bit") _maybe_remove(out_64) call_tox('pyinstaller', '-r', python=python_x64) shutil.move(out_pyinstaller, out_64) patch_windows(out_64) - utils.print_title("Building installers") + scriptutils.print_title("Building installers") subprocess.check_call(['makensis.exe', '/DVERSION={}'.format(qutebrowser.__version__), 'misc/qutebrowser.nsi']) @@ -227,12 +228,12 @@ def build_windows(): 'Windows 64bit installer'), ] - utils.print_title("Running 32bit smoke test") + scriptutils.print_title("Running 32bit smoke test") smoke_test(os.path.join(out_32, 'qutebrowser.exe')) - utils.print_title("Running 64bit smoke test") + scriptutils.print_title("Running 64bit smoke test") smoke_test(os.path.join(out_64, 'qutebrowser.exe')) - utils.print_title("Zipping 32bit standalone...") + scriptutils.print_title("Zipping 32bit standalone...") name = 'qutebrowser-{}-windows-standalone-win32'.format( qutebrowser.__version__) shutil.make_archive(name, 'zip', 'dist', os.path.basename(out_32)) @@ -240,7 +241,7 @@ def build_windows(): 'application/zip', 'Windows 32bit standalone')) - utils.print_title("Zipping 64bit standalone...") + scriptutils.print_title("Zipping 64bit standalone...") name = 'qutebrowser-{}-windows-standalone-amd64'.format( qutebrowser.__version__) shutil.make_archive(name, 'zip', 'dist', os.path.basename(out_64)) @@ -253,7 +254,7 @@ def build_windows(): def build_sdist(): """Build an sdist and list the contents.""" - utils.print_title("Building sdist") + scriptutils.print_title("Building sdist") _maybe_remove('dist') @@ -276,10 +277,10 @@ def build_sdist(): assert '.pyc' not in by_ext - utils.print_title("sdist contents") + scriptutils.print_title("sdist contents") for ext, files in sorted(by_ext.items()): - utils.print_subtitle(ext) + scriptutils.print_subtitle(ext) print('\n'.join(files)) filename = 'qutebrowser-{}.tar.gz'.format(qutebrowser.__version__) @@ -308,7 +309,7 @@ def github_upload(artifacts, tag): tag: The name of the release tag """ import github3 - utils.print_title("Uploading to github...") + scriptutils.print_title("Uploading to github...") token = read_github_token() gh = github3.login(token=token) @@ -343,7 +344,7 @@ def main(): parser.add_argument('--upload', help="Tag to upload the release for", nargs=1, required=False, metavar='TAG') args = parser.parse_args() - utils.change_cwd() + scriptutils.change_cwd() upload_to_pypi = False @@ -353,7 +354,7 @@ def main(): import github3 # pylint: disable=unused-variable read_github_token() - if os.name == 'nt': + if utils.is_windows: if sys.maxsize > 2**32: # WORKAROUND print("Due to a python/Windows bug, this script needs to be run ") @@ -364,7 +365,7 @@ def main(): sys.exit(1) run_asciidoc2html(args) artifacts = build_windows() - elif sys.platform == 'darwin': + elif utils.is_mac: run_asciidoc2html(args) artifacts = build_mac() else: @@ -372,7 +373,7 @@ def main(): upload_to_pypi = True if args.upload is not None: - utils.print_title("Press enter to release...") + scriptutils.print_title("Press enter to release...") input() github_upload(artifacts, args.upload[0]) if upload_to_pypi: diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py index dfd336d16..aa5072536 100644 --- a/scripts/dev/check_coverage.py +++ b/scripts/dev/check_coverage.py @@ -32,7 +32,8 @@ import attr sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) -from scripts import utils +from scripts import utils as scriptutils +from qutebrowser.utils import utils @attr.s @@ -207,7 +208,7 @@ def _get_filename(filename): def check(fileobj, perfect_files): """Main entry point which parses/checks coverage.xml if applicable.""" - if sys.platform != 'linux': + if not utils.is_linux: raise Skipped("on non-Linux system.") elif '-k' in sys.argv[1:]: raise Skipped("because -k is given.") @@ -272,7 +273,7 @@ def main_check(): if messages: print() print() - utils.print_title("Coverage check failed") + scriptutils.print_title("Coverage check failed") for msg in messages: print(msg.text) print() @@ -323,7 +324,7 @@ def main_check_all(): def main(): - utils.change_cwd() + scriptutils.change_cwd() if '--check-all' in sys.argv: return main_check_all() else: diff --git a/tests/conftest.py b/tests/conftest.py index b12f85d6e..db50afcd6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,7 @@ from helpers import logfail from helpers.logfail import fail_on_logging from helpers.messagemock import message_mock from helpers.fixtures import * -from qutebrowser.utils import qtutils, standarddir, usertypes +from qutebrowser.utils import qtutils, standarddir, usertypes, utils from qutebrowser.misc import objects import qutebrowser.app # To register commands @@ -50,18 +50,18 @@ hypothesis.settings.load_profile('default') def _apply_platform_markers(config, item): """Apply a skip marker to a given item.""" markers = [ - ('posix', os.name != 'posix', "Requires a POSIX os"), - ('windows', os.name != 'nt', "Requires Windows"), - ('linux', not sys.platform.startswith('linux'), "Requires Linux"), - ('mac', sys.platform != 'darwin', "Requires macOS"), - ('not_mac', sys.platform == 'darwin', "Skipped on macOS"), + ('posix', not utils.is_posix, "Requires a POSIX os"), + ('windows', not utils.is_windows, "Requires Windows"), + ('linux', not utils.is_linux, "Requires Linux"), + ('mac', not utils.is_mac, "Requires macOS"), + ('not_mac', utils.is_mac, "Skipped on macOS"), ('not_frozen', getattr(sys, 'frozen', False), "Can't be run when frozen"), ('frozen', not getattr(sys, 'frozen', False), "Can only run when frozen"), ('ci', 'CI' not in os.environ, "Only runs on CI."), ('no_ci', 'CI' in os.environ, "Skipped on CI."), - ('issue2478', os.name == 'nt' and config.webengine, + ('issue2478', utils.is_windows and config.webengine, "Broken with QtWebEngine on Windows"), ] @@ -181,7 +181,7 @@ def check_display(request): request.config.xvfb is not None): raise Exception("Xvfb is running on buildbot!") - if sys.platform == 'linux' and not os.environ.get('DISPLAY', ''): + if utils.is_linux and not os.environ.get('DISPLAY', ''): raise Exception("No display and no Xvfb available!") @@ -193,6 +193,37 @@ def set_backend(monkeypatch, request): monkeypatch.setattr(objects, 'backend', backend) +@pytest.fixture(autouse=True) +def apply_fake_os(monkeypatch, request): + fake_os = request.node.get_marker('fake_os') + if not fake_os: + return + + name = fake_os.args[0] + mac = False + windows = False + linux = False + posix = False + + if name == 'unknown': + pass + elif name == 'mac': + mac = True + posix = True + elif name == 'windows': + windows = True + elif name == 'linux': + linux = True + posix = True + else: + raise ValueError("Invalid fake_os {}".format(name)) + + monkeypatch.setattr('qutebrowser.utils.utils.is_mac', mac) + monkeypatch.setattr('qutebrowser.utils.utils.is_linux', linux) + monkeypatch.setattr('qutebrowser.utils.utils.is_windows', windows) + monkeypatch.setattr('qutebrowser.utils.utils.is_posix', posix) + + @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """Make test information available in fixtures. diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py index 1fd6eb874..51a0497bf 100644 --- a/tests/end2end/conftest.py +++ b/tests/end2end/conftest.py @@ -38,7 +38,7 @@ from end2end.fixtures.webserver import server, server_after_test, ssl_server from end2end.fixtures.quteprocess import (quteproc_process, quteproc, quteproc_new) from end2end.fixtures.testprocess import pytest_runtest_makereport -from qutebrowser.utils import qtutils +from qutebrowser.utils import qtutils, utils def pytest_configure(config): @@ -144,7 +144,7 @@ def pytest_collection_modifyitems(config, items): ('qtwebengine_flaky', 'Flaky with QtWebEngine', pytest.mark.skipif, config.webengine), ('qtwebengine_mac_xfail', 'Fails on macOS with QtWebEngine', - pytest.mark.xfail, config.webengine and sys.platform == 'darwin'), + pytest.mark.xfail, config.webengine and utils.is_mac), ] for item in items: diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 52dfe616c..7f5b4e2a6 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -31,9 +31,9 @@ import textwrap import pytest import pytest_bdd as bdd -from qutebrowser.utils import log +from qutebrowser.utils import log, utils from qutebrowser.browser import pdfjs -from helpers import utils +from helpers import utils as testutils def _get_echo_exe_path(): @@ -42,8 +42,9 @@ def _get_echo_exe_path(): Return: Path to the "echo"-utility. """ - if sys.platform == "win32": - return os.path.join(utils.abs_datapath(), 'userscripts', 'echo.bat') + if utils.is_windows: + return os.path.join(testutils.abs_datapath(), 'userscripts', + 'echo.bat') else: return 'echo' @@ -255,7 +256,7 @@ def run_command(quteproc, server, tmpdir, command): invalid = False command = command.replace('(port)', str(server.port)) - command = command.replace('(testdata)', utils.abs_datapath()) + command = command.replace('(testdata)', testutils.abs_datapath()) command = command.replace('(tmpdir)', str(tmpdir)) command = command.replace('(dirsep)', os.sep) command = command.replace('(echo-exe)', _get_echo_exe_path()) @@ -349,7 +350,7 @@ def hint(quteproc, args): @bdd.when(bdd.parsers.parse('I hint with args "{args}" and follow {letter}')) def hint_and_follow(quteproc, args, letter): - args = args.replace('(testdata)', utils.abs_datapath()) + args = args.replace('(testdata)', testutils.abs_datapath()) quteproc.send_cmd(':hint {}'.format(args)) quteproc.wait_for(message='hints: *') quteproc.send_cmd(':follow-hint {}'.format(letter)) @@ -502,7 +503,7 @@ def check_header(quteproc, header, value): assert header not in data['headers'] else: actual = data['headers'][header] - assert utils.pattern_match(pattern=value, value=actual) + assert testutils.pattern_match(pattern=value, value=actual) @bdd.then(bdd.parsers.parse('the page should contain the html "{text}"')) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 496eb486d..09307711f 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -279,7 +279,7 @@ def session_manager_stub(stubs): @pytest.fixture -def tabbed_browser_stubs(stubs, win_registry): +def tabbed_browser_stubs(qapp, stubs, win_registry): """Fixture providing a fake tabbed-browser object on win_id 0 and 1.""" win_registry.add_window(1) stubs = [stubs.TabbedBrowserStub(), stubs.TabbedBrowserStub()] diff --git a/tests/unit/browser/webkit/network/test_filescheme.py b/tests/unit/browser/webkit/network/test_filescheme.py index 0cc485556..13700a103 100644 --- a/tests/unit/browser/webkit/network/test_filescheme.py +++ b/tests/unit/browser/webkit/network/test_filescheme.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkRequest from qutebrowser.browser.webkit.network import filescheme -from qutebrowser.utils import urlutils +from qutebrowser.utils import urlutils, utils @pytest.mark.parametrize('create_file, create_dir, filterfunc, expected', [ @@ -228,10 +228,7 @@ class TestDirbrowserHtml: assert parsed.folders == [bar_item] def test_root_dir(self, tmpdir, parser): - if os.name == 'nt': - root_dir = 'C:\\' - else: - root_dir = '/' + root_dir = 'C:\\' if utils.is_windows else '/' parsed = parser(root_dir) assert not parsed.parent diff --git a/tests/unit/commands/test_userscripts.py b/tests/unit/commands/test_userscripts.py index 7829fbca9..9a6490aca 100644 --- a/tests/unit/commands/test_userscripts.py +++ b/tests/unit/commands/test_userscripts.py @@ -27,6 +27,7 @@ import pytest from PyQt5.QtCore import QFileSystemWatcher from qutebrowser.commands import userscripts +from qutebrowser.utils import utils @pytest.mark.posix @@ -60,7 +61,7 @@ class TestQtFIFOReader: userscripts._WindowsUserscriptRunner, ]) def runner(request, runtime_tmpdir): - if (os.name != 'posix' and + if (not utils.is_posix and request.param is userscripts._POSIXUserscriptRunner): pytest.skip("Requires a POSIX os") else: @@ -245,8 +246,8 @@ def test_unicode_error(caplog, qtbot, py_proc, runner): assert caplog.records[0].message == expected -def test_unsupported(monkeypatch, tabbed_browser_stubs): - monkeypatch.setattr(userscripts.os, 'name', 'toaster') +@pytest.mark.fake_os('unknown') +def test_unsupported(tabbed_browser_stubs): with pytest.raises(userscripts.UnsupportedError, match="Userscripts are " "not supported on this platform!"): userscripts.run_async(tab=None, cmd=None, win_id=0, env=None) diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 1fc939373..b7dba3b18 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -19,11 +19,11 @@ """Tests for qutebrowser.config.configfiles.""" import os -import sys import pytest from qutebrowser.config import config, configfiles, configexc +from qutebrowser.utils import utils from PyQt5.QtCore import QSettings @@ -343,7 +343,7 @@ def test_init(init_patch, config_tmpdir): configfiles.init() # Make sure qsettings land in a subdir - if sys.platform == 'linux': + if utils.is_linux: settings = QSettings() settings.setValue("hello", "world") settings.sync() diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py index 18f04266d..e7131f92c 100644 --- a/tests/unit/keyinput/test_basekeyparser.py +++ b/tests/unit/keyinput/test_basekeyparser.py @@ -19,7 +19,6 @@ """Tests for BaseKeyParser.""" -import sys import logging from unittest import mock @@ -166,7 +165,7 @@ class TestSpecialKeys: keyparser._read_config('prompt') def test_valid_key(self, fake_keyevent_factory, keyparser): - if sys.platform == 'darwin': + if utils.is_mac: modifier = Qt.MetaModifier else: modifier = Qt.ControlModifier @@ -176,7 +175,7 @@ class TestSpecialKeys: 'message-info ctrla', keyparser.Type.special, None) def test_valid_key_count(self, fake_keyevent_factory, keyparser): - if sys.platform == 'darwin': + if utils.is_mac: modifier = Qt.MetaModifier else: modifier = Qt.ControlModifier @@ -210,7 +209,7 @@ class TestKeyChain: keyparser._read_config('prompt') def test_valid_special_key(self, fake_keyevent_factory, keyparser): - if sys.platform == 'darwin': + if utils.is_mac: modifier = Qt.MetaModifier else: modifier = Qt.ControlModifier diff --git a/tests/unit/misc/test_ipc.py b/tests/unit/misc/test_ipc.py index 2b7c17e62..d8024b207 100644 --- a/tests/unit/misc/test_ipc.py +++ b/tests/unit/misc/test_ipc.py @@ -19,7 +19,6 @@ """Tests for qutebrowser.misc.ipc.""" -import sys import os import getpass import logging @@ -35,7 +34,7 @@ from PyQt5.QtTest import QSignalSpy import qutebrowser from qutebrowser.misc import ipc -from qutebrowser.utils import objreg, standarddir +from qutebrowser.utils import objreg, standarddir, utils from helpers import stubs @@ -228,11 +227,11 @@ class TestSocketName: We probably would adjust the code first to make it work on that platform. """ - if os.name == 'nt': + if utils.is_windows: pass - elif sys.platform == 'darwin': + elif utils.is_mac: pass - elif sys.platform.startswith('linux'): + elif utils.is_linux: pass else: raise Exception("Unexpected platform!") @@ -431,7 +430,7 @@ class TestHandleConnection: @pytest.fixture def connected_socket(qtbot, qlocalsocket, ipc_server): - if sys.platform == 'darwin': + if utils.is_mac: pytest.skip("Skipping connected_socket test - " "https://github.com/qutebrowser/qutebrowser/issues/1045") ipc_server.listen() diff --git a/tests/unit/misc/test_msgbox.py b/tests/unit/misc/test_msgbox.py index 2c1268bd8..5f0058dea 100644 --- a/tests/unit/misc/test_msgbox.py +++ b/tests/unit/misc/test_msgbox.py @@ -18,11 +18,10 @@ """Tests for qutebrowser.misc.msgbox.""" -import sys - import pytest from qutebrowser.misc import msgbox +from qutebrowser.utils import utils from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QMessageBox, QWidget @@ -40,7 +39,7 @@ def test_attributes(qtbot): box = msgbox.msgbox(parent=parent, title=title, text=text, icon=icon, buttons=buttons) qtbot.add_widget(box) - if sys.platform != 'darwin': + if not utils.is_mac: assert box.windowTitle() == title assert box.icon() == icon assert box.standardButtons() == buttons @@ -82,7 +81,7 @@ def test_finished_signal(qtbot): def test_information(qtbot): box = msgbox.information(parent=None, title='foo', text='bar') qtbot.add_widget(box) - if sys.platform != 'darwin': + if not utils.is_mac: assert box.windowTitle() == 'foo' assert box.text() == 'bar' assert box.icon() == QMessageBox.Information diff --git a/tests/unit/misc/test_utilcmds.py b/tests/unit/misc/test_utilcmds.py index 238244617..dfb99115d 100644 --- a/tests/unit/misc/test_utilcmds.py +++ b/tests/unit/misc/test_utilcmds.py @@ -21,7 +21,6 @@ import contextlib import logging -import os import signal import time @@ -29,6 +28,7 @@ import pytest from qutebrowser.misc import utilcmds from qutebrowser.commands import cmdexc +from qutebrowser.utils import utils @contextlib.contextmanager @@ -45,7 +45,7 @@ def test_debug_crash_exception(): utilcmds.debug_crash(typ='exception') -@pytest.mark.skipif(os.name == 'nt', +@pytest.mark.skipif(utils.is_windows, reason="current CPython/win can't recover from SIGSEGV") def test_debug_crash_segfault(): """Verify that debug_crash crashes as intended.""" diff --git a/tests/unit/scripts/test_check_coverage.py b/tests/unit/scripts/test_check_coverage.py index 440c743cf..6b18568c5 100644 --- a/tests/unit/scripts/test_check_coverage.py +++ b/tests/unit/scripts/test_check_coverage.py @@ -207,8 +207,8 @@ def test_skipped_args(covtest, args, reason): covtest.check_skipped(args, reason) -def test_skipped_windows(covtest, monkeypatch): - monkeypatch.setattr(check_coverage.sys, 'platform', 'toaster') +@pytest.mark.fake_os('windows') +def test_skipped_non_linux(covtest): covtest.check_skipped([], "on non-Linux system.") diff --git a/tests/unit/utils/test_error.py b/tests/unit/utils/test_error.py index 4f4905365..47a1c52d9 100644 --- a/tests/unit/utils/test_error.py +++ b/tests/unit/utils/test_error.py @@ -18,12 +18,11 @@ """Tests for qutebrowser.utils.error.""" -import sys import logging import pytest -from qutebrowser.utils import error +from qutebrowser.utils import error, utils from qutebrowser.misc import ipc from PyQt5.QtCore import QTimer @@ -84,7 +83,7 @@ def test_err_windows(qtbot, qapp, fake_args, pre_text, post_text, expected): w = qapp.activeModalWidget() try: qtbot.add_widget(w) - if sys.platform != 'darwin': + if not utils.is_mac: assert w.windowTitle() == 'title' assert w.icon() == QMessageBox.Critical assert w.standardButtons() == QMessageBox.Ok diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index c2a72de1a..1278d7bc1 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -89,7 +89,7 @@ def test_resource_url(): path = url.path() - if os.name == "nt": + if utils.is_windows: path = path.lstrip('/') path = path.replace('/', os.sep) diff --git a/tests/unit/utils/test_qtutils.py b/tests/unit/utils/test_qtutils.py index f3c1afc04..8045bb0d3 100644 --- a/tests/unit/utils/test_qtutils.py +++ b/tests/unit/utils/test_qtutils.py @@ -21,7 +21,6 @@ import io import os -import sys import os.path import unittest import unittest.mock @@ -36,7 +35,7 @@ import pytest from PyQt5.QtCore import (QDataStream, QPoint, QUrl, QByteArray, QIODevice, QTimer, QBuffer, QFile, QProcess, QFileDevice) -from qutebrowser.utils import qtutils +from qutebrowser.utils import qtutils, utils import overflow_test_cases @@ -458,13 +457,13 @@ class TestSavefileOpen: with qtutils.savefile_open(str(filename)) as f: f.write('foo\nbar\nbaz') data = filename.read_binary() - if os.name == 'nt': + if utils.is_windows: assert data == b'foo\r\nbar\r\nbaz' else: assert data == b'foo\nbar\nbaz' -if test_file is not None and sys.platform != 'darwin': +if test_file is not None and not utils.is_mac: # If we were able to import Python's test_file module, we run some code # here which defines unittest TestCases to run the python tests over # PyQIODevice. diff --git a/tests/unit/utils/test_standarddir.py b/tests/unit/utils/test_standarddir.py index 5230c9c78..3dcb282d2 100644 --- a/tests/unit/utils/test_standarddir.py +++ b/tests/unit/utils/test_standarddir.py @@ -32,7 +32,7 @@ import attr from PyQt5.QtCore import QStandardPaths import pytest -from qutebrowser.utils import standarddir +from qutebrowser.utils import standarddir, utils # Use a different application name for tests to make sure we don't change real @@ -78,9 +78,9 @@ def test_unset_organization_no_qapp(monkeypatch): pass +@pytest.mark.fake_os('mac') def test_fake_mac_config(tmpdir, monkeypatch): """Test standardir.config on a fake Mac.""" - monkeypatch.setattr(sys, 'platform', 'darwin') monkeypatch.setenv('HOME', str(tmpdir)) expected = str(tmpdir) + '/.qute_test' # always with / standarddir._init_config(args=None) @@ -89,9 +89,9 @@ def test_fake_mac_config(tmpdir, monkeypatch): @pytest.mark.parametrize('what', ['data', 'config', 'cache']) @pytest.mark.not_mac +@pytest.mark.fake_os('windows') def test_fake_windows(tmpdir, monkeypatch, what): """Make sure the config/data/cache dirs are correct on a fake Windows.""" - monkeypatch.setattr(os, 'name', 'nt') monkeypatch.setattr(standarddir.QStandardPaths, 'writableLocation', lambda typ: str(tmpdir / APPNAME)) @@ -173,9 +173,9 @@ class TestStandardDir: standarddir._init_dirs() assert standarddir.runtime() == str(tmpdir / 'temp' / APPNAME) + @pytest.mark.fake_os('windows') def test_runtimedir_empty_tempdir(self, monkeypatch, tmpdir): """With an empty tempdir on non-Linux, we should raise.""" - monkeypatch.setattr(standarddir.sys, 'platform', 'nt') monkeypatch.setattr(standarddir.QStandardPaths, 'writableLocation', lambda typ: '') with pytest.raises(standarddir.EmptyValueError): @@ -294,7 +294,7 @@ class TestCreatingDir: assert basedir.exists() - if os.name == 'posix': + if utils.is_posix: assert basedir.stat().mode & 0o777 == 0o700 @pytest.mark.parametrize('typ', DIR_TYPES) @@ -324,9 +324,9 @@ class TestSystemData: """Test system data path.""" + @pytest.mark.linux def test_system_datadir_exist_linux(self, monkeypatch): """Test that /usr/share/qute_test is used if path exists.""" - monkeypatch.setattr('sys.platform', "linux") monkeypatch.setattr(os.path, 'exists', lambda path: True) standarddir._init_dirs() assert standarddir.data(system=True) == "/usr/share/qute_test" @@ -493,10 +493,10 @@ def test_init(mocker, tmpdir, args_kind): assert standarddir._locations != {} if args_kind == 'normal': - if sys.platform == 'darwin': + if utils.is_mac: assert not m_windows.called assert m_mac.called - elif os.name == 'nt': + elif utils.is_windows: assert m_windows.called assert not m_mac.called else: diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index 623f77cc7..f6eef7f4f 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -355,7 +355,7 @@ class TestKeyEventToString: def test_key_and_modifier(self, fake_keyevent_factory): """Test with key and modifier pressed.""" evt = fake_keyevent_factory(key=Qt.Key_A, modifiers=Qt.ControlModifier) - expected = 'meta+a' if sys.platform == 'darwin' else 'ctrl+a' + expected = 'meta+a' if utils.is_mac else 'ctrl+a' assert utils.keyevent_to_string(evt) == expected def test_key_and_modifiers(self, fake_keyevent_factory): @@ -365,9 +365,9 @@ class TestKeyEventToString: Qt.MetaModifier | Qt.ShiftModifier)) assert utils.keyevent_to_string(evt) == 'ctrl+alt+meta+shift+a' - def test_mac(self, monkeypatch, fake_keyevent_factory): + @pytest.mark.fake_os('mac') + def test_mac(self, fake_keyevent_factory): """Test with a simulated mac.""" - monkeypatch.setattr(sys, 'platform', 'darwin') evt = fake_keyevent_factory(key=Qt.Key_A, modifiers=Qt.ControlModifier) assert utils.keyevent_to_string(evt) == 'meta+a' diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index c3243862b..489806f8c 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -36,7 +36,7 @@ import attr import pytest import qutebrowser -from qutebrowser.utils import version, usertypes +from qutebrowser.utils import version, usertypes, utils from qutebrowser.browser import pdfjs @@ -333,7 +333,7 @@ class TestGitStrSubprocess: 'GIT_COMMITTER_EMAIL': 'mail@qutebrowser.org', 'GIT_COMMITTER_DATE': 'Thu 1 Jan 01:00:00 CET 1970', }) - if os.name == 'nt': + if utils.is_windows: # If we don't call this with shell=True it might fail under # some environments on Windows... # http://bugs.python.org/issue24493 @@ -662,12 +662,12 @@ class TestOsInfo: """Tests for _os_info.""" + @pytest.mark.fake_os('linux') def test_linux_fake(self, monkeypatch): """Test with a fake Linux. No args because osver is set to '' if the OS is linux. """ - monkeypatch.setattr(version.sys, 'platform', 'linux') monkeypatch.setattr(version, '_release_info', lambda: [('releaseinfo', 'Hello World')]) ret = version._os_info() @@ -675,15 +675,16 @@ class TestOsInfo: '--- releaseinfo ---', 'Hello World'] assert ret == expected + @pytest.mark.fake_os('windows') def test_windows_fake(self, monkeypatch): """Test with a fake Windows.""" - monkeypatch.setattr(version.sys, 'platform', 'win32') monkeypatch.setattr(version.platform, 'win32_ver', lambda: ('eggs', 'bacon', 'ham', 'spam')) ret = version._os_info() expected = ['OS Version: eggs, bacon, ham, spam'] assert ret == expected + @pytest.mark.fake_os('mac') @pytest.mark.parametrize('mac_ver, mac_ver_str', [ (('x', ('', '', ''), 'y'), 'x, y'), (('', ('', '', ''), ''), ''), @@ -696,15 +697,14 @@ class TestOsInfo: mac_ver: The tuple to set platform.mac_ver() to. mac_ver_str: The expected Mac version string in version._os_info(). """ - monkeypatch.setattr(version.sys, 'platform', 'darwin') monkeypatch.setattr(version.platform, 'mac_ver', lambda: mac_ver) ret = version._os_info() expected = ['OS Version: {}'.format(mac_ver_str)] assert ret == expected - def test_unknown_fake(self, monkeypatch): - """Test with a fake unknown sys.platform.""" - monkeypatch.setattr(version.sys, 'platform', 'toaster') + @pytest.mark.fake_os('unknown') + def test_unknown_fake(self): + """Test with a fake unknown platform.""" ret = version._os_info() expected = ['OS Version: ?'] assert ret == expected