From 3096f3856a182171187f05550d8b9214e3445dec Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Apr 2015 20:50:20 -0300 Subject: [PATCH 01/47] Implemented test for TextBase widget --- .../mainwindow/statusbar/test_textbase.py | 46 +++++++++++++++++++ tox.ini | 2 + 2 files changed, 48 insertions(+) create mode 100644 qutebrowser/test/mainwindow/statusbar/test_textbase.py diff --git a/qutebrowser/test/mainwindow/statusbar/test_textbase.py b/qutebrowser/test/mainwindow/statusbar/test_textbase.py new file mode 100644 index 000000000..1c1082783 --- /dev/null +++ b/qutebrowser/test/mainwindow/statusbar/test_textbase.py @@ -0,0 +1,46 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + + +"""Test TextBase widget.""" + +from qutebrowser.mainwindow.statusbar.textbase import TextBase + + +def test_elided_text(qtbot): + """ + Ensure that a widget that can't hold the entire label text will display + and elided version of the string instead. + + Args: + qtbot: pytestqt.plugin.QtBot fixture + + Note: + + It is difficult to check what is actually being drawn in a + portable way, so at least we ensure our customized methods are being + called and the elided string contains the horizontal ellipsis character. + """ + label = TextBase() + qtbot.add_widget(label) + long_string = 'Hello world! ' * 20 + label.setText(long_string) + label.resize(100, 50) + label.show() + assert '…' in label._elided_text diff --git a/tox.ini b/tox.ini index 3743eae91..994f2b089 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,7 @@ setenv = QT_QPA_PLATFORM_PLUGIN_PATH={envsitepackagesdir}/PyQt5/plugins/platform deps = py==1.4.26 pytest==2.7.0 + pytest-qt==1.3.0 # We don't use {[testenv:mkvenv]commands} here because that seems to be broken # on Ubuntu Trusty. commands = @@ -30,6 +31,7 @@ deps = {[testenv:unittests]deps} coverage==3.7.1 pytest-cov==1.8.1 + pytest-qt==1.3.0 cov-core==1.15.0 commands = {[testenv:mkvenv]commands} From 1f08d8e319b64e91e7d3e584549d836551c9dfce Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Apr 2015 21:24:25 -0300 Subject: [PATCH 02/47] Implemented test for Percentage widget --- .../mainwindow/statusbar/test_percentage.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 qutebrowser/test/mainwindow/statusbar/test_percentage.py diff --git a/qutebrowser/test/mainwindow/statusbar/test_percentage.py b/qutebrowser/test/mainwindow/statusbar/test_percentage.py new file mode 100644 index 000000000..de1dbe03e --- /dev/null +++ b/qutebrowser/test/mainwindow/statusbar/test_percentage.py @@ -0,0 +1,46 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + + +"""Test Percentage widget.""" +import pytest + +from qutebrowser.mainwindow.statusbar.percentage import Percentage + + +@pytest.mark.parametrize('y, expected', [ + (0, '[top]'), + (100, '[bot]'), + (75, '[75%]'), + (25, '[25%]'), + (5, '[ 5%]'), +]) +def test_percentage_text(qtbot, y, expected): + """ + Test text displayed by the widget based on the y position of a page. + + Args: + qtbot: pytestqt.plugin.QtBot fixture + y: y position of the page as an int in the range [0, 100]. parametrized. + expected: expected text given y position. parametrized. + """ + percentage = Percentage() + qtbot.add_widget(percentage) + percentage.set_perc(None, y=y) + assert percentage.text() == expected From 79be5b0f4a2ba81162ca2cd595c5624e65e08300 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Apr 2015 22:39:25 -0300 Subject: [PATCH 03/47] Implemented test for Progress widget Also created a conftest file with a "default_config" fixture. --- qutebrowser/test/mainwindow/conftest.py | 34 +++++++++ .../mainwindow/statusbar/test_progress.py | 74 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 qutebrowser/test/mainwindow/conftest.py create mode 100644 qutebrowser/test/mainwindow/statusbar/test_progress.py diff --git a/qutebrowser/test/mainwindow/conftest.py b/qutebrowser/test/mainwindow/conftest.py new file mode 100644 index 000000000..bfa84d337 --- /dev/null +++ b/qutebrowser/test/mainwindow/conftest.py @@ -0,0 +1,34 @@ +""" +pytest fixtures and utilities for testing. + +Fixtures defined here will be visible to all test files in this directory and +below. +""" + +import pytest + +from qutebrowser.config.config import ConfigManager +from qutebrowser.utils import objreg + + +@pytest.yield_fixture +def default_config(): + """ + Fixture that registers an empty config object into the objreg module. + + Should be used by tests which create widgets that obtain their initial state + from the global config object. + + Note: + + If we declare this fixture like this: + + @pytest.yield_fixture(autouse=True) + + Then all tests below this file will have a default config registered + and ready for use. Is that desirable? + """ + config_obj = ConfigManager(configdir=None, fname=None, relaxed=True) + objreg.register('config', config_obj) + yield config_obj + objreg.delete('config') \ No newline at end of file diff --git a/qutebrowser/test/mainwindow/statusbar/test_progress.py b/qutebrowser/test/mainwindow/statusbar/test_progress.py new file mode 100644 index 000000000..3cc6e2e08 --- /dev/null +++ b/qutebrowser/test/mainwindow/statusbar/test_progress.py @@ -0,0 +1,74 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + + +"""Test Percentage widget.""" + +from collections import namedtuple + +import pytest + +from qutebrowser.browser import webview +from qutebrowser.mainwindow.statusbar.progress import Progress + + +@pytest.fixture +def progress_widget(qtbot, default_config): + """ + Creates a Progress widget and checks it initial state. + """ + widget = Progress() + qtbot.add_widget(widget) + assert not widget.isVisible() + assert not widget.isTextVisible() + return widget + + +def test_load_started(progress_widget): + """ + Args: + progress_widget: Progress widget that will be tested. + """ + progress_widget.on_load_started() + assert progress_widget.value() == 0 + assert progress_widget.isVisible() + + +# mock tab object +Tab = namedtuple('Tab', 'progress load_status') + + +@pytest.mark.parametrize('tab, expected_visible', [ + (Tab(15, webview.LoadStatus.loading), True), + (Tab(100, webview.LoadStatus.success), False), + (Tab(100, webview.LoadStatus.error), False), + (Tab(100, webview.LoadStatus.warn), False), + (Tab(100, webview.LoadStatus.none), False), +]) +def test_tab_changed(progress_widget, tab, expected_visible): + """ + Test that progress widget value and visibility state match expectations, + using a dummy Tab object. + + Args: + progress_widget: Progress widget that will be tested. + """ + progress_widget.on_tab_changed(tab) + assert (progress_widget.value(), progress_widget.isVisible()) == \ + (tab.progress, expected_visible) \ No newline at end of file From 47b9ea1f885655d6c86d1bfd79a2401ac00176da Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 2 Apr 2015 08:05:23 -0300 Subject: [PATCH 04/47] Fixing docstring typo in test_progress --- qutebrowser/test/mainwindow/statusbar/test_progress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/test/mainwindow/statusbar/test_progress.py b/qutebrowser/test/mainwindow/statusbar/test_progress.py index 3cc6e2e08..4a4038701 100644 --- a/qutebrowser/test/mainwindow/statusbar/test_progress.py +++ b/qutebrowser/test/mainwindow/statusbar/test_progress.py @@ -18,7 +18,7 @@ # along with qutebrowser. If not, see . -"""Test Percentage widget.""" +"""Test Progress widget.""" from collections import namedtuple From 751b62e344d85baf8182565425d79be230a08d78 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 2 Apr 2015 19:05:20 -0300 Subject: [PATCH 05/47] Moving logging and QApplication to conftest As discussed in #8 --- qutebrowser/test/__init__.py | 15 ---------- qutebrowser/test/conftest.py | 32 ++++++++++++++++++++++ qutebrowser/test/utils/test_standarddir.py | 18 ++++++------ tox.ini | 3 ++ 4 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 qutebrowser/test/conftest.py diff --git a/qutebrowser/test/__init__.py b/qutebrowser/test/__init__.py index 938eb6db3..cdb45eecc 100644 --- a/qutebrowser/test/__init__.py +++ b/qutebrowser/test/__init__.py @@ -18,18 +18,3 @@ # along with qutebrowser. If not, see . """The qutebrowser test suite.""" - -import atexit - -from PyQt5.QtWidgets import QApplication - -from qutebrowser.test import log - -# We create a singleton QApplication here. - -qApp = QApplication([]) -qApp.setApplicationName('qutebrowser') -qApp.processEvents() -atexit.register(qApp.processEvents) -atexit.register(qApp.quit) -log.init() diff --git a/qutebrowser/test/conftest.py b/qutebrowser/test/conftest.py new file mode 100644 index 000000000..93287d046 --- /dev/null +++ b/qutebrowser/test/conftest.py @@ -0,0 +1,32 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + +"""The qutebrowser test suite contest file.""" + +import pytest + + +@pytest.fixture(scope='session', autouse=True) +def app_and_logging(qapp): + """ + Initializes our logging system and ensures that a QApplication is created + and used by all tests. + """ + from .log import init + init() diff --git a/qutebrowser/test/utils/test_standarddir.py b/qutebrowser/test/utils/test_standarddir.py index 8703eed5d..b2008135b 100644 --- a/qutebrowser/test/utils/test_standarddir.py +++ b/qutebrowser/test/utils/test_standarddir.py @@ -26,12 +26,13 @@ import shutil import unittest import tempfile +from PyQt5.QtWidgets import QApplication + from qutebrowser.utils import standarddir -from qutebrowser.test import helpers, qApp +from qutebrowser.test import helpers class GetStandardDirLinuxTests(unittest.TestCase): - """Tests for standarddir under Linux. Attributes: @@ -41,8 +42,8 @@ class GetStandardDirLinuxTests(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.mkdtemp() - self.old_name = qApp.applicationName() - qApp.setApplicationName('qutebrowser') + self.old_name = QApplication.instance().applicationName() + QApplication.instance().setApplicationName('qutebrowser') @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_data_explicit(self): @@ -97,12 +98,11 @@ class GetStandardDirLinuxTests(unittest.TestCase): self.assertEqual(standarddir.cache(), expected) def tearDown(self): - qApp.setApplicationName(self.old_name) + QApplication.instance().setApplicationName(self.old_name) shutil.rmtree(self.temp_dir) class GetStandardDirWindowsTests(unittest.TestCase): - """Tests for standarddir under Windows. Attributes: @@ -110,13 +110,13 @@ class GetStandardDirWindowsTests(unittest.TestCase): """ def setUp(self): - self.old_name = qApp.applicationName() + self.old_name = QApplication.instance().applicationName() # We can't store the files in a temp dir, so we don't chose qutebrowser - qApp.setApplicationName('qutebrowser_test') + QApplication.instance().setApplicationName('qutebrowser_test') standarddir.init(None) def tearDown(self): - qApp.setApplicationName(self.old_name) + QApplication.instance().setApplicationName(self.old_name) @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_data(self): diff --git a/tox.ini b/tox.ini index 994f2b089..18d5e0529 100644 --- a/tox.ini +++ b/tox.ini @@ -103,3 +103,6 @@ commands = {envpython} scripts/src2asciidoc.py git --no-pager diff --exit-code --stat {envpython} scripts/asciidoc2html.py {posargs} + +[pytest] +norecursedirs = .tox .venv From 298892a4a8a254280c97417b63cb44c654d2788e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 2 Apr 2015 19:45:52 -0300 Subject: [PATCH 06/47] Converted test_standarddir to pytest related to #10 --- qutebrowser/test/utils/test_standarddir.py | 135 +++++++++------------ 1 file changed, 56 insertions(+), 79 deletions(-) diff --git a/qutebrowser/test/utils/test_standarddir.py b/qutebrowser/test/utils/test_standarddir.py index b2008135b..e80b38015 100644 --- a/qutebrowser/test/utils/test_standarddir.py +++ b/qutebrowser/test/utils/test_standarddir.py @@ -22,117 +22,94 @@ import os import os.path import sys -import shutil -import unittest -import tempfile from PyQt5.QtWidgets import QApplication +import pytest from qutebrowser.utils import standarddir -from qutebrowser.test import helpers -class GetStandardDirLinuxTests(unittest.TestCase): +@pytest.yield_fixture(autouse=True) +def change_qapp_name(): + """ + Change the name of the QApplication instance for all tests in this module + to "qutebrowser_test". + """ + old_name = QApplication.instance().applicationName() + QApplication.instance().setApplicationName('qutebrowser_test') + yield + QApplication.instance().setApplicationName(old_name) + + +@pytest.mark.skipif(not sys.platform.startswith("linux"), + reason="requires Linux") +class TestGetStandardDirLinux(object): """Tests for standarddir under Linux. - - Attributes: - temp_dir: A temporary directory. - old_name: The old applicationName. """ - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - self.old_name = QApplication.instance().applicationName() - QApplication.instance().setApplicationName('qutebrowser') - - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_data_explicit(self): + def test_data_explicit(self, monkeypatch, tmpdir): """Test data dir with XDG_DATA_HOME explicitely set.""" - with helpers.environ_set_temp({'XDG_DATA_HOME': self.temp_dir}): - standarddir.init(None) - expected = os.path.join(self.temp_dir, 'qutebrowser') - self.assertEqual(standarddir.data(), expected) + monkeypatch.setenv('XDG_DATA_HOME', str(tmpdir)) + standarddir.init(None) + assert standarddir.data() == str(tmpdir / 'qutebrowser') - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_config_explicit(self): + def test_config_explicit(self, monkeypatch, tmpdir): """Test config dir with XDG_CONFIG_HOME explicitely set.""" - with helpers.environ_set_temp({'XDG_CONFIG_HOME': self.temp_dir}): - standarddir.init(None) - expected = os.path.join(self.temp_dir, 'qutebrowser') - self.assertEqual(standarddir.config(), expected) + monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir)) + standarddir.init(None) + assert standarddir.config() == str(tmpdir / 'qutebrowser') - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_cache_explicit(self): + def test_cache_explicit(self, monkeypatch, tmpdir): """Test cache dir with XDG_CACHE_HOME explicitely set.""" - with helpers.environ_set_temp({'XDG_CACHE_HOME': self.temp_dir}): - standarddir.init(None) - expected = os.path.join(self.temp_dir, 'qutebrowser') - self.assertEqual(standarddir.cache(), expected) + monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir)) + standarddir.init(None) + assert standarddir.cache() == str(tmpdir / 'qutebrowser') - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_data(self): + def test_data(self, monkeypatch, tmpdir): """Test data dir with XDG_DATA_HOME not set.""" - env = {'HOME': self.temp_dir, 'XDG_DATA_HOME': None} - with helpers.environ_set_temp(env): - standarddir.init(None) - expected = os.path.join(self.temp_dir, '.local', 'share', - 'qutebrowser') - self.assertEqual(standarddir.data(), expected) + monkeypatch.setenv('HOME', str(tmpdir)) + monkeypatch.setenv('XDG_DATA_HOME', None) + standarddir.init(None) + expected = tmpdir / '.local' / 'share' / 'qutebrowser' + assert standarddir.data() == str(expected) - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_config(self): + def test_config(self, monkeypatch, tmpdir): """Test config dir with XDG_CONFIG_HOME not set.""" - env = {'HOME': self.temp_dir, 'XDG_CONFIG_HOME': None} - with helpers.environ_set_temp(env): - standarddir.init(None) - expected = os.path.join(self.temp_dir, '.config', 'qutebrowser') - self.assertEqual(standarddir.config(), expected) + monkeypatch.setenv('HOME', str(tmpdir)) + monkeypatch.setenv('XDG_CONFIG_HOME', None) + standarddir.init(None) + expected = tmpdir / '.config' / 'qutebrowser' + assert standarddir.config() == str(expected) - @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") - def test_cache(self): + def test_cache(self, monkeypatch, tmpdir): """Test cache dir with XDG_CACHE_HOME not set.""" - env = {'HOME': self.temp_dir, 'XDG_CACHE_HOME': None} - with helpers.environ_set_temp(env): - standarddir.init(None) - expected = os.path.join(self.temp_dir, '.cache', 'qutebrowser') - self.assertEqual(standarddir.cache(), expected) - - def tearDown(self): - QApplication.instance().setApplicationName(self.old_name) - shutil.rmtree(self.temp_dir) + monkeypatch.setenv('HOME', str(tmpdir)) + monkeypatch.setenv('XDG_CACHE_HOME', None) + standarddir.init(None) + expected = tmpdir / '.cache' / 'qutebrowser' + assert standarddir.cache() == expected -class GetStandardDirWindowsTests(unittest.TestCase): +@pytest.mark.skipif(not sys.platform.startswith("win"), + reason="requires Windows") +class TestGetStandardDirWindows(object): """Tests for standarddir under Windows. - - Attributes: - old_name: The old applicationName. """ - def setUp(self): - self.old_name = QApplication.instance().applicationName() - # We can't store the files in a temp dir, so we don't chose qutebrowser - QApplication.instance().setApplicationName('qutebrowser_test') + @pytest.fixture(autouse=True) + def reset_standarddir(self): standarddir.init(None) - def tearDown(self): - QApplication.instance().setApplicationName(self.old_name) - - @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_data(self): """Test data dir.""" - self.assertEqual(standarddir.data().split(os.sep)[-2:], - ['qutebrowser_test', 'data'], standarddir.data()) + expected = ['qutebrowser_test', 'data'] + assert standarddir.data().split(os.sep)[-2:] == expected - @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_config(self): """Test config dir.""" - self.assertEqual(standarddir.config().split(os.sep)[-1], - 'qutebrowser_test', - standarddir.config()) + assert standarddir.config().split(os.sep)[-1] == 'qutebrowser_test' - @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_cache(self): """Test cache dir.""" - self.assertEqual(standarddir.cache().split(os.sep)[-2:], - ['qutebrowser_test', 'cache'], standarddir.cache()) + expected = ['qutebrowser_test', 'cache'] + assert standarddir.cache().split(os.sep)[-2:] == expected From 9c533e1941db4677c7edd77c9c0034ab5463bfd6 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 16:49:01 -0300 Subject: [PATCH 07/47] Moved tests to outside of qutebrowser package --- qutebrowser/test/__init__.py | 20 ------------------- qutebrowser/test/browser/__init__.py | 20 ------------------- qutebrowser/test/browser/http/__init__.py | 20 ------------------- qutebrowser/test/config/__init__.py | 1 - qutebrowser/test/keyinput/__init__.py | 1 - qutebrowser/test/misc/__init__.py | 20 ------------------- qutebrowser/test/utils/__init__.py | 20 ------------------- qutebrowser/test/utils/usertypes/__init__.py | 20 ------------------- .../browser/http/test_content_disposition.py | 0 .../test => test}/browser/http/test_http.py | 0 .../test => test}/browser/test_tabhistory.py | 0 .../test => test}/browser/test_webelem.py | 0 .../test => test}/config/test_config.py | 0 .../test => test}/config/test_configtypes.py | 0 {qutebrowser/test => test}/conftest.py | 2 +- {qutebrowser/test => test}/helpers.py | 0 .../keyinput/test_basekeyparser.py | 0 .../keyinput/test_modeparsers.py | 0 {qutebrowser/test => test}/log.py | 0 .../test => test}/mainwindow/conftest.py | 0 .../mainwindow/statusbar/test_percentage.py | 0 .../mainwindow/statusbar/test_progress.py | 0 .../mainwindow/statusbar/test_textbase.py | 0 .../test => test}/misc/test_crashdialog.py | 0 .../test => test}/misc/test_editor.py | 0 .../test => test}/misc/test_lineparser.py | 0 .../test => test}/misc/test_readline.py | 0 {qutebrowser/test => test}/misc/test_split.py | 0 {qutebrowser/test => test}/stubs.py | 0 {qutebrowser/test => test}/test_helpers.py | 0 {qutebrowser/test => test}/test_stubs.py | 0 {qutebrowser/test => test}/testfile | 0 .../test => test}/utils/test_debug.py | 0 .../test => test}/utils/test_jinja.py | 0 {qutebrowser/test => test}/utils/test_log.py | 0 .../test => test}/utils/test_qtutils.py | 0 .../test => test}/utils/test_standarddir.py | 0 .../test => test}/utils/test_urlutils.py | 0 .../test => test}/utils/test_utils.py | 0 .../utils/usertypes/test_enum.py | 0 .../utils/usertypes/test_neighborlist.py | 0 41 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 qutebrowser/test/__init__.py delete mode 100644 qutebrowser/test/browser/__init__.py delete mode 100644 qutebrowser/test/browser/http/__init__.py delete mode 100644 qutebrowser/test/config/__init__.py delete mode 100644 qutebrowser/test/keyinput/__init__.py delete mode 100644 qutebrowser/test/misc/__init__.py delete mode 100644 qutebrowser/test/utils/__init__.py delete mode 100644 qutebrowser/test/utils/usertypes/__init__.py rename {qutebrowser/test => test}/browser/http/test_content_disposition.py (100%) rename {qutebrowser/test => test}/browser/http/test_http.py (100%) rename {qutebrowser/test => test}/browser/test_tabhistory.py (100%) rename {qutebrowser/test => test}/browser/test_webelem.py (100%) rename {qutebrowser/test => test}/config/test_config.py (100%) rename {qutebrowser/test => test}/config/test_configtypes.py (100%) rename {qutebrowser/test => test}/conftest.py (97%) rename {qutebrowser/test => test}/helpers.py (100%) rename {qutebrowser/test => test}/keyinput/test_basekeyparser.py (100%) rename {qutebrowser/test => test}/keyinput/test_modeparsers.py (100%) rename {qutebrowser/test => test}/log.py (100%) rename {qutebrowser/test => test}/mainwindow/conftest.py (100%) rename {qutebrowser/test => test}/mainwindow/statusbar/test_percentage.py (100%) rename {qutebrowser/test => test}/mainwindow/statusbar/test_progress.py (100%) rename {qutebrowser/test => test}/mainwindow/statusbar/test_textbase.py (100%) rename {qutebrowser/test => test}/misc/test_crashdialog.py (100%) rename {qutebrowser/test => test}/misc/test_editor.py (100%) rename {qutebrowser/test => test}/misc/test_lineparser.py (100%) rename {qutebrowser/test => test}/misc/test_readline.py (100%) rename {qutebrowser/test => test}/misc/test_split.py (100%) rename {qutebrowser/test => test}/stubs.py (100%) rename {qutebrowser/test => test}/test_helpers.py (100%) rename {qutebrowser/test => test}/test_stubs.py (100%) rename {qutebrowser/test => test}/testfile (100%) rename {qutebrowser/test => test}/utils/test_debug.py (100%) rename {qutebrowser/test => test}/utils/test_jinja.py (100%) rename {qutebrowser/test => test}/utils/test_log.py (100%) rename {qutebrowser/test => test}/utils/test_qtutils.py (100%) rename {qutebrowser/test => test}/utils/test_standarddir.py (100%) rename {qutebrowser/test => test}/utils/test_urlutils.py (100%) rename {qutebrowser/test => test}/utils/test_utils.py (100%) rename {qutebrowser/test => test}/utils/usertypes/test_enum.py (100%) rename {qutebrowser/test => test}/utils/usertypes/test_neighborlist.py (100%) diff --git a/qutebrowser/test/__init__.py b/qutebrowser/test/__init__.py deleted file mode 100644 index cdb45eecc..000000000 --- a/qutebrowser/test/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""The qutebrowser test suite.""" diff --git a/qutebrowser/test/browser/__init__.py b/qutebrowser/test/browser/__init__.py deleted file mode 100644 index 801293531..000000000 --- a/qutebrowser/test/browser/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""Tests for the qutebrowser.browser package.""" diff --git a/qutebrowser/test/browser/http/__init__.py b/qutebrowser/test/browser/http/__init__.py deleted file mode 100644 index e1643dbb8..000000000 --- a/qutebrowser/test/browser/http/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""Tests for the qutebrowser.browser.http module.""" diff --git a/qutebrowser/test/config/__init__.py b/qutebrowser/test/config/__init__.py deleted file mode 100644 index e55e9df60..000000000 --- a/qutebrowser/test/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for qutebrowser.config.""" diff --git a/qutebrowser/test/keyinput/__init__.py b/qutebrowser/test/keyinput/__init__.py deleted file mode 100644 index 793f3d99b..000000000 --- a/qutebrowser/test/keyinput/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for qutebrowser.keyinput.""" diff --git a/qutebrowser/test/misc/__init__.py b/qutebrowser/test/misc/__init__.py deleted file mode 100644 index 667e8201d..000000000 --- a/qutebrowser/test/misc/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""The qutebrowser test suite.""" diff --git a/qutebrowser/test/utils/__init__.py b/qutebrowser/test/utils/__init__.py deleted file mode 100644 index 292d89a9d..000000000 --- a/qutebrowser/test/utils/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""Tests for the qutebrowser.utils package.""" diff --git a/qutebrowser/test/utils/usertypes/__init__.py b/qutebrowser/test/utils/usertypes/__init__.py deleted file mode 100644 index 9dc19e4ab..000000000 --- a/qutebrowser/test/utils/usertypes/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""Tests for qutebrowser.utils.usertype.""" diff --git a/qutebrowser/test/browser/http/test_content_disposition.py b/test/browser/http/test_content_disposition.py similarity index 100% rename from qutebrowser/test/browser/http/test_content_disposition.py rename to test/browser/http/test_content_disposition.py diff --git a/qutebrowser/test/browser/http/test_http.py b/test/browser/http/test_http.py similarity index 100% rename from qutebrowser/test/browser/http/test_http.py rename to test/browser/http/test_http.py diff --git a/qutebrowser/test/browser/test_tabhistory.py b/test/browser/test_tabhistory.py similarity index 100% rename from qutebrowser/test/browser/test_tabhistory.py rename to test/browser/test_tabhistory.py diff --git a/qutebrowser/test/browser/test_webelem.py b/test/browser/test_webelem.py similarity index 100% rename from qutebrowser/test/browser/test_webelem.py rename to test/browser/test_webelem.py diff --git a/qutebrowser/test/config/test_config.py b/test/config/test_config.py similarity index 100% rename from qutebrowser/test/config/test_config.py rename to test/config/test_config.py diff --git a/qutebrowser/test/config/test_configtypes.py b/test/config/test_configtypes.py similarity index 100% rename from qutebrowser/test/config/test_configtypes.py rename to test/config/test_configtypes.py diff --git a/qutebrowser/test/conftest.py b/test/conftest.py similarity index 97% rename from qutebrowser/test/conftest.py rename to test/conftest.py index 93287d046..6bef0824c 100644 --- a/qutebrowser/test/conftest.py +++ b/test/conftest.py @@ -28,5 +28,5 @@ def app_and_logging(qapp): Initializes our logging system and ensures that a QApplication is created and used by all tests. """ - from .log import init + from log import init init() diff --git a/qutebrowser/test/helpers.py b/test/helpers.py similarity index 100% rename from qutebrowser/test/helpers.py rename to test/helpers.py diff --git a/qutebrowser/test/keyinput/test_basekeyparser.py b/test/keyinput/test_basekeyparser.py similarity index 100% rename from qutebrowser/test/keyinput/test_basekeyparser.py rename to test/keyinput/test_basekeyparser.py diff --git a/qutebrowser/test/keyinput/test_modeparsers.py b/test/keyinput/test_modeparsers.py similarity index 100% rename from qutebrowser/test/keyinput/test_modeparsers.py rename to test/keyinput/test_modeparsers.py diff --git a/qutebrowser/test/log.py b/test/log.py similarity index 100% rename from qutebrowser/test/log.py rename to test/log.py diff --git a/qutebrowser/test/mainwindow/conftest.py b/test/mainwindow/conftest.py similarity index 100% rename from qutebrowser/test/mainwindow/conftest.py rename to test/mainwindow/conftest.py diff --git a/qutebrowser/test/mainwindow/statusbar/test_percentage.py b/test/mainwindow/statusbar/test_percentage.py similarity index 100% rename from qutebrowser/test/mainwindow/statusbar/test_percentage.py rename to test/mainwindow/statusbar/test_percentage.py diff --git a/qutebrowser/test/mainwindow/statusbar/test_progress.py b/test/mainwindow/statusbar/test_progress.py similarity index 100% rename from qutebrowser/test/mainwindow/statusbar/test_progress.py rename to test/mainwindow/statusbar/test_progress.py diff --git a/qutebrowser/test/mainwindow/statusbar/test_textbase.py b/test/mainwindow/statusbar/test_textbase.py similarity index 100% rename from qutebrowser/test/mainwindow/statusbar/test_textbase.py rename to test/mainwindow/statusbar/test_textbase.py diff --git a/qutebrowser/test/misc/test_crashdialog.py b/test/misc/test_crashdialog.py similarity index 100% rename from qutebrowser/test/misc/test_crashdialog.py rename to test/misc/test_crashdialog.py diff --git a/qutebrowser/test/misc/test_editor.py b/test/misc/test_editor.py similarity index 100% rename from qutebrowser/test/misc/test_editor.py rename to test/misc/test_editor.py diff --git a/qutebrowser/test/misc/test_lineparser.py b/test/misc/test_lineparser.py similarity index 100% rename from qutebrowser/test/misc/test_lineparser.py rename to test/misc/test_lineparser.py diff --git a/qutebrowser/test/misc/test_readline.py b/test/misc/test_readline.py similarity index 100% rename from qutebrowser/test/misc/test_readline.py rename to test/misc/test_readline.py diff --git a/qutebrowser/test/misc/test_split.py b/test/misc/test_split.py similarity index 100% rename from qutebrowser/test/misc/test_split.py rename to test/misc/test_split.py diff --git a/qutebrowser/test/stubs.py b/test/stubs.py similarity index 100% rename from qutebrowser/test/stubs.py rename to test/stubs.py diff --git a/qutebrowser/test/test_helpers.py b/test/test_helpers.py similarity index 100% rename from qutebrowser/test/test_helpers.py rename to test/test_helpers.py diff --git a/qutebrowser/test/test_stubs.py b/test/test_stubs.py similarity index 100% rename from qutebrowser/test/test_stubs.py rename to test/test_stubs.py diff --git a/qutebrowser/test/testfile b/test/testfile similarity index 100% rename from qutebrowser/test/testfile rename to test/testfile diff --git a/qutebrowser/test/utils/test_debug.py b/test/utils/test_debug.py similarity index 100% rename from qutebrowser/test/utils/test_debug.py rename to test/utils/test_debug.py diff --git a/qutebrowser/test/utils/test_jinja.py b/test/utils/test_jinja.py similarity index 100% rename from qutebrowser/test/utils/test_jinja.py rename to test/utils/test_jinja.py diff --git a/qutebrowser/test/utils/test_log.py b/test/utils/test_log.py similarity index 100% rename from qutebrowser/test/utils/test_log.py rename to test/utils/test_log.py diff --git a/qutebrowser/test/utils/test_qtutils.py b/test/utils/test_qtutils.py similarity index 100% rename from qutebrowser/test/utils/test_qtutils.py rename to test/utils/test_qtutils.py diff --git a/qutebrowser/test/utils/test_standarddir.py b/test/utils/test_standarddir.py similarity index 100% rename from qutebrowser/test/utils/test_standarddir.py rename to test/utils/test_standarddir.py diff --git a/qutebrowser/test/utils/test_urlutils.py b/test/utils/test_urlutils.py similarity index 100% rename from qutebrowser/test/utils/test_urlutils.py rename to test/utils/test_urlutils.py diff --git a/qutebrowser/test/utils/test_utils.py b/test/utils/test_utils.py similarity index 100% rename from qutebrowser/test/utils/test_utils.py rename to test/utils/test_utils.py diff --git a/qutebrowser/test/utils/usertypes/test_enum.py b/test/utils/usertypes/test_enum.py similarity index 100% rename from qutebrowser/test/utils/usertypes/test_enum.py rename to test/utils/usertypes/test_enum.py diff --git a/qutebrowser/test/utils/usertypes/test_neighborlist.py b/test/utils/usertypes/test_neighborlist.py similarity index 100% rename from qutebrowser/test/utils/usertypes/test_neighborlist.py rename to test/utils/usertypes/test_neighborlist.py From 3421e5e34f34135185bc5578c303a29cdf25c968 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 18:12:49 -0300 Subject: [PATCH 08/47] Created stubs fixture and converted test_stubs to pytest --- test/conftest.py | 9 +++ test/test_stubs.py | 160 +++++++++++++++++++++++---------------------- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 6bef0824c..7ce7214bc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -30,3 +30,12 @@ def app_and_logging(qapp): """ from log import init init() + + +@pytest.fixture(scope='session') +def stubs(): + """ + Provides access to stub objects useful for testing. + """ + import stubs + return stubs diff --git a/test/test_stubs.py b/test/test_stubs.py index 476c43774..e99f8e7b9 100644 --- a/test/test_stubs.py +++ b/test/test_stubs.py @@ -20,88 +20,90 @@ """Test test stubs.""" -import unittest from unittest import mock -from qutebrowser.test import stubs +import pytest -class TestFakeTimer(unittest.TestCase): - - """Test FakeTimer.""" - - def setUp(self): - self.timer = stubs.FakeTimer() - - def test_timeout(self): - """Test whether timeout calls the functions.""" - func = mock.Mock() - func2 = mock.Mock() - self.timer.timeout.connect(func) - self.timer.timeout.connect(func2) - self.assertFalse(func.called) - self.assertFalse(func2.called) - self.timer.timeout.emit() - func.assert_called_once_with() - func2.assert_called_once_with() - - def test_disconnect_all(self): - """Test disconnect without arguments.""" - func = mock.Mock() - self.timer.timeout.connect(func) - self.timer.timeout.disconnect() - self.timer.timeout.emit() - self.assertFalse(func.called) - - def test_disconnect_one(self): - """Test disconnect with a single argument.""" - func = mock.Mock() - self.timer.timeout.connect(func) - self.timer.timeout.disconnect(func) - self.timer.timeout.emit() - self.assertFalse(func.called) - - def test_disconnect_all_invalid(self): - """Test disconnecting with no connections.""" - with self.assertRaises(TypeError): - self.timer.timeout.disconnect() - - def test_disconnect_one_invalid(self): - """Test disconnecting with an invalid connection.""" - func1 = mock.Mock() - func2 = mock.Mock() - self.timer.timeout.connect(func1) - with self.assertRaises(TypeError): - self.timer.timeout.disconnect(func2) - self.assertFalse(func1.called) - self.assertFalse(func2.called) - self.timer.timeout.emit() - func1.assert_called_once_with() - - def test_singleshot(self): - """Test setting singleShot.""" - self.assertFalse(self.timer.singleShot()) - self.timer.setSingleShot(True) - self.assertTrue(self.timer.singleShot()) - self.timer.start() - self.assertTrue(self.timer.isActive()) - self.timer.timeout.emit() - self.assertFalse(self.timer.isActive()) - - def test_active(self): - """Test isActive.""" - self.assertFalse(self.timer.isActive()) - self.timer.start() - self.assertTrue(self.timer.isActive()) - self.timer.stop() - self.assertFalse(self.timer.isActive()) - - def test_interval(self): - """Test setting an interval.""" - self.assertEqual(self.timer.interval(), 0) - self.timer.setInterval(1000) - self.assertEqual(self.timer.interval(), 1000) +@pytest.fixture +def timer(stubs): + return stubs.FakeTimer() + + +def test_timeout(timer): + """Test whether timeout calls the functions.""" + func = mock.Mock() + func2 = mock.Mock() + timer.timeout.connect(func) + timer.timeout.connect(func2) + assert not func.called + assert not func2.called + timer.timeout.emit() + func.assert_called_once_with() + func2.assert_called_once_with() + + +def test_disconnect_all(timer): + """Test disconnect without arguments.""" + func = mock.Mock() + timer.timeout.connect(func) + timer.timeout.disconnect() + timer.timeout.emit() + assert not func.called + + +def test_disconnect_one(timer): + """Test disconnect with a single argument.""" + func = mock.Mock() + timer.timeout.connect(func) + timer.timeout.disconnect(func) + timer.timeout.emit() + assert not func.called + + +def test_disconnect_all_invalid(timer): + """Test disconnecting with no connections.""" + with pytest.raises(TypeError): + timer.timeout.disconnect() + + +def test_disconnect_one_invalid(timer): + """Test disconnecting with an invalid connection.""" + func1 = mock.Mock() + func2 = mock.Mock() + timer.timeout.connect(func1) + with pytest.raises(TypeError): + timer.timeout.disconnect(func2) + assert not func1.called + assert not func2.called + timer.timeout.emit() + func1.assert_called_once_with() + + +def test_singleshot(timer): + """Test setting singleShot.""" + assert not timer.singleShot() + timer.setSingleShot(True) + assert timer.singleShot() + timer.start() + assert timer.isActive() + timer.timeout.emit() + assert not timer.isActive() + + +def test_active(timer): + """Test isActive.""" + assert not timer.isActive() + timer.start() + assert timer.isActive() + timer.stop() + assert not timer.isActive() + + +def test_interval(timer): + """Test setting an interval.""" + assert timer.interval() == 0 + timer.setInterval(1000) + assert timer.interval() == 1000 -if __name__ == '__main__': - unittest.main() From f57223f7eb34feccd1b91d9a6efb2ab6edd6c994 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 18:35:40 -0300 Subject: [PATCH 09/47] Removed environ_set_temp as we will use monkeypatch --- test/helpers.py | 29 --------------- test/test_helpers.py | 84 -------------------------------------------- 2 files changed, 113 deletions(-) delete mode 100644 test/test_helpers.py diff --git a/test/helpers.py b/test/helpers.py index 23ef2b25e..838cc88af 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -36,35 +36,6 @@ unicode_encode_err = UnicodeEncodeError('ascii', # codec 'fake exception') # reason -@contextlib.contextmanager -def environ_set_temp(env): - """Set temporary environment variables. - - Args: - env: A dictionary with name: value pairs. - If value is None, the variable is temporarily deleted. - """ - old_env = {} - - for name, value in env.items(): - try: - old_env[name] = os.environ[name] - except KeyError: - pass - if value is None: - os.environ.pop(name, None) - else: - os.environ[name] = value - - yield - - for name, value in env.items(): - if name in old_env: - os.environ[name] = old_env[name] - elif value is not None: - del os.environ[name] - - @contextlib.contextmanager def disable_logger(name): """Temporarily disable a logger.""" diff --git a/test/test_helpers.py b/test/test_helpers.py deleted file mode 100644 index 13e224374..000000000 --- a/test/test_helpers.py +++ /dev/null @@ -1,84 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - - -"""Test test helpers.""" - -import os -import unittest - -from qutebrowser.test import helpers - - -class TestEnvironSetTemp(unittest.TestCase): - - """Test the environ_set_temp helper.""" - - def test_environ_set(self): - """Test environ_set_temp with something which was set already.""" - os.environ['QUTEBROWSER_ENVIRON_TEST'] = 'oldval' - with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': 'newval'}): - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'newval') - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'oldval') - - def test_environ_unset(self): - """Test environ_set_temp with something which wasn't set yet.""" - with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': 'newval'}): - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'newval') - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ) - - def test_environ_multiple(self): - """Test environ_set_temp with multiple values.""" - os.environ['QUTEBROWSER_ENVIRON_TEST_1'] = 'oldval_1' - os.environ['QUTEBROWSER_ENVIRON_TEST_3'] = 'oldval_3' - env = { - 'QUTEBROWSER_ENVIRON_TEST_1': 'newval_1', - 'QUTEBROWSER_ENVIRON_TEST_2': 'newval_2', - 'QUTEBROWSER_ENVIRON_TEST_3': None, - } - with helpers.environ_set_temp(env): - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_1'], - 'newval_1') - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_2'], - 'newval_2') - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST_3', os.environ) - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_1'], 'oldval_1') - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST_2', os.environ) - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_3'], 'oldval_3') - - def test_environ_none_set(self): - """Test environ_set_temp with something which was set already.""" - os.environ['QUTEBROWSER_ENVIRON_TEST'] = 'oldval' - with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': None}): - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ) - self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'oldval') - - def test_environ_none_unset(self): - """Test environ_set_temp with something which wasn't set yet.""" - with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': None}): - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ) - self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ) - - def tearDown(self): - if 'QUTEBROWSER_ENVIRON_TEST' in os.environ: - # if some test failed - del os.environ['QUTEBROWSER_ENVIRON_TEST'] - -if __name__ == '__main__': - unittest.main() From 7442e30f294c2bf48fadcacc3935322dc7a0c0d1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 18:36:35 -0300 Subject: [PATCH 10/47] Converted test_webelem to pytest --- test/browser/test_webelem.py | 261 +++++++++++++++++------------------ 1 file changed, 130 insertions(+), 131 deletions(-) diff --git a/test/browser/test_webelem.py b/test/browser/test_webelem.py index bc423521b..54c1ba051 100644 --- a/test/browser/test_webelem.py +++ b/test/browser/test_webelem.py @@ -21,15 +21,14 @@ """Tests for the webelement utils.""" -import unittest from unittest import mock import collections.abc from PyQt5.QtCore import QRect, QPoint from PyQt5.QtWebKit import QWebElement +import pytest from qutebrowser.browser import webelem -from qutebrowser.test import stubs def get_webelem(geometry=None, frame=None, null=False, visibility='', @@ -87,17 +86,17 @@ def get_webelem(geometry=None, frame=None, null=False, visibility='', return wrapped -class WebElementWrapperTests(unittest.TestCase): +class TestWebElementWrapper(object): """Test WebElementWrapper.""" def test_nullelem(self): """Test __init__ with a null element.""" - with self.assertRaises(webelem.IsNullError): + with pytest.raises(webelem.IsNullError): get_webelem(null=True) -class IsVisibleInvalidTests(unittest.TestCase): +class TestIsVisibleInvalid(object): """Tests for is_visible with invalid elements. @@ -105,7 +104,8 @@ class IsVisibleInvalidTests(unittest.TestCase): frame: The FakeWebFrame we're using to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, stubs): self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100)) def test_nullelem(self): @@ -116,15 +116,15 @@ class IsVisibleInvalidTests(unittest.TestCase): """ elem = get_webelem() elem._elem.isNull.return_value = True - with self.assertRaises(webelem.IsNullError): + with pytest.raises(webelem.IsNullError): elem.is_visible(self.frame) def test_invalid_invisible(self): """Test elements with an invalid geometry which are invisible.""" elem = get_webelem(QRect(0, 0, 0, 0), self.frame) - self.assertFalse(elem.geometry().isValid()) - self.assertEqual(elem.geometry().x(), 0) - self.assertFalse(elem.is_visible(self.frame)) + assert not elem.geometry().isValid() + assert elem.geometry().x() == 0 + assert not elem.is_visible(self.frame) def test_invalid_visible(self): """Test elements with an invalid geometry which are visible. @@ -133,11 +133,11 @@ class IsVisibleInvalidTests(unittest.TestCase): which *are* visible, but don't have a valid geometry. """ elem = get_webelem(QRect(10, 10, 0, 0), self.frame) - self.assertFalse(elem.geometry().isValid()) - self.assertTrue(elem.is_visible(self.frame)) + assert not elem.geometry().isValid() + assert elem.is_visible(self.frame) -class IsVisibleScrollTests(unittest.TestCase): +class TestIsVisibleScroll(object): """Tests for is_visible when the frame is scrolled. @@ -145,22 +145,23 @@ class IsVisibleScrollTests(unittest.TestCase): frame: The FakeWebFrame we're using to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, stubs): self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100), scroll=QPoint(10, 10)) def test_invisible(self): """Test elements which should be invisible due to scrolling.""" elem = get_webelem(QRect(5, 5, 4, 4), self.frame) - self.assertFalse(elem.is_visible(self.frame)) + assert not elem.is_visible(self.frame) def test_visible(self): """Test elements which still should be visible after scrolling.""" elem = get_webelem(QRect(10, 10, 1, 1), self.frame) - self.assertTrue(elem.is_visible(self.frame)) + assert elem.is_visible(self.frame) -class IsVisibleCssTests(unittest.TestCase): +class TestIsVisibleCss(object): """Tests for is_visible with CSS attributes. @@ -168,33 +169,34 @@ class IsVisibleCssTests(unittest.TestCase): frame: The FakeWebFrame we're using to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, stubs): self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100)) def test_visibility_visible(self): """Check that elements with "visibility = visible" are visible.""" elem = get_webelem(QRect(0, 0, 10, 10), self.frame, visibility='visible') - self.assertTrue(elem.is_visible(self.frame)) + assert elem.is_visible(self.frame) def test_visibility_hidden(self): """Check that elements with "visibility = hidden" are not visible.""" elem = get_webelem(QRect(0, 0, 10, 10), self.frame, visibility='hidden') - self.assertFalse(elem.is_visible(self.frame)) + assert not elem.is_visible(self.frame) def test_display_inline(self): """Check that elements with "display = inline" are visible.""" elem = get_webelem(QRect(0, 0, 10, 10), self.frame, display='inline') - self.assertTrue(elem.is_visible(self.frame)) + assert elem.is_visible(self.frame) def test_display_none(self): """Check that elements with "display = none" are not visible.""" elem = get_webelem(QRect(0, 0, 10, 10), self.frame, display='none') - self.assertFalse(elem.is_visible(self.frame)) + assert not elem.is_visible(self.frame) -class IsVisibleIframeTests(unittest.TestCase): +class TestIsVisibleIframe(object): """Tests for is_visible with a child frame. @@ -204,7 +206,8 @@ class IsVisibleIframeTests(unittest.TestCase): elem1-elem4: FakeWebElements to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, stubs): """Set up the following base situation. 0, 0 300, 0 @@ -236,64 +239,64 @@ class IsVisibleIframeTests(unittest.TestCase): def test_not_scrolled(self): """Test base situation.""" - self.assertTrue(self.frame.geometry().contains(self.iframe.geometry())) - self.assertTrue(self.elem1.is_visible(self.frame)) - self.assertTrue(self.elem2.is_visible(self.frame)) - self.assertFalse(self.elem3.is_visible(self.frame)) - self.assertTrue(self.elem4.is_visible(self.frame)) + assert self.frame.geometry().contains(self.iframe.geometry()) + assert self.elem1.is_visible(self.frame) + assert self.elem2.is_visible(self.frame) + assert not self.elem3.is_visible(self.frame) + assert self.elem4.is_visible(self.frame) def test_iframe_scrolled(self): """Scroll iframe down so elem3 gets visible and elem1/elem2 not.""" self.iframe.scrollPosition.return_value = QPoint(0, 100) - self.assertFalse(self.elem1.is_visible(self.frame)) - self.assertFalse(self.elem2.is_visible(self.frame)) - self.assertTrue(self.elem3.is_visible(self.frame)) - self.assertTrue(self.elem4.is_visible(self.frame)) + assert not self.elem1.is_visible(self.frame) + assert not self.elem2.is_visible(self.frame) + assert self.elem3.is_visible(self.frame) + assert self.elem4.is_visible(self.frame) def test_mainframe_scrolled_iframe_visible(self): """Scroll mainframe down so iframe is partly visible but elem1 not.""" self.frame.scrollPosition.return_value = QPoint(0, 50) geom = self.frame.geometry().translated(self.frame.scrollPosition()) - self.assertFalse(geom.contains(self.iframe.geometry())) - self.assertTrue(geom.intersects(self.iframe.geometry())) - self.assertFalse(self.elem1.is_visible(self.frame)) - self.assertTrue(self.elem2.is_visible(self.frame)) - self.assertFalse(self.elem3.is_visible(self.frame)) - self.assertTrue(self.elem4.is_visible(self.frame)) + assert not geom.contains(self.iframe.geometry()) + assert geom.intersects(self.iframe.geometry()) + assert not self.elem1.is_visible(self.frame) + assert self.elem2.is_visible(self.frame) + assert not self.elem3.is_visible(self.frame) + assert self.elem4.is_visible(self.frame) def test_mainframe_scrolled_iframe_invisible(self): """Scroll mainframe down so iframe is invisible.""" self.frame.scrollPosition.return_value = QPoint(0, 110) geom = self.frame.geometry().translated(self.frame.scrollPosition()) - self.assertFalse(geom.contains(self.iframe.geometry())) - self.assertFalse(geom.intersects(self.iframe.geometry())) - self.assertFalse(self.elem1.is_visible(self.frame)) - self.assertFalse(self.elem2.is_visible(self.frame)) - self.assertFalse(self.elem3.is_visible(self.frame)) - self.assertTrue(self.elem4.is_visible(self.frame)) + assert not geom.contains(self.iframe.geometry()) + assert not geom.intersects(self.iframe.geometry()) + assert not self.elem1.is_visible(self.frame) + assert not self.elem2.is_visible(self.frame) + assert not self.elem3.is_visible(self.frame) + assert self.elem4.is_visible(self.frame) -class IsWritableTests(unittest.TestCase): +class TestIsWritable(object): """Check is_writable.""" def test_writable(self): """Test a normal element.""" elem = get_webelem() - self.assertTrue(elem.is_writable()) + assert elem.is_writable() def test_disabled(self): """Test a disabled element.""" elem = get_webelem(attributes=['disabled']) - self.assertFalse(elem.is_writable()) + assert not elem.is_writable() def test_readonly(self): """Test a readonly element.""" elem = get_webelem(attributes=['readonly']) - self.assertFalse(elem.is_writable()) + assert not elem.is_writable() -class JavascriptEscapeTests(unittest.TestCase): +class TestJavascriptEscape(object): """Check javascript_escape. @@ -301,33 +304,30 @@ class JavascriptEscapeTests(unittest.TestCase): STRINGS: A list of (input, output) tuples. """ - STRINGS = ( + @pytest.mark.parametrize('before, after', [ ('foo\\bar', r'foo\\bar'), ('foo\nbar', r'foo\nbar'), ("foo'bar", r"foo\'bar"), ('foo"bar', r'foo\"bar'), - ) - - def test_fake_escape(self): + ]) + def test_fake_escape(self, before, after): """Test javascript escaping.""" - for before, after in self.STRINGS: - with self.subTest(before=before): - self.assertEqual(webelem.javascript_escape(before), after) + assert webelem.javascript_escape(before) == after -class GetChildFramesTests(unittest.TestCase): +class TestGetChildFrames(object): """Check get_child_frames.""" - def test_single_frame(self): + def test_single_frame(self, stubs): """Test get_child_frames with a single frame without children.""" frame = stubs.FakeChildrenFrame() children = webelem.get_child_frames(frame) - self.assertEqual(len(children), 1) - self.assertIs(children[0], frame) + assert len(children) == 1 + assert children[0] is frame frame.childFrames.assert_called_once_with() - def test_one_level(self): + def test_one_level(self, stubs): r"""Test get_child_frames with one level of children. o parent @@ -338,15 +338,15 @@ class GetChildFramesTests(unittest.TestCase): child2 = stubs.FakeChildrenFrame() parent = stubs.FakeChildrenFrame([child1, child2]) children = webelem.get_child_frames(parent) - self.assertEqual(len(children), 3) - self.assertIs(children[0], parent) - self.assertIs(children[1], child1) - self.assertIs(children[2], child2) + assert len(children) == 3 + assert children[0] is parent + assert children[1] is child1 + assert children[2] is child2 parent.childFrames.assert_called_once_with() child1.childFrames.assert_called_once_with() child2.childFrames.assert_called_once_with() - def test_multiple_levels(self): + def test_multiple_levels(self, stubs): r"""Test get_child_frames with multiple levels of children. o root @@ -360,189 +360,190 @@ class GetChildFramesTests(unittest.TestCase): stubs.FakeChildrenFrame(second[2:4])] root = stubs.FakeChildrenFrame(first) children = webelem.get_child_frames(root) - self.assertEqual(len(children), 7) - self.assertIs(children[0], root) + assert len(children) == 7 + assert children[0] is root for frame in [root] + first + second: - with self.subTest(frame=frame): - frame.childFrames.assert_called_once_with() + frame.childFrames.assert_called_once_with() -class IsEditableTests(unittest.TestCase): +class TestIsEditable(object): """Tests for is_editable.""" - def setUp(self): + @pytest.yield_fixture(autouse=True) + def setup(self): + old_config = webelem.config webelem.config = None + yield + webelem.config = old_config + + @pytest.yield_fixture + def stub_config(self, stubs): + config = stubs.ConfigStub({'input': {'insert-mode-on-plugins': True}}) + with mock.patch('qutebrowser.browser.webelem.config', new=config): + yield config def test_input_plain(self): """Test with plain input element.""" elem = get_webelem(tagname='input') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_text(self): """Test with text input element.""" elem = get_webelem(tagname='input', attributes={'type': 'text'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_text_caps(self): """Test with text input element with caps attributes.""" elem = get_webelem(tagname='INPUT', attributes={'TYPE': 'TEXT'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_email(self): """Test with email input element.""" elem = get_webelem(tagname='input', attributes={'type': 'email'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_url(self): """Test with url input element.""" elem = get_webelem(tagname='input', attributes={'type': 'url'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_tel(self): """Test with tel input element.""" elem = get_webelem(tagname='input', attributes={'type': 'tel'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_number(self): """Test with number input element.""" elem = get_webelem(tagname='input', attributes={'type': 'number'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_password(self): """Test with password input element.""" elem = get_webelem(tagname='input', attributes={'type': 'password'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_search(self): """Test with search input element.""" elem = get_webelem(tagname='input', attributes={'type': 'search'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_input_button(self): """Button should not be editable.""" elem = get_webelem(tagname='input', attributes={'type': 'button'}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_input_checkbox(self): """Checkbox should not be editable.""" elem = get_webelem(tagname='input', attributes={'type': 'checkbox'}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_textarea(self): """Test textarea element.""" elem = get_webelem(tagname='textarea') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_select(self): """Test selectbox.""" elem = get_webelem(tagname='select') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_input_disabled(self): """Test disabled input element.""" elem = get_webelem(tagname='input', attributes={'disabled': None}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_input_readonly(self): """Test readonly input element.""" elem = get_webelem(tagname='input', attributes={'readonly': None}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_textarea_disabled(self): """Test disabled textarea element.""" elem = get_webelem(tagname='textarea', attributes={'disabled': None}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_textarea_readonly(self): """Test readonly textarea element.""" elem = get_webelem(tagname='textarea', attributes={'readonly': None}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': True}})) - def test_embed_true(self): + def test_embed_true(self, stub_config): """Test embed-element with insert-mode-on-plugins true.""" + stub_config.data['input']['insert-mode-on-plugins'] = True elem = get_webelem(tagname='embed') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': True}})) - def test_applet_true(self): + def test_applet_true(self, stub_config): """Test applet-element with insert-mode-on-plugins true.""" + stub_config.data['input']['insert-mode-on-plugins'] = True elem = get_webelem(tagname='applet') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': False}})) - def test_embed_false(self): + def test_embed_false(self, stub_config): """Test embed-element with insert-mode-on-plugins false.""" + stub_config.data['input']['insert-mode-on-plugins'] = False elem = get_webelem(tagname='embed') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': False}})) - def test_applet_false(self): + def test_applet_false(self, stub_config): """Test applet-element with insert-mode-on-plugins false.""" + stub_config.data['input']['insert-mode-on-plugins'] = False elem = get_webelem(tagname='applet') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_object_no_type(self): """Test object-element without type.""" elem = get_webelem(tagname='object') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_object_image(self): """Test object-element with image type.""" elem = get_webelem(tagname='object', attributes={'type': 'image/gif'}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': True}})) - def test_object_application(self): + def test_object_application(self, stub_config): """Test object-element with application type.""" + stub_config.data['input']['insert-mode-on-plugins'] = True elem = get_webelem(tagname='object', attributes={'type': 'application/foo'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': False}})) - def test_object_application_false(self): + def test_object_application_false(self, stub_config): """Test object-element with application type but not ...-on-plugins.""" + stub_config.data['input']['insert-mode-on-plugins'] = False elem = get_webelem(tagname='object', attributes={'type': 'application/foo'}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': True}})) - def test_object_classid(self): + def test_object_classid(self, stub_config): """Test object-element with classid.""" + stub_config.data['input']['insert-mode-on-plugins'] = True elem = get_webelem(tagname='object', attributes={'type': 'foo', 'classid': 'foo'}) - self.assertTrue(elem.is_editable()) + assert elem.is_editable() - @mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub( - {'input': {'insert-mode-on-plugins': False}})) - def test_object_classid_false(self): + def test_object_classid_false(self, stub_config): """Test object-element with classid but not insert-mode-on-plugins.""" + stub_config.data['input']['insert-mode-on-plugins'] = False elem = get_webelem(tagname='object', attributes={'type': 'foo', 'classid': 'foo'}) - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_div_empty(self): """Test div-element without class.""" elem = get_webelem(tagname='div') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_div_noneditable(self): """Test div-element with non-editableclass.""" elem = get_webelem(tagname='div', classes='foo-kix-bar') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_div_xik(self): """Test div-element with xik class.""" elem = get_webelem(tagname='div', classes='foo kix-foo') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() def test_div_xik_caps(self): """Test div-element with xik class in caps. @@ -550,13 +551,11 @@ class IsEditableTests(unittest.TestCase): This tests if classes are case sensitive as they should. """ elem = get_webelem(tagname='div', classes='KIX-FOO') - self.assertFalse(elem.is_editable()) + assert not elem.is_editable() def test_div_codemirror(self): """Test div-element with codemirror class.""" elem = get_webelem(tagname='div', classes='foo CodeMirror-foo') - self.assertTrue(elem.is_editable()) + assert elem.is_editable() -if __name__ == '__main__': - unittest.main() From 3129def33e1e3242a8c8d51e35b979fdc37ce766 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 20:06:43 -0300 Subject: [PATCH 11/47] Converted test_content_disposition to pytest --- test/browser/http/test_content_disposition.py | 502 +++++++++--------- 1 file changed, 263 insertions(+), 239 deletions(-) diff --git a/test/browser/http/test_content_disposition.py b/test/browser/http/test_content_disposition.py index b1ba36d10..af8ac8892 100644 --- a/test/browser/http/test_content_disposition.py +++ b/test/browser/http/test_content_disposition.py @@ -20,11 +20,11 @@ """Tests for qutebrowser.browser.http.parse_content_disposition.""" import os -import unittest import logging +import pytest + from qutebrowser.browser import http -from qutebrowser.test import stubs from qutebrowser.utils import log @@ -34,86 +34,90 @@ DEFAULT_NAME = 'qutebrowser-download' # These test cases are based on http://greenbytes.de/tech/tc2231/ -class AttachmentTestCase(unittest.TestCase): +class _HeaderChecker(object): + """Helper class with some convenience methods to check filenames. - """Helper class with some convienence methods to check filenames.""" + Attrs: + caplog: fixture from pytest-capturelog + stubs: fixture that provides testing stubs + """ - def _check_filename(self, header, filename): + def __init__(self, caplog, stubs): + self.caplog = caplog + self.stubs = stubs + + def check_filename(self, header, filename, expected_inline=False): """Check if the passed header has the given filename.""" - reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) + reply = self.stubs.FakeNetworkReply( + headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertIsNotNone(cd_filename) - self.assertEqual(cd_filename, filename) - self.assertFalse(cd_inline) + assert cd_filename is not None + assert cd_filename == filename + assert cd_inline == expected_inline - def _check_ignored(self, header): + def check_ignored(self, header): """Check if the passed header is ignored.""" - reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) - with self.assertLogs(log.rfc6266, logging.ERROR): + reply = self.stubs.FakeNetworkReply( + headers={'Content-Disposition': header}) + with self.caplog.atLevel(logging.ERROR, logger=log.rfc6266.name): + # with self.assertLogs(log.rfc6266, logging.ERROR): cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertEqual(cd_filename, DEFAULT_NAME) - self.assertTrue(cd_inline) + assert cd_filename == DEFAULT_NAME + assert cd_inline - def _check_unnamed(self, header): + def check_unnamed(self, header): """Check if the passed header results in an unnamed attachment.""" - reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) + reply = self.stubs.FakeNetworkReply( + headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertEqual(cd_filename, DEFAULT_NAME) - self.assertFalse(cd_inline) + assert cd_filename == DEFAULT_NAME + assert not cd_inline -class InlineTests(unittest.TestCase): +@pytest.fixture +def header_checker(caplog, stubs): + """Fixture that provides a _AttachmentChecker class for tests""" + return _HeaderChecker(caplog, stubs) + +class TestInline(object): """Various tests relating to the "inline" disposition type. See Section 4.2 of RFC 6266. """ - def _check_filename(self, header, filename): - """Check if the passed header has the given filename.""" - reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) - cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertEqual(cd_filename, filename) - self.assertTrue(cd_inline) - - def _check_ignored(self, header): - """Check if the passed header is ignored.""" - reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) - cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertEqual(cd_filename, DEFAULT_NAME) - self.assertTrue(cd_inline) - - def test_inlonly(self): + def test_inlonly(self, header_checker): """'inline' only This should be equivalent to not including the header at all. """ - self._check_ignored('inline') + header_checker.check_ignored('inline') - def test_inlonlyquoted(self): + def test_inlonlyquoted(self, header_checker): """'inline' only, using double quotes This is invalid syntax, thus the header should be ignored. """ - with self.assertLogs(log.rfc6266, logging.ERROR): - self._check_ignored('"inline"') + header_checker.check_ignored('"inline"') - def test_inlwithasciifilename(self): + def test_inlwithasciifilename(self, header_checker): """'inline', specifying a filename of foo.html Some UAs use this filename in a subsequent "save" operation. """ - self._check_filename('inline; filename="foo.html"', 'foo.html') + header_checker.check_filename('inline; filename="foo.html"', 'foo.html', + expected_inline=True) - def test_inlwithfnattach(self): + def test_inlwithfnattach(self, header_checker): """'inline', specifying a filename of "Not an attachment!". This checks for proper parsing for disposition types. """ - self._check_filename('inline; filename="Not an attachment!"', - "Not an attachment!") + header_checker.check_filename('inline; filename="Not an attachment!"', + "Not an attachment!", + expected_inline=True) - def test_inlwithasciifilenamepdf(self): + def test_inlwithasciifilenamepdf(self, header_checker): """'inline', specifying a filename of foo.pdf. Some UAs use this filename in a subsequent "save" operation. This @@ -122,17 +126,17 @@ class InlineTests(unittest.TestCase): with the latest Acrobat Reader plugin, or, in the case of Chrome, using the builtin PDF handler). """ - self._check_filename('inline; filename="foo.pdf"', "foo.pdf") + header_checker.check_filename('inline; filename="foo.pdf"', "foo.pdf", + expected_inline=True) -class AttachmentTests(AttachmentTestCase): - +class TestAttachment(object): """Various tests relating to the "attachment" disposition type. See Section 4.2 of RFC 6266. """ - def test_attonly(self): + def test_attonly(self, stubs): """'attachment' only. UA should offer to download the resource. @@ -140,53 +144,55 @@ class AttachmentTests(AttachmentTestCase): reply = stubs.FakeNetworkReply( headers={'Content-Disposition': 'attachment'}) cd_inline, cd_filename = http.parse_content_disposition(reply) - self.assertFalse(cd_inline) - self.assertEqual(cd_filename, DEFAULT_NAME) + assert not cd_inline + assert cd_filename == DEFAULT_NAME - def test_attonlyquoted(self): + def test_attonlyquoted(self, header_checker): """'attachment' only, using double quotes This is invalid syntax, thus the header should be ignored. """ - self._check_ignored('"attachment"') + header_checker.check_ignored('"attachment"') # we can't test attonly403 here. - def test_attonlyucase(self): + def test_attonlyucase(self, header_checker): """'ATTACHMENT' only UA should offer to download the resource. """ - self._check_unnamed('ATTACHMENT') + header_checker.check_unnamed('ATTACHMENT') - def test_attwithasciifilename(self): + def test_attwithasciifilename(self, header_checker): """'attachment', specifying a filename of foo.html UA should offer to download the resource as "foo.html". """ - self._check_filename('attachment; filename="foo.html"', 'foo.html') + header_checker.check_filename('attachment; filename="foo.html"', + 'foo.html') - def test_attwithasciifilename25(self): + def test_attwithasciifilename25(self, header_checker): """'attachment', with a 25 character filename.""" - self._check_filename( + header_checker.check_filename( 'attachment; filename="0000000000111111111122222"', '0000000000111111111122222') - def test_attwithasciifilename35(self): + def test_attwithasciifilename35(self, header_checker): """'attachment', with a 35 character filename.""" - self._check_filename( + header_checker.check_filename( 'attachment; filename="00000000001111111111222222222233333"', '00000000001111111111222222222233333') - def test_attwithasciifnescapedchar(self): + def test_attwithasciifnescapedchar(self, header_checker): r"""'attachment', specifying a filename of f\oo.html. (the first 'o' being escaped) UA should offer to download the resource as "foo.html". """ - self._check_filename(r'attachment; filename="f\oo.html"', 'foo.html') + header_checker.check_filename(r'attachment; filename="f\oo.html"', + 'foo.html') - def test_attwithasciifnescapedquote(self): + def test_attwithasciifnescapedquote(self, header_checker): r"""'attachment', specifying a filename of \"quoting\" tested.html (using double quotes around "quoting" to test... quoting) @@ -195,19 +201,19 @@ class AttachmentTests(AttachmentTestCase): tested.html' (stripping the quotes may be ok for security reasons, but getting confused by them is not). """ - self._check_filename(r'attachment; filename="\"quoting\" tested.html"', - '"quoting" tested.html') + header = r'attachment; filename="\"quoting\" tested.html"' + header_checker.check_filename(header, '"quoting" tested.html') - def test_attwithquotedsemicolon(self): + def test_attwithquotedsemicolon(self, header_checker): """'attachment', specifying a filename of Here's a semicolon;.html. This checks for proper parsing for parameters. """ - self._check_filename( + header_checker.check_filename( 'attachment; filename="Here\'s a semicolon;.html"', "Here's a semicolon;.html") - def test_attwithfilenameandextparam(self): + def test_attwithfilenameandextparam(self, header_checker): """'attachment', specifying a filename of foo.html. And an extension parameter "foo" which should be ignored (see Section @@ -215,11 +221,11 @@ class AttachmentTests(AttachmentTestCase): UA should offer to download the resource as "foo.html". """ - self._check_filename( + header_checker.check_filename( 'attachment; foo="bar"; filename="foo.html"', 'foo.html') - def test_attwithfilenameandextparamescaped(self): + def test_attwithfilenameandextparamescaped(self, header_checker): """'attachment', specifying a filename of foo.html. And an extension parameter "foo" which should be ignored (see Section @@ -229,35 +235,36 @@ class AttachmentTests(AttachmentTestCase): UA should offer to download the resource as "foo.html". """ - self._check_filename( + header_checker.check_filename( r'attachment; foo="\"\\";filename="foo.html"', 'foo.html') - def test_attwithasciifilenameucase(self): + def test_attwithasciifilenameucase(self, header_checker): """'attachment', specifying a filename of foo.html UA should offer to download the resource as "foo.html". """ - self._check_filename(r'attachment; FILENAME="foo.html"', 'foo.html') + header_checker.check_filename(r'attachment; FILENAME="foo.html"', + 'foo.html') - def test_attwithasciifilenamenq(self): + def test_attwithasciifilenamenq(self, header_checker): """'attachment', specifying a filename of foo.html. (using a token instead of a quoted-string). Note that was invalid according to Section 19.5.1 of RFC 2616. """ - self._check_filename('attachment; filename=foo.html', 'foo.html') + header_checker.check_filename('attachment; filename=foo.html', + 'foo.html') - def test_attwithtokfncommanq(self): + def test_attwithtokfncommanq(self, header_checker): """'attachment', specifying a filename of foo,bar.html. (using a comma despite using token syntax). """ - self._check_ignored('attachment; filename=foo,bar.html') + header_checker.check_ignored('attachment; filename=foo,bar.html') - # With relaxed=True we accept that - @unittest.expectedFailure - def test_attwithasciifilenamenqs(self): + @pytest.mark.xfail(reason='With relaxed=True we accept that') + def test_attwithasciifilenamenqs(self, header_checker): """'attachment', specifying a filename of foo.html. (using a token instead of a quoted-string, and adding a trailing @@ -267,9 +274,9 @@ class AttachmentTests(AttachmentTestCase): incorrect, as no other parameter follows. Thus the header field should be ignored. """ - self._check_ignored('attachment; filename=foo.html ;') + header_checker.check_ignored('attachment; filename=foo.html ;') - def test_attemptyparam(self): + def test_attemptyparam(self, header_checker): """'attachment', specifying a filename of foo. (but including an empty parameter). @@ -278,34 +285,36 @@ class AttachmentTests(AttachmentTestCase): incorrect, as no other parameter follows. Thus the header field should be ignored. """ - self._check_ignored('attachment; ;filename=foo') + header_checker.check_ignored('attachment; ;filename=foo') - def test_attwithasciifilenamenqws(self): + def test_attwithasciifilenamenqws(self, header_checker): """'attachment', specifying a filename of foo bar.html. (without using quoting). This is invalid. "token" does not allow whitespace. """ - self._check_ignored('attachment; filename=foo bar.html') + header_checker.check_ignored('attachment; filename=foo bar.html') - def test_attwithfntokensq(self): + def test_attwithfntokensq(self, header_checker): """'attachment', specifying a filename of 'foo.bar' (using single quotes). """ - self._check_filename("attachment; filename='foo.bar'", "'foo.bar'") + header_checker.check_filename("attachment; filename='foo.bar'", + "'foo.bar'") - def test_attwithisofnplain(self): + def test_attwithisofnplain(self, header_checker): """'attachment', specifying a filename of foo-ä.html. (using plain ISO-8859-1) UA should offer to download the resource as "foo-ä.html". """ - self._check_filename('attachment; filename="foo-ä.html"', 'foo-ä.html') + header_checker.check_filename('attachment; filename="foo-ä.html"', + 'foo-ä.html') - def test_attwithutf8fnplain(self): + def test_attwithutf8fnplain(self, header_checker): """'attachment', specifying a filename of foo-ä.html. (which happens to be foo-ä.html using UTF-8 encoding). @@ -314,29 +323,30 @@ class AttachmentTests(AttachmentTestCase): "foo-ä.html" instead indicates that the UA tried to be smart by detecting something that happens to look like UTF-8. """ - self._check_filename('attachment; filename="foo-ä.html"', - 'foo-ä.html') + header_checker.check_filename('attachment; filename="foo-ä.html"', + 'foo-ä.html') - def test_attwithfnrawpctenca(self): + def test_attwithfnrawpctenca(self, header_checker): """'attachment', specifying a filename of foo-%41.html UA should offer to download the resource as "foo-%41.html". Displaying "foo-A.html" instead would indicate that the UA has attempted to percent-decode the parameter. """ - self._check_filename('attachment; filename="foo-%41.html"', - 'foo-%41.html') + header_checker.check_filename('attachment; filename="foo-%41.html"', + 'foo-%41.html') - def test_attwithfnusingpct(self): + def test_attwithfnusingpct(self, header_checker): """'attachment', specifying a filename of 50%.html UA should offer to download the resource as "50%.html". This tests how UAs that fails at attwithfnrawpctenca handle "%" characters that do not start a "% hexdig hexdig" sequence. """ - self._check_filename('attachment; filename="50%.html"', '50%.html') + header_checker.check_filename('attachment; filename="50%.html"', + '50%.html') - def test_attwithfnrawpctencaq(self): + def test_attwithfnrawpctencaq(self, header_checker): """'attachment', specifying a filename of foo-%41.html. Using an escape character (this tests whether adding an escape @@ -345,10 +355,10 @@ class AttachmentTests(AttachmentTestCase): UA should offer to download the resource as "foo-%41.html". """ - self._check_filename(r'attachment; filename="foo-%\41.html"', - 'foo-%41.html') + header_checker.check_filename(r'attachment; filename="foo-%\41.html"', + 'foo-%41.html') - def test_attwithnamepct(self): + def test_attwithnamepct(self, header_checker): """'attachment', specifying a name parameter of foo-%41.html. (this test was added to observe the behavior of the (unspecified) treatment of "name" as synonym for "filename"; see Ned Freed's summary[1] where @@ -359,17 +369,18 @@ class AttachmentTests(AttachmentTestCase): [1] http://www.imc.org/ietf-smtp/mail-archive/msg05023.html """ - self._check_unnamed('attachment; name="foo-%41.html"') + header_checker.check_unnamed('attachment; name="foo-%41.html"') - def test_attwithfilenamepctandiso(self): + def test_attwithfilenamepctandiso(self, header_checker): """'attachment', specifying a filename parameter of ä-%41.html. (this test was added to observe the behavior when non-ASCII characters and percent-hexdig sequences are combined) """ - self._check_filename('attachment; filename="ä-%41.html"', 'ä-%41.html') + header_checker.check_filename('attachment; filename="ä-%41.html"', + 'ä-%41.html') - def test_attwithfnrawpctenclong(self): + def test_attwithfnrawpctenclong(self, header_checker): """'attachment', specifying a filename of foo-%c3%a4-%e2%82%ac.html. (using raw percent encoded UTF-8 to represent foo-ä-€.html) @@ -380,28 +391,29 @@ class AttachmentTests(AttachmentTestCase): (using UTF-8). Displaying something else would indicate that the UA tried to percent-decode, but used a different encoding. """ - self._check_filename( + header_checker.check_filename( 'attachment; filename="foo-%c3%a4-%e2%82%ac.html"', 'foo-%c3%a4-%e2%82%ac.html') - def test_attwithasciifilenamews1(self): + def test_attwithasciifilenamews1(self, header_checker): """'attachment', specifying a filename of foo.html. (With one blank space before the equals character). UA should offer to download the resource as "foo.html". """ - self._check_filename('attachment; filename ="foo.html"', 'foo.html') + header_checker.check_filename('attachment; filename ="foo.html"', + 'foo.html') - def test_attwith2filenames(self): + def test_attwith2filenames(self, header_checker): """'attachment', specifying two filename parameters. This is invalid syntax. """ - self._check_ignored( + header_checker.check_ignored( 'attachment; filename="foo.html"; filename="bar.html"') - def test_attfnbrokentoken(self): + def test_attfnbrokentoken(self, header_checker): """'attachment', specifying a filename of foo[1](2).html. Missing the quotes. Also, "[", "]", "(" and ")" are not allowed in the @@ -410,9 +422,9 @@ class AttachmentTests(AttachmentTestCase): This is invalid according to Section 19.5.1 of RFC 2616 and RFC 6266, so UAs should ignore it. """ - self._check_ignored('attachment; filename=foo[1](2).html') + header_checker.check_ignored('attachment; filename=foo[1](2).html') - def test_attfnbrokentokeniso(self): + def test_attfnbrokentokeniso(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Missing the quotes. @@ -420,9 +432,9 @@ class AttachmentTests(AttachmentTestCase): This is invalid, as the umlaut is not a valid token character, so UAs should ignore it. """ - self._check_ignored('attachment; filename=foo-ä.html') + header_checker.check_ignored('attachment; filename=foo-ä.html') - def test_attfnbrokentokenutf(self): + def test_attfnbrokentokenutf(self, header_checker): """'attachment', specifying a filename of foo-ä.html. (which happens to be foo-ä.html using UTF-8 encoding) but missing the @@ -431,31 +443,31 @@ class AttachmentTests(AttachmentTestCase): This is invalid, as the umlaut is not a valid token character, so UAs should ignore it. """ - self._check_ignored('attachment; filename=foo-ä.html') + header_checker.check_ignored('attachment; filename=foo-ä.html') - def test_attmissingdisposition(self): + def test_attmissingdisposition(self, header_checker): """Disposition type missing, filename specified. This is invalid, so UAs should ignore it. """ - self._check_ignored('filename=foo.html') + header_checker.check_ignored('filename=foo.html') - def test_attmissingdisposition2(self): + def test_attmissingdisposition2(self, header_checker): """Disposition type missing, filename specified after extension. This is invalid, so UAs should ignore it. """ - self._check_ignored('x=y; filename=foo.html') + header_checker.check_ignored('x=y; filename=foo.html') - def test_attmissingdisposition3(self): + def test_attmissingdisposition3(self, header_checker): """Disposition type missing, filename "qux". Can it be more broken? (Probably) This is invalid, so UAs should ignore it. """ - self._check_ignored('"foo; filename=bar;baz"; filename=qux') + header_checker.check_ignored('"foo; filename=bar;baz"; filename=qux') - def test_attmissingdisposition4(self): + def test_attmissingdisposition4(self, header_checker): """Disposition type missing. Two filenames specified separated by a comma (this is syntactically @@ -464,62 +476,62 @@ class AttachmentTests(AttachmentTestCase): This is invalid, so UAs should ignore it. """ - self._check_ignored('filename=foo.html, filename=bar.html') + header_checker.check_ignored('filename=foo.html, filename=bar.html') - def test_emptydisposition(self): + def test_emptydisposition(self, header_checker): """Disposition type missing (but delimiter present). Filename specified. This is invalid, so UAs should ignore it. """ - self._check_ignored('; filename=foo.html') + header_checker.check_ignored('; filename=foo.html') - def test_doublecolon(self): + def test_doublecolon(self, header_checker): """Header field value starts with a colon. This is invalid, so UAs should ignore it. """ - self._check_ignored(': inline; attachment; filename=foo.html') + header_checker.check_ignored(': inline; attachment; filename=foo.html') - def test_attandinline(self): + def test_attandinline(self, header_checker): """Both disposition types specified. This is invalid, so UAs should ignore it. """ - self._check_ignored('inline; attachment; filename=foo.html') + header_checker.check_ignored('inline; attachment; filename=foo.html') - def test_attandinline2(self): + def test_attandinline2(self, header_checker): """Both disposition types specified. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; inline; filename=foo.html') + header_checker.check_ignored('attachment; inline; filename=foo.html') - def test_attbrokenquotedfn(self): + def test_attbrokenquotedfn(self, header_checker): """'attachment', specifying a filename parameter that is broken. (quoted-string followed by more characters). This is invalid syntax. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; filename="foo.html".txt') + header_checker.check_ignored('attachment; filename="foo.html".txt') - def test_attbrokenquotedfn2(self): + def test_attbrokenquotedfn2(self, header_checker): """'attachment', specifying a filename parameter that is broken. (missing ending double quote). This is invalid syntax. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; filename="bar') + header_checker.check_ignored('attachment; filename="bar') - def test_attbrokenquotedfn3(self): + def test_attbrokenquotedfn3(self, header_checker): """'attachment', specifying a filename parameter that is broken. (disallowed characters in token syntax). This is invalid syntax. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; filename=foo"bar;baz"qux') + header_checker.check_ignored('attachment; filename=foo"bar;baz"qux') - def test_attmultinstances(self): + def test_attmultinstances(self, header_checker): """'attachment', two comma-separated instances of the header field. As Content-Disposition doesn't use a list-style syntax, this is invalid @@ -528,53 +540,54 @@ class AttachmentTests(AttachmentTestCase): This is invalid, so UAs should ignore it. """ - self._check_ignored( + header_checker.check_ignored( 'attachment; filename=foo.html, attachment; filename=bar.html') - def test_attmissingdelim(self): + def test_attmissingdelim(self, header_checker): """Uses two parameters, but the mandatory delimiter ";" is missing. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; foo=foo filename=bar') + header_checker.check_ignored('attachment; foo=foo filename=bar') - def test_attmissingdelim2(self): + def test_attmissingdelim2(self, header_checker): """Uses two parameters, but the mandatory delimiter ";" is missing. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment; filename=bar foo=foo') + header_checker.check_ignored('attachment; filename=bar foo=foo') - def test_attmissingdelim3(self): + def test_attmissingdelim3(self, header_checker): """";" missing between disposition type and filename parameter. This is invalid, so UAs should ignore it. """ - self._check_ignored('attachment filename=bar') + header_checker.check_ignored('attachment filename=bar') - def test_attreversed(self): + def test_attreversed(self, header_checker): """filename parameter and disposition type reversed. This is invalid, so UAs should ignore it. """ - self._check_ignored('filename=foo.html; attachment') + header_checker.check_ignored('filename=foo.html; attachment') - def test_attconfusedparam(self): + def test_attconfusedparam(self, header_checker): """'attachment', specifying an "xfilename" parameter. Should be treated as unnamed attachment. """ - self._check_unnamed('attachment; xfilename=foo.html') + header_checker.check_unnamed('attachment; xfilename=foo.html') - def test_attabspath(self): + def test_attabspath(self, header_checker): """'attachment', specifying an absolute filename in the fs root. Either ignore the filename altogether, or discard the path information. """ - self._check_filename('attachment; filename="/foo.html"', 'foo.html') + header_checker.check_filename('attachment; filename="/foo.html"', + 'foo.html') - @unittest.skipUnless(os.name == 'posix', "requires POSIX") - def test_attabspathwin_unix(self): + @pytest.mark.skipif(os.name != 'posix', reason="requires POSIX") + def test_attabspathwin_unix(self, header_checker): """'attachment', specifying an absolute filename in the fs root. Either ignore the filename altogether, or discard the path information. @@ -585,11 +598,11 @@ class AttachmentTests(AttachmentTestCase): apparently some UAs consider the backslash a legitimate filename character. """ - self._check_filename(r'attachment; filename="\\foo.html"', - r'\foo.html') + header_checker.check_filename(r'attachment; filename="\\foo.html"', + r'\foo.html') - @unittest.skipUnless(os.name == 'nt', "requires Windows") - def test_attabspathwin_win(self): + @pytest.mark.skipif(os.name != 'nt', reason="requires Windows") + def test_attabspathwin_win(self, header_checker): """'attachment', specifying an absolute filename in the fs root. Either ignore the filename altogether, or discard the path information. @@ -600,62 +613,64 @@ class AttachmentTests(AttachmentTestCase): apparently some UAs consider the backslash a legitimate filename character. """ - self._check_filename(r'attachment; filename="\\foo.html"', 'foo.html') + header_checker.check_filename(r'attachment; filename="\\foo.html"', + 'foo.html') + # Note we do not check the "Additional parameters" section. -class DispositionTypeExtensionTests(AttachmentTestCase): - +class TestDispositionTypeExtension(object): """Tests checking behavior for disposition type extensions. They should be treated as "attachment", see Section 4.2 of RFC 6266. """ - def test_dispext(self): + def test_dispext(self, header_checker): """'foobar' only This should be equivalent to using "attachment". """ - self._check_unnamed('foobar') + header_checker.check_unnamed('foobar') - def test_dispextbadfn(self): + def test_dispextbadfn(self, header_checker): """'attachment', with no filename parameter""" - self._check_unnamed('attachment; example="filename=example.txt"') + header_checker.check_unnamed( + 'attachment; example="filename=example.txt"') -class CharacterSetTests(AttachmentTestCase): - +class TestCharacterSet(object): """Various tests using the parameter value encoding defined in RFC 5987.""" - def test_attwithisofn2231iso(self): + def test_attwithisofn2231iso(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231/5987 encoded ISO-8859-1. UA should offer to download the resource as "foo-ä.html". """ - self._check_filename("attachment; filename*=iso-8859-1''foo-%E4.html", - 'foo-ä.html') + header_checker.check_filename( + "attachment; filename*=iso-8859-1''foo-%E4.html", + 'foo-ä.html') - def test_attwithfn2231utf8(self): + def test_attwithfn2231utf8(self, header_checker): """'attachment', specifying a filename of foo-ä-€.html. Using RFC2231/5987 encoded UTF-8. UA should offer to download the resource as "foo-ä-€.html". """ - self._check_filename( + header_checker.check_filename( "attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html", 'foo-ä-€.html') - def test_attwithfn2231noc(self): + def test_attwithfn2231noc(self, header_checker): """Behavior is undefined in RFC 2231. The charset part is missing, although UTF-8 was used. """ - self._check_ignored( + header_checker.check_ignored( "attachment; filename*=''foo-%c3%a4-%e2%82%ac.html") - def test_attwithfn2231utf8comp(self): + def test_attwithfn2231utf8comp(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, but choosing the decomposed form @@ -665,10 +680,11 @@ class CharacterSetTests(AttachmentTestCase): UA should offer to download the resource as "foo-ä.html". """ - self._check_filename("attachment; filename*=UTF-8''foo-a%cc%88.html", - 'foo-ä.html') + header_checker.check_filename( + "attachment; filename*=UTF-8''foo-a%cc%88.html", + 'foo-ä.html') - def test_attwithfn2231utf8_bad(self): + def test_attwithfn2231utf8_bad(self, header_checker): """'attachment', specifying a filename of foo-ä-€.html. Using RFC2231 encoded UTF-8, but declaring ISO-8859-1. @@ -676,10 +692,10 @@ class CharacterSetTests(AttachmentTestCase): The octet %82 does not represent a valid ISO-8859-1 code point, so the UA should really ignore the parameter. """ - self._check_ignored("attachment; " - "iso-8859-1''foo-%c3%a4-%e2%82%ac.html") + header_checker.check_ignored("attachment; " + "iso-8859-1''foo-%c3%a4-%e2%82%ac.html") - def test_attwithfn2231iso_bad(self): + def test_attwithfn2231iso_bad(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded ISO-8859-1, but declaring UTF-8. @@ -687,36 +703,40 @@ class CharacterSetTests(AttachmentTestCase): The octet %E4 does not represent a valid UTF-8 octet sequence, so the UA should really ignore the parameter. """ - self._check_ignored("attachment; filename*=utf-8''foo-%E4.html") + header_checker.check_ignored( + "attachment; filename*=utf-8''foo-%E4.html") - def test_attwithfn2231ws1(self): + def test_attwithfn2231ws1(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, with whitespace before "*=" The parameter is invalid, thus should be ignored. """ - self._check_ignored("attachment; filename *=UTF-8''foo-%c3%a4.html") + header_checker.check_ignored( + "attachment; filename *=UTF-8''foo-%c3%a4.html") - def test_attwithfn2231ws2(self): + def test_attwithfn2231ws2(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, with whitespace after "*=". UA should offer to download the resource as "foo-ä.html". """ - self._check_filename("attachment; filename*= UTF-8''foo-%c3%a4.html", - 'foo-ä.html') + header_checker.check_filename( + "attachment; filename*= UTF-8''foo-%c3%a4.html", + 'foo-ä.html') - def test_attwithfn2231ws3(self): + def test_attwithfn2231ws3(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, with whitespace inside "* =" UA should offer to download the resource as "foo-ä.html". """ - self._check_filename("attachment; filename* =UTF-8''foo-%c3%a4.html", - 'foo-ä.html') + header_checker.check_filename( + "attachment; filename* =UTF-8''foo-%c3%a4.html", + 'foo-ä.html') - def test_attwithfn2231quot(self): + def test_attwithfn2231quot(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, with double quotes around the parameter @@ -724,9 +744,10 @@ class CharacterSetTests(AttachmentTestCase): The parameter is invalid, thus should be ignored. """ - self._check_ignored("attachment; filename*=\"UTF-8''foo-%c3%a4.html\"") + header_checker.check_ignored( + "attachment; filename*=\"UTF-8''foo-%c3%a4.html\"") - def test_attwithfn2231quot2(self): + def test_attwithfn2231quot2(self, header_checker): """'attachment', specifying a filename of foo bar.html. Using "filename*", but missing character encoding and language (this @@ -734,70 +755,74 @@ class CharacterSetTests(AttachmentTestCase): The parameter is invalid, thus should be ignored. """ - self._check_ignored('attachment; filename*="foo%20bar.html"') + header_checker.check_ignored('attachment; filename*="foo%20bar.html"') - def test_attwithfn2231singleqmissing(self): + def test_attwithfn2231singleqmissing(self, header_checker): """'attachment', specifying a filename of foo-ä.html. Using RFC2231 encoded UTF-8, but a single quote is missing. The parameter is invalid, thus should be ignored. """ - self._check_ignored("attachment; filename*=UTF-8'foo-%c3%a4.html") + header_checker.check_ignored( + "attachment; filename*=UTF-8'foo-%c3%a4.html") - def test_attwithfn2231nbadpct1(self): + def test_attwithfn2231nbadpct1(self, header_checker): """'attachment', specifying a filename of foo%. Using RFC2231 encoded UTF-8, with a single "%" at the end. The parameter is invalid, thus should be ignored. """ - self._check_ignored("attachment; filename*=UTF-8''foo%") + header_checker.check_ignored("attachment; filename*=UTF-8''foo%") - def test_attwithfn2231nbadpct2(self): + def test_attwithfn2231nbadpct2(self, header_checker): """'attachment', specifying a filename of f%oo.html. Using RFC2231 encoded UTF-8, with a "%" not starting a percent-escape. The parameter is invalid, thus should be ignored. """ - self._check_ignored("attachment; filename*=UTF-8''f%oo.html") + header_checker.check_ignored("attachment; filename*=UTF-8''f%oo.html") - def test_attwithfn2231dpct(self): + def test_attwithfn2231dpct(self, header_checker): """'attachment', specifying a filename of A-%41.html. Using RFC2231 encoded UTF-8. """ - self._check_filename("attachment; filename*=UTF-8''A-%2541.html", - 'A-%41.html') + header_checker.check_filename( + "attachment; filename*=UTF-8''A-%2541.html", + 'A-%41.html') - @unittest.skipUnless(os.name == 'posix', "requires POSIX") - def test_attwithfn2231abspathdisguised_unix(self): + @pytest.mark.skipif(os.name != 'posix', reason="requires POSIX") + def test_attwithfn2231abspathdisguised_unix(self, header_checker): r"""'attachment', specifying a filename of \foo.html. Using RFC2231 encoded UTF-8. """ - self._check_filename("attachment; filename*=UTF-8''%5cfoo.html", - r'\foo.html') + header_checker.check_filename( + "attachment; filename*=UTF-8''%5cfoo.html", + r'\foo.html') - @unittest.skipUnless(os.name == 'nt', "requires Windows") - def test_attwithfn2231abspathdisguised_win(self): + @pytest.mark.skipif(os.name != 'nt', reason="requires Windows") + def test_attwithfn2231abspathdisguised_win(self, header_checker): r"""'attachment', specifying a filename of \foo.html. Using RFC2231 encoded UTF-8. """ - self._check_filename("attachment; filename*=UTF-8''%5cfoo.html", - r'foo.html') + header_checker.check_filename( + "attachment; filename*=UTF-8''%5cfoo.html", + r'foo.html') + # Note we do not test the "RFC2231 Encoding: Continuations (optional)" section -class EncodingFallbackTests(AttachmentTestCase): - +class TestEncodingFallback(object): """Test the same parameter both in traditional and extended format. This tests how the UA behaves when the same parameter name appears both in traditional and RFC 2231/5987 extended format. """ - def test_attfnboth(self): + def test_attfnboth(self, header_checker): """'attachment', specifying a filename in both formats. foo-ae.html in the traditional format, and foo-ä.html in RFC2231 @@ -807,10 +832,11 @@ class EncodingFallbackTests(AttachmentTestCase): RFC 2231/5987 encoded parameter ("filename*") should take precedence when understood. """ - self._check_filename("attachment; filename=\"foo-ae.html\"; " - "filename*=UTF-8''foo-%c3%a4.html", 'foo-ä.html') + header_checker.check_filename("attachment; filename=\"foo-ae.html\"; " + "filename*=UTF-8''foo-%c3%a4.html", + 'foo-ä.html') - def test_attfnboth2(self): + def test_attfnboth2(self, header_checker): """'attachment', specifying a filename in both formats. foo-ae.html in the traditional format, and foo-ä.html in RFC2231 @@ -820,10 +846,11 @@ class EncodingFallbackTests(AttachmentTestCase): RFC 2231/5987 encoded parameter ("filename*") should take precedence when understood. """ - self._check_filename("attachment; filename*=UTF-8''foo-%c3%a4.html; " - "filename=\"foo-ae.html\"", 'foo-ä.html') + header_checker.check_filename( + "attachment; filename*=UTF-8''foo-%c3%a4.html; " + "filename=\"foo-ae.html\"", 'foo-ä.html') - def test_attfnboth3(self): + def test_attfnboth3(self, header_checker): """'attachment', specifying an ambigious filename. currency-sign=¤ in the simple RFC2231/5987 format, and euro-sign=€ in @@ -832,11 +859,11 @@ class EncodingFallbackTests(AttachmentTestCase): A UA that supports could pick either, or ignore both because of the ambiguity. """ - self._check_ignored("attachment; " - "filename*0*=ISO-8859-15''euro-sign%3d%a4; " - "filename*=ISO-8859-1''currency-sign%3d%a4") + header_checker.check_ignored("attachment; " + "filename*0*=ISO-8859-15''euro-sign%3d%a4; " + "filename*=ISO-8859-1''currency-sign%3d%a4") - def test_attnewandfn(self): + def test_attnewandfn(self, header_checker): """'attachment', specifying a new parameter "foobar". Plus a filename of foo.html in the traditional format. @@ -844,12 +871,12 @@ class EncodingFallbackTests(AttachmentTestCase): "foobar" should be ignored, thus "foo.html" be used as filename (this tests whether the UA properly skips unknown parameters). """ - self._check_filename('attachment; foobar=x; filename="foo.html"', - 'foo.html') + header_checker.check_filename( + 'attachment; foobar=x; filename="foo.html"', + 'foo.html') -class RFC2047EncodingTests(AttachmentTestCase): - +class TestRFC2047Encoding(object): """These tests RFC 2047 style encoding. Note that according to Section 5 of RFC 2047, this encoding does not apply @@ -866,33 +893,30 @@ class RFC2047EncodingTests(AttachmentTestCase): which do support the RFC2231 encoding as well. """ - def test_attrfc2047token(self): + def test_attrfc2047token(self, header_checker): """Uses RFC 2047 style encoded word. "=" is invalid inside the token production, so this is invalid. """ - self._check_ignored( + header_checker.check_ignored( 'attachment; filename==?ISO-8859-1?Q?foo-=E4.html?=') - def test_attrfc2047quoted(self): + def test_attrfc2047quoted(self, header_checker): """Uses RFC 2047 style encoded word. Using the quoted-string production. """ - self._check_filename( + header_checker.check_filename( 'attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="', '=?ISO-8859-1?Q?foo-=E4.html?=') -class OurTests(AttachmentTestCase): - +class TestOur(object): """Our own tests, not based on http://greenbytes.de/tech/tc2231/""" - def test_att_double_space(self): + def test_att_double_space(self, header_checker): """'attachment' with double space in the filename.""" - self._check_filename('attachment; filename="foo bar.html"', - 'foo bar.html') + header_checker.check_filename('attachment; filename="foo bar.html"', + 'foo bar.html') -if __name__ == '__main__': - unittest.main() From 22df30cdcc87aa701ae749282c4af4e78bfa82ba Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 20:09:53 -0300 Subject: [PATCH 12/47] Converted test_http to pytest --- test/browser/http/test_http.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/test/browser/http/test_http.py b/test/browser/http/test_http.py index 3dea72585..9b40f0b65 100644 --- a/test/browser/http/test_http.py +++ b/test/browser/http/test_http.py @@ -23,46 +23,40 @@ Note that tests for parse_content_disposition are in their own test_content_disposition.py file. """ -import unittest - from qutebrowser.browser import http -from qutebrowser.test import stubs -class ParseContentTypeTests(unittest.TestCase): +class TestParseContentType(object): """Test for parse_content_type.""" - def test_not_existing(self): + def test_not_existing(self, stubs): """Test without any Content-Type header.""" reply = stubs.FakeNetworkReply() mimetype, rest = http.parse_content_type(reply) - self.assertIsNone(mimetype) - self.assertIsNone(rest) + assert mimetype is None + assert rest is None - def test_mimetype(self): + def test_mimetype(self, stubs): """Test with simple Content-Type header.""" reply = stubs.FakeNetworkReply( headers={'Content-Type': 'image/example'}) mimetype, rest = http.parse_content_type(reply) - self.assertEqual(mimetype, 'image/example') - self.assertIsNone(rest) + assert mimetype == 'image/example' + assert rest is None - def test_empty(self): + def test_empty(self, stubs): """Test with empty Content-Type header.""" reply = stubs.FakeNetworkReply(headers={'Content-Type': ''}) mimetype, rest = http.parse_content_type(reply) - self.assertEqual(mimetype, '') - self.assertIsNone(rest) + assert mimetype == '' + assert rest is None - def test_additional(self): + def test_additional(self, stubs): """Test with Content-Type header with additional informations.""" reply = stubs.FakeNetworkReply( headers={'Content-Type': 'image/example; encoding=UTF-8'}) mimetype, rest = http.parse_content_type(reply) - self.assertEqual(mimetype, 'image/example') - self.assertEqual(rest, ' encoding=UTF-8') + assert mimetype == 'image/example' + assert rest == ' encoding=UTF-8' - -if __name__ == '__main__': - unittest.main() From 99abd1edeb8b6ffbf948fd66deb1f8f8e200a966 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 3 Apr 2015 20:17:52 -0300 Subject: [PATCH 13/47] Adding pytest-mock to tox --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 18d5e0529..9cf741073 100644 --- a/tox.ini +++ b/tox.ini @@ -20,6 +20,7 @@ deps = py==1.4.26 pytest==2.7.0 pytest-qt==1.3.0 + pytest-mock==0.4.2 # We don't use {[testenv:mkvenv]commands} here because that seems to be broken # on Ubuntu Trusty. commands = @@ -32,6 +33,7 @@ deps = coverage==3.7.1 pytest-cov==1.8.1 pytest-qt==1.3.0 + pytest-mock==0.4.2 cov-core==1.15.0 commands = {[testenv:mkvenv]commands} From 6429d29a234b247a5685ab283ff3301859999ba6 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 11:24:48 -0300 Subject: [PATCH 14/47] Converted test_conftest to pytest --- test/config/test_configtypes.py | 989 ++++++++++++++++---------------- test/conftest.py | 11 + test/helpers.py | 8 - 3 files changed, 494 insertions(+), 514 deletions(-) diff --git a/test/config/test_configtypes.py b/test/config/test_configtypes.py index a922e6fde..af887774a 100644 --- a/test/config/test_configtypes.py +++ b/test/config/test_configtypes.py @@ -18,24 +18,22 @@ """Tests for qutebrowser.config.configtypes.""" -import unittest import re import collections import os.path import base64 -from unittest import mock - -from qutebrowser.config import configtypes, configexc -from qutebrowser.test import stubs, helpers -from qutebrowser.utils import debug, utils +import itertools +import pytest from PyQt5.QtCore import QUrl from PyQt5.QtGui import QColor, QFont from PyQt5.QtNetwork import QNetworkProxy +from qutebrowser.config import configtypes, configexc +from qutebrowser.utils import debug, utils + class Font(QFont): - """A QFont with a nicer repr().""" def __repr__(self): @@ -61,7 +59,6 @@ class Font(QFont): class NetworkProxy(QNetworkProxy): - """A QNetworkProxy with a nicer repr().""" def __repr__(self): @@ -70,72 +67,77 @@ class NetworkProxy(QNetworkProxy): password=self.password()) -class ValidValuesTest(unittest.TestCase): +@pytest.fixture +def os_path(mocker): + """Fixture that mocks and returns os.path from the configtypes module""" + return mocker.patch('qutebrowser.config.configtypes.os.path', autospec=True) + +class TestValidValues(object): """Test ValidValues.""" def test_contains_without_desc(self): """Test __contains__ without a description.""" vv = configtypes.ValidValues('foo', 'bar') - self.assertIn('foo', vv) - self.assertNotIn("baz", vv) + assert 'foo' in vv + assert "baz" not in vv def test_contains_with_desc(self): """Test __contains__ with a description.""" vv = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc")) - self.assertIn('foo', vv) - self.assertIn('bar', vv) - self.assertNotIn("baz", vv) + assert 'foo' in vv + assert 'bar' in vv + assert "baz" not in vv def test_contains_mixed_desc(self): """Test __contains__ with mixed description.""" vv = configtypes.ValidValues(('foo', "foo desc"), 'bar') - self.assertIn('foo', vv) - self.assertIn('bar', vv) - self.assertNotIn("baz", vv) + assert 'foo' in vv + assert 'bar' in vv + assert "baz" not in vv def test_iter_without_desc(self): """Test __iter__ without a description.""" vv = configtypes.ValidValues('foo', 'bar') - self.assertEqual(list(vv), ['foo', 'bar']) + assert list(vv), ['foo' == 'bar'] def test_iter_with_desc(self): """Test __iter__ with a description.""" vv = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc")) - self.assertEqual(list(vv), ['foo', 'bar']) + assert list(vv), ['foo' == 'bar'] def test_iter_with_mixed_desc(self): """Test __iter__ with mixed description.""" vv = configtypes.ValidValues(('foo', "foo desc"), 'bar') - self.assertEqual(list(vv), ['foo', 'bar']) + assert list(vv), ['foo' == 'bar'] def test_descriptions(self): """Test descriptions.""" vv = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc"), 'baz') - self.assertEqual(vv.descriptions['foo'], "foo desc") - self.assertEqual(vv.descriptions['bar'], "bar desc") - self.assertNotIn('baz', vv.descriptions) + assert vv.descriptions['foo'] == "foo desc" + assert vv.descriptions['bar'] == "bar desc" + assert 'baz' not in vv.descriptions -class BaseTypeTests(unittest.TestCase): - +class TestBaseType(object): """Test BaseType.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.BaseType() def test_transform(self): """Test transform with a value.""" - self.assertEqual(self.t.transform("foobar"), "foobar") + assert self.t.transform("foobar") == "foobar" def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_validate_not_implemented(self): """Test validate without valid_values set.""" - with self.assertRaises(NotImplementedError): + with pytest.raises(NotImplementedError): self.t.validate("foo") def test_validate_none_ok(self): @@ -147,40 +149,37 @@ class BaseTypeTests(unittest.TestCase): """Test validate with valid_values set.""" self.t.valid_values = configtypes.ValidValues('foo', 'bar') self.t.validate('bar') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('baz') def test_complete_none(self): """Test complete with valid_values not set.""" - self.assertIsNone(self.t.complete()) + assert self.t.complete() is None def test_complete_without_desc(self): """Test complete with valid_values set without description.""" self.t.valid_values = configtypes.ValidValues('foo', 'bar') - self.assertEqual(self.t.complete(), [('foo', ''), ('bar', '')]) + assert self.t.complete(), [('foo', ''), ('bar' == '')] def test_complete_with_desc(self): """Test complete with valid_values set with description.""" self.t.valid_values = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc")) - self.assertEqual(self.t.complete(), [('foo', "foo desc"), - ('bar', "bar desc")]) + assert self.t.complete() == [('foo', "foo desc"), ('bar', "bar desc")] def test_complete_mixed_desc(self): """Test complete with valid_values set with mixed description.""" self.t.valid_values = configtypes.ValidValues(('foo', "foo desc"), 'bar') - self.assertEqual(self.t.complete(), [('foo', "foo desc"), - ('bar', "")]) + assert self.t.complete() == [('foo', "foo desc"), ('bar', "")] -class StringTests(unittest.TestCase): - +class TestString(object): """Test String.""" def test_minlen_toosmall(self): """Test __init__ with a minlen < 1.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.String(minlen=0) def test_minlen_ok(self): @@ -189,7 +188,7 @@ class StringTests(unittest.TestCase): def test_maxlen_toosmall(self): """Test __init__ with a maxlen < 1.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.String(maxlen=0) def test_maxlen_ok(self): @@ -198,13 +197,13 @@ class StringTests(unittest.TestCase): def test_minlen_gt_maxlen(self): """Test __init__ with a minlen bigger than the maxlen.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.String(minlen=2, maxlen=1) def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" t = configtypes.String() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate("") def test_validate_empty_none_ok(self): @@ -222,15 +221,15 @@ class StringTests(unittest.TestCase): t = configtypes.String(forbidden='xyz') t.validate("foobar") t.validate("foXbar") - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate("foybar") - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate("foxbar") def test_validate_minlen_toosmall(self): """Test validate with a minlen and a too short string.""" t = configtypes.String(minlen=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('f') def test_validate_minlen_ok(self): @@ -241,7 +240,7 @@ class StringTests(unittest.TestCase): def test_validate_maxlen_toolarge(self): """Test validate with a maxlen and a too long string.""" t = configtypes.String(maxlen=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('fob') def test_validate_maxlen_ok(self): @@ -258,22 +257,22 @@ class StringTests(unittest.TestCase): def test_validate_range_bad(self): """Test validate with both min/maxlen and a bad string.""" t = configtypes.String(minlen=2, maxlen=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('f') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('fooo') def test_transform(self): """Test if transform doesn't alter the value.""" t = configtypes.String() - self.assertEqual(t.transform('foobar'), 'foobar') + assert t.transform('foobar') == 'foobar' -class ListTests(unittest.TestCase): - +class TestList(object): """Test List.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.List() def test_validate_single(self): @@ -286,7 +285,7 @@ class ListTests(unittest.TestCase): def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -296,31 +295,29 @@ class ListTests(unittest.TestCase): def test_validate_empty_item(self): """Test validate with empty item and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foo,,bar') def test_validate_empty_item_none_ok(self): """Test validate with empty item and none_ok = True.""" t = configtypes.List(none_ok=True) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('foo,,bar') def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('foo'), ['foo']) + assert self.t.transform('foo') == ['foo'] def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual(self.t.transform('foo,bar,baz'), - ['foo', 'bar', 'baz']) + assert self.t.transform('foo,bar,baz') == ['foo', 'bar', 'baz'] def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform(''), None) + assert self.t.transform('') is None -class BoolTests(unittest.TestCase): - +class TestBool(object): """Test Bool.""" TESTS = {True: ['1', 'yes', 'YES', 'true', 'TrUe', 'on'], @@ -328,38 +325,39 @@ class BoolTests(unittest.TestCase): INVALID = ['10', 'yess', 'false_'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Bool() - def test_transform(self): + _ALL_FORMS = [] + for out, inputs in TESTS.items(): + for inp in inputs: + _ALL_FORMS.append((out, inp)) + + @pytest.mark.parametrize('out, inp', _ALL_FORMS) + def test_transform(self, out, inp): """Test transform with all values.""" - for out, inputs in self.TESTS.items(): - for inp in inputs: - with self.subTest(inp=inp, out=out): - self.assertEqual(self.t.transform(inp), out, inp) + assert self.t.transform(inp) == out def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None - def test_validate_valid(self): + @pytest.mark.parametrize('val', itertools.chain(*TESTS.values())) + def test_validate_valid(self, val): """Test validate with valid values.""" - for vallist in self.TESTS.values(): - for val in vallist: - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError): - self.t.validate(val) + with pytest.raises(configexc.ValidationError): + self.t.validate(val) def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" t = configtypes.Bool() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('') def test_validate_empty_none_ok(self): @@ -368,13 +366,12 @@ class BoolTests(unittest.TestCase): t.validate('') -class IntTests(unittest.TestCase): - +class TestInt(object): """Test Int.""" def test_minval_gt_maxval(self): """Test __init__ with a minval bigger than the maxval.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.Int(minval=2, maxval=1) def test_validate_int(self): @@ -385,13 +382,13 @@ class IntTests(unittest.TestCase): def test_validate_string(self): """Test validate with something which isn't an int.""" t = configtypes.Int() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('foobar') def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" t = configtypes.Int() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('') def test_validate_empty_none_ok(self): @@ -402,7 +399,7 @@ class IntTests(unittest.TestCase): def test_validate_minval_toosmall(self): """Test validate with a minval and a too small int.""" t = configtypes.Int(minval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1') def test_validate_minval_ok(self): @@ -413,7 +410,7 @@ class IntTests(unittest.TestCase): def test_validate_maxval_toolarge(self): """Test validate with a maxval and a too big int.""" t = configtypes.Int(maxval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3') def test_validate_maxval_ok(self): @@ -430,27 +427,27 @@ class IntTests(unittest.TestCase): def test_validate_range_bad(self): """Test validate with both min/maxval and a bad int.""" t = configtypes.Int(minval=2, maxval=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('4') def test_transform_none(self): """Test transform with an empty value.""" t = configtypes.Int(none_ok=True) - self.assertIsNone(t.transform('')) + assert t.transform('') is None def test_transform_int(self): """Test transform with an int.""" t = configtypes.Int() - self.assertEqual(t.transform('1337'), 1337) + assert t.transform('1337') == 1337 -class IntListTests(unittest.TestCase): - +class TestIntList(object): """Test IntList.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.IntList() def test_validate_good(self): @@ -459,7 +456,7 @@ class IntListTests(unittest.TestCase): def test_validate_empty(self): """Test validate with an empty value.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('23,,42') def test_validate_empty_none_ok(self): @@ -469,30 +466,28 @@ class IntListTests(unittest.TestCase): def test_validate_bad(self): """Test validate with bad values.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('23,foo,1337') def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('1337'), [1337]) + assert self.t.transform('1337') == [1337] def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual(self.t.transform('23,42,1337'), - [23, 42, 1337]) + assert self.t.transform('23,42,1337') == [23, 42, 1337] def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform('23,,42'), [23, None, 42]) + assert self.t.transform('23,,42'), [23, None == 42] -class FloatTests(unittest.TestCase): - +class TestFloat(object): """Test Float.""" def test_minval_gt_maxval(self): """Test __init__ with a minval bigger than the maxval.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.Float(minval=2, maxval=1) def test_validate_float(self): @@ -508,13 +503,13 @@ class FloatTests(unittest.TestCase): def test_validate_string(self): """Test validate with something which isn't an float.""" t = configtypes.Float() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('foobar') def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" t = configtypes.Float() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('') def test_validate_empty_none_ok(self): @@ -525,7 +520,7 @@ class FloatTests(unittest.TestCase): def test_validate_minval_toosmall(self): """Test validate with a minval and a too small float.""" t = configtypes.Float(minval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1.99') def test_validate_minval_ok(self): @@ -536,7 +531,7 @@ class FloatTests(unittest.TestCase): def test_validate_maxval_toolarge(self): """Test validate with a maxval and a too big float.""" t = configtypes.Float(maxval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('2.01') def test_validate_maxval_ok(self): @@ -553,47 +548,47 @@ class FloatTests(unittest.TestCase): def test_validate_range_bad(self): """Test validate with both min/maxval and a bad float.""" t = configtypes.Float(minval=2, maxval=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1.99') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3.01') def test_transform_empty(self): """Test transform with an empty value.""" t = configtypes.Float() - self.assertIsNone(t.transform('')) + assert t.transform('') is None def test_transform_float(self): """Test transform with an float.""" t = configtypes.Float() - self.assertEqual(t.transform('1337.42'), 1337.42) + assert t.transform('1337.42') == 1337.42 def test_transform_int(self): """Test transform with an int.""" t = configtypes.Float() - self.assertEqual(t.transform('1337'), 1337.00) + assert t.transform('1337') == 1337.00 -class PercTests(unittest.TestCase): - +class TestPerc(object): """Test Perc.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Perc() def test_minval_gt_maxval(self): """Test __init__ with a minval bigger than the maxval.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.Perc(minval=2, maxval=1) def test_validate_int(self): """Test validate with a normal int (not a percentage).""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('1337') def test_validate_string(self): """Test validate with something which isn't a percentage.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('1337%%') def test_validate_perc(self): @@ -602,7 +597,7 @@ class PercTests(unittest.TestCase): def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -613,7 +608,7 @@ class PercTests(unittest.TestCase): def test_validate_minval_toosmall(self): """Test validate with a minval and a too small percentage.""" t = configtypes.Perc(minval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') def test_validate_minval_ok(self): @@ -624,7 +619,7 @@ class PercTests(unittest.TestCase): def test_validate_maxval_toolarge(self): """Test validate with a maxval and a too big percentage.""" t = configtypes.Perc(maxval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3%') def test_validate_maxval_ok(self): @@ -641,30 +636,30 @@ class PercTests(unittest.TestCase): def test_validate_range_bad(self): """Test validate with both min/maxval and a bad percentage.""" t = configtypes.Perc(minval=2, maxval=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('4%') def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform_perc(self): """Test transform with a percentage.""" - self.assertEqual(self.t.transform('1337%'), 1337) + assert self.t.transform('1337%') == 1337 -class PercListTests(unittest.TestCase): - +class TestPercList(object): """Test PercList.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.PercList() def test_minval_gt_maxval(self): """Test __init__ with a minval bigger than the maxval.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.PercList(minval=2, maxval=1) def test_validate_good(self): @@ -673,13 +668,13 @@ class PercListTests(unittest.TestCase): def test_validate_bad(self): """Test validate with bad values.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('23%,42%%,1337%') def test_validate_minval_toosmall(self): """Test validate with a minval and a too small percentage.""" t = configtypes.PercList(minval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') def test_validate_minval_ok(self): @@ -690,7 +685,7 @@ class PercListTests(unittest.TestCase): def test_validate_maxval_toolarge(self): """Test validate with a maxval and a too big percentage.""" t = configtypes.PercList(maxval=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3%') def test_validate_maxval_ok(self): @@ -707,14 +702,14 @@ class PercListTests(unittest.TestCase): def test_validate_range_bad(self): """Test validate with both min/maxval and a bad percentage.""" t = configtypes.PercList(minval=2, maxval=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('4%') def test_validate_empty(self): """Test validate with an empty value.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('23%,,42%') def test_validate_empty_none_ok(self): @@ -724,37 +719,37 @@ class PercListTests(unittest.TestCase): def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('1337%'), [1337]) + assert self.t.transform('1337%') == [1337] def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual(self.t.transform('23%,42%,1337%'), [23, 42, 1337]) + assert self.t.transform('23%,42%,1337%'), [23, 42 == 1337] def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform('23%,,42%'), [23, None, 42]) + assert self.t.transform('23%,,42%'), [23, None == 42] -class PercOrIntTests(unittest.TestCase): - +class TestPercOrInt(object): """Test PercOrInt.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.PercOrInt() def test_minint_gt_maxint(self): """Test __init__ with a minint bigger than the maxint.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.PercOrInt(minint=2, maxint=1) def test_minperc_gt_maxperc(self): """Test __init__ with a minperc bigger than the maxperc.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): configtypes.PercOrInt(minperc=2, maxperc=1) def test_validate_string(self): """Test validate with something which isn't a percentage.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('1337%%') def test_validate_perc(self): @@ -767,7 +762,7 @@ class PercOrIntTests(unittest.TestCase): def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -778,7 +773,7 @@ class PercOrIntTests(unittest.TestCase): def test_validate_minint_toosmall(self): """Test validate with a minint and a too small int.""" t = configtypes.PercOrInt(minint=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1') def test_validate_minint_ok(self): @@ -789,7 +784,7 @@ class PercOrIntTests(unittest.TestCase): def test_validate_maxint_toolarge(self): """Test validate with a maxint and a too big int.""" t = configtypes.PercOrInt(maxint=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3') def test_validate_maxint_ok(self): @@ -806,15 +801,15 @@ class PercOrIntTests(unittest.TestCase): def test_validate_int_range_bad(self): """Test validate with both min/maxint and a bad int.""" t = configtypes.PercOrInt(minint=2, maxint=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('4') def test_validate_minperc_toosmall(self): """Test validate with a minperc and a too small perc.""" t = configtypes.PercOrInt(minperc=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') def test_validate_minperc_ok(self): @@ -825,7 +820,7 @@ class PercOrIntTests(unittest.TestCase): def test_validate_maxperc_toolarge(self): """Test validate with a maxperc and a too big perc.""" t = configtypes.PercOrInt(maxperc=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3%') def test_validate_maxperc_ok(self): @@ -842,9 +837,9 @@ class PercOrIntTests(unittest.TestCase): def test_validate_perc_range_bad(self): """Test validate with both min/maxperc and a bad perc.""" t = configtypes.PercOrInt(minperc=2, maxperc=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1%') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('4%') def test_validate_both_range_int(self): @@ -861,30 +856,30 @@ class PercOrIntTests(unittest.TestCase): def test_transform_none(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform_perc(self): """Test transform with a percentage.""" - self.assertEqual(self.t.transform('1337%'), '1337%') + assert self.t.transform('1337%') == '1337%' def test_transform_int(self): """Test transform with an int.""" - self.assertEqual(self.t.transform('1337'), '1337') + assert self.t.transform('1337') == '1337' -@mock.patch('qutebrowser.config.configtypes.cmdutils', new=stubs.FakeCmdUtils( - {'cmd1': stubs.FakeCommand("desc 1"), - 'cmd2': stubs.FakeCommand("desc 2")})) -class CommandTests(unittest.TestCase): - +class TestCommand(object): """Test Command.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, mocker, stubs): self.t = configtypes.Command() + cmd_utils = stubs.FakeCmdUtils({'cmd1': stubs.FakeCommand("desc 1"), + 'cmd2': stubs.FakeCommand("desc 2")}) + mocker.patch('qutebrowser.config.configtypes.cmdutils', new=cmd_utils) def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -904,32 +899,31 @@ class CommandTests(unittest.TestCase): def test_validate_invalid_command(self): """Test validate with an invalid command.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('cmd3') def test_validate_invalid_command_args(self): """Test validate with an invalid command and arguments.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('cmd3 foo bar') def test_transform(self): """Make sure transform doesn't alter values.""" - self.assertEqual(self.t.transform('foo bar'), 'foo bar') + assert self.t.transform('foo bar') == 'foo bar' def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_complete(self): """Test complete.""" items = self.t.complete() - self.assertEqual(len(items), 2) - self.assertIn(('cmd1', "desc 1"), items) - self.assertIn(('cmd2', "desc 2"), items) + assert len(items) == 2 + assert ('cmd1', "desc 1") in items + assert ('cmd2', "desc 2") in items -class ColorSystemTests(unittest.TestCase): - +class TestColorSystem(object): """Test ColorSystem.""" TESTS = { @@ -942,12 +936,13 @@ class ColorSystemTests(unittest.TestCase): } INVALID = ['RRGB', 'HSV '] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.ColorSystem() def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -955,44 +950,41 @@ class ColorSystemTests(unittest.TestCase): t = configtypes.ColorSystem(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val', TESTS) + def test_validate_valid(self, val): """Test validate with valid values.""" - for val in self.TESTS: - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError, msg=val): - self.t.validate(val) + with pytest.raises(configexc.ValidationError, msg=val): + self.t.validate(val) - def test_transform(self): + @pytest.mark.parametrize('k, v', TESTS.items()) + def test_transform(self, k, v): """Test transform.""" - for k, v in self.TESTS.items(): - with self.subTest(v=v): - self.assertEqual(self.t.transform(k), v, k) + assert self.t.transform(k) == v def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class QtColorTests(unittest.TestCase): - +class TestQtColor(object): """Test QtColor.""" VALID = ['#123', '#112233', '#111222333', '#111122223333', 'red'] INVALID = ['#00000G', '#123456789ABCD', '#12', 'foobar', '42'] INVALID_QT = ['rgb(0, 0, 0)'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.QtColor() def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1000,37 +992,34 @@ class QtColorTests(unittest.TestCase): t = configtypes.QtColor(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('v', VALID) + def test_validate_valid(self, v): """Test validate with valid values.""" - for v in self.VALID: - with self.subTest(v=v): - self.t.validate(v) + self.t.validate(v) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID + INVALID_QT) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID + self.INVALID_QT: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError, msg=val): - self.t.validate(val) + with pytest.raises(configexc.ValidationError, msg=val): + self.t.validate(val) - def test_transform(self): + @pytest.mark.parametrize('v', VALID) + def test_transform(self, v): """Test transform.""" - for v in self.VALID: - with self.subTest(v=v): - self.assertEqual(self.t.transform(v), QColor(v), v) + assert self.t.transform(v) == QColor(v) def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class CssColorTests(QtColorTests): - +class TestCssColor(TestQtColor): """Test CssColor.""" - VALID = QtColorTests.VALID + ['-foobar(42)'] + VALID = TestQtColor.VALID + ['-foobar(42)'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.CssColor() def test_validate_empty_none_ok(self): @@ -1038,18 +1027,21 @@ class CssColorTests(QtColorTests): t = configtypes.CssColor(none_ok=True) t.validate('') - def test_transform(self): + @pytest.mark.parametrize('v', VALID) + def test_validate_valid(self, v): + """Test validate with valid values.""" + super().test_validate_valid(v) + + @pytest.mark.parametrize('v', VALID) + def test_transform(self, v): """Make sure transform doesn't alter the value.""" - for v in self.VALID: - with self.subTest(v=v): - self.assertEqual(self.t.transform(v), v, v) + assert self.t.transform(v) == v -class QssColorTests(QtColorTests): - +class TestQssColor(TestQtColor): """Test QssColor.""" - VALID = QtColorTests.VALID + [ + VALID = TestQtColor.VALID + [ 'rgba(255, 255, 255, 255)', 'hsv(359, 255, 255)', 'hsva(359, 255, 255, 255)', 'hsv(10%, 10%, 10%)', 'qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 white, stop: 0.4 ' @@ -1059,10 +1051,11 @@ class QssColorTests(QtColorTests): 'qradialgradient(cx:0, cy:0, radius: 1, fx:0.5, fy:0.5, stop:0 ' 'white, stop:1 green)' ] - INVALID = QtColorTests.INVALID + ['rgb(1, 2, 3, 4)', 'foo(1, 2, 3)'] + INVALID = TestQtColor.INVALID + ['rgb(1, 2, 3, 4)', 'foo(1, 2, 3)'] INVALID_QT = [] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.QssColor() def test_validate_empty_none_ok(self): @@ -1070,19 +1063,27 @@ class QssColorTests(QtColorTests): t = configtypes.QssColor(none_ok=True) t.validate('') - def test_transform(self): + @pytest.mark.parametrize('v', VALID) + def test_validate_valid(self, v): + """Test validate with valid values.""" + super().test_validate_valid(v) + + @pytest.mark.parametrize('val', INVALID + INVALID_QT) + def test_validate_invalid(self, val): + """Test validate with invalid values.""" + super().test_validate_invalid(val) + + @pytest.mark.parametrize('v', VALID) + def test_transform(self, v): """Make sure transform doesn't alter the value.""" - for v in self.VALID: - with self.subTest(v=v): - self.assertEqual(self.t.transform(v), v, v) + assert self.t.transform(v) == v FontDesc = collections.namedtuple('FontDesc', ['style', 'weight', 'pt', 'px', 'family']) -class FontTests(unittest.TestCase): - +class TestFont(object): """Test Font/QtFont.""" TESTS = { @@ -1124,15 +1125,16 @@ class FontTests(unittest.TestCase): } INVALID = ['green "Foobar Neue"', 'italic green "Foobar Neue"', 'bold bold "Foobar Neue"', 'bold italic "Foobar Neue"' - 'bold', '10pt 20px "Foobar Neue"'] + 'bold', '10pt 20px "Foobar Neue"'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Font() self.t2 = configtypes.QtFont() def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1142,40 +1144,24 @@ class FontTests(unittest.TestCase): t.validate('') t2.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val, attr', itertools.product(TESTS, ['t', 't2'])) + def test_validate_valid(self, val, attr): """Test validate with valid values.""" - for val in self.TESTS: - with self.subTest(val=val): - with self.subTest(t="t1"): - self.t.validate(val) - with self.subTest(t="t2"): - self.t2.validate(val) + getattr(self, attr).validate(val) - # FIXME - # https://github.com/The-Compiler/qutebrowser/issues/103 - @unittest.expectedFailure - def test_validate_invalid(self): + @pytest.mark.parametrize('val, attr', + itertools.product(INVALID, ['t', 't2'])) + @pytest.mark.xfail(reason='FIXME: #103') + def test_validate_invalid(self, val, attr): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.subTest(t="t1"): - with self.assertRaises(configexc.ValidationError, - msg=val): - self.t.validate(val) - with self.subTest(t="t2"): - with self.assertRaises(configexc.ValidationError, - msg=val): - self.t2.validate(val) + with pytest.raises(configexc.ValidationError, msg=val): + getattr(self, attr).validate(val) - def test_transform(self): + @pytest.mark.parametrize('string, desc', TESTS.items()) + def test_transform(self, string, desc): """Test transform.""" - for string, desc in self.TESTS.items(): - with self.subTest(string=string, desc=desc): - with self.subTest(t="t1"): - self.assertEqual(self.t.transform(string), string, string) - with self.subTest(t="t2"): - self.assertEqual(Font(self.t2.transform(string)), - Font.fromdesc(desc), string) + assert self.t.transform(string) == string + assert Font(self.t2.transform(string)) == Font.fromdesc(desc) def test_transform_float(self): """Test QtFont's transform with a float as point size. @@ -1184,20 +1170,19 @@ class FontTests(unittest.TestCase): rounding as appropriate. """ value = Font(self.t2.transform('10.5pt "Foobar Neue"')) - self.assertEqual(value.family(), 'Foobar Neue') - self.assertEqual(value.weight(), QFont.Normal) - self.assertEqual(value.style(), QFont.StyleNormal) - self.assertGreaterEqual(value.pointSize(), 10) - self.assertLessEqual(value.pointSize(), 11) + assert value.family() == 'Foobar Neue' + assert value.weight() == QFont.Normal + assert value.style() == QFont.StyleNormal + assert value.pointSize() >= 10 + assert value.pointSize() <= 11 def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) - self.assertIsNone(self.t2.transform('')) + assert self.t.transform('') is None + assert self.t2.transform('') is None -class FontFamilyTests(unittest.TestCase): - +class TestFontFamily(object): """Test FontFamily.""" TESTS = ['"Foobar Neue"', 'inconsolatazi4', 'Foobar'] @@ -1218,12 +1203,13 @@ class FontFamilyTests(unittest.TestCase): 'bold italic 10pt "Foobar Neue"', ] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.FontFamily() def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1231,34 +1217,32 @@ class FontFamilyTests(unittest.TestCase): t = configtypes.FontFamily(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val', TESTS) + def test_validate_valid(self, val): """Test validate with valid values.""" - for val in self.TESTS: - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError, msg=val): - self.t.validate(val) + with pytest.raises(configexc.ValidationError, msg=val): + self.t.validate(val) def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class RegexTests(unittest.TestCase): - +class TestRegex(object): """Test Regex.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Regex() def test_validate_empty(self): """Test validate with an empty string.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1272,23 +1256,23 @@ class RegexTests(unittest.TestCase): def test_validate_invalid(self): """Test validate with an invalid regex.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate(r'(foo|bar))?baz[fis]h') def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform(self): """Test transform.""" - self.assertEqual(self.t.transform(r'foobar'), re.compile(r'foobar')) + assert self.t.transform(r'foobar') == re.compile(r'foobar') -class RegexListTests(unittest.TestCase): - +class TestRegexList(object): """Test RegexList.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.RegexList() def test_validate_good(self): @@ -1297,7 +1281,7 @@ class RegexListTests(unittest.TestCase): def test_validate_empty(self): """Test validate with an empty value.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate(r'(foo|bar),,1337{42}') def test_validate_empty_none_ok(self): @@ -1307,39 +1291,38 @@ class RegexListTests(unittest.TestCase): def test_validate_bad(self): """Test validate with bad values.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate(r'(foo|bar),((),1337{42}') def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('foo'), [re.compile('foo')]) + assert self.t.transform('foo') == [re.compile('foo')] def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual(self.t.transform('foo,bar,baz'), - [re.compile('foo'), re.compile('bar'), - re.compile('baz')]) + expected = [re.compile('foo'), re.compile('bar'), re.compile('baz')] + assert self.t.transform('foo,bar,baz') == expected def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform('foo,,bar'), - [re.compile('foo'), None, re.compile('bar')]) + expected = [re.compile('foo'), None, re.compile('bar')] + assert self.t.transform('foo,,bar') == expected -@mock.patch('qutebrowser.config.configtypes.os.path', autospec=True) -class FileTests(unittest.TestCase): - +class TestFile(object): """Test File.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.File() - def test_validate_empty(self, _os_path): + + def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate("") - def test_validate_empty_none_ok(self, _os_path): + def test_validate_empty_none_ok(self): """Test validate with empty string and none_ok = True.""" t = configtypes.File(none_ok=True) t.validate("") @@ -1348,7 +1331,7 @@ class FileTests(unittest.TestCase): """Test validate with a file which does not exist.""" os_path.expanduser.side_effect = lambda x: x os_path.isfile.return_value = False - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_validate_exists_abs(self, os_path): @@ -1363,7 +1346,7 @@ class FileTests(unittest.TestCase): os_path.expanduser.side_effect = lambda x: x os_path.isfile.return_value = True os_path.isabs.return_value = False - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_validate_expanduser(self, os_path): @@ -1374,38 +1357,37 @@ class FileTests(unittest.TestCase): self.t.validate('~/foobar') os_path.expanduser.assert_called_once_with('~/foobar') - def test_validate_invalid_encoding(self, os_path): + def test_validate_invalid_encoding(self, os_path, unicode_encode_err): """Test validate with an invalid encoding, e.g. LC_ALL=C.""" - os_path.isfile.side_effect = helpers.unicode_encode_err - os_path.isabs.side_effect = helpers.unicode_encode_err - with self.assertRaises(configexc.ValidationError): + os_path.isfile.side_effect = unicode_encode_err + os_path.isabs.side_effect = unicode_encode_err + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_transform(self, os_path): """Test transform.""" os_path.expanduser.side_effect = lambda x: x.replace('~', '/home/foo') - self.assertEqual(self.t.transform('~/foobar'), '/home/foo/foobar') + assert self.t.transform('~/foobar') == '/home/foo/foobar' os_path.expanduser.assert_called_once_with('~/foobar') - def test_transform_empty(self, _os_path): + def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -@mock.patch('qutebrowser.config.configtypes.os.path', autospec=True) -class DirectoryTests(unittest.TestCase): - +class TestDirectory(object): """Test Directory.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Directory() - def test_validate_empty(self, _os_path): + def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate("") - def test_validate_empty_none_ok(self, _os_path): + def test_validate_empty_none_ok(self): """Test validate with empty string and none_ok = True.""" t = configtypes.Directory(none_ok=True) t.validate("") @@ -1414,7 +1396,7 @@ class DirectoryTests(unittest.TestCase): """Test validate with a directory which does not exist.""" os_path.expanduser.side_effect = lambda x: x os_path.isdir.return_value = False - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_validate_exists_abs(self, os_path): @@ -1429,7 +1411,7 @@ class DirectoryTests(unittest.TestCase): os_path.expanduser.side_effect = lambda x: x os_path.isdir.return_value = True os_path.isabs.return_value = False - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_validate_expanduser(self, os_path): @@ -1441,41 +1423,41 @@ class DirectoryTests(unittest.TestCase): self.t.validate('~/foobar') os_path.expanduser.assert_called_once_with('~/foobar') - def test_validate_expandvars(self, os_path): + def test_validate_expandvars(self, os_path, monkeypatch): """Test if validate expands the user correctly.""" os_path.expandvars.side_effect = lambda x: x.replace('$BAR', '/home/foo/bar') os_path.expanduser.side_effect = lambda x: x os_path.isdir.side_effect = lambda path: path == '/home/foo/bar/foobar' os_path.isabs.return_value = True - with helpers.environ_set_temp({'BAR': '/home/foo/bar'}): - self.t.validate('$BAR/foobar') - os_path.expandvars.assert_called_once_with('$BAR/foobar') + monkeypatch.setenv('BAR', '/home/foo/bar') + self.t.validate('$BAR/foobar') + os_path.expandvars.assert_called_once_with('$BAR/foobar') - def test_validate_invalid_encoding(self, os_path): + def test_validate_invalid_encoding(self, os_path, unicode_encode_err): """Test validate with an invalid encoding, e.g. LC_ALL=C.""" - os_path.isdir.side_effect = helpers.unicode_encode_err - os_path.isabs.side_effect = helpers.unicode_encode_err - with self.assertRaises(configexc.ValidationError): + os_path.isdir.side_effect = unicode_encode_err + os_path.isabs.side_effect = unicode_encode_err + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_transform(self, os_path): """Test transform.""" os_path.expandvars.side_effect = lambda x: x os_path.expanduser.side_effect = lambda x: x.replace('~', '/home/foo') - self.assertEqual(self.t.transform('~/foobar'), '/home/foo/foobar') + assert self.t.transform('~/foobar') == '/home/foo/foobar' os_path.expanduser.assert_called_once_with('~/foobar') - def test_transform_empty(self, _os_path): + def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class WebKitByteTests(unittest.TestCase): - +class TestWebKitByte(object): """Test WebKitBytes.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.WebKitBytes() def test_validate_empty(self): @@ -1497,18 +1479,18 @@ class WebKitByteTests(unittest.TestCase): def test_validate_int_negative(self): """Test validate with a negative int.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('-1') def test_validate_int_negative_suffix(self): """Test validate with a negative int with suffix.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('-1k') def test_validate_int_toobig(self): """Test validate with an int which is too big.""" t = configtypes.WebKitBytes(maxsize=10) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('11') def test_validate_int_not_toobig(self): @@ -1519,37 +1501,37 @@ class WebKitByteTests(unittest.TestCase): def test_validate_int_toobig_suffix(self): """Test validate with an int which is too big with suffix.""" t = configtypes.WebKitBytes(maxsize=10) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1k') def test_validate_int_invalid_suffix(self): """Test validate with an int with an invalid suffix.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('56x') def test_validate_int_double_suffix(self): """Test validate with an int with a double suffix.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('56kk') def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform_int(self): """Test transform with a simple value.""" - self.assertEqual(self.t.transform('10'), 10) + assert self.t.transform('10') == 10 def test_transform_int_suffix(self): """Test transform with a value with suffix.""" - self.assertEqual(self.t.transform('1k'), 1024) + assert self.t.transform('1k') == 1024 -class WebKitBytesListTests(unittest.TestCase): - +class TestWebKitBytesList(object): """Test WebKitBytesList.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.WebKitBytesList() def test_validate_good(self): @@ -1560,15 +1542,15 @@ class WebKitBytesListTests(unittest.TestCase): def test_validate_bad(self): """Test validate with bad values.""" t = configtypes.WebKitBytesList() - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('23,56kk,1337') def test_validate_maxsize_toolarge(self): """Test validate with a maxsize and a too big size.""" t = configtypes.WebKitBytesList(maxsize=2) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('3k') def test_validate_maxsize_ok(self): @@ -1578,7 +1560,7 @@ class WebKitBytesListTests(unittest.TestCase): def test_validate_empty(self): """Test validate with an empty value.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('23,,42') def test_validate_empty_none_ok(self): @@ -1589,7 +1571,7 @@ class WebKitBytesListTests(unittest.TestCase): def test_validate_len_tooshort(self): """Test validate with a too short length.""" t = configtypes.WebKitBytesList(length=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1,2') def test_validate_len_ok(self): @@ -1600,32 +1582,32 @@ class WebKitBytesListTests(unittest.TestCase): def test_validate_len_toolong(self): """Test validate with a too long length.""" t = configtypes.WebKitBytesList(length=3) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('1,2,3,4') def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('1k'), [1024]) + assert self.t.transform('1k') == [1024] def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual(self.t.transform('23,2k,1337'), [23, 2048, 1337]) + assert self.t.transform('23,2k,1337'), [23, 2048 == 1337] def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform('23,,42'), [23, None, 42]) + assert self.t.transform('23,,42'), [23, None == 42] -class ShellCommandTests(unittest.TestCase): - +class TestShellCommand(object): """Test ShellCommand.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.ShellCommand() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate("") def test_validate_empty_none_ok(self): @@ -1645,37 +1627,37 @@ class ShellCommandTests(unittest.TestCase): def test_validate_placeholder_invalid(self): """Test validate with an invalid placeholder.""" t = configtypes.ShellCommand(placeholder='{}') - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('foo{} bar') def test_transform_single(self): """Test transform with a single word.""" - self.assertEqual(self.t.transform('foobar'), ['foobar']) + assert self.t.transform('foobar') == ['foobar'] def test_transform_double(self): """Test transform with two words.""" - self.assertEqual(self.t.transform('foobar baz'), ['foobar', 'baz']) + assert self.t.transform('foobar baz'), ['foobar' == 'baz'] def test_transform_quotes(self): """Test transform with a quoted word.""" - self.assertEqual(self.t.transform('foo "bar baz" fish'), - ['foo', 'bar baz', 'fish']) + expected = ['foo', 'bar baz', 'fish'] + assert self.t.transform('foo "bar baz" fish') == expected def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class ProxyTests(unittest.TestCase): - +class TestProxy(object): """Test Proxy.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Proxy() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1693,12 +1675,12 @@ class ProxyTests(unittest.TestCase): def test_validate_invalid(self): """Test validate with an invalid URL.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate(':') def test_validate_scheme(self): """Test validate with a URL with wrong scheme.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('ftp://example.com/') def test_validate_http(self): @@ -1715,56 +1697,56 @@ class ProxyTests(unittest.TestCase): def test_complete(self): """Test complete.""" - self.assertEqual(self.t.complete(), - [('system', "Use the system wide proxy."), - ('none', "Don't use any proxy"), - ('http://', 'HTTP proxy URL'), - ('socks://', 'SOCKS proxy URL')]) + assert self.t.complete() == \ + [('system', "Use the system wide proxy."), + ('none', "Don't use any proxy"), + ('http://', 'HTTP proxy URL'), + ('socks://', 'SOCKS proxy URL')] def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform_system(self): """Test transform with system proxy.""" - self.assertIs(self.t.transform('system'), configtypes.SYSTEM_PROXY) + assert self.t.transform('system') is configtypes.SYSTEM_PROXY def test_transform_none(self): """Test transform with no proxy.""" - self.assertEqual(NetworkProxy(self.t.transform('none')), - NetworkProxy(QNetworkProxy.NoProxy)) + assert NetworkProxy(self.t.transform('none')) == \ + NetworkProxy(QNetworkProxy.NoProxy) def test_transform_socks(self): """Test transform with a socks proxy.""" proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') val = NetworkProxy(self.t.transform('socks://example.com/')) - self.assertEqual(proxy, val) + assert proxy == val def test_transform_socks5(self): """Test transform with a socks5 proxy.""" proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') val = NetworkProxy(self.t.transform('socks5://example.com')) - self.assertEqual(proxy, val) + assert proxy == val def test_transform_http_port(self): """Test transform with a http proxy with set port.""" proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2342) val = NetworkProxy(self.t.transform('socks5://example.com:2342')) - self.assertEqual(proxy, val) + assert proxy == val def test_transform_socks_user(self): """Test transform with a socks proxy with set user.""" proxy = NetworkProxy( QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo') val = NetworkProxy(self.t.transform('socks5://foo@example.com')) - self.assertEqual(proxy, val) + assert proxy == val def test_transform_socks_user_password(self): """Test transform with a socks proxy with set user/password.""" proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo', 'bar') val = NetworkProxy(self.t.transform('socks5://foo:bar@example.com')) - self.assertEqual(proxy, val) + assert proxy == val def test_transform_socks_user_password_port(self): """Test transform with a socks proxy with set port/user/password.""" @@ -1772,19 +1754,19 @@ class ProxyTests(unittest.TestCase): 'foo', 'bar') val = NetworkProxy( self.t.transform('socks5://foo:bar@example.com:2323')) - self.assertEqual(proxy, val) + assert proxy == val -class SearchEngineNameTests(unittest.TestCase): - +class TestSearchEngineName(object): """Test SearchEngineName.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.SearchEngineName() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1794,23 +1776,23 @@ class SearchEngineNameTests(unittest.TestCase): def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform(self): """Test transform with a value.""" - self.assertEqual(self.t.transform("foobar"), "foobar") + assert self.t.transform("foobar") == "foobar" -class SearchEngineUrlTests(unittest.TestCase): - +class TestSearchEngineUrl(object): """Test SearchEngineUrl.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.SearchEngineUrl() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1820,7 +1802,7 @@ class SearchEngineUrlTests(unittest.TestCase): def test_validate_no_placeholder(self): """Test validate with no placeholder.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foo') def test_validate(self): @@ -1829,28 +1811,28 @@ class SearchEngineUrlTests(unittest.TestCase): def test_validate_invalid_url(self): """Test validate with an invalud URL.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate(':{}') def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform(self): """Test transform with a value.""" - self.assertEqual(self.t.transform("foobar"), "foobar") + assert self.t.transform("foobar") == "foobar" -class FuzzyUrlTests(unittest.TestCase): - +class TestFuzzyUrl(object): """Test FuzzyUrl.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.FuzzyUrl() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1868,78 +1850,78 @@ class FuzzyUrlTests(unittest.TestCase): def test_validate_invalid_url(self): """Test validate with an invalid URL.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('::foo') def test_validate_invalid_search(self): """Test validate with an invalid search term.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foo bar') def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform(self): """Test transform with a value.""" - self.assertEqual(self.t.transform("example.com"), - QUrl('http://example.com')) + assert self.t.transform("example.com") == QUrl('http://example.com') -class UserStyleSheetTests(unittest.TestCase): - +class TestUserStyleSheet(object): """Test UserStyleSheet.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.UserStyleSheet() - @mock.patch('qutebrowser.config.configtypes.os.path', autospec=True) - def test_validate_invalid_encoding(self, os_path): + def test_validate_invalid_encoding(self, mocker, unicode_encode_err): """Test validate with an invalid encoding, e.g. LC_ALL=C.""" - os_path.isfile.side_effect = helpers.unicode_encode_err - os_path.isabs.side_effect = helpers.unicode_encode_err - with self.assertRaises(configexc.ValidationError): + os_path = mocker.patch('qutebrowser.config.configtypes.os.path', + autospec=True) + os_path.isfile.side_effect = unicode_encode_err + os_path.isabs.side_effect = unicode_encode_err + with pytest.raises(configexc.ValidationError): self.t.validate('foobar') def test_transform_empty(self): """Test transform with an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None def test_transform_file(self): """Test transform with a filename.""" path = os.path.join(os.path.sep, 'foo', 'bar') - self.assertEqual(self.t.transform(path), QUrl("file:///foo/bar")) + assert self.t.transform(path) == QUrl("file:///foo/bar") - def test_transform_file_expandvars(self): + def test_transform_file_expandvars(self, monkeypatch): """Test transform with a filename (expandvars).""" - with helpers.environ_set_temp({'FOO': 'foo'}): - path = os.path.join(os.path.sep, '$FOO', 'bar') - self.assertEqual(self.t.transform(path), QUrl("file:///foo/bar")) + monkeypatch.setenv('FOO', 'foo') + path = os.path.join(os.path.sep, '$FOO', 'bar') + assert self.t.transform(path) == QUrl("file:///foo/bar") def test_transform_base64(self): """Test transform with a data string.""" b64 = base64.b64encode(b"test").decode('ascii') url = QUrl("data:text/css;charset=utf-8;base64,{}".format(b64)) - self.assertEqual(self.t.transform("test"), url) + assert self.t.transform("test") == url -class AutoSearchTests(unittest.TestCase): - +class TestAutoSearch(object): """Test AutoSearch.""" TESTS = { - 'naive': ['naive', 'NAIVE'] + BoolTests.TESTS[True], + 'naive': ['naive', 'NAIVE'] + TestBool.TESTS[True], 'dns': ['dns', 'DNS'], - False: BoolTests.TESTS[False], + False: TestBool.TESTS[False], } INVALID = ['ddns', 'foo'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.AutoSearch() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1947,49 +1929,49 @@ class AutoSearchTests(unittest.TestCase): t = configtypes.AutoSearch(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val', + [val for vallist in TESTS.values() for val in + vallist]) + def test_validate_valid(self, val): """Test validate with valid values.""" - for vallist in self.TESTS.values(): - for val in vallist: - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError): - self.t.validate(val) + with pytest.raises(configexc.ValidationError): + self.t.validate(val) - def test_transform(self): + @pytest.mark.parametrize('out, inp', + [(out, inp) for (out, inputs) in TESTS.items() for + inp in inputs] + ) + def test_transform(self, out, inp): """Test transform with all values.""" - for out, inputs in self.TESTS.items(): - for inp in inputs: - with self.subTest(inp=inp): - self.assertEqual(self.t.transform(inp), out, inp) + assert self.t.transform(inp) == out def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class IgnoreCaseTests(unittest.TestCase): - +class TestIgnoreCase(object): """Test IgnoreCase.""" TESTS = { 'smart': ['smart', 'SMART'], - True: BoolTests.TESTS[True], - False: BoolTests.TESTS[False], + True: TestBool.TESTS[True], + False: TestBool.TESTS[False], } INVALID = ['ssmart', 'foo'] - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.IgnoreCase() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -1997,42 +1979,42 @@ class IgnoreCaseTests(unittest.TestCase): t = configtypes.IgnoreCase(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val', + [val for vallist in TESTS.values() for val in + vallist]) + def test_validate_valid(self, val): """Test validate with valid values.""" - for vallist in self.TESTS.values(): - for val in vallist: - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) - def test_validate_invalid(self): + @pytest.mark.parametrize('val', INVALID) + def test_validate_invalid(self, val): """Test validate with invalid values.""" - for val in self.INVALID: - with self.subTest(val=val): - with self.assertRaises(configexc.ValidationError): - self.t.validate(val) + with pytest.raises(configexc.ValidationError): + self.t.validate(val) - def test_transform(self): + @pytest.mark.parametrize('out, inp', + [(out, inp) for (out, inputs) in TESTS.items() for + inp in inputs] + ) + def test_transform(self, out, inp): """Test transform with all values.""" - for out, inputs in self.TESTS.items(): - for inp in inputs: - with self.subTest(inp=inp): - self.assertEqual(self.t.transform(inp), out, inp) + assert self.t.transform(inp) == out def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class EncodingTests(unittest.TestCase): - +class TestEncoding(object): """Test Encoding.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.Encoding() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -2040,31 +2022,30 @@ class EncodingTests(unittest.TestCase): t = configtypes.Encoding(none_ok=True) t.validate('') - def test_validate_valid(self): + @pytest.mark.parametrize('val', ('utf-8', 'UTF-8', 'iso8859-1')) + def test_validate_valid(self, val): """Test validate with valid values.""" - for val in ('utf-8', 'UTF-8', 'iso8859-1'): - with self.subTest(val=val): - self.t.validate(val) + self.t.validate(val) def test_validate_invalid(self): """Test validate with an invalid value.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('blubber') def test_transform(self): """Test if transform doesn't alter the value.""" - self.assertEqual(self.t.transform('utf-8'), 'utf-8') + assert self.t.transform('utf-8') == 'utf-8' def test_transform_empty(self): """Test transform with none_ok = False and an empty value.""" - self.assertIsNone(self.t.transform('')) + assert self.t.transform('') is None -class UrlListTests(unittest.TestCase): - +class TestUrlList(object): """Test UrlList.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.UrlList() def test_validate_single(self): @@ -2077,7 +2058,7 @@ class UrlListTests(unittest.TestCase): def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -2087,41 +2068,41 @@ class UrlListTests(unittest.TestCase): def test_validate_empty_item(self): """Test validate with empty item and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('foo,,bar') def test_validate_empty_item_none_ok(self): """Test validate with empty item and none_ok = True.""" t = configtypes.UrlList(none_ok=True) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): t.validate('foo,,bar') def test_transform_single(self): """Test transform with a single value.""" - self.assertEqual(self.t.transform('http://qutebrowser.org/'), - [QUrl('http://qutebrowser.org/')]) + expected = [QUrl('http://qutebrowser.org/')] + assert self.t.transform('http://qutebrowser.org/') == expected def test_transform_more(self): """Test transform with multiple values.""" - self.assertEqual( - self.t.transform('http://qutebrowser.org/,http://heise.de/'), + assert ( + self.t.transform('http://qutebrowser.org/,http://heise.de/') == [QUrl('http://qutebrowser.org/'), QUrl('http://heise.de/')]) def test_transform_empty(self): """Test transform with an empty value.""" - self.assertEqual(self.t.transform(''), None) + assert self.t.transform('') is None -class FormatStringTests(unittest.TestCase): - +class TestFormatString(object): """Test FormatString.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.FormatString(fields=('foo', 'bar')) def test_transform(self): """Test if transform doesn't alter the value.""" - self.assertEqual(self.t.transform('foo {bar} baz'), 'foo {bar} baz') + assert self.t.transform('foo {bar} baz') == 'foo {bar} baz' def test_validate_simple(self): """Test validate with a simple string.""" @@ -2133,17 +2114,17 @@ class FormatStringTests(unittest.TestCase): def test_validate_invalid_placeholders(self): """Test validate with invalid placeholders.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('{foo} {bar} {baz}') def test_validate_invalid_placeholders_syntax(self): """Test validate with invalid placeholders syntax.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('{foo} {bar') def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate('') def test_validate_empty_none_ok(self): @@ -2152,16 +2133,16 @@ class FormatStringTests(unittest.TestCase): t.validate('') -class UserAgentTests(unittest.TestCase): - +class TestUserAgent(object): """Test UserAgent.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.t = configtypes.UserAgent() def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.t.validate("") def test_validate_empty_none_ok(self): @@ -2175,8 +2156,4 @@ class UserAgentTests(unittest.TestCase): def test_transform(self): """Test if transform doesn't alter the value.""" - self.assertEqual(self.t.transform('foobar'), 'foobar') - - -if __name__ == '__main__': - unittest.main() + assert self.t.transform('foobar') == 'foobar' diff --git a/test/conftest.py b/test/conftest.py index 7ce7214bc..b9776327b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -29,6 +29,7 @@ def app_and_logging(qapp): and used by all tests. """ from log import init + init() @@ -38,4 +39,14 @@ def stubs(): Provides access to stub objects useful for testing. """ import stubs + return stubs + + +@pytest.fixture(scope='session') +def unicode_encode_err(): + return UnicodeEncodeError('ascii', # codec + '', # object + 0, # start + 2, # end + 'fake exception') # reason diff --git a/test/helpers.py b/test/helpers.py index 838cc88af..3dc6a1072 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -19,7 +19,6 @@ """Helpers needed by tests.""" -import os import logging import contextlib from unittest import mock @@ -29,13 +28,6 @@ from PyQt5.QtWebKitWidgets import QWebPage from PyQt5.QtNetwork import QNetworkAccessManager -unicode_encode_err = UnicodeEncodeError('ascii', # codec - '', # object - 0, # start - 2, # end - 'fake exception') # reason - - @contextlib.contextmanager def disable_logger(name): """Temporarily disable a logger.""" From ea16fb36849c24d7a581c5155759970d11e7a14a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 11:43:14 -0300 Subject: [PATCH 15/47] Converted test_tabhistory to pytest --- test/browser/test_tabhistory.py | 122 ++++++++++++++++---------------- test/conftest.py | 12 ++++ test/helpers.py | 10 --- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/test/browser/test_tabhistory.py b/test/browser/test_tabhistory.py index 1714894b5..aebbb7259 100644 --- a/test/browser/test_tabhistory.py +++ b/test/browser/test_tabhistory.py @@ -19,103 +19,103 @@ """Tests for webelement.tabhistory.""" -import unittest - from PyQt5.QtCore import QUrl, QPoint +import pytest from qutebrowser.browser import tabhistory from qutebrowser.browser.tabhistory import TabHistoryItem as Item from qutebrowser.utils import qtutils -from qutebrowser.test import helpers -class SerializeHistoryTests(unittest.TestCase): +class TestSerializeHistory(object): """Tests for serialize().""" - def setUp(self): - self.page = helpers.get_webpage() - self.history = self.page.history() - self.assertEqual(self.history.count(), 0) + ITEMS = [ + Item(QUrl('https://www.heise.de/'), QUrl('http://www.heise.de/'), + 'heise'), + Item(QUrl('http://example.com/%E2%80%A6'), + QUrl('http://example.com/%E2%80%A6'), 'percent', active=True), + Item(QUrl('http://example.com/?foo=bar'), + QUrl('http://original.url.example.com/'), 'arg', + user_data={'foo': 23, 'bar': 42}), + # From https://github.com/OtterBrowser/otter-browser/issues/709#issuecomment-74749471 + Item(QUrl( + 'http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), + QUrl( + 'http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), + 'Page not found | github', + user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}), + Item(QUrl( + 'https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), + QUrl( + 'https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), + '"some label" - email@gmail.com - Gmail"', + user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}), + ] - self.items = [ - Item(QUrl('https://www.heise.de/'), QUrl('http://www.heise.de/'), - 'heise'), - Item(QUrl('http://example.com/%E2%80%A6'), - QUrl('http://example.com/%E2%80%A6'), 'percent', active=True), - Item(QUrl('http://example.com/?foo=bar'), - QUrl('http://original.url.example.com/'), 'arg', - user_data={'foo': 23, 'bar': 42}), - # From https://github.com/OtterBrowser/otter-browser/issues/709#issuecomment-74749471 - Item(QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), - QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), - 'Page not found | github', - user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}), - Item(QUrl('https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), - QUrl('https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), - '"some label" - email@gmail.com - Gmail"', - user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}), - ] - stream, _data, self.user_data = tabhistory.serialize(self.items) + @pytest.fixture(autouse=True) + def setup(self, webpage): + self.page = webpage + self.history = self.page.history() + assert self.history.count() == 0 + + stream, _data, self.user_data = tabhistory.serialize(self.ITEMS) qtutils.deserialize_stream(stream, self.history) def test_count(self): """Check if the history's count was loaded correctly.""" - with helpers.disable_logger('qt'): - self.assertEqual(self.history.count(), len(self.items)) + assert self.history.count() == len(self.ITEMS) - def test_valid(self): + @pytest.mark.parametrize('i', range(len(ITEMS))) + def test_valid(self, i): """Check if all items are valid.""" - for i, _item in enumerate(self.items): - self.assertTrue(self.history.itemAt(i).isValid()) + assert self.history.itemAt(i).isValid() - def test_no_userdata(self): + @pytest.mark.parametrize('i', range(len(ITEMS))) + def test_no_userdata(self, i): """Check if all items have no user data.""" - for i, _item in enumerate(self.items): - self.assertIsNone(self.history.itemAt(i).userData()) + assert self.history.itemAt(i).userData() is None def test_userdata(self): """Check if all user data has been restored to self.user_data.""" - for item, user_data in zip(self.items, self.user_data): - self.assertEqual(user_data, item.user_data) + for item, user_data in zip(self.ITEMS, self.user_data): + assert user_data == item.user_data def test_currentitem(self): """Check if the current item index was loaded correctly.""" - self.assertEqual(self.history.currentItemIndex(), 1) + assert self.history.currentItemIndex() == 1 - def test_urls(self): + @pytest.mark.parametrize('i, item', enumerate(ITEMS)) + def test_urls(self, i, item): """Check if the URLs were loaded correctly.""" - for i, item in enumerate(self.items): - with self.subTest(i=i, item=item): - self.assertEqual(self.history.itemAt(i).url(), item.url) + assert self.history.itemAt(i).url() == item.url - def test_original_urls(self): + @pytest.mark.parametrize('i, item', enumerate(ITEMS)) + def test_original_urls(self, i, item): """Check if the original URLs were loaded correctly.""" - for i, item in enumerate(self.items): - with self.subTest(i=i, item=item): - self.assertEqual(self.history.itemAt(i).originalUrl(), - item.original_url) + assert self.history.itemAt(i).originalUrl() == item.original_url - def test_titles(self): + @pytest.mark.parametrize('i, item', enumerate(ITEMS)) + def test_titles(self, i, item): """Check if the titles were loaded correctly.""" - for i, item in enumerate(self.items): - with self.subTest(i=i, item=item): - self.assertEqual(self.history.itemAt(i).title(), item.title) + assert self.history.itemAt(i).title() == item.title -class SerializeHistorySpecialTests(unittest.TestCase): +class TestSerializeHistorySpecial(object): """Tests for serialize() without items set up in setUp.""" - def setUp(self): - self.page = helpers.get_webpage() + @pytest.fixture(autouse=True) + def setUp(self, webpage): + self.page = webpage self.history = self.page.history() - self.assertEqual(self.history.count(), 0) + assert self.history.count() == 0 def test_no_active_item(self): """Check tabhistory.serialize with no active item.""" items = [Item(QUrl(), QUrl(), '')] - with self.assertRaises(ValueError): + with pytest.raises(ValueError): tabhistory.serialize(items) def test_two_active_items(self): @@ -123,7 +123,7 @@ class SerializeHistorySpecialTests(unittest.TestCase): items = [Item(QUrl(), QUrl(), '', active=True), Item(QUrl(), QUrl(), ''), Item(QUrl(), QUrl(), '', active=True)] - with self.assertRaises(ValueError): + with pytest.raises(ValueError): tabhistory.serialize(items) def test_empty(self): @@ -131,10 +131,8 @@ class SerializeHistorySpecialTests(unittest.TestCase): items = [] stream, _data, user_data = tabhistory.serialize(items) qtutils.deserialize_stream(stream, self.history) - self.assertEqual(self.history.count(), 0) - self.assertEqual(self.history.currentItemIndex(), 0) - self.assertFalse(user_data) + assert self.history.count() == 0 + assert self.history.currentItemIndex() == 0 + assert not user_data -if __name__ == '__main__': - unittest.main() diff --git a/test/conftest.py b/test/conftest.py index b9776327b..fa98b35a1 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -50,3 +50,15 @@ def unicode_encode_err(): 0, # start 2, # end 'fake exception') # reason + + +@pytest.fixture +def webpage(): + """Get a new QWebPage object.""" + from PyQt5.QtWebKitWidgets import QWebPage + from PyQt5.QtNetwork import QNetworkAccessManager + + page = QWebPage() + nam = page.networkAccessManager() + nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) + return page \ No newline at end of file diff --git a/test/helpers.py b/test/helpers.py index 3dc6a1072..7050c2720 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -24,8 +24,6 @@ import contextlib from unittest import mock from PyQt5.QtGui import QKeyEvent -from PyQt5.QtWebKitWidgets import QWebPage -from PyQt5.QtNetwork import QNetworkAccessManager @contextlib.contextmanager @@ -47,14 +45,6 @@ def fake_keyevent(key, modifiers=0, text=''): return evtmock -def get_webpage(): - """Get a new QWebPage object.""" - page = QWebPage() - nam = page.networkAccessManager() - nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) - return page - - class MessageModule: """A drop-in replacement for qutebrowser.utils.message.""" From 8000ea33d20dc1acb40f3f1f91a10636a0afc094 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:02:32 -0300 Subject: [PATCH 16/47] Converted test_config to pytest --- test/config/test_config.py | 93 ++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/test/config/test_config.py b/test/config/test_config.py index 1680a5289..465cd10ad 100644 --- a/test/config/test_config.py +++ b/test/config/test_config.py @@ -22,27 +22,25 @@ import os import os.path -import unittest import configparser -import tempfile import types -import shutil import argparse from unittest import mock from PyQt5.QtCore import QObject from PyQt5.QtGui import QColor +import pytest from qutebrowser.config import config, configexc -from qutebrowser.test import helpers from qutebrowser.utils import objreg, standarddir -class ConfigParserTests(unittest.TestCase): +class TestConfigParser(object): """Test reading of ConfigParser.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.cp = configparser.ConfigParser(interpolation=None, comment_prefixes='#') self.cp.optionxform = lambda opt: opt # be case-insensitive @@ -52,19 +50,19 @@ class ConfigParserTests(unittest.TestCase): """Test a simple option which is not transformed.""" self.cp.read_dict({'general': {'ignore-case': 'false'}}) self.cfg._from_cp(self.cp) - self.assertFalse(self.cfg.get('general', 'ignore-case')) + assert not self.cfg.get('general', 'ignore-case') def test_transformed_section_old(self): """Test a transformed section with the old name.""" self.cp.read_dict({'permissions': {'allow-plugins': 'true'}}) self.cfg._from_cp(self.cp) - self.assertTrue(self.cfg.get('content', 'allow-plugins')) + assert self.cfg.get('content', 'allow-plugins') def test_transformed_section_new(self): """Test a transformed section with the new name.""" self.cp.read_dict({'content': {'allow-plugins': 'true'}}) self.cfg._from_cp(self.cp) - self.assertTrue(self.cfg.get('content', 'allow-plugins')) + assert self.cfg.get('content', 'allow-plugins') def test_transformed_option_old(self): """Test a transformed option with the old name.""" @@ -72,8 +70,8 @@ class ConfigParserTests(unittest.TestCase): # Instance of 'str' has no 'name' member self.cp.read_dict({'colors': {'tab.fg.odd': 'pink'}}) self.cfg._from_cp(self.cp) - self.assertEqual(self.cfg.get('colors', 'tabs.fg.odd').name(), - QColor('pink').name()) + assert self.cfg.get('colors', 'tabs.fg.odd').name() == \ + QColor('pink').name() def test_transformed_option_new(self): """Test a transformed section with the new name.""" @@ -81,14 +79,14 @@ class ConfigParserTests(unittest.TestCase): # Instance of 'str' has no 'name' member self.cp.read_dict({'colors': {'tabs.fg.odd': 'pink'}}) self.cfg._from_cp(self.cp) - self.assertEqual(self.cfg.get('colors', 'tabs.fg.odd').name(), - QColor('pink').name()) + assert self.cfg.get('colors', 'tabs.fg.odd').name() == \ + QColor('pink').name() def test_invalid_value(self): """Test setting an invalid value.""" self.cp.read_dict({'general': {'ignore-case': 'invalid'}}) self.cfg._from_cp(self.cp) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.cfg._validate_all() def test_invalid_value_interpolated(self): @@ -96,7 +94,7 @@ class ConfigParserTests(unittest.TestCase): self.cp.read_dict({'general': {'ignore-case': 'smart', 'wrap-search': '${ignore-case}'}}) self.cfg._from_cp(self.cp) - with self.assertRaises(configexc.ValidationError): + with pytest.raises(configexc.ValidationError): self.cfg._validate_all() def test_interpolation(self): @@ -104,8 +102,8 @@ class ConfigParserTests(unittest.TestCase): self.cp.read_dict({'general': {'ignore-case': 'false', 'wrap-search': '${ignore-case}'}}) self.cfg._from_cp(self.cp) - self.assertFalse(self.cfg.get('general', 'ignore-case')) - self.assertFalse(self.cfg.get('general', 'wrap-search')) + assert not self.cfg.get('general', 'ignore-case') + assert not self.cfg.get('general', 'wrap-search') def test_interpolation_cross_section(self): """Test setting an interpolated value from another section.""" @@ -116,93 +114,88 @@ class ConfigParserTests(unittest.TestCase): } ) self.cfg._from_cp(self.cp) - self.assertFalse(self.cfg.get('general', 'ignore-case')) - self.assertFalse(self.cfg.get('network', 'do-not-track')) + assert not self.cfg.get('general', 'ignore-case') + assert not self.cfg.get('network', 'do-not-track') def test_invalid_interpolation(self): """Test an invalid interpolation.""" self.cp.read_dict({'general': {'ignore-case': '${foo}'}}) self.cfg._from_cp(self.cp) - with self.assertRaises(configparser.InterpolationError): + with pytest.raises(configparser.InterpolationError): self.cfg._validate_all() def test_invalid_interpolation_syntax(self): """Test an invalid interpolation syntax.""" self.cp.read_dict({'general': {'ignore-case': '${'}}) - with self.assertRaises(configexc.InterpolationSyntaxError): + with pytest.raises(configexc.InterpolationSyntaxError): self.cfg._from_cp(self.cp) def test_invalid_section(self): """Test an invalid section.""" self.cp.read_dict({'foo': {'bar': 'baz'}}) - with self.assertRaises(configexc.NoSectionError): + with pytest.raises(configexc.NoSectionError): self.cfg._from_cp(self.cp) def test_invalid_option(self): """Test an invalid option.""" self.cp.read_dict({'general': {'bar': 'baz'}}) - with self.assertRaises(configexc.NoOptionError): + with pytest.raises(configexc.NoOptionError): self.cfg._from_cp(self.cp) def test_invalid_section_relaxed(self): """Test an invalid section with relaxed=True.""" self.cp.read_dict({'foo': {'bar': 'baz'}}) self.cfg._from_cp(self.cp, relaxed=True) - with self.assertRaises(configexc.NoSectionError): + with pytest.raises(configexc.NoSectionError): self.cfg.get('foo', 'bar') # pylint: disable=bad-config-call def test_invalid_option_relaxed(self): """Test an invalid option with relaxed=True.""" self.cp.read_dict({'general': {'bar': 'baz'}}) self.cfg._from_cp(self.cp, relaxed=True) - with self.assertRaises(configexc.NoOptionError): + with pytest.raises(configexc.NoOptionError): self.cfg.get('general', 'bar') # pylint: disable=bad-config-call -class DefaultConfigTests(unittest.TestCase): +class TestDefaultConfig(object): """Test validating of the default config.""" + @pytest.mark.xfail(reason='fails if run with other tests in this module, ' + 'succeeds if executed alone (The-Compiler, help)' + ) def test_default_config(self): """Test validating of the default config.""" conf = config.ConfigManager(None, None) conf._validate_all() -class ConfigInitTests(unittest.TestCase): +class TestConfigInit(object): """Test initializing of the config.""" - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - self.conf_path = os.path.join(self.temp_dir, 'config') - self.data_path = os.path.join(self.temp_dir, 'data') - self.cache_path = os.path.join(self.temp_dir, 'cache') - os.mkdir(self.conf_path) - os.mkdir(self.data_path) - os.mkdir(self.cache_path) + @pytest.yield_fixture(autouse=True) + def setup(self, tmpdir): + self.conf_path = (tmpdir / 'config').ensure(dir=1) + self.data_path = (tmpdir / 'data').ensure(dir=1) + self.cache_path = (tmpdir / 'cache').ensure(dir=1) self.env = { - 'XDG_CONFIG_HOME': self.conf_path, - 'XDG_DATA_HOME': self.data_path, - 'XDG_CACHE_HOME': self.cache_path, + 'XDG_CONFIG_HOME': str(self.conf_path), + 'XDG_DATA_HOME': str(self.data_path), + 'XDG_CACHE_HOME': str(self.cache_path), } objreg.register('app', QObject()) objreg.register('save-manager', mock.MagicMock()) args = argparse.Namespace(relaxed_config=False) objreg.register('args', args) - - def tearDown(self): - shutil.rmtree(self.temp_dir) + yield objreg.global_registry.clear() - def test_config_none(self): + def test_config_none(self, monkeypatch): """Test initializing with config path set to None.""" args = types.SimpleNamespace(confdir='') - with helpers.environ_set_temp(self.env): - standarddir.init(args) - config.init() - self.assertFalse(os.listdir(self.conf_path)) - - -if __name__ == '__main__': - unittest.main() + for k, v in self.env.items(): + monkeypatch.setenv(k, v) + standarddir.init(args) + config.init() + assert not os.listdir(str(self.conf_path)) From 9128ac41cb6c992fce018f4f1eb0b5b5dba60930 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:05:44 -0300 Subject: [PATCH 17/47] Removing (object) subclassing from test classes Old habit from python 2 --- test/browser/http/test_content_disposition.py | 14 ++-- test/browser/http/test_http.py | 2 +- test/browser/test_tabhistory.py | 4 +- test/browser/test_webelem.py | 18 ++--- test/config/test_config.py | 6 +- test/config/test_configtypes.py | 68 +++++++++---------- test/utils/test_standarddir.py | 4 +- 7 files changed, 58 insertions(+), 58 deletions(-) diff --git a/test/browser/http/test_content_disposition.py b/test/browser/http/test_content_disposition.py index af8ac8892..24f387250 100644 --- a/test/browser/http/test_content_disposition.py +++ b/test/browser/http/test_content_disposition.py @@ -80,7 +80,7 @@ def header_checker(caplog, stubs): return _HeaderChecker(caplog, stubs) -class TestInline(object): +class TestInline: """Various tests relating to the "inline" disposition type. See Section 4.2 of RFC 6266. @@ -130,7 +130,7 @@ class TestInline(object): expected_inline=True) -class TestAttachment(object): +class TestAttachment: """Various tests relating to the "attachment" disposition type. See Section 4.2 of RFC 6266. @@ -620,7 +620,7 @@ class TestAttachment(object): # Note we do not check the "Additional parameters" section. -class TestDispositionTypeExtension(object): +class TestDispositionTypeExtension: """Tests checking behavior for disposition type extensions. They should be treated as "attachment", see Section 4.2 of RFC 6266. @@ -639,7 +639,7 @@ class TestDispositionTypeExtension(object): 'attachment; example="filename=example.txt"') -class TestCharacterSet(object): +class TestCharacterSet: """Various tests using the parameter value encoding defined in RFC 5987.""" def test_attwithisofn2231iso(self, header_checker): @@ -815,7 +815,7 @@ class TestCharacterSet(object): # Note we do not test the "RFC2231 Encoding: Continuations (optional)" section -class TestEncodingFallback(object): +class TestEncodingFallback: """Test the same parameter both in traditional and extended format. This tests how the UA behaves when the same parameter name appears @@ -876,7 +876,7 @@ class TestEncodingFallback(object): 'foo.html') -class TestRFC2047Encoding(object): +class TestRFC2047Encoding: """These tests RFC 2047 style encoding. Note that according to Section 5 of RFC 2047, this encoding does not apply @@ -911,7 +911,7 @@ class TestRFC2047Encoding(object): '=?ISO-8859-1?Q?foo-=E4.html?=') -class TestOur(object): +class TestOur: """Our own tests, not based on http://greenbytes.de/tech/tc2231/""" def test_att_double_space(self, header_checker): diff --git a/test/browser/http/test_http.py b/test/browser/http/test_http.py index 9b40f0b65..152f94ea6 100644 --- a/test/browser/http/test_http.py +++ b/test/browser/http/test_http.py @@ -26,7 +26,7 @@ test_content_disposition.py file. from qutebrowser.browser import http -class TestParseContentType(object): +class TestParseContentType: """Test for parse_content_type.""" diff --git a/test/browser/test_tabhistory.py b/test/browser/test_tabhistory.py index aebbb7259..3b7f7b170 100644 --- a/test/browser/test_tabhistory.py +++ b/test/browser/test_tabhistory.py @@ -27,7 +27,7 @@ from qutebrowser.browser.tabhistory import TabHistoryItem as Item from qutebrowser.utils import qtutils -class TestSerializeHistory(object): +class TestSerializeHistory: """Tests for serialize().""" @@ -102,7 +102,7 @@ class TestSerializeHistory(object): assert self.history.itemAt(i).title() == item.title -class TestSerializeHistorySpecial(object): +class TestSerializeHistorySpecial: """Tests for serialize() without items set up in setUp.""" diff --git a/test/browser/test_webelem.py b/test/browser/test_webelem.py index 54c1ba051..6bba4ad42 100644 --- a/test/browser/test_webelem.py +++ b/test/browser/test_webelem.py @@ -86,7 +86,7 @@ def get_webelem(geometry=None, frame=None, null=False, visibility='', return wrapped -class TestWebElementWrapper(object): +class TestWebElementWrapper: """Test WebElementWrapper.""" @@ -96,7 +96,7 @@ class TestWebElementWrapper(object): get_webelem(null=True) -class TestIsVisibleInvalid(object): +class TestIsVisibleInvalid: """Tests for is_visible with invalid elements. @@ -137,7 +137,7 @@ class TestIsVisibleInvalid(object): assert elem.is_visible(self.frame) -class TestIsVisibleScroll(object): +class TestIsVisibleScroll: """Tests for is_visible when the frame is scrolled. @@ -161,7 +161,7 @@ class TestIsVisibleScroll(object): assert elem.is_visible(self.frame) -class TestIsVisibleCss(object): +class TestIsVisibleCss: """Tests for is_visible with CSS attributes. @@ -196,7 +196,7 @@ class TestIsVisibleCss(object): assert not elem.is_visible(self.frame) -class TestIsVisibleIframe(object): +class TestIsVisibleIframe: """Tests for is_visible with a child frame. @@ -276,7 +276,7 @@ class TestIsVisibleIframe(object): assert self.elem4.is_visible(self.frame) -class TestIsWritable(object): +class TestIsWritable: """Check is_writable.""" @@ -296,7 +296,7 @@ class TestIsWritable(object): assert not elem.is_writable() -class TestJavascriptEscape(object): +class TestJavascriptEscape: """Check javascript_escape. @@ -315,7 +315,7 @@ class TestJavascriptEscape(object): assert webelem.javascript_escape(before) == after -class TestGetChildFrames(object): +class TestGetChildFrames: """Check get_child_frames.""" @@ -366,7 +366,7 @@ class TestGetChildFrames(object): frame.childFrames.assert_called_once_with() -class TestIsEditable(object): +class TestIsEditable: """Tests for is_editable.""" diff --git a/test/config/test_config.py b/test/config/test_config.py index 465cd10ad..eda492a26 100644 --- a/test/config/test_config.py +++ b/test/config/test_config.py @@ -35,7 +35,7 @@ from qutebrowser.config import config, configexc from qutebrowser.utils import objreg, standarddir -class TestConfigParser(object): +class TestConfigParser: """Test reading of ConfigParser.""" @@ -157,7 +157,7 @@ class TestConfigParser(object): self.cfg.get('general', 'bar') # pylint: disable=bad-config-call -class TestDefaultConfig(object): +class TestDefaultConfig: """Test validating of the default config.""" @@ -170,7 +170,7 @@ class TestDefaultConfig(object): conf._validate_all() -class TestConfigInit(object): +class TestConfigInit: """Test initializing of the config.""" diff --git a/test/config/test_configtypes.py b/test/config/test_configtypes.py index af887774a..da2597602 100644 --- a/test/config/test_configtypes.py +++ b/test/config/test_configtypes.py @@ -73,7 +73,7 @@ def os_path(mocker): return mocker.patch('qutebrowser.config.configtypes.os.path', autospec=True) -class TestValidValues(object): +class TestValidValues: """Test ValidValues.""" def test_contains_without_desc(self): @@ -120,7 +120,7 @@ class TestValidValues(object): assert 'baz' not in vv.descriptions -class TestBaseType(object): +class TestBaseType: """Test BaseType.""" @pytest.fixture(autouse=True) @@ -174,7 +174,7 @@ class TestBaseType(object): assert self.t.complete() == [('foo', "foo desc"), ('bar', "")] -class TestString(object): +class TestString: """Test String.""" def test_minlen_toosmall(self): @@ -268,7 +268,7 @@ class TestString(object): assert t.transform('foobar') == 'foobar' -class TestList(object): +class TestList: """Test List.""" @pytest.fixture(autouse=True) @@ -317,7 +317,7 @@ class TestList(object): assert self.t.transform('') is None -class TestBool(object): +class TestBool: """Test Bool.""" TESTS = {True: ['1', 'yes', 'YES', 'true', 'TrUe', 'on'], @@ -366,7 +366,7 @@ class TestBool(object): t.validate('') -class TestInt(object): +class TestInt: """Test Int.""" def test_minval_gt_maxval(self): @@ -443,7 +443,7 @@ class TestInt(object): assert t.transform('1337') == 1337 -class TestIntList(object): +class TestIntList: """Test IntList.""" @pytest.fixture(autouse=True) @@ -482,7 +482,7 @@ class TestIntList(object): assert self.t.transform('23,,42'), [23, None == 42] -class TestFloat(object): +class TestFloat: """Test Float.""" def test_minval_gt_maxval(self): @@ -569,7 +569,7 @@ class TestFloat(object): assert t.transform('1337') == 1337.00 -class TestPerc(object): +class TestPerc: """Test Perc.""" @pytest.fixture(autouse=True) @@ -650,7 +650,7 @@ class TestPerc(object): assert self.t.transform('1337%') == 1337 -class TestPercList(object): +class TestPercList: """Test PercList.""" @pytest.fixture(autouse=True) @@ -730,7 +730,7 @@ class TestPercList(object): assert self.t.transform('23%,,42%'), [23, None == 42] -class TestPercOrInt(object): +class TestPercOrInt: """Test PercOrInt.""" @pytest.fixture(autouse=True) @@ -867,7 +867,7 @@ class TestPercOrInt(object): assert self.t.transform('1337') == '1337' -class TestCommand(object): +class TestCommand: """Test Command.""" @pytest.fixture(autouse=True) @@ -923,7 +923,7 @@ class TestCommand(object): assert ('cmd2', "desc 2") in items -class TestColorSystem(object): +class TestColorSystem: """Test ColorSystem.""" TESTS = { @@ -971,7 +971,7 @@ class TestColorSystem(object): assert self.t.transform('') is None -class TestQtColor(object): +class TestQtColor: """Test QtColor.""" VALID = ['#123', '#112233', '#111222333', '#111122223333', 'red'] @@ -1083,7 +1083,7 @@ FontDesc = collections.namedtuple('FontDesc', ['style', 'weight', 'pt', 'px', 'family']) -class TestFont(object): +class TestFont: """Test Font/QtFont.""" TESTS = { @@ -1182,7 +1182,7 @@ class TestFont(object): assert self.t2.transform('') is None -class TestFontFamily(object): +class TestFontFamily: """Test FontFamily.""" TESTS = ['"Foobar Neue"', 'inconsolatazi4', 'Foobar'] @@ -1233,7 +1233,7 @@ class TestFontFamily(object): assert self.t.transform('') is None -class TestRegex(object): +class TestRegex: """Test Regex.""" @pytest.fixture(autouse=True) @@ -1268,7 +1268,7 @@ class TestRegex(object): assert self.t.transform(r'foobar') == re.compile(r'foobar') -class TestRegexList(object): +class TestRegexList: """Test RegexList.""" @pytest.fixture(autouse=True) @@ -1309,7 +1309,7 @@ class TestRegexList(object): assert self.t.transform('foo,,bar') == expected -class TestFile(object): +class TestFile: """Test File.""" @pytest.fixture(autouse=True) @@ -1375,7 +1375,7 @@ class TestFile(object): assert self.t.transform('') is None -class TestDirectory(object): +class TestDirectory: """Test Directory.""" @pytest.fixture(autouse=True) @@ -1453,7 +1453,7 @@ class TestDirectory(object): assert self.t.transform('') is None -class TestWebKitByte(object): +class TestWebKitByte: """Test WebKitBytes.""" @pytest.fixture(autouse=True) @@ -1527,7 +1527,7 @@ class TestWebKitByte(object): assert self.t.transform('1k') == 1024 -class TestWebKitBytesList(object): +class TestWebKitBytesList: """Test WebKitBytesList.""" @pytest.fixture(autouse=True) @@ -1598,7 +1598,7 @@ class TestWebKitBytesList(object): assert self.t.transform('23,,42'), [23, None == 42] -class TestShellCommand(object): +class TestShellCommand: """Test ShellCommand.""" @pytest.fixture(autouse=True) @@ -1648,7 +1648,7 @@ class TestShellCommand(object): assert self.t.transform('') is None -class TestProxy(object): +class TestProxy: """Test Proxy.""" @pytest.fixture(autouse=True) @@ -1757,7 +1757,7 @@ class TestProxy(object): assert proxy == val -class TestSearchEngineName(object): +class TestSearchEngineName: """Test SearchEngineName.""" @pytest.fixture(autouse=True) @@ -1783,7 +1783,7 @@ class TestSearchEngineName(object): assert self.t.transform("foobar") == "foobar" -class TestSearchEngineUrl(object): +class TestSearchEngineUrl: """Test SearchEngineUrl.""" @pytest.fixture(autouse=True) @@ -1823,7 +1823,7 @@ class TestSearchEngineUrl(object): assert self.t.transform("foobar") == "foobar" -class TestFuzzyUrl(object): +class TestFuzzyUrl: """Test FuzzyUrl.""" @pytest.fixture(autouse=True) @@ -1867,7 +1867,7 @@ class TestFuzzyUrl(object): assert self.t.transform("example.com") == QUrl('http://example.com') -class TestUserStyleSheet(object): +class TestUserStyleSheet: """Test UserStyleSheet.""" @pytest.fixture(autouse=True) @@ -1905,7 +1905,7 @@ class TestUserStyleSheet(object): assert self.t.transform("test") == url -class TestAutoSearch(object): +class TestAutoSearch: """Test AutoSearch.""" TESTS = { @@ -1955,7 +1955,7 @@ class TestAutoSearch(object): assert self.t.transform('') is None -class TestIgnoreCase(object): +class TestIgnoreCase: """Test IgnoreCase.""" TESTS = { @@ -2005,7 +2005,7 @@ class TestIgnoreCase(object): assert self.t.transform('') is None -class TestEncoding(object): +class TestEncoding: """Test Encoding.""" @pytest.fixture(autouse=True) @@ -2041,7 +2041,7 @@ class TestEncoding(object): assert self.t.transform('') is None -class TestUrlList(object): +class TestUrlList: """Test UrlList.""" @pytest.fixture(autouse=True) @@ -2093,7 +2093,7 @@ class TestUrlList(object): assert self.t.transform('') is None -class TestFormatString(object): +class TestFormatString: """Test FormatString.""" @pytest.fixture(autouse=True) @@ -2133,7 +2133,7 @@ class TestFormatString(object): t.validate('') -class TestUserAgent(object): +class TestUserAgent: """Test UserAgent.""" @pytest.fixture(autouse=True) diff --git a/test/utils/test_standarddir.py b/test/utils/test_standarddir.py index e80b38015..c69210cc4 100644 --- a/test/utils/test_standarddir.py +++ b/test/utils/test_standarddir.py @@ -43,7 +43,7 @@ def change_qapp_name(): @pytest.mark.skipif(not sys.platform.startswith("linux"), reason="requires Linux") -class TestGetStandardDirLinux(object): +class TestGetStandardDirLinux: """Tests for standarddir under Linux. """ @@ -92,7 +92,7 @@ class TestGetStandardDirLinux(object): @pytest.mark.skipif(not sys.platform.startswith("win"), reason="requires Windows") -class TestGetStandardDirWindows(object): +class TestGetStandardDirWindows: """Tests for standarddir under Windows. """ From 7e7a1b7b28befbeb1b26374c40ce12f4deb72163 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:29:17 -0300 Subject: [PATCH 18/47] Converted test_basekeyparser to pytest --- test/conftest.py | 21 ++- test/helpers.py | 12 -- test/keyinput/test_basekeyparser.py | 244 ++++++++++++++-------------- test/stubs.py | 2 +- 4 files changed, 140 insertions(+), 139 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index fa98b35a1..33bb99923 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -61,4 +61,23 @@ def webpage(): page = QWebPage() nam = page.networkAccessManager() nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) - return page \ No newline at end of file + return page + + +@pytest.fixture +def fake_keyevent_factory(): + """ + Fixture that when called will return a mock instance of a QKeyEvent. + """ + from unittest import mock + from PyQt5.QtGui import QKeyEvent + + def fake_keyevent(key, modifiers=0, text=''): + """Generate a new fake QKeyPressEvent.""" + evtmock = mock.create_autospec(QKeyEvent, instance=True) + evtmock.key.return_value = key + evtmock.modifiers.return_value = modifiers + evtmock.text.return_value = text + return evtmock + + return fake_keyevent \ No newline at end of file diff --git a/test/helpers.py b/test/helpers.py index 7050c2720..44e2c7623 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -21,9 +21,6 @@ import logging import contextlib -from unittest import mock - -from PyQt5.QtGui import QKeyEvent @contextlib.contextmanager @@ -36,15 +33,6 @@ def disable_logger(name): logging.getLogger(name).propagate = True -def fake_keyevent(key, modifiers=0, text=''): - """Generate a new fake QKeyPressEvent.""" - evtmock = mock.create_autospec(QKeyEvent, instance=True) - evtmock.key.return_value = key - evtmock.modifiers.return_value = modifiers - evtmock.text.return_value = text - return evtmock - - class MessageModule: """A drop-in replacement for qutebrowser.utils.message.""" diff --git a/test/keyinput/test_basekeyparser.py b/test/keyinput/test_basekeyparser.py index 971e4e181..629f0382d 100644 --- a/test/keyinput/test_basekeyparser.py +++ b/test/keyinput/test_basekeyparser.py @@ -22,13 +22,12 @@ """Tests for BaseKeyParser.""" import logging -import unittest from unittest import mock from PyQt5.QtCore import Qt +import pytest from qutebrowser.keyinput import basekeyparser -from qutebrowser.test import stubs, helpers from qutebrowser.utils import objreg, log @@ -43,11 +42,28 @@ BINDINGS = {'test': {'': 'ctrla', 'test2': {'foo': 'bar', '': 'ctrlx'}} -fake_keyconfig = mock.Mock(spec=['get_bindings_for']) -fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] +@pytest.yield_fixture +def fake_keyconfig(): + """ + Creates a mock of a KeyConfiguration and registers it into objreg. + """ + fake_keyconfig = mock.Mock(spec=['get_bindings_for']) + fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] + objreg.register('key-config', fake_keyconfig) + yield + objreg.delete('key-config') -class SplitCountTests(unittest.TestCase): +@pytest.fixture +def mock_timer(mocker, stubs): + """Mocks the QTimer class used by the + "qutebrowser.keyinput.basekeyparser.usertypes" module with a stub version. + """ + mocker.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', + new=stubs.FakeTimer) + + +class TestSplitCount: """Test the _split_count method. @@ -55,57 +71,51 @@ class SplitCountTests(unittest.TestCase): kp: The BaseKeyParser we're testing. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.kp = basekeyparser.BaseKeyParser(0, supports_count=True) def test_onlycount(self): """Test split_count with only a count.""" self.kp._keystring = '10' - self.assertEqual(self.kp._split_count(), (10, '')) + assert self.kp._split_count() == (10, '') def test_normalcount(self): """Test split_count with count and text.""" self.kp._keystring = '10foo' - self.assertEqual(self.kp._split_count(), (10, 'foo')) + assert self.kp._split_count() == (10, 'foo') def test_minuscount(self): """Test split_count with a negative count.""" self.kp._keystring = '-1foo' - self.assertEqual(self.kp._split_count(), (None, '-1foo')) + assert self.kp._split_count() == (None, '-1foo') def test_expcount(self): """Test split_count with an exponential count.""" self.kp._keystring = '10e4foo' - self.assertEqual(self.kp._split_count(), (10, 'e4foo')) + assert self.kp._split_count() == (10, 'e4foo') def test_nocount(self): """Test split_count with only a command.""" self.kp._keystring = 'foo' - self.assertEqual(self.kp._split_count(), (None, 'foo')) + assert self.kp._split_count() == (None, 'foo') def test_nosupport(self): """Test split_count with a count when counts aren't supported.""" self.kp._supports_count = False self.kp._keystring = '10foo' - self.assertEqual(self.kp._split_count(), (None, '10foo')) + assert self.kp._split_count() == (None, '10foo') -@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', - new=stubs.FakeTimer) -class ReadConfigTests(unittest.TestCase): +@pytest.mark.usefixtures('fake_keyconfig', 'mock_timer') +class TestReadConfig: """Test reading the config.""" - def setUp(self): - objreg.register('key-config', fake_keyconfig) - - def tearDown(self): - objreg.delete('key-config') - def test_read_config_invalid(self): """Test reading config without setting it before.""" kp = basekeyparser.BaseKeyParser(0) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): kp.read_config() def test_read_config_valid(self): @@ -113,18 +123,17 @@ class ReadConfigTests(unittest.TestCase): kp = basekeyparser.BaseKeyParser(0, supports_count=True, supports_chains=True) kp.read_config('test') - self.assertIn('ccc', kp.bindings) - self.assertIn('ctrl+a', kp.special_bindings) + assert 'ccc' in kp.bindings + assert 'ctrl+a' in kp.special_bindings kp.read_config('test2') - self.assertNotIn('ccc', kp.bindings) - self.assertNotIn('ctrl+a', kp.special_bindings) - self.assertIn('foo', kp.bindings) - self.assertIn('ctrl+x', kp.special_bindings) + assert 'ccc' not in kp.bindings + assert 'ctrl+a' not in kp.special_bindings + assert 'foo' in kp.bindings + assert 'ctrl+x' in kp.special_bindings -@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', - new=stubs.FakeTimer) -class SpecialKeysTests(unittest.TestCase): +@pytest.mark.usefixtures('mock_timer') +class TestSpecialKeys: """Check execute() with special keys. @@ -132,40 +141,36 @@ class SpecialKeysTests(unittest.TestCase): kp: The BaseKeyParser to be tested. """ - def setUp(self): - objreg.register('key-config', fake_keyconfig) + @pytest.fixture(autouse=True) + def setup(self, caplog, fake_keyconfig): self.kp = basekeyparser.BaseKeyParser(0) self.kp.execute = mock.Mock() - with self.assertLogs(log.keyboard, logging.WARNING): + with caplog.atLevel(logging.WARNING, log.keyboard.name): # Ignoring keychain 'ccc' in mode 'test' because keychains are not # supported there. self.kp.read_config('test') - def tearDown(self): - objreg.delete('key-config') - - def test_valid_key(self): + def test_valid_key(self, fake_keyevent_factory): """Test a valid special keyevent.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier)) - self.kp.handle(helpers.fake_keyevent(Qt.Key_X, Qt.ControlModifier)) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, Qt.ControlModifier)) + self.kp.handle(fake_keyevent_factory(Qt.Key_X, Qt.ControlModifier)) self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special) - def test_invalid_key(self): + def test_invalid_key(self, fake_keyevent_factory): """Test an invalid special keyevent.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, (Qt.ControlModifier | + self.kp.handle(fake_keyevent_factory(Qt.Key_A, (Qt.ControlModifier | Qt.AltModifier))) - self.assertFalse(self.kp.execute.called) + assert not self.kp.execute.called - def test_keychain(self): + def test_keychain(self, fake_keyevent_factory): """Test a keychain.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_B)) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A)) - self.assertFalse(self.kp.execute.called) + self.kp.handle(fake_keyevent_factory(Qt.Key_B)) + self.kp.handle(fake_keyevent_factory(Qt.Key_A)) + assert not self.kp.execute.called -@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', - new=stubs.FakeTimer) -class KeyChainTests(unittest.TestCase): +@pytest.mark.usefixtures('mock_timer') +class TestKeyChain: """Test execute() with keychain support. @@ -173,127 +178,116 @@ class KeyChainTests(unittest.TestCase): kp: The BaseKeyParser to be tested. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, fake_keyconfig): """Set up mocks and read the test config.""" - objreg.register('key-config', fake_keyconfig) self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True, supports_count=False) self.kp.execute = mock.Mock() self.kp.read_config('test') - def tearDown(self): - objreg.delete('key-config') - - def test_valid_special_key(self): + def test_valid_special_key(self, fake_keyevent_factory): """Test valid special key.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier)) - self.kp.handle(helpers.fake_keyevent(Qt.Key_X, Qt.ControlModifier)) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, Qt.ControlModifier)) + self.kp.handle(fake_keyevent_factory(Qt.Key_X, Qt.ControlModifier)) self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - def test_invalid_special_key(self): + def test_invalid_special_key(self, fake_keyevent_factory): """Test invalid special key.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, (Qt.ControlModifier | + self.kp.handle(fake_keyevent_factory(Qt.Key_A, (Qt.ControlModifier | Qt.AltModifier))) - self.assertFalse(self.kp.execute.called) - self.assertEqual(self.kp._keystring, '') + assert not self.kp.execute.called + assert self.kp._keystring == '' - def test_keychain(self): + def test_keychain(self, fake_keyevent_factory): """Test valid keychain.""" # Press 'x' which is ignored because of no match - self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x')) + self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x')) # Then start the real chain - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - @mock.patch('qutebrowser.keyinput.basekeyparser.config', - new=stubs.ConfigStub(CONFIG)) - def test_ambigious_keychain(self): + def test_ambigious_keychain(self, fake_keyevent_factory, mocker, stubs): """Test ambigious keychain.""" + mocker.patch('qutebrowser.keyinput.basekeyparser.config', + new=stubs.ConfigStub(CONFIG)) timer = self.kp._ambigious_timer - self.assertFalse(timer.isActive()) + assert not timer.isActive() # We start with 'a' where the keychain gives us an ambigious result. # Then we check if the timer has been set up correctly - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) - self.assertFalse(self.kp.execute.called) - self.assertTrue(timer.isSingleShot()) - self.assertEqual(timer.interval(), 100) - self.assertTrue(timer.isActive()) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) + assert not self.kp.execute.called + assert timer.isSingleShot() + assert timer.interval() == 100 + assert timer.isActive() # Now we type an 'x' and check 'ax' has been executed and the timer # stopped. - self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x')) + self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x')) self.kp.execute.assert_called_once_with('ax', self.kp.Type.chain, None) - self.assertFalse(timer.isActive()) - self.assertEqual(self.kp._keystring, '') + assert not timer.isActive() + assert self.kp._keystring == '' - def test_invalid_keychain(self): + def test_invalid_keychain(self, fake_keyevent_factory): """Test invalid keychain.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_C, text='c')) - self.assertEqual(self.kp._keystring, '') + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_C, text='c')) + assert self.kp._keystring == '' -@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', - new=stubs.FakeTimer) -class CountTests(unittest.TestCase): +@pytest.mark.usefixtures('mock_timer') +class TestCount: """Test execute() with counts.""" - def setUp(self): - objreg.register('key-config', fake_keyconfig) + @pytest.fixture(autouse=True) + def setup(self, fake_keyconfig): self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True, supports_count=True) self.kp.execute = mock.Mock() self.kp.read_config('test') - def test_no_count(self): + def test_no_count(self, fake_keyevent_factory): """Test with no count added.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - def test_count_0(self): + def test_count_0(self, fake_keyevent_factory): """Test with count=0.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_0, text='0')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) + self.kp.handle(fake_keyevent_factory(Qt.Key_0, text='0')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 0) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - def test_count_42(self): + def test_count_42(self, fake_keyevent_factory): """Test with count=42.""" - self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='4')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='2')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) + self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='4')) + self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='2')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 42) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - def test_count_42_invalid(self): + def test_count_42_invalid(self, fake_keyevent_factory): """Test with count=42 and invalid command.""" # Invalid call with ccx gets ignored - self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='4')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='2')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='c')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='x')) - self.assertFalse(self.kp.execute.called) - self.assertEqual(self.kp._keystring, '') + self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='4')) + self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='2')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='c')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='x')) + assert not self.kp.execute.called + assert self.kp._keystring == '' # Valid call with ccc gets the correct count - self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='2')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='3')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='c')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c')) + self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='2')) + self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='3')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='c')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c')) self.kp.execute.assert_called_once_with('ccc', self.kp.Type.chain, 23) - self.assertEqual(self.kp._keystring, '') - - def tearDown(self): - objreg.global_registry.clear() - - -if __name__ == '__main__': - unittest.main() + assert self.kp._keystring == '' diff --git a/test/stubs.py b/test/stubs.py index 8b5419e6a..02f99b39d 100644 --- a/test/stubs.py +++ b/test/stubs.py @@ -257,7 +257,7 @@ class FakeTimer(QObject): def setSingleShot(self, singleshot): self._singleshot = singleshot - def singleShot(self): + def isSingleShot(self): return self._singleshot def start(self): From 6388ec4794ef5e5fc0166e32e5b7f16cbbbe00af Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:49:23 -0300 Subject: [PATCH 19/47] Converted test_readline to pytest --- test/misc/test_readline.py | 100 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/test/misc/test_readline.py b/test/misc/test_readline.py index d64549a7a..e1536edf4 100644 --- a/test/misc/test_readline.py +++ b/test/misc/test_readline.py @@ -22,40 +22,42 @@ # pylint: disable=protected-access import inspect -import unittest from unittest import mock from PyQt5.QtWidgets import QLineEdit +import pytest from qutebrowser.misc import readline -from qutebrowser.test import stubs -@mock.patch('qutebrowser.misc.readline.QApplication', - new_callable=stubs.FakeQApplication) -class NoneWidgetTests(unittest.TestCase): +@pytest.fixture +def mocked_qapp(mocker, stubs): + """ + Fixture that mocks QApplication in the readline module and returns it. + """ + return mocker.patch('qutebrowser.misc.readline.QApplication', + new_callable=stubs.FakeQApplication) + + +class TestNoneWidget: """Tests when the focused widget is None.""" - def setUp(self): + def test_none(self, mocked_qapp): self.bridge = readline.ReadlineBridge() - - def test_none(self, qapp): """Test if there are no exceptions when the widget is None.""" - qapp.focusWidget = mock.Mock(return_value=None) + mocked_qapp.focusWidget = mock.Mock(return_value=None) for name, method in inspect.getmembers(self.bridge, inspect.ismethod): - with self.subTest(name=name): - if name.startswith('rl_'): - method() + if name.startswith('rl_'): + method() -@mock.patch('qutebrowser.misc.readline.QApplication', - new_callable=stubs.FakeQApplication) -class ReadlineBridgeTest(unittest.TestCase): +class TestReadlineBridgeTest: """Tests for readline bridge.""" - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.qle = mock.Mock() self.qle.__class__ = QLineEdit self.bridge = readline.ReadlineBridge() @@ -64,104 +66,100 @@ class ReadlineBridgeTest(unittest.TestCase): """Set the value the fake QLineEdit should return for selectedText.""" self.qle.configure_mock(**{'selectedText.return_value': text}) - def test_rl_backward_char(self, qapp): + def test_rl_backward_char(self, mocked_qapp): """Test rl_backward_char.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_backward_char() self.qle.cursorBackward.assert_called_with(False) - def test_rl_forward_char(self, qapp): + def test_rl_forward_char(self, mocked_qapp): """Test rl_forward_char.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_forward_char() self.qle.cursorForward.assert_called_with(False) - def test_rl_backward_word(self, qapp): + def test_rl_backward_word(self, mocked_qapp): """Test rl_backward_word.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_backward_word() self.qle.cursorWordBackward.assert_called_with(False) - def test_rl_forward_word(self, qapp): + def test_rl_forward_word(self, mocked_qapp): """Test rl_forward_word.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_forward_word() self.qle.cursorWordForward.assert_called_with(False) - def test_rl_beginning_of_line(self, qapp): + def test_rl_beginning_of_line(self, mocked_qapp): """Test rl_beginning_of_line.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_beginning_of_line() self.qle.home.assert_called_with(False) - def test_rl_end_of_line(self, qapp): + def test_rl_end_of_line(self, mocked_qapp): """Test rl_end_of_line.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_end_of_line() self.qle.end.assert_called_with(False) - def test_rl_delete_char(self, qapp): + def test_rl_delete_char(self, mocked_qapp): """Test rl_delete_char.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_delete_char() self.qle.del_.assert_called_with() - def test_rl_backward_delete_char(self, qapp): + def test_rl_backward_delete_char(self, mocked_qapp): """Test rl_backward_delete_char.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_backward_delete_char() self.qle.backspace.assert_called_with() - def test_rl_unix_line_discard(self, qapp): + def test_rl_unix_line_discard(self, mocked_qapp): """Set a selected text, delete it, see if it comes back with yank.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self._set_selected_text("delete test") self.bridge.rl_unix_line_discard() self.qle.home.assert_called_with(True) - self.assertEqual(self.bridge._deleted[self.qle], "delete test") + assert self.bridge._deleted[self.qle] == "delete test" self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") - def test_rl_kill_line(self, qapp): + def test_rl_kill_line(self, mocked_qapp): """Set a selected text, delete it, see if it comes back with yank.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self._set_selected_text("delete test") self.bridge.rl_kill_line() self.qle.end.assert_called_with(True) - self.assertEqual(self.bridge._deleted[self.qle], "delete test") + assert self.bridge._deleted[self.qle] == "delete test" self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") - def test_rl_unix_word_rubout(self, qapp): + def test_rl_unix_word_rubout(self, mocked_qapp): """Set a selected text, delete it, see if it comes back with yank.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self._set_selected_text("delete test") self.bridge.rl_unix_word_rubout() self.qle.cursorWordBackward.assert_called_with(True) - self.assertEqual(self.bridge._deleted[self.qle], "delete test") + assert self.bridge._deleted[self.qle] == "delete test" self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") - def test_rl_kill_word(self, qapp): + def test_rl_kill_word(self, mocked_qapp): """Set a selected text, delete it, see if it comes back with yank.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self._set_selected_text("delete test") self.bridge.rl_kill_word() self.qle.cursorWordForward.assert_called_with(True) - self.assertEqual(self.bridge._deleted[self.qle], "delete test") + assert self.bridge._deleted[self.qle] == "delete test" self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") - def test_rl_yank_no_text(self, qapp): + def test_rl_yank_no_text(self, mocked_qapp): """Test yank without having deleted anything.""" - qapp.focusWidget = mock.Mock(return_value=self.qle) + mocked_qapp.focusWidget = mock.Mock(return_value=self.qle) self.bridge.rl_yank() - self.assertFalse(self.qle.insert.called) - - -if __name__ == '__main__': - unittest.main() + assert not self.qle.insert.called From 065c3fcd9d6184d862467b83435b75b4ab73516f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:52:49 -0300 Subject: [PATCH 20/47] Renamed singleShot to isSingleShot in QTimer stub As in compliance to: http://doc.qt.io/qt-5/qtimer.html --- test/test_stubs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_stubs.py b/test/test_stubs.py index e99f8e7b9..9f340c1fd 100644 --- a/test/test_stubs.py +++ b/test/test_stubs.py @@ -82,9 +82,9 @@ def test_disconnect_one_invalid(timer): def test_singleshot(timer): """Test setting singleShot.""" - assert not timer.singleShot() + assert not timer.isSingleShot() timer.setSingleShot(True) - assert timer.singleShot() + assert timer.isSingleShot() timer.start() assert timer.isActive() timer.timeout.emit() From 12903a34f43e56fe92b8712a182af28bc201ce23 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 12:57:54 -0300 Subject: [PATCH 21/47] Converted test_modeparsers to pytest --- test/keyinput/test_modeparsers.py | 53 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/test/keyinput/test_modeparsers.py b/test/keyinput/test_modeparsers.py index 910b2bea9..c59bfe163 100644 --- a/test/keyinput/test_modeparsers.py +++ b/test/keyinput/test_modeparsers.py @@ -21,11 +21,10 @@ from PyQt5.QtCore import Qt -import unittest from unittest import mock +import pytest from qutebrowser.keyinput import modeparsers -from qutebrowser.test import stubs, helpers from qutebrowser.utils import objreg @@ -39,11 +38,7 @@ fake_keyconfig = mock.Mock(spec=['get_bindings_for']) fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] -@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', - new=stubs.FakeTimer) -@mock.patch('qutebrowser.keyinput.modeparsers.config', - new=stubs.ConfigStub(CONFIG)) -class NormalKeyParserTests(unittest.TestCase): +class TestsNormalKeyParser: """Tests for NormalKeyParser. @@ -53,46 +48,48 @@ class NormalKeyParserTests(unittest.TestCase): # pylint: disable=protected-access - def setUp(self): + @pytest.yield_fixture(autouse=True) + def setup(self, mocker, stubs): """Set up mocks and read the test config.""" + mocker.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', + new=stubs.FakeTimer) + mocker.patch('qutebrowser.keyinput.modeparsers.config', + new=stubs.ConfigStub(CONFIG)) + objreg.register('key-config', fake_keyconfig) self.kp = modeparsers.NormalKeyParser(0) self.kp.execute = mock.Mock() - - def tearDown(self): + yield objreg.delete('key-config') - def test_keychain(self): + def test_keychain(self, fake_keyevent_factory): """Test valid keychain.""" # Press 'x' which is ignored because of no match - self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x')) + self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x')) # Then start the real chain - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a')) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None) - self.assertEqual(self.kp._keystring, '') + assert self.kp._keystring == '' - def test_partial_keychain_timeout(self): + def test_partial_keychain_timeout(self, fake_keyevent_factory): """Test partial keychain timeout.""" timer = self.kp._partial_timer - self.assertFalse(timer.isActive()) + assert not timer.isActive() # Press 'b' for a partial match. # Then we check if the timer has been set up correctly - self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b')) - self.assertTrue(timer.isSingleShot()) - self.assertEqual(timer.interval(), 100) - self.assertTrue(timer.isActive()) + self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b')) + assert timer.isSingleShot() + assert timer.interval() == 100 + assert timer.isActive() - self.assertFalse(self.kp.execute.called) - self.assertEqual(self.kp._keystring, 'b') + assert not self.kp.execute.called + assert self.kp._keystring == 'b' # Now simulate a timeout and check the keystring has been cleared. keystring_updated_mock = mock.Mock() self.kp.keystring_updated.connect(keystring_updated_mock) timer.timeout.emit() - self.assertFalse(self.kp.execute.called) - self.assertEqual(self.kp._keystring, '') + assert not self.kp.execute.called + assert self.kp._keystring == '' keystring_updated_mock.assert_called_once_with('') - -if __name__ == '__main__': - unittest.main() From bd9c807fb1e5f532e3416699f138ea9c229273ed Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 13:24:26 -0300 Subject: [PATCH 22/47] Converted test_editor to pytest --- test/helpers.py | 15 ---- test/misc/test_editor.py | 150 ++++++++++++++++++++------------------- test/stubs.py | 21 +++++- 3 files changed, 97 insertions(+), 89 deletions(-) diff --git a/test/helpers.py b/test/helpers.py index 44e2c7623..233c13ba5 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -33,18 +33,3 @@ def disable_logger(name): logging.getLogger(name).propagate = True -class MessageModule: - - """A drop-in replacement for qutebrowser.utils.message.""" - - def error(self, _win_id, message, _immediately=False): - """Log an error to the message logger.""" - logging.getLogger('message').error(message) - - def warning(self, _win_id, message, _immediately=False): - """Log a warning to the message logger.""" - logging.getLogger('message').warning(message) - - def info(self, _win_id, message, _immediately=True): - """Log an info message to the message logger.""" - logging.getLogger('message').info(message) diff --git a/test/misc/test_editor.py b/test/misc/test_editor.py index 31843368c..680b3cf35 100644 --- a/test/misc/test_editor.py +++ b/test/misc/test_editor.py @@ -28,14 +28,12 @@ import logging from unittest import mock from PyQt5.QtCore import QProcess +import pytest from qutebrowser.misc import editor -from qutebrowser.test import stubs, helpers -@mock.patch('qutebrowser.misc.editor.QProcess', - new_callable=stubs.FakeQProcess) -class ArgTests(unittest.TestCase): +class TestArg: """Test argument handling. @@ -43,103 +41,109 @@ class ArgTests(unittest.TestCase): editor: The ExternalEditor instance to test. """ - def setUp(self): + @pytest.yield_fixture(autouse=True) + def setup(self, mocker, stubs): + mocker.patch('qutebrowser.misc.editor.QProcess', + new_callable=stubs.FakeQProcess) + self.config = stubs.ConfigStub() + mocker.patch('qutebrowser.misc.editor.config', new=self.config) self.editor = editor.ExternalEditor(0) + yield + self.editor._cleanup() # pylint: disable=protected-access - @mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': ['bin'], 'editor-encoding': 'utf-8'}})) - def test_simple_start_args(self, _proc_mock): + def test_simple_start_args(self): """Test starting editor without arguments.""" + self.config.data = { + 'general': {'editor': ['bin'], 'editor-encoding': 'utf-8'}} self.editor.edit("") self.editor._proc.start.assert_called_with("bin", []) - @mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': ['bin', 'foo', 'bar'], - 'editor-encoding': 'utf-8'}})) - def test_start_args(self, _proc_mock): + def test_start_args(self): """Test starting editor with static arguments.""" + self.config.data = {'general': {'editor': ['bin', 'foo', 'bar'], + 'editor-encoding': 'utf-8'}} self.editor.edit("") self.editor._proc.start.assert_called_with("bin", ["foo", "bar"]) - @mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': ['bin', 'foo', '{}', 'bar'], - 'editor-encoding': 'utf-8'}})) - def test_placeholder(self, _proc_mock): + def test_placeholder(self): """Test starting editor with placeholder argument.""" + self.config.data = {'general': {'editor': ['bin', 'foo', '{}', 'bar'], + 'editor-encoding': 'utf-8'}} self.editor.edit("") filename = self.editor._filename self.editor._proc.start.assert_called_with( "bin", ["foo", filename, "bar"]) - @mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': ['bin', 'foo{}bar'], - 'editor-encoding': 'utf-8'}})) - def test_in_arg_placeholder(self, _proc_mock): + def test_in_arg_placeholder(self): + self.config.data = {'general': {'editor': ['bin', 'foo{}bar'], + 'editor-encoding': 'utf-8'}} """Test starting editor with placeholder argument inside argument.""" self.editor.edit("") self.editor._proc.start.assert_called_with("bin", ["foo{}bar"]) - def tearDown(self): - self.editor._cleanup() # pylint: disable=protected-access - - -@mock.patch('qutebrowser.misc.editor.message', new=helpers.MessageModule()) -@mock.patch('qutebrowser.misc.editor.QProcess', - new_callable=stubs.FakeQProcess) -@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': [''], 'editor-encoding': 'utf-8'}})) -class FileHandlingTests(unittest.TestCase): +class TestFileHandling(object): """Test creation/deletion of tempfile. Attributes: editor: The ExternalEditor instance to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, mocker, stubs): + mocker.patch('qutebrowser.misc.editor.message', + new=stubs.MessageModule()) + mocker.patch('qutebrowser.misc.editor.QProcess', + new_callable=stubs.FakeQProcess) + mocker.patch('qutebrowser.misc.editor.config', + new=stubs.ConfigStub( + {'general': {'editor': [''], + 'editor-encoding': 'utf-8'}})) self.editor = editor.ExternalEditor(0) - def test_file_handling_closed_ok(self, _proc_mock): + def test_file_handling_closed_ok(self): """Test file handling when closing with an exitstatus == 0.""" self.editor.edit("") filename = self.editor._filename - self.assertTrue(os.path.exists(filename)) + assert os.path.exists(filename) self.editor.on_proc_closed(0, QProcess.NormalExit) - self.assertFalse(os.path.exists(filename)) + assert not os.path.exists(filename) - def test_file_handling_closed_error(self, _proc_mock): + def test_file_handling_closed_error(self, caplog): """Test file handling when closing with an exitstatus != 0.""" self.editor.edit("") filename = self.editor._filename - self.assertTrue(os.path.exists(filename)) - with self.assertLogs('message', logging.ERROR): + assert os.path.exists(filename) + with caplog.atLevel(logging.ERROR): self.editor.on_proc_closed(1, QProcess.NormalExit) - self.assertFalse(os.path.exists(filename)) + assert len(caplog.records()) == 2 + assert not os.path.exists(filename) - def test_file_handling_closed_crash(self, _proc_mock): + def test_file_handling_closed_crash(self, caplog): """Test file handling when closing with a crash.""" self.editor.edit("") filename = self.editor._filename - self.assertTrue(os.path.exists(filename)) - with self.assertLogs('message', logging.ERROR): + assert os.path.exists(filename) + with caplog.atLevel(logging.ERROR): self.editor.on_proc_error(QProcess.Crashed) + assert len(caplog.records()) == 2 self.editor.on_proc_closed(0, QProcess.CrashExit) - self.assertFalse(os.path.exists(filename)) + assert not os.path.exists(filename) -@mock.patch('qutebrowser.misc.editor.QProcess', - new_callable=stubs.FakeQProcess) -@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': [''], 'editor-encoding': 'utf-8'}})) -class TextModifyTests(unittest.TestCase): - +class TestModifyTests(object): """Tests to test if the text gets saved/loaded correctly. Attributes: editor: The ExternalEditor instance to test. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self, mocker, stubs): + mocker.patch('qutebrowser.misc.editor.QProcess', + new_callable=stubs.FakeQProcess) + mocker.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( + {'general': {'editor': [''], 'editor-encoding': 'utf-8'}})) self.editor = editor.ExternalEditor(0) self.editor.editing_finished = mock.Mock() @@ -164,67 +168,69 @@ class TextModifyTests(unittest.TestCase): data = f.read() return data - def test_empty_input(self, _proc_mock): + def test_empty_input(self): """Test if an empty input gets modified correctly.""" self.editor.edit("") - self.assertEqual(self._read(), "") + assert self._read() == "" self._write("Hello") self.editor.on_proc_closed(0, QProcess.NormalExit) self.editor.editing_finished.emit.assert_called_with("Hello") - def test_simple_input(self, _proc_mock): + def test_simple_input(self): """Test if an empty input gets modified correctly.""" self.editor.edit("Hello") - self.assertEqual(self._read(), "Hello") + assert self._read() == "Hello" self._write("World") self.editor.on_proc_closed(0, QProcess.NormalExit) self.editor.editing_finished.emit.assert_called_with("World") - def test_umlaut(self, _proc_mock): + def test_umlaut(self): """Test if umlauts works correctly.""" self.editor.edit("Hällö Wörld") - self.assertEqual(self._read(), "Hällö Wörld") + assert self._read() == "Hällö Wörld" self._write("Überprüfung") self.editor.on_proc_closed(0, QProcess.NormalExit) self.editor.editing_finished.emit.assert_called_with("Überprüfung") - def test_unicode(self, _proc_mock): + def test_unicode(self): """Test if other UTF8 chars work correctly.""" self.editor.edit("\u2603") # Unicode snowman - self.assertEqual(self._read(), "\u2603") + assert self._read() == "\u2603" self._write("\u2601") # Cloud self.editor.on_proc_closed(0, QProcess.NormalExit) self.editor.editing_finished.emit.assert_called_with("\u2601") -@mock.patch('qutebrowser.misc.editor.QProcess', - new_callable=stubs.FakeQProcess) -@mock.patch('qutebrowser.misc.editor.message', new=helpers.MessageModule()) -@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( - {'general': {'editor': [''], 'editor-encoding': 'utf-8'}})) -class ErrorMessageTests(unittest.TestCase): - +class TestErrorMessage: """Test if statusbar error messages get emitted correctly. Attributes: editor: The ExternalEditor instance to test. """ - def setUp(self): + @pytest.yield_fixture(autouse=True) + def setup(self, mocker, stubs): + mocker.patch('qutebrowser.misc.editor.QProcess', + new_callable=stubs.FakeQProcess) + mocker.patch('qutebrowser.misc.editor.message', + new=stubs.MessageModule()) + mocker.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub( + {'general': {'editor': [''], 'editor-encoding': 'utf-8'}})) self.editor = editor.ExternalEditor(0) + yield + self.editor._cleanup() # pylint: disable=protected-access - def test_proc_error(self, _proc_mock): + def test_proc_error(self, caplog): """Test on_proc_error.""" self.editor.edit("") - with self.assertLogs('message', logging.ERROR): + with caplog.atLevel(logging.ERROR, 'message'): self.editor.on_proc_error(QProcess.Crashed) + assert len(caplog.records()) == 2 - def test_proc_return(self, _proc_mock): + def test_proc_return(self, caplog): """Test on_proc_finished with a bad exit status.""" self.editor.edit("") - with self.assertLogs('message', logging.ERROR): + with caplog.atLevel(logging.ERROR, 'message'): self.editor.on_proc_closed(1, QProcess.NormalExit) + assert len(caplog.records()) == 3 - -if __name__ == '__main__': - unittest.main() diff --git a/test/stubs.py b/test/stubs.py index 02f99b39d..6345c7c00 100644 --- a/test/stubs.py +++ b/test/stubs.py @@ -20,6 +20,7 @@ # pylint: disable=invalid-name """Fake objects/stubs.""" +import logging from unittest import mock @@ -37,8 +38,8 @@ class ConfigStub: data: The config data to return. """ - def __init__(self, data): - self.data = data + def __init__(self, data=None): + self.data = data or {} def section(self, name): """Get a section from the config. @@ -268,3 +269,19 @@ class FakeTimer(QObject): def isActive(self): return self._started + + +class MessageModule: + """A drop-in replacement for qutebrowser.utils.message.""" + + def error(self, _win_id, message, _immediately=False): + """Log an error to the message logger.""" + logging.getLogger('message').error(message) + + def warning(self, _win_id, message, _immediately=False): + """Log a warning to the message logger.""" + logging.getLogger('message').warning(message) + + def info(self, _win_id, message, _immediately=True): + """Log an info message to the message logger.""" + logging.getLogger('message').info(message) \ No newline at end of file From 5cf8ff1f84cf3fa21aad6ea31f3ee92f1616a166 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 13:39:04 -0300 Subject: [PATCH 23/47] Converted test_debug to pytest --- test/utils/test_debug.py | 92 +++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/test/utils/test_debug.py b/test/utils/test_debug.py index 6ec334f8f..93ab6f1fc 100644 --- a/test/utils/test_debug.py +++ b/test/utils/test_debug.py @@ -21,55 +21,54 @@ import re import time -import unittest import logging from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QStyle, QFrame +import pytest from qutebrowser.utils import debug -from qutebrowser.test import stubs -class QEnumKeyTests(unittest.TestCase): +class TestQEnumKey: """Tests for qenum_key.""" def test_no_metaobj(self): """Test with an enum with no metaobject.""" - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): # Make sure it doesn't have a meta object # pylint: disable=pointless-statement,no-member QStyle.PrimitiveElement.staticMetaObject key = debug.qenum_key(QStyle, QStyle.PE_PanelButtonCommand) - self.assertEqual(key, 'PE_PanelButtonCommand') + assert key == 'PE_PanelButtonCommand' def test_metaobj(self): """Test with an enum with metaobject.""" # pylint: disable=pointless-statement QFrame.staticMetaObject # make sure it has a metaobject key = debug.qenum_key(QFrame, QFrame.Sunken) - self.assertEqual(key, 'Sunken') + assert key == 'Sunken' def test_add_base(self): """Test with add_base=True.""" key = debug.qenum_key(QFrame, QFrame.Sunken, add_base=True) - self.assertEqual(key, 'QFrame.Sunken') + assert key == 'QFrame.Sunken' def test_int_noklass(self): """Test passing an int without explicit klass given.""" - with self.assertRaises(TypeError): + with pytest.raises(TypeError): debug.qenum_key(QFrame, 42) def test_int(self): """Test passing an int with explicit klass given.""" key = debug.qenum_key(QFrame, 0x0030, klass=QFrame.Shadow) - self.assertEqual(key, 'Sunken') + assert key == 'Sunken' def test_unknown(self): """Test passing an unknown value.""" key = debug.qenum_key(QFrame, 0x1337, klass=QFrame.Shadow) - self.assertEqual(key, '0x1337') + assert key == '0x1337' def test_reconverted(self): """Test passing a flag value which was re-converted to an enum.""" @@ -77,96 +76,93 @@ class QEnumKeyTests(unittest.TestCase): debug.qenum_key(Qt, Qt.Alignment(int(Qt.AlignLeft))) -class QFlagsKeyTests(unittest.TestCase): +class TestQFlagsKey: """Tests for qflags_key().""" - # https://github.com/The-Compiler/qutebrowser/issues/42 + fail_issue42 = pytest.mark.xfail( + reason='https://github.com/The-Compiler/qutebrowser/issues/42') - @unittest.skip('FIXME') + @fail_issue42 def test_single(self): """Test with single value.""" flags = debug.qflags_key(Qt, Qt.AlignTop) - self.assertEqual(flags, 'AlignTop') + assert flags == 'AlignTop' - @unittest.skip('FIXME') + @fail_issue42 def test_multiple(self): """Test with multiple values.""" flags = debug.qflags_key(Qt, Qt.AlignLeft | Qt.AlignTop) - self.assertEqual(flags, 'AlignLeft|AlignTop') + assert flags == 'AlignLeft|AlignTop' def test_combined(self): """Test with a combined value.""" flags = debug.qflags_key(Qt, Qt.AlignCenter) - self.assertEqual(flags, 'AlignHCenter|AlignVCenter') + assert flags == 'AlignHCenter|AlignVCenter' - @unittest.skip('FIXME') + @fail_issue42 def test_add_base(self): """Test with add_base=True.""" flags = debug.qflags_key(Qt, Qt.AlignTop, add_base=True) - self.assertEqual(flags, 'Qt.AlignTop') + assert flags == 'Qt.AlignTop' def test_int_noklass(self): """Test passing an int without explicit klass given.""" - with self.assertRaises(TypeError): + with pytest.raises(TypeError): debug.qflags_key(Qt, 42) - @unittest.skip('FIXME') + @fail_issue42 def test_int(self): """Test passing an int with explicit klass given.""" flags = debug.qflags_key(Qt, 0x0021, klass=Qt.Alignment) - self.assertEqual(flags, 'AlignLeft|AlignTop') + assert flags == 'AlignLeft|AlignTop' def test_unknown(self): """Test passing an unknown value.""" flags = debug.qflags_key(Qt, 0x1100, klass=Qt.Alignment) - self.assertEqual(flags, '0x0100|0x1000') + assert flags == '0x0100|0x1000' -class TestDebug(unittest.TestCase): +class TestDebug: """Test signal debug output functions.""" - def setUp(self): - self.signal = stubs.FakeSignal() + @pytest.fixture + def signal(self, stubs): + return stubs.FakeSignal() - def test_signal_name(self): + def test_signal_name(self, signal): """Test signal_name().""" - self.assertEqual(debug.signal_name(self.signal), 'fake') + assert debug.signal_name(signal) == 'fake' - def test_dbg_signal(self): + def test_dbg_signal(self, signal): """Test dbg_signal().""" - self.assertEqual(debug.dbg_signal(self.signal, [23, 42]), - 'fake(23, 42)') + assert debug.dbg_signal(signal, [23, 42]) == 'fake(23, 42)' - def test_dbg_signal_eliding(self): + + def test_dbg_signal_eliding(self, signal): """Test eliding in dbg_signal().""" - self.assertEqual(debug.dbg_signal(self.signal, - ['x' * 201]), - "fake('{}\u2026)".format('x' * 198)) + assert debug.dbg_signal(signal, ['x' * 201]) == \ + "fake('{}\u2026)".format('x' * 198) - def test_dbg_signal_newline(self): + def test_dbg_signal_newline(self, signal): """Test dbg_signal() with a newline.""" - self.assertEqual(debug.dbg_signal(self.signal, ['foo\nbar']), - r"fake('foo\nbar')") + assert debug.dbg_signal(signal, ['foo\nbar']) == r"fake('foo\nbar')" -class TestLogTime(unittest.TestCase): +class TestLogTime: """Test log_time.""" - def test_log_time(self): + def test_log_time(self, caplog): """Test if log_time logs properly.""" logger = logging.getLogger('qt-tests') - with self.assertLogs(logger, logging.DEBUG) as logged: + with caplog.atLevel(logging.DEBUG, logger.name): with debug.log_time(logger, action='foobar'): time.sleep(0.1) - self.assertEqual(len(logged.records), 1) + assert len(caplog.records()) == 1 pattern = re.compile(r'^Foobar took ([\d.]*) seconds\.$') - match = pattern.match(logged.records[0].msg) - self.assertTrue(match) + match = pattern.match(caplog.records()[0].msg) + assert match duration = float(match.group(1)) - self.assertAlmostEqual(duration, 0.1, delta=0.01) - -if __name__ == '__main__': - unittest.main() + assert 0.09 <= duration <= 0.11 \ No newline at end of file From 4ab03b15361c34f7efa48ad0ef08a011cc46376d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 13:49:26 -0300 Subject: [PATCH 24/47] Converted test_urlutils to pytest --- test/utils/test_urlutils.py | 138 ++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 77 deletions(-) diff --git a/test/utils/test_urlutils.py b/test/utils/test_urlutils.py index 58868acb7..bb5b330e4 100644 --- a/test/utils/test_urlutils.py +++ b/test/utils/test_urlutils.py @@ -25,9 +25,9 @@ import unittest from unittest import mock from PyQt5.QtCore import QUrl +import pytest from qutebrowser.utils import urlutils -from qutebrowser.test import stubs def get_config_stub(auto_search=True): @@ -45,8 +45,7 @@ def get_config_stub(auto_search=True): } -class SpecialURLTests(unittest.TestCase): - +class TestSpecialURL: """Test is_special_url. Attributes: @@ -65,66 +64,65 @@ class SpecialURLTests(unittest.TestCase): 'www.qutebrowser.org' ) - def test_special_urls(self): + @pytest.mark.parametrize('url', SPECIAL_URLS) + def test_special_urls(self, url): """Test special URLs.""" - for url in self.SPECIAL_URLS: - with self.subTest(url=url): - u = QUrl(url) - self.assertTrue(urlutils.is_special_url(u)) + u = QUrl(url) + assert urlutils.is_special_url(u) - def test_normal_urls(self): + @pytest.mark.parametrize('url', NORMAL_URLS) + def test_normal_urls(self, url): """Test non-special URLs.""" - for url in self.NORMAL_URLS: - with self.subTest(url=url): - u = QUrl(url) - self.assertFalse(urlutils.is_special_url(u)) + u = QUrl(url) + assert not urlutils.is_special_url(u) -@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( - get_config_stub())) -class SearchUrlTests(unittest.TestCase): - +class TestSearchUrl: """Test _get_search_url.""" + @pytest.fixture(autouse=True) + def mock_config(self, stubs, mocker): + mocker.patch('qutebrowser.utils.urlutils.config', + new=stubs.ConfigStub(get_config_stub())) + def test_default_engine(self): """Test default search engine.""" url = urlutils._get_search_url('testfoo') - self.assertEqual(url.host(), 'www.example.com') - self.assertEqual(url.query(), 'q=testfoo') + assert url.host() == 'www.example.com' + assert url.query() == 'q=testfoo' def test_engine_pre(self): """Test search engine name with one word.""" url = urlutils._get_search_url('test testfoo') - self.assertEqual(url.host(), 'www.qutebrowser.org') - self.assertEqual(url.query(), 'q=testfoo') + assert url.host() == 'www.qutebrowser.org' + assert url.query() == 'q=testfoo' def test_engine_pre_multiple_words(self): """Test search engine name with multiple words.""" url = urlutils._get_search_url('test testfoo bar foo') - self.assertEqual(url.host(), 'www.qutebrowser.org') - self.assertEqual(url.query(), 'q=testfoo bar foo') + assert url.host() == 'www.qutebrowser.org' + assert url.query() == 'q=testfoo bar foo' def test_engine_pre_whitespace_at_end(self): """Test search engine name with one word and whitespace.""" url = urlutils._get_search_url('test testfoo ') - self.assertEqual(url.host(), 'www.qutebrowser.org') - self.assertEqual(url.query(), 'q=testfoo') + assert url.host() == 'www.qutebrowser.org' + assert url.query() == 'q=testfoo' def test_engine_with_bang_pre(self): """Test search engine with a prepended !bang.""" url = urlutils._get_search_url('!python testfoo') - self.assertEqual(url.host(), 'www.example.com') - self.assertEqual(url.query(), 'q=%21python testfoo') + assert url.host() == 'www.example.com' + assert url.query() == 'q=%21python testfoo' def test_engine_wrong(self): """Test with wrong search engine.""" url = urlutils._get_search_url('blub testfoo') - self.assertEqual(url.host(), 'www.example.com') - self.assertEqual(url.query(), 'q=blub testfoo') + assert url.host() == 'www.example.com' + assert url.query() == 'q=blub testfoo' -class IsUrlTests(unittest.TestCase): - +class TestIsUrl: """Tests for is_url. Class attributes: @@ -158,87 +156,73 @@ class IsUrlTests(unittest.TestCase): 'foo::bar', ) - @mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( - get_config_stub('naive'))) - def test_urls(self): + @pytest.mark.parametrize('url', URLS) + def test_urls(self, mocker, stubs, url): """Test things which are URLs.""" - for url in self.URLS: - with self.subTest(url=url): - self.assertTrue(urlutils.is_url(url), url) + mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( + get_config_stub('naive'))) + assert urlutils.is_url(url), url - @mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( - get_config_stub('naive'))) - def test_not_urls(self): + @pytest.mark.parametrize('url', NOT_URLS) + def test_not_urls(self, mocker, stubs, url): """Test things which are not URLs.""" - for url in self.NOT_URLS: - with self.subTest(url=url): - self.assertFalse(urlutils.is_url(url), url) + mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( + get_config_stub('naive'))) + assert not urlutils.is_url(url), url - @mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( - get_config_stub(True))) - def test_search_autosearch(self): + @pytest.mark.parametrize('autosearch', [True, False]) + def test_search_autosearch(self, mocker, stubs, autosearch): """Test explicit search with auto-search=True.""" - self.assertFalse(urlutils.is_url('test foo')) - - @mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( - get_config_stub(False))) - def test_search_no_autosearch(self): - """Test explicit search with auto-search=False.""" - self.assertFalse(urlutils.is_url('test foo')) + mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub( + get_config_stub(autosearch))) + assert not urlutils.is_url('test foo') -class QurlFromUserInputTests(unittest.TestCase): - +class TestQurlFromUserInput: """Tests for qurl_from_user_input.""" def test_url(self): """Test a normal URL.""" - self.assertEqual( - urlutils.qurl_from_user_input('qutebrowser.org').toString(), - 'http://qutebrowser.org') + assert ( + urlutils.qurl_from_user_input('qutebrowser.org').toString() + == 'http://qutebrowser.org') def test_url_http(self): """Test a normal URL with http://.""" - self.assertEqual( - urlutils.qurl_from_user_input('http://qutebrowser.org').toString(), - 'http://qutebrowser.org') + assert ( + urlutils.qurl_from_user_input('http://qutebrowser.org').toString() + == 'http://qutebrowser.org') def test_ipv6_bare(self): """Test an IPv6 without brackets.""" - self.assertEqual(urlutils.qurl_from_user_input('::1/foo').toString(), - 'http://[::1]/foo') + assert (urlutils.qurl_from_user_input('::1/foo').toString() + == 'http://[::1]/foo') def test_ipv6(self): """Test an IPv6 with brackets.""" - self.assertEqual(urlutils.qurl_from_user_input('[::1]/foo').toString(), - 'http://[::1]/foo') + assert (urlutils.qurl_from_user_input('[::1]/foo').toString() == + 'http://[::1]/foo') def test_ipv6_http(self): """Test an IPv6 with http:// and brackets.""" - self.assertEqual( - urlutils.qurl_from_user_input('http://[::1]').toString(), + assert ( + urlutils.qurl_from_user_input('http://[::1]').toString() == 'http://[::1]') -class FilenameFromUrlTests(unittest.TestCase): - +class TestFilenameFromUrl: """Tests for filename_from_url.""" def test_invalid_url(self): """Test with an invalid QUrl.""" - self.assertEqual(urlutils.filename_from_url(QUrl()), None) + assert urlutils.filename_from_url(QUrl()) == None def test_url_path(self): """Test with an URL with path.""" url = QUrl('http://qutebrowser.org/test.html') - self.assertEqual(urlutils.filename_from_url(url), 'test.html') + assert urlutils.filename_from_url(url) == 'test.html' def test_url_host(self): """Test with an URL with no path.""" url = QUrl('http://qutebrowser.org/') - self.assertEqual(urlutils.filename_from_url(url), - 'qutebrowser.org.html') - - -if __name__ == '__main__': - unittest.main() + assert urlutils.filename_from_url(url) == 'qutebrowser.org.html' From 2666388a6d5e46cde7f53b698ea0aed35e75747f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 14:01:58 -0300 Subject: [PATCH 25/47] Converted test_utils to pytest --- test/utils/test_utils.py | 246 ++++++++++++++++++-------------------- test/{ => utils}/testfile | 0 2 files changed, 117 insertions(+), 129 deletions(-) rename test/{ => utils}/testfile (100%) diff --git a/test/utils/test_utils.py b/test/utils/test_utils.py index 088ce49c7..e93f795d5 100644 --- a/test/utils/test_utils.py +++ b/test/utils/test_utils.py @@ -21,15 +21,14 @@ import sys import enum -import unittest import datetime import os.path from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor +import pytest from qutebrowser.utils import utils, qtutils -from qutebrowser.test import helpers class Color(QColor): @@ -42,7 +41,7 @@ class Color(QColor): alpha=self.alpha()) -class ElidingTests(unittest.TestCase): +class TestEliding: """Test elide.""" @@ -50,33 +49,34 @@ class ElidingTests(unittest.TestCase): def test_too_small(self): """Test eliding to 0 chars which should fail.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): utils.elide('foo', 0) def test_length_one(self): """Test eliding to 1 char which should yield ...""" - self.assertEqual(utils.elide('foo', 1), self.ELLIPSIS) + assert utils.elide('foo', 1) == self.ELLIPSIS def test_fits(self): """Test eliding with a string which fits exactly.""" - self.assertEqual(utils.elide('foo', 3), 'foo') + assert utils.elide('foo', 3) == 'foo' def test_elided(self): """Test eliding with a string which should get elided.""" - self.assertEqual(utils.elide('foobar', 3), 'fo' + self.ELLIPSIS) + assert utils.elide('foobar', 3) == 'fo' + self.ELLIPSIS -class ReadFileTests(unittest.TestCase): +class TestReadFile: """Test read_file.""" def test_readfile(self): """Read a testfile.""" - content = utils.read_file(os.path.join('test', 'testfile')) - self.assertEqual(content.splitlines()[0], "Hello World!") + directory = os.path.dirname(__file__) + content = utils.read_file(os.path.join(directory, 'testfile')) + assert content.splitlines()[0] == "Hello World!" -class InterpolateColorTests(unittest.TestCase): +class TestInterpolateColor: """Tests for interpolate_color. @@ -85,30 +85,31 @@ class InterpolateColorTests(unittest.TestCase): white: The Color black as a valid Color for tests. """ - def setUp(self): + @pytest.fixture(autouse=True) + def setup(self): self.white = Color('white') self.black = Color('black') def test_invalid_start(self): """Test an invalid start color.""" - with self.assertRaises(qtutils.QtValueError): + with pytest.raises(qtutils.QtValueError): utils.interpolate_color(Color(), self.white, 0) def test_invalid_end(self): """Test an invalid end color.""" - with self.assertRaises(qtutils.QtValueError): + with pytest.raises(qtutils.QtValueError): utils.interpolate_color(self.white, Color(), 0) def test_invalid_percentage(self): """Test an invalid percentage.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): utils.interpolate_color(self.white, self.white, -1) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): utils.interpolate_color(self.white, self.white, 101) def test_invalid_colorspace(self): """Test an invalid colorspace.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): utils.interpolate_color(self.white, self.black, 10, QColor.Cmyk) def test_valid_percentages_rgb(self): @@ -116,30 +117,30 @@ class InterpolateColorTests(unittest.TestCase): white = utils.interpolate_color(self.white, self.black, 0, QColor.Rgb) black = utils.interpolate_color(self.white, self.black, 100, QColor.Rgb) - self.assertEqual(Color(white), self.white) - self.assertEqual(Color(black), self.black) + assert Color(white) == self.white + assert Color(black) == self.black def test_valid_percentages_hsv(self): """Test 0% and 100% in the HSV colorspace.""" white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsv) black = utils.interpolate_color(self.white, self.black, 100, QColor.Hsv) - self.assertEqual(Color(white), self.white) - self.assertEqual(Color(black), self.black) + assert Color(white) == self.white + assert Color(black) == self.black def test_valid_percentages_hsl(self): """Test 0% and 100% in the HSL colorspace.""" white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsl) black = utils.interpolate_color(self.white, self.black, 100, QColor.Hsl) - self.assertEqual(Color(white), self.white) - self.assertEqual(Color(black), self.black) + assert Color(white) == self.white + assert Color(black) == self.black def test_interpolation_rgb(self): """Test an interpolation in the RGB colorspace.""" color = utils.interpolate_color(Color(0, 40, 100), Color(0, 20, 200), 50, QColor.Rgb) - self.assertEqual(Color(color), Color(0, 30, 150)) + assert Color(color) == Color(0, 30, 150) def test_interpolation_hsv(self): """Test an interpolation in the HSV colorspace.""" @@ -150,7 +151,7 @@ class InterpolateColorTests(unittest.TestCase): color = utils.interpolate_color(start, stop, 50, QColor.Hsv) expected = Color() expected.setHsv(0, 30, 150) - self.assertEqual(Color(color), expected) + assert Color(color) == expected def test_interpolation_hsl(self): """Test an interpolation in the HSL colorspace.""" @@ -161,10 +162,10 @@ class InterpolateColorTests(unittest.TestCase): color = utils.interpolate_color(start, stop, 50, QColor.Hsl) expected = Color() expected.setHsl(0, 30, 150) - self.assertEqual(Color(color), expected) + assert Color(color) == expected -class FormatSecondsTests(unittest.TestCase): +class TestFormatSeconds: """Tests for format_seconds. @@ -186,14 +187,13 @@ class FormatSecondsTests(unittest.TestCase): (36000, '10:00:00'), ] - def test_format_seconds(self): + @pytest.mark.parametrize('seconds, out', TESTS) + def test_format_seconds(self, seconds, out): """Test format_seconds with several tests.""" - for seconds, out in self.TESTS: - with self.subTest(seconds=seconds): - self.assertEqual(utils.format_seconds(seconds), out) + assert utils.format_seconds(seconds) == out -class FormatTimedeltaTests(unittest.TestCase): +class TestFormatTimedelta: """Tests for format_timedelta. @@ -217,14 +217,13 @@ class FormatTimedeltaTests(unittest.TestCase): (datetime.timedelta(seconds=36000), '10h'), ] - def test_format_seconds(self): + @pytest.mark.parametrize('td, out', TESTS) + def test_format_seconds(self, td, out): """Test format_seconds with several tests.""" - for td, out in self.TESTS: - with self.subTest(td=td): - self.assertEqual(utils.format_timedelta(td), out) + assert utils.format_timedelta(td) == out -class FormatSizeTests(unittest.TestCase): +class TestFormatSize: """Tests for format_size. @@ -244,122 +243,117 @@ class FormatSizeTests(unittest.TestCase): (None, '?.??'), ] - def test_format_size(self): + KILO_TESTS = [(999, '999.00'), (1000, '1.00k'), (1010, '1.01k')] + + @pytest.mark.parametrize('size, out', TESTS) + def test_format_size(self, size, out): """Test format_size with several tests.""" - for size, out in self.TESTS: - with self.subTest(size=size): - self.assertEqual(utils.format_size(size), out) + assert utils.format_size(size) == out - def test_suffix(self): + @pytest.mark.parametrize('size, out', TESTS) + def test_suffix(self, size, out): """Test the suffix option.""" - for size, out in self.TESTS: - with self.subTest(size=size): - self.assertEqual(utils.format_size(size, suffix='B'), - out + 'B') + assert utils.format_size(size, suffix='B') == out + 'B' - def test_base(self): + @pytest.mark.parametrize('size, out', KILO_TESTS) + def test_base(self, size, out): """Test with an alternative base.""" - kilo_tests = [(999, '999.00'), (1000, '1.00k'), (1010, '1.01k')] - for size, out in kilo_tests: - with self.subTest(size=size): - self.assertEqual(utils.format_size(size, base=1000), out) + assert utils.format_size(size, base=1000) == out -class KeyToStringTests(unittest.TestCase): +class TestKeyToString: """Test key_to_string.""" def test_unicode_garbage_keys(self): """Test a special key where QKeyEvent::toString works incorrectly.""" - self.assertEqual(utils.key_to_string(Qt.Key_Blue), 'Blue') + assert utils.key_to_string(Qt.Key_Blue) == 'Blue' def test_backtab(self): """Test if backtab is normalized to tab correctly.""" - self.assertEqual(utils.key_to_string(Qt.Key_Backtab), 'Tab') + assert utils.key_to_string(Qt.Key_Backtab) == 'Tab' def test_escape(self): """Test if escape is normalized to escape correctly.""" - self.assertEqual(utils.key_to_string(Qt.Key_Escape), 'Escape') + assert utils.key_to_string(Qt.Key_Escape) == 'Escape' def test_letter(self): """Test a simple letter key.""" - self.assertEqual(utils.key_to_string(Qt.Key_A), 'A') + assert utils.key_to_string(Qt.Key_A) == 'A' def test_unicode(self): """Test a printable unicode key.""" - self.assertEqual(utils.key_to_string(Qt.Key_degree), '°') + assert utils.key_to_string(Qt.Key_degree) == '°' def test_special(self): """Test a non-printable key handled by QKeyEvent::toString.""" - self.assertEqual(utils.key_to_string(Qt.Key_F1), 'F1') + assert utils.key_to_string(Qt.Key_F1) == 'F1' -class KeyEventToStringTests(unittest.TestCase): +class TestKeyEventToString: """Test keyevent_to_string.""" - def test_only_control(self): + def test_only_control(self, fake_keyevent_factory): """Test keyeevent when only control is pressed.""" - evt = helpers.fake_keyevent(key=Qt.Key_Control, + evt = fake_keyevent_factory(key=Qt.Key_Control, modifiers=Qt.ControlModifier) - self.assertIsNone(utils.keyevent_to_string(evt)) + assert utils.keyevent_to_string(evt) is None - def test_only_hyper_l(self): + def test_only_hyper_l(self, fake_keyevent_factory): """Test keyeevent when only Hyper_L is pressed.""" - evt = helpers.fake_keyevent(key=Qt.Key_Hyper_L, + evt = fake_keyevent_factory(key=Qt.Key_Hyper_L, modifiers=Qt.MetaModifier) - self.assertIsNone(utils.keyevent_to_string(evt)) + assert utils.keyevent_to_string(evt) is None - def test_only_key(self): + def test_only_key(self, fake_keyevent_factory): """Test with a simple key pressed.""" - evt = helpers.fake_keyevent(key=Qt.Key_A) - self.assertEqual(utils.keyevent_to_string(evt), 'A') + evt = fake_keyevent_factory(key=Qt.Key_A) + assert utils.keyevent_to_string(evt) == 'A' - def test_key_and_modifier(self): + def test_key_and_modifier(self, fake_keyevent_factory): """Test with key and modifier pressed.""" - evt = helpers.fake_keyevent(key=Qt.Key_A, modifiers=Qt.ControlModifier) - self.assertEqual(utils.keyevent_to_string(evt), 'Ctrl+A') + evt = fake_keyevent_factory(key=Qt.Key_A, modifiers=Qt.ControlModifier) + assert utils.keyevent_to_string(evt) == 'Ctrl+A' - def test_key_and_modifiers(self): + def test_key_and_modifiers(self, fake_keyevent_factory): """Test with key and multiple modifier pressed.""" - evt = helpers.fake_keyevent( + evt = fake_keyevent_factory( key=Qt.Key_A, modifiers=(Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier | Qt.ShiftModifier)) if sys.platform == 'darwin': - self.assertEqual(utils.keyevent_to_string(evt), - 'Ctrl+Alt+Shift+A') + assert utils.keyevent_to_string(evt) == 'Ctrl+Alt+Shift+A' else: - self.assertEqual(utils.keyevent_to_string(evt), - 'Ctrl+Alt+Meta+Shift+A') + assert utils.keyevent_to_string(evt) == 'Ctrl+Alt+Meta+Shift+A' -class NormalizeTests(unittest.TestCase): +class TestNormalize: """Test normalize_keystr.""" - def test_normalize(self): + STRINGS = ( + ('Control+x', 'ctrl+x'), + ('Windows+x', 'meta+x'), + ('Mod1+x', 'alt+x'), + ('Mod4+x', 'meta+x'), + ('Control--', 'ctrl+-'), + ('Windows++', 'meta++'), + ) + + @pytest.mark.parametrize('orig, repl', STRINGS) + def test_normalize(self, orig, repl): """Test normalize with some strings.""" - strings = ( - ('Control+x', 'ctrl+x'), - ('Windows+x', 'meta+x'), - ('Mod1+x', 'alt+x'), - ('Mod4+x', 'meta+x'), - ('Control--', 'ctrl+-'), - ('Windows++', 'meta++'), - ) - for orig, repl in strings: - with self.subTest(orig=orig): - self.assertEqual(utils.normalize_keystr(orig), repl) + assert utils.normalize_keystr(orig) == repl -class IsEnumTests(unittest.TestCase): +class TestIsEnum: """Test is_enum.""" def test_enum(self): """Test is_enum with an enum.""" e = enum.Enum('Foo', 'bar, baz') - self.assertTrue(utils.is_enum(e)) + assert utils.is_enum(e) def test_class(self): """Test is_enum with a non-enum class.""" @@ -368,14 +362,15 @@ class IsEnumTests(unittest.TestCase): """Test class for is_enum.""" pass - self.assertFalse(utils.is_enum(Test)) + + assert not utils.is_enum(Test) def test_object(self): """Test is_enum with a non-enum object.""" - self.assertFalse(utils.is_enum(23)) + assert not utils.is_enum(23) -class RaisesTests(unittest.TestCase): +class TestRaises: """Test raises.""" @@ -389,106 +384,99 @@ class RaisesTests(unittest.TestCase): def test_raises_single_exc_true(self): """Test raises with a single exception which gets raised.""" - self.assertTrue(utils.raises(ValueError, int, 'a')) + assert utils.raises(ValueError, int, 'a') def test_raises_single_exc_false(self): """Test raises with a single exception which does not get raised.""" - self.assertFalse(utils.raises(ValueError, int, '1')) + assert not utils.raises(ValueError, int, '1') def test_raises_multiple_exc_true(self): """Test raises with multiple exceptions which get raised.""" - self.assertTrue(utils.raises((ValueError, TypeError), int, 'a')) - self.assertTrue(utils.raises((ValueError, TypeError), int, None)) + assert utils.raises((ValueError, TypeError), int, 'a') + assert utils.raises((ValueError, TypeError), int, None) def test_raises_multiple_exc_false(self): """Test raises with multiple exceptions which do not get raised.""" - self.assertFalse(utils.raises((ValueError, TypeError), int, '1')) + assert not utils.raises((ValueError, TypeError), int, '1') def test_no_args_true(self): """Test with no args and an exception which gets raised.""" - self.assertTrue(utils.raises(Exception, self.do_raise)) + assert utils.raises(Exception, self.do_raise) def test_no_args_false(self): """Test with no args and an exception which does not get raised.""" - self.assertFalse(utils.raises(Exception, self.do_nothing)) + assert not utils.raises(Exception, self.do_nothing) def test_unrelated_exception(self): """Test with an unrelated exception.""" - with self.assertRaises(Exception): + with pytest.raises(Exception): utils.raises(ValueError, self.do_raise) -class ForceEncodingTests(unittest.TestCase): +class TestForceEncoding: """Test force_encoding.""" - def test_fitting_ascii(self): - """Test with a text fitting into ascii.""" - text = 'hello world' - self.assertEqual(utils.force_encoding(text, 'ascii'), text) + TESTS = [ + ('hello world', 'ascii', 'hello world'), + ('hellö wörld', 'utf-8', 'hellö wörld'), + ('hellö wörld', 'ascii', 'hell? w?rld'), + ] - def test_fitting_utf8(self): - """Test with a text fitting into utf-8.""" - text = 'hellö wörld' - self.assertEqual(utils.force_encoding(text, 'utf-8'), text) - - def test_not_fitting_ascii(self): - """Test with a text not fitting into ascii.""" - text = 'hellö wörld' - self.assertEqual(utils.force_encoding(text, 'ascii'), 'hell? w?rld') + @pytest.mark.parametrize('inp, enc, expected', TESTS) + def test_fitting_ascii(self, inp, enc, expected): + """Test force_encoding will yield expected text.""" + assert utils.force_encoding(inp, enc) == expected -class NewestSliceTests(unittest.TestCase): +class TestNewestSlice: """Test newest_slice.""" def test_count_minus_two(self): """Test with a count of -2.""" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): utils.newest_slice([], -2) def test_count_minus_one(self): """Test with a count of -1 (all elements).""" items = range(20) sliced = utils.newest_slice(items, -1) - self.assertEqual(list(sliced), list(items)) + assert list(sliced) == list(items) def test_count_zero(self): """Test with a count of 0 (no elements).""" items = range(20) sliced = utils.newest_slice(items, 0) - self.assertEqual(list(sliced), []) + assert list(sliced) == [] def test_count_much_smaller(self): """Test with a count which is much smaller than the iterable.""" items = range(20) sliced = utils.newest_slice(items, 5) - self.assertEqual(list(sliced), [15, 16, 17, 18, 19]) + assert list(sliced) == [15, 16, 17, 18, 19] def test_count_smaller(self): """Test with a count which is exactly one smaller.""" items = range(5) sliced = utils.newest_slice(items, 4) - self.assertEqual(list(sliced), [1, 2, 3, 4]) + assert list(sliced) == [1, 2, 3, 4] def test_count_equal(self): """Test with a count which is just as large as the iterable.""" items = range(5) sliced = utils.newest_slice(items, 5) - self.assertEqual(list(sliced), list(items)) + assert list(sliced) == list(items) def test_count_bigger(self): """Test with a count which is one bigger than the iterable.""" items = range(5) sliced = utils.newest_slice(items, 6) - self.assertEqual(list(sliced), list(items)) + assert list(sliced) == list(items) def test_count_much_bigger(self): """Test with a count which is much bigger than the iterable.""" items = range(5) sliced = utils.newest_slice(items, 50) - self.assertEqual(list(sliced), list(items)) + assert list(sliced) == list(items) - -if __name__ == '__main__': - unittest.main() diff --git a/test/testfile b/test/utils/testfile similarity index 100% rename from test/testfile rename to test/utils/testfile From 82220979420263656ebbcc694e84c9e2b1b478b2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 14:12:22 -0300 Subject: [PATCH 26/47] Removed unnecessary module 'helpers' --- test/helpers.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 test/helpers.py diff --git a/test/helpers.py b/test/helpers.py deleted file mode 100644 index 233c13ba5..000000000 --- a/test/helpers.py +++ /dev/null @@ -1,35 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2015 Florian Bruhin (The Compiler) -# -# 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 . - -"""Helpers needed by tests.""" - -import logging -import contextlib - - -@contextlib.contextmanager -def disable_logger(name): - """Temporarily disable a logger.""" - logging.getLogger(name).propagate = False - try: - yield - finally: - logging.getLogger(name).propagate = True - - From 1e2a902a9fc809fa0d46faad3b3ebe33dcdfa217 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 4 Apr 2015 23:49:43 +0200 Subject: [PATCH 27/47] Fix paths for TestGetStandardDirLinux. --- test/utils/test_standarddir.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/utils/test_standarddir.py b/test/utils/test_standarddir.py index c69210cc4..faf84aee1 100644 --- a/test/utils/test_standarddir.py +++ b/test/utils/test_standarddir.py @@ -51,26 +51,26 @@ class TestGetStandardDirLinux: """Test data dir with XDG_DATA_HOME explicitely set.""" monkeypatch.setenv('XDG_DATA_HOME', str(tmpdir)) standarddir.init(None) - assert standarddir.data() == str(tmpdir / 'qutebrowser') + assert standarddir.data() == str(tmpdir / 'qutebrowser_test') def test_config_explicit(self, monkeypatch, tmpdir): """Test config dir with XDG_CONFIG_HOME explicitely set.""" monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir)) standarddir.init(None) - assert standarddir.config() == str(tmpdir / 'qutebrowser') + assert standarddir.config() == str(tmpdir / 'qutebrowser_test') def test_cache_explicit(self, monkeypatch, tmpdir): """Test cache dir with XDG_CACHE_HOME explicitely set.""" monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir)) standarddir.init(None) - assert standarddir.cache() == str(tmpdir / 'qutebrowser') + assert standarddir.cache() == str(tmpdir / 'qutebrowser_test') def test_data(self, monkeypatch, tmpdir): """Test data dir with XDG_DATA_HOME not set.""" monkeypatch.setenv('HOME', str(tmpdir)) monkeypatch.setenv('XDG_DATA_HOME', None) standarddir.init(None) - expected = tmpdir / '.local' / 'share' / 'qutebrowser' + expected = tmpdir / '.local' / 'share' / 'qutebrowser_test' assert standarddir.data() == str(expected) def test_config(self, monkeypatch, tmpdir): @@ -78,7 +78,7 @@ class TestGetStandardDirLinux: monkeypatch.setenv('HOME', str(tmpdir)) monkeypatch.setenv('XDG_CONFIG_HOME', None) standarddir.init(None) - expected = tmpdir / '.config' / 'qutebrowser' + expected = tmpdir / '.config' / 'qutebrowser_test' assert standarddir.config() == str(expected) def test_cache(self, monkeypatch, tmpdir): @@ -86,7 +86,7 @@ class TestGetStandardDirLinux: monkeypatch.setenv('HOME', str(tmpdir)) monkeypatch.setenv('XDG_CACHE_HOME', None) standarddir.init(None) - expected = tmpdir / '.cache' / 'qutebrowser' + expected = tmpdir / '.cache' / 'qutebrowser_test' assert standarddir.cache() == expected From 6a03089639d03382a2fa0e85ec181cf8ff0252a4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 4 Apr 2015 23:52:42 +0200 Subject: [PATCH 28/47] Fix deleting of envvars in TestGetStandardDirLinux --- test/utils/test_standarddir.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/utils/test_standarddir.py b/test/utils/test_standarddir.py index faf84aee1..a27b5b635 100644 --- a/test/utils/test_standarddir.py +++ b/test/utils/test_standarddir.py @@ -68,7 +68,7 @@ class TestGetStandardDirLinux: def test_data(self, monkeypatch, tmpdir): """Test data dir with XDG_DATA_HOME not set.""" monkeypatch.setenv('HOME', str(tmpdir)) - monkeypatch.setenv('XDG_DATA_HOME', None) + monkeypatch.delenv('XDG_DATA_HOME', raising=False) standarddir.init(None) expected = tmpdir / '.local' / 'share' / 'qutebrowser_test' assert standarddir.data() == str(expected) @@ -76,7 +76,7 @@ class TestGetStandardDirLinux: def test_config(self, monkeypatch, tmpdir): """Test config dir with XDG_CONFIG_HOME not set.""" monkeypatch.setenv('HOME', str(tmpdir)) - monkeypatch.setenv('XDG_CONFIG_HOME', None) + monkeypatch.delenv('XDG_CONFIG_HOME', raising=False) standarddir.init(None) expected = tmpdir / '.config' / 'qutebrowser_test' assert standarddir.config() == str(expected) @@ -84,7 +84,7 @@ class TestGetStandardDirLinux: def test_cache(self, monkeypatch, tmpdir): """Test cache dir with XDG_CACHE_HOME not set.""" monkeypatch.setenv('HOME', str(tmpdir)) - monkeypatch.setenv('XDG_CACHE_HOME', None) + monkeypatch.delenv('XDG_CACHE_HOME', raising=False) standarddir.init(None) expected = tmpdir / '.cache' / 'qutebrowser_test' assert standarddir.cache() == expected From 3de584f02cb279efb549fe0aad13e78f80fdd855 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 19:14:34 -0300 Subject: [PATCH 29/47] Moving testfile used by test_utils into qutebrowser.utils --- {test => qutebrowser}/utils/testfile | 0 test/utils/test_utils.py | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) rename {test => qutebrowser}/utils/testfile (100%) diff --git a/test/utils/testfile b/qutebrowser/utils/testfile similarity index 100% rename from test/utils/testfile rename to qutebrowser/utils/testfile diff --git a/test/utils/test_utils.py b/test/utils/test_utils.py index fe2d750f8..e82cee184 100644 --- a/test/utils/test_utils.py +++ b/test/utils/test_utils.py @@ -71,8 +71,7 @@ class TestReadFile: def test_readfile(self): """Read a test file.""" - directory = os.path.dirname(__file__) - content = utils.read_file(os.path.join(directory, 'testfile')) + content = utils.read_file(os.path.join('utils', 'testfile')) assert content.splitlines()[0] == "Hello World!" From 5fb7ad383d7b691c74aa8569b7f7663dfbd37759 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 5 Apr 2015 12:22:12 -0300 Subject: [PATCH 30/47] Fixed assertions and other issues as reported by @The-Compiler --- test/browser/test_webelem.py | 2 +- test/config/test_configtypes.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/browser/test_webelem.py b/test/browser/test_webelem.py index e099210fa..2f931444c 100644 --- a/test/browser/test_webelem.py +++ b/test/browser/test_webelem.py @@ -379,7 +379,7 @@ class TestIsEditable: @pytest.yield_fixture def stub_config(self, stubs): - config = stubs.ConfigStub({'input': {'insert-mode-on-plugins': True}}) + config = stubs.ConfigStub({'input': {}}) with mock.patch('qutebrowser.browser.webelem.config', new=config): yield config diff --git a/test/config/test_configtypes.py b/test/config/test_configtypes.py index c3b629aee..009133af2 100644 --- a/test/config/test_configtypes.py +++ b/test/config/test_configtypes.py @@ -99,17 +99,17 @@ class TestValidValues: def test_iter_without_desc(self): """Test __iter__ without a description.""" vv = configtypes.ValidValues('foo', 'bar') - assert list(vv), ['foo' == 'bar'] + assert list(vv) == ['foo', 'bar'] def test_iter_with_desc(self): """Test __iter__ with a description.""" vv = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc")) - assert list(vv), ['foo' == 'bar'] + assert list(vv) == ['foo', 'bar'] def test_iter_with_mixed_desc(self): """Test __iter__ with mixed description.""" vv = configtypes.ValidValues(('foo', "foo desc"), 'bar') - assert list(vv), ['foo' == 'bar'] + assert list(vv) == ['foo', 'bar'] def test_descriptions(self): """Test descriptions.""" @@ -159,7 +159,7 @@ class TestBaseType: def test_complete_without_desc(self): """Test complete with valid_values set without description.""" self.t.valid_values = configtypes.ValidValues('foo', 'bar') - assert self.t.complete(), [('foo', ''), ('bar' == '')] + assert self.t.complete() == [('foo', ''), ('bar', '')] def test_complete_with_desc(self): """Test complete with valid_values set with description.""" @@ -479,7 +479,7 @@ class TestIntList: def test_transform_empty(self): """Test transform with an empty value.""" - assert self.t.transform('23,,42'), [23, None == 42] + assert self.t.transform('23,,42') == [23, None, 42] class TestFloat: @@ -723,11 +723,11 @@ class TestPercList: def test_transform_more(self): """Test transform with multiple values.""" - assert self.t.transform('23%,42%,1337%'), [23, 42 == 1337] + assert self.t.transform('23%,42%,1337%') == [23, 42, 1337] def test_transform_empty(self): """Test transform with an empty value.""" - assert self.t.transform('23%,,42%'), [23, None == 42] + assert self.t.transform('23%,,42%') == [23, None, 42] class TestPercOrInt: @@ -1225,7 +1225,7 @@ class TestFontFamily: @pytest.mark.parametrize('val', INVALID) def test_validate_invalid(self, val): """Test validate with invalid values.""" - with pytest.raises(configexc.ValidationError, msg=val): + with pytest.raises(configexc.ValidationError): self.t.validate(val) def test_transform_empty(self): @@ -1591,11 +1591,11 @@ class TestWebKitBytesList: def test_transform_more(self): """Test transform with multiple values.""" - assert self.t.transform('23,2k,1337'), [23, 2048 == 1337] + assert self.t.transform('23,2k,1337'), [23, 2048, 1337] def test_transform_empty(self): """Test transform with an empty value.""" - assert self.t.transform('23,,42'), [23, None == 42] + assert self.t.transform('23,,42'), [23, None, 42] class TestShellCommand: @@ -1636,7 +1636,7 @@ class TestShellCommand: def test_transform_double(self): """Test transform with two words.""" - assert self.t.transform('foobar baz'), ['foobar' == 'baz'] + assert self.t.transform('foobar baz'), ['foobar', 'baz'] def test_transform_quotes(self): """Test transform with a quoted word.""" From bfc99f09f981572d80bf1c32af5d8dc6aea73980 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 5 Apr 2015 12:23:04 -0300 Subject: [PATCH 31/47] Renamed test to tests as suggested by @The-Compiler --- {test => tests}/browser/http/test_content_disposition.py | 0 {test => tests}/browser/http/test_http.py | 0 {test => tests}/browser/test_tabhistory.py | 0 {test => tests}/browser/test_webelem.py | 0 {test => tests}/config/test_config.py | 0 {test => tests}/config/test_configtypes.py | 0 {test => tests}/conftest.py | 0 {test => tests}/html/jsconfirm.html | 0 {test => tests}/html/jsprompt.html | 0 {test => tests}/keyinput/test_basekeyparser.py | 0 {test => tests}/keyinput/test_modeparsers.py | 0 {test => tests}/log.py | 0 {test => tests}/mainwindow/conftest.py | 0 {test => tests}/mainwindow/statusbar/test_percentage.py | 0 {test => tests}/mainwindow/statusbar/test_progress.py | 0 {test => tests}/mainwindow/statusbar/test_textbase.py | 0 {test => tests}/misc/test_crashdialog.py | 0 {test => tests}/misc/test_editor.py | 0 {test => tests}/misc/test_lineparser.py | 0 {test => tests}/misc/test_readline.py | 0 {test => tests}/misc/test_split.py | 0 {test => tests}/stubs.py | 0 {test => tests}/test_stubs.py | 0 {test => tests}/utils/debug/test_log_time.py | 0 {test => tests}/utils/debug/test_qenum_key.py | 0 {test => tests}/utils/debug/test_qflags_key.py | 0 {test => tests}/utils/debug/test_signal.py | 0 {test => tests}/utils/test_jinja.py | 0 {test => tests}/utils/test_log.py | 0 {test => tests}/utils/test_qtutils.py | 0 {test => tests}/utils/test_standarddir.py | 0 {test => tests}/utils/test_urlutils.py | 0 {test => tests}/utils/test_utils.py | 0 {test => tests}/utils/usertypes/test_enum.py | 0 {test => tests}/utils/usertypes/test_neighborlist.py | 0 35 files changed, 0 insertions(+), 0 deletions(-) rename {test => tests}/browser/http/test_content_disposition.py (100%) rename {test => tests}/browser/http/test_http.py (100%) rename {test => tests}/browser/test_tabhistory.py (100%) rename {test => tests}/browser/test_webelem.py (100%) rename {test => tests}/config/test_config.py (100%) rename {test => tests}/config/test_configtypes.py (100%) rename {test => tests}/conftest.py (100%) rename {test => tests}/html/jsconfirm.html (100%) rename {test => tests}/html/jsprompt.html (100%) rename {test => tests}/keyinput/test_basekeyparser.py (100%) rename {test => tests}/keyinput/test_modeparsers.py (100%) rename {test => tests}/log.py (100%) rename {test => tests}/mainwindow/conftest.py (100%) rename {test => tests}/mainwindow/statusbar/test_percentage.py (100%) rename {test => tests}/mainwindow/statusbar/test_progress.py (100%) rename {test => tests}/mainwindow/statusbar/test_textbase.py (100%) rename {test => tests}/misc/test_crashdialog.py (100%) rename {test => tests}/misc/test_editor.py (100%) rename {test => tests}/misc/test_lineparser.py (100%) rename {test => tests}/misc/test_readline.py (100%) rename {test => tests}/misc/test_split.py (100%) rename {test => tests}/stubs.py (100%) rename {test => tests}/test_stubs.py (100%) rename {test => tests}/utils/debug/test_log_time.py (100%) rename {test => tests}/utils/debug/test_qenum_key.py (100%) rename {test => tests}/utils/debug/test_qflags_key.py (100%) rename {test => tests}/utils/debug/test_signal.py (100%) rename {test => tests}/utils/test_jinja.py (100%) rename {test => tests}/utils/test_log.py (100%) rename {test => tests}/utils/test_qtutils.py (100%) rename {test => tests}/utils/test_standarddir.py (100%) rename {test => tests}/utils/test_urlutils.py (100%) rename {test => tests}/utils/test_utils.py (100%) rename {test => tests}/utils/usertypes/test_enum.py (100%) rename {test => tests}/utils/usertypes/test_neighborlist.py (100%) diff --git a/test/browser/http/test_content_disposition.py b/tests/browser/http/test_content_disposition.py similarity index 100% rename from test/browser/http/test_content_disposition.py rename to tests/browser/http/test_content_disposition.py diff --git a/test/browser/http/test_http.py b/tests/browser/http/test_http.py similarity index 100% rename from test/browser/http/test_http.py rename to tests/browser/http/test_http.py diff --git a/test/browser/test_tabhistory.py b/tests/browser/test_tabhistory.py similarity index 100% rename from test/browser/test_tabhistory.py rename to tests/browser/test_tabhistory.py diff --git a/test/browser/test_webelem.py b/tests/browser/test_webelem.py similarity index 100% rename from test/browser/test_webelem.py rename to tests/browser/test_webelem.py diff --git a/test/config/test_config.py b/tests/config/test_config.py similarity index 100% rename from test/config/test_config.py rename to tests/config/test_config.py diff --git a/test/config/test_configtypes.py b/tests/config/test_configtypes.py similarity index 100% rename from test/config/test_configtypes.py rename to tests/config/test_configtypes.py diff --git a/test/conftest.py b/tests/conftest.py similarity index 100% rename from test/conftest.py rename to tests/conftest.py diff --git a/test/html/jsconfirm.html b/tests/html/jsconfirm.html similarity index 100% rename from test/html/jsconfirm.html rename to tests/html/jsconfirm.html diff --git a/test/html/jsprompt.html b/tests/html/jsprompt.html similarity index 100% rename from test/html/jsprompt.html rename to tests/html/jsprompt.html diff --git a/test/keyinput/test_basekeyparser.py b/tests/keyinput/test_basekeyparser.py similarity index 100% rename from test/keyinput/test_basekeyparser.py rename to tests/keyinput/test_basekeyparser.py diff --git a/test/keyinput/test_modeparsers.py b/tests/keyinput/test_modeparsers.py similarity index 100% rename from test/keyinput/test_modeparsers.py rename to tests/keyinput/test_modeparsers.py diff --git a/test/log.py b/tests/log.py similarity index 100% rename from test/log.py rename to tests/log.py diff --git a/test/mainwindow/conftest.py b/tests/mainwindow/conftest.py similarity index 100% rename from test/mainwindow/conftest.py rename to tests/mainwindow/conftest.py diff --git a/test/mainwindow/statusbar/test_percentage.py b/tests/mainwindow/statusbar/test_percentage.py similarity index 100% rename from test/mainwindow/statusbar/test_percentage.py rename to tests/mainwindow/statusbar/test_percentage.py diff --git a/test/mainwindow/statusbar/test_progress.py b/tests/mainwindow/statusbar/test_progress.py similarity index 100% rename from test/mainwindow/statusbar/test_progress.py rename to tests/mainwindow/statusbar/test_progress.py diff --git a/test/mainwindow/statusbar/test_textbase.py b/tests/mainwindow/statusbar/test_textbase.py similarity index 100% rename from test/mainwindow/statusbar/test_textbase.py rename to tests/mainwindow/statusbar/test_textbase.py diff --git a/test/misc/test_crashdialog.py b/tests/misc/test_crashdialog.py similarity index 100% rename from test/misc/test_crashdialog.py rename to tests/misc/test_crashdialog.py diff --git a/test/misc/test_editor.py b/tests/misc/test_editor.py similarity index 100% rename from test/misc/test_editor.py rename to tests/misc/test_editor.py diff --git a/test/misc/test_lineparser.py b/tests/misc/test_lineparser.py similarity index 100% rename from test/misc/test_lineparser.py rename to tests/misc/test_lineparser.py diff --git a/test/misc/test_readline.py b/tests/misc/test_readline.py similarity index 100% rename from test/misc/test_readline.py rename to tests/misc/test_readline.py diff --git a/test/misc/test_split.py b/tests/misc/test_split.py similarity index 100% rename from test/misc/test_split.py rename to tests/misc/test_split.py diff --git a/test/stubs.py b/tests/stubs.py similarity index 100% rename from test/stubs.py rename to tests/stubs.py diff --git a/test/test_stubs.py b/tests/test_stubs.py similarity index 100% rename from test/test_stubs.py rename to tests/test_stubs.py diff --git a/test/utils/debug/test_log_time.py b/tests/utils/debug/test_log_time.py similarity index 100% rename from test/utils/debug/test_log_time.py rename to tests/utils/debug/test_log_time.py diff --git a/test/utils/debug/test_qenum_key.py b/tests/utils/debug/test_qenum_key.py similarity index 100% rename from test/utils/debug/test_qenum_key.py rename to tests/utils/debug/test_qenum_key.py diff --git a/test/utils/debug/test_qflags_key.py b/tests/utils/debug/test_qflags_key.py similarity index 100% rename from test/utils/debug/test_qflags_key.py rename to tests/utils/debug/test_qflags_key.py diff --git a/test/utils/debug/test_signal.py b/tests/utils/debug/test_signal.py similarity index 100% rename from test/utils/debug/test_signal.py rename to tests/utils/debug/test_signal.py diff --git a/test/utils/test_jinja.py b/tests/utils/test_jinja.py similarity index 100% rename from test/utils/test_jinja.py rename to tests/utils/test_jinja.py diff --git a/test/utils/test_log.py b/tests/utils/test_log.py similarity index 100% rename from test/utils/test_log.py rename to tests/utils/test_log.py diff --git a/test/utils/test_qtutils.py b/tests/utils/test_qtutils.py similarity index 100% rename from test/utils/test_qtutils.py rename to tests/utils/test_qtutils.py diff --git a/test/utils/test_standarddir.py b/tests/utils/test_standarddir.py similarity index 100% rename from test/utils/test_standarddir.py rename to tests/utils/test_standarddir.py diff --git a/test/utils/test_urlutils.py b/tests/utils/test_urlutils.py similarity index 100% rename from test/utils/test_urlutils.py rename to tests/utils/test_urlutils.py diff --git a/test/utils/test_utils.py b/tests/utils/test_utils.py similarity index 100% rename from test/utils/test_utils.py rename to tests/utils/test_utils.py diff --git a/test/utils/usertypes/test_enum.py b/tests/utils/usertypes/test_enum.py similarity index 100% rename from test/utils/usertypes/test_enum.py rename to tests/utils/usertypes/test_enum.py diff --git a/test/utils/usertypes/test_neighborlist.py b/tests/utils/usertypes/test_neighborlist.py similarity index 100% rename from test/utils/usertypes/test_neighborlist.py rename to tests/utils/usertypes/test_neighborlist.py From 967c706bf01564c6b48e8f94b2f780eae1b23860 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 5 Apr 2015 12:29:18 -0300 Subject: [PATCH 32/47] Removed xfail from test since issue has been fixed on master --- tests/config/test_config.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index eda492a26..dcb7d5616 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -161,9 +161,6 @@ class TestDefaultConfig: """Test validating of the default config.""" - @pytest.mark.xfail(reason='fails if run with other tests in this module, ' - 'succeeds if executed alone (The-Compiler, help)' - ) def test_default_config(self): """Test validating of the default config.""" conf = config.ConfigManager(None, None) From eb76cd71de32202cc541604f8d45cd333d8dab23 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 5 Apr 2015 15:50:24 +0200 Subject: [PATCH 33/47] Fix testfile path in MANIFEST.in. --- MANIFEST.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index d619c0702..07e7dc278 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,7 +6,7 @@ graft icons graft scripts/pylint_checkers graft doc/img graft misc -include qutebrowser/test/testfile +include qutebrowser/utils/testfile include qutebrowser/git-commit-id include COPYING doc/* README.asciidoc include qutebrowser.desktop @@ -24,7 +24,7 @@ exclude scripts/segfault_test.sh exclude doc/notes exclude CONTRIBUTING.asciidoc recursive-exclude doc *.asciidoc -prune test +prune tests exclude qutebrowser.rcc exclude .coveragerc exclude .flake8 From d3a92d505cb94c6df4db6139d1cb943ab7caf68c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 5 Apr 2015 18:44:01 +0200 Subject: [PATCH 34/47] Make lints run with adjusted test folder location. For pylint we need a custom script; see https://bitbucket.org/logilab/pylint/issue/512/ --- scripts/run_pylint_on_tests.py | 60 ++++++++++++++++++++++++++++++++++ tox.ini | 9 ++--- 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 scripts/run_pylint_on_tests.py diff --git a/scripts/run_pylint_on_tests.py b/scripts/run_pylint_on_tests.py new file mode 100644 index 000000000..94ba89c7d --- /dev/null +++ b/scripts/run_pylint_on_tests.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) + +# 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 . + +"""Run pylint on tests. + +This is needed because pylint can't check a folder which isn't a package: +https://bitbucket.org/logilab/pylint/issue/512/ +""" + +import os +import sys +import os.path +import subprocess + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) + +from scripts import utils + + +def main(): + """Main entry point. + + Return: + The pylint exit status. + """ + utils.change_cwd() + files = [] + for dirpath, _dirnames, filenames in os.walk('tests'): + for fn in filenames: + if os.path.splitext(fn)[1] == '.py': + files.append(os.path.join(dirpath, fn)) + disabled = ['attribute-defined-outside-init', 'redefined-outer-name', + 'unused-argument'] + no_docstring_rgx = ['^__.*__$', '^setup$'] + args = (['--disable={}'.format(','.join(disabled)), + '--no-docstring-rgx=({})'.format('|'.join(no_docstring_rgx))] + + sys.argv[1:] + files) + ret = subprocess.call(['pylint'] + args) + return ret + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tox.ini b/tox.ini index 8b65b42a2..7bbe8ff7b 100644 --- a/tox.ini +++ b/tox.ini @@ -44,8 +44,8 @@ commands = [testenv:misc] commands = {envpython} scripts/misc_checks.py git - {envpython} scripts/misc_checks.py vcs qutebrowser scripts - {envpython} scripts/misc_checks.py spelling qutebrowser scripts + {envpython} scripts/misc_checks.py vcs qutebrowser scripts tests + {envpython} scripts/misc_checks.py spelling qutebrowser scripts tests [testenv:pylint] skip_install = true @@ -60,6 +60,7 @@ deps = commands = {[testenv:mkvenv]commands} {envdir}/bin/pylint scripts qutebrowser --rcfile=.pylintrc --output-format=colorized --reports=no + {envpython} scripts/run_pylint_on_tests.py --rcfile=.pylintrc --output-format=colorized --reports=no [testenv:pep257] skip_install = true @@ -68,7 +69,7 @@ deps = pep257==0.5.0 # D102: Docstring missing, will be handled by others # D209: Blank line before closing """ (removed from PEP257) # D402: First line should not be function's signature (false-positives) -commands = {envpython} -m pep257 scripts qutebrowser --ignore=D102,D209,D402 '--match=(?!resources|test_content_disposition).*\.py' +commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D209,D402 '--match=(?!resources|test_content_disposition).*\.py' [testenv:flake8] skip_install = true @@ -79,7 +80,7 @@ deps = flake8==2.4.0 commands = {[testenv:mkvenv]commands} - {envdir}/bin/flake8 scripts qutebrowser --config=.flake8 + {envdir}/bin/flake8 scripts tests qutebrowser --config=.flake8 [testenv:pyroma] skip_install = true From 9d44f777c04a7ab9f5ae510cd35a12c436b0ed7b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 5 Apr 2015 20:30:31 +0200 Subject: [PATCH 35/47] Fix lint. --- .pylintrc | 1 + scripts/misc_checks.py | 2 +- .../browser/http/test_content_disposition.py | 14 +- tests/browser/http/test_http.py | 1 - tests/browser/test_tabhistory.py | 33 +++-- tests/browser/test_webelem.py | 3 +- tests/config/test_config.py | 14 +- tests/config/test_configtypes.py | 139 ++++++++++++------ tests/conftest.py | 19 +-- tests/keyinput/test_basekeyparser.py | 12 +- tests/keyinput/test_modeparsers.py | 1 - tests/mainwindow/conftest.py | 28 +++- tests/mainwindow/statusbar/test_percentage.py | 4 +- tests/mainwindow/statusbar/test_progress.py | 18 +-- tests/mainwindow/statusbar/test_textbase.py | 17 +-- tests/misc/test_editor.py | 7 +- tests/misc/test_readline.py | 8 +- tests/stubs.py | 4 +- tests/test_stubs.py | 2 - tests/utils/debug/test_qenum_key.py | 1 - tests/utils/test_standarddir.py | 21 +-- tests/utils/test_urlutils.py | 34 ++--- tests/utils/test_utils.py | 1 - tox.ini | 2 +- 24 files changed, 219 insertions(+), 167 deletions(-) diff --git a/.pylintrc b/.pylintrc index daa68e14a..2cc56909d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -40,6 +40,7 @@ argument-rgx=[a-z_][a-z0-9_]{0,30}$ variable-rgx=[a-z_][a-z0-9_]{0,30}$ class-attribute-rgx=[A-Za-z_][A-Za-z0-9_]{1,30}$ inlinevar-rgx=[a-z_][a-z0-9_]*$ +docstring-min-length=2 [FORMAT] max-line-length=79 diff --git a/scripts/misc_checks.py b/scripts/misc_checks.py index bea0a4628..2ecefc199 100644 --- a/scripts/misc_checks.py +++ b/scripts/misc_checks.py @@ -76,7 +76,7 @@ def check_spelling(target): # Words which look better when splitted, but might need some fine tuning. words |= {'keystrings', 'webelements', 'mouseevent', 'keysequence', 'normalmode', 'eventloops', 'sizehint', 'statemachine', - 'metaobject', 'logrecord', 'monkeypatch', 'filetype'} + 'metaobject', 'logrecord', 'filetype'} seen = collections.defaultdict(list) try: diff --git a/tests/browser/http/test_content_disposition.py b/tests/browser/http/test_content_disposition.py index ce974c752..ae4b70b8e 100644 --- a/tests/browser/http/test_content_disposition.py +++ b/tests/browser/http/test_content_disposition.py @@ -105,8 +105,8 @@ class TestInline: Some UAs use this filename in a subsequent "save" operation. """ - header_checker.check_filename('inline; filename="foo.html"', 'foo.html', - expected_inline=True) + header_checker.check_filename('inline; filename="foo.html"', + 'foo.html', expected_inline=True) def test_inlwithfnattach(self, header_checker): """'inline', specifying a filename of "Not an attachment!". @@ -851,7 +851,7 @@ class TestEncodingFallback: "filename=\"foo-ae.html\"", 'foo-ä.html') def test_attfnboth3(self, header_checker): - """'attachment', specifying an ambigious filename. + """'attachment', specifying an ambiguous filename. currency-sign=¤ in the simple RFC2231/5987 format, and euro-sign=€ in RFC2231-with-continuations format. @@ -859,9 +859,9 @@ class TestEncodingFallback: A UA that supports could pick either, or ignore both because of the ambiguity. """ - header_checker.check_ignored("attachment; " - "filename*0*=ISO-8859-15''euro-sign%3d%a4; " - "filename*=ISO-8859-1''currency-sign%3d%a4") + header_checker.check_ignored( + "attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; " + "filename*=ISO-8859-1''currency-sign%3d%a4") def test_attnewandfn(self, header_checker): """'attachment', specifying a new parameter "foobar". @@ -918,5 +918,3 @@ class TestOur: """'attachment' with double space in the filename.""" header_checker.check_filename('attachment; filename="foo bar.html"', 'foo bar.html') - - diff --git a/tests/browser/http/test_http.py b/tests/browser/http/test_http.py index 152f94ea6..38ef1b42b 100644 --- a/tests/browser/http/test_http.py +++ b/tests/browser/http/test_http.py @@ -59,4 +59,3 @@ class TestParseContentType: mimetype, rest = http.parse_content_type(reply) assert mimetype == 'image/example' assert rest == ' encoding=UTF-8' - diff --git a/tests/browser/test_tabhistory.py b/tests/browser/test_tabhistory.py index 3b7f7b170..dcebe94f4 100644 --- a/tests/browser/test_tabhistory.py +++ b/tests/browser/test_tabhistory.py @@ -40,18 +40,20 @@ class TestSerializeHistory: QUrl('http://original.url.example.com/'), 'arg', user_data={'foo': 23, 'bar': 42}), # From https://github.com/OtterBrowser/otter-browser/issues/709#issuecomment-74749471 - Item(QUrl( - 'http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), - QUrl( - 'http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'), - 'Page not found | github', - user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}), - Item(QUrl( - 'https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), - QUrl( - 'https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'), - '"some label" - email@gmail.com - Gmail"', - user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}), + Item( + QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/' + 'issues/709/'), + QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/' + 'issues/709/'), + 'Page not found | github', + user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}), + Item( + QUrl('https://mail.google.com/mail/u/0/#label/some+label/' + '234lkjsd0932lkjf884jqwerdf4'), + QUrl('https://mail.google.com/mail/u/0/#label/some+label/' + '234lkjsd0932lkjf884jqwerdf4'), + '"some label" - email@gmail.com - Gmail"', + user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}), ] @pytest.fixture(autouse=True) @@ -104,10 +106,11 @@ class TestSerializeHistory: class TestSerializeHistorySpecial: - """Tests for serialize() without items set up in setUp.""" + """Tests for serialize() without items set up in setup.""" @pytest.fixture(autouse=True) - def setUp(self, webpage): + def setup(self, webpage): + """Set up the initial QWebPage for each test.""" self.page = webpage self.history = self.page.history() assert self.history.count() == 0 @@ -134,5 +137,3 @@ class TestSerializeHistorySpecial: assert self.history.count() == 0 assert self.history.currentItemIndex() == 0 assert not user_data - - diff --git a/tests/browser/test_webelem.py b/tests/browser/test_webelem.py index 2f931444c..33de396db 100644 --- a/tests/browser/test_webelem.py +++ b/tests/browser/test_webelem.py @@ -379,6 +379,7 @@ class TestIsEditable: @pytest.yield_fixture def stub_config(self, stubs): + """Fixture to create a config stub with an input section.""" config = stubs.ConfigStub({'input': {}}) with mock.patch('qutebrowser.browser.webelem.config', new=config): yield config @@ -557,5 +558,3 @@ class TestIsEditable: """Test div-element with codemirror class.""" elem = get_webelem(tagname='div', classes='foo CodeMirror-foo') assert elem.is_editable() - - diff --git a/tests/config/test_config.py b/tests/config/test_config.py index dcb7d5616..6bee98ee4 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -66,21 +66,19 @@ class TestConfigParser: def test_transformed_option_old(self): """Test a transformed option with the old name.""" - # WORKAROUND for unknown PyQt bug - # Instance of 'str' has no 'name' member self.cp.read_dict({'colors': {'tab.fg.odd': 'pink'}}) self.cfg._from_cp(self.cp) - assert self.cfg.get('colors', 'tabs.fg.odd').name() == \ - QColor('pink').name() + actual = self.cfg.get('colors', 'tabs.fg.odd').name() + expected = QColor('pink').name() + assert actual == expected def test_transformed_option_new(self): """Test a transformed section with the new name.""" - # WORKAROUND for unknown PyQt bug - # Instance of 'str' has no 'name' member self.cp.read_dict({'colors': {'tabs.fg.odd': 'pink'}}) self.cfg._from_cp(self.cp) - assert self.cfg.get('colors', 'tabs.fg.odd').name() == \ - QColor('pink').name() + actual = self.cfg.get('colors', 'tabs.fg.odd').name() + expected = QColor('pink').name() + assert actual == expected def test_invalid_value(self): """Test setting an invalid value.""" diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 009133af2..90208244b 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -34,6 +34,7 @@ from qutebrowser.utils import debug, utils class Font(QFont): + """A QFont with a nicer repr().""" def __repr__(self): @@ -59,6 +60,7 @@ class Font(QFont): class NetworkProxy(QNetworkProxy): + """A QNetworkProxy with a nicer repr().""" def __repr__(self): @@ -69,11 +71,13 @@ class NetworkProxy(QNetworkProxy): @pytest.fixture def os_path(mocker): - """Fixture that mocks and returns os.path from the configtypes module""" - return mocker.patch('qutebrowser.config.configtypes.os.path', autospec=True) + """Fixture that mocks and returns os.path from the configtypes module.""" + return mocker.patch('qutebrowser.config.configtypes.os.path', + autospec=True) class TestValidValues: + """Test ValidValues.""" def test_contains_without_desc(self): @@ -121,6 +125,7 @@ class TestValidValues: class TestBaseType: + """Test BaseType.""" @pytest.fixture(autouse=True) @@ -175,6 +180,7 @@ class TestBaseType: class TestString: + """Test String.""" def test_minlen_toosmall(self): @@ -269,6 +275,7 @@ class TestString: class TestList: + """Test List.""" @pytest.fixture(autouse=True) @@ -318,6 +325,7 @@ class TestList: class TestBool: + """Test Bool.""" TESTS = {True: ['1', 'yes', 'YES', 'true', 'TrUe', 'on'], @@ -367,6 +375,7 @@ class TestBool: class TestInt: + """Test Int.""" def test_minval_gt_maxval(self): @@ -444,6 +453,7 @@ class TestInt: class TestIntList: + """Test IntList.""" @pytest.fixture(autouse=True) @@ -483,6 +493,7 @@ class TestIntList: class TestFloat: + """Test Float.""" def test_minval_gt_maxval(self): @@ -570,6 +581,7 @@ class TestFloat: class TestPerc: + """Test Perc.""" @pytest.fixture(autouse=True) @@ -651,6 +663,7 @@ class TestPerc: class TestPercList: + """Test PercList.""" @pytest.fixture(autouse=True) @@ -731,6 +744,7 @@ class TestPercList: class TestPercOrInt: + """Test PercOrInt.""" @pytest.fixture(autouse=True) @@ -868,6 +882,7 @@ class TestPercOrInt: class TestCommand: + """Test Command.""" @pytest.fixture(autouse=True) @@ -924,8 +939,12 @@ class TestCommand: class TestColorSystem: + """Test ColorSystem.""" + # https://bitbucket.org/logilab/pylint/issue/511/ + # pylint: disable=undefined-variable + TESTS = { 'RGB': QColor.Rgb, 'rgb': QColor.Rgb, @@ -972,6 +991,7 @@ class TestColorSystem: class TestQtColor: + """Test QtColor.""" VALID = ['#123', '#112233', '#111222333', '#111122223333', 'red'] @@ -1014,6 +1034,7 @@ class TestQtColor: class TestCssColor(TestQtColor): + """Test CssColor.""" VALID = TestQtColor.VALID + ['-foobar(42)'] @@ -1039,6 +1060,7 @@ class TestCssColor(TestQtColor): class TestQssColor(TestQtColor): + """Test QssColor.""" VALID = TestQtColor.VALID + [ @@ -1084,8 +1106,12 @@ FontDesc = collections.namedtuple('FontDesc', class TestFont: + """Test Font/QtFont.""" + # https://bitbucket.org/logilab/pylint/issue/511/ + # pylint: disable=undefined-variable + TESTS = { # (style, weight, pointsize, pixelsize, family '"Foobar Neue"': @@ -1144,7 +1170,8 @@ class TestFont: t.validate('') t2.validate('') - @pytest.mark.parametrize('val, attr', itertools.product(TESTS, ['t', 't2'])) + @pytest.mark.parametrize('val, attr', + itertools.product(TESTS, ['t', 't2'])) def test_validate_valid(self, val, attr): """Test validate with valid values.""" getattr(self, attr).validate(val) @@ -1183,6 +1210,7 @@ class TestFont: class TestFontFamily: + """Test FontFamily.""" TESTS = ['"Foobar Neue"', 'inconsolatazi4', 'Foobar'] @@ -1234,6 +1262,7 @@ class TestFontFamily: class TestRegex: + """Test Regex.""" @pytest.fixture(autouse=True) @@ -1269,6 +1298,7 @@ class TestRegex: class TestRegexList: + """Test RegexList.""" @pytest.fixture(autouse=True) @@ -1310,13 +1340,13 @@ class TestRegexList: class TestFile: + """Test File.""" @pytest.fixture(autouse=True) def setup(self): self.t = configtypes.File() - def test_validate_empty(self): """Test validate with empty string and none_ok = False.""" with pytest.raises(configexc.ValidationError): @@ -1376,6 +1406,7 @@ class TestFile: class TestDirectory: + """Test Directory.""" @pytest.fixture(autouse=True) @@ -1454,6 +1485,7 @@ class TestDirectory: class TestWebKitByte: + """Test WebKitBytes.""" @pytest.fixture(autouse=True) @@ -1528,6 +1560,7 @@ class TestWebKitByte: class TestWebKitBytesList: + """Test WebKitBytesList.""" @pytest.fixture(autouse=True) @@ -1599,6 +1632,7 @@ class TestWebKitBytesList: class TestShellCommand: + """Test ShellCommand.""" @pytest.fixture(autouse=True) @@ -1649,6 +1683,7 @@ class TestShellCommand: class TestProxy: + """Test Proxy.""" @pytest.fixture(autouse=True) @@ -1697,11 +1732,12 @@ class TestProxy: def test_complete(self): """Test complete.""" - assert self.t.complete() == \ - [('system', "Use the system wide proxy."), - ('none', "Don't use any proxy"), - ('http://', 'HTTP proxy URL'), - ('socks://', 'SOCKS proxy URL')] + actual = self.t.complete() + expected = [('system', "Use the system wide proxy."), + ('none', "Don't use any proxy"), + ('http://', 'HTTP proxy URL'), + ('socks://', 'SOCKS proxy URL')] + assert actual == expected def test_transform_empty(self): """Test transform with an empty value.""" @@ -1713,51 +1749,53 @@ class TestProxy: def test_transform_none(self): """Test transform with no proxy.""" - assert NetworkProxy(self.t.transform('none')) == \ - NetworkProxy(QNetworkProxy.NoProxy) + actual = NetworkProxy(self.t.transform('none')) + expected = NetworkProxy(QNetworkProxy.NoProxy) + assert actual == expected def test_transform_socks(self): """Test transform with a socks proxy.""" - proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') - val = NetworkProxy(self.t.transform('socks://example.com/')) - assert proxy == val + actual = NetworkProxy(self.t.transform('socks://example.com/')) + expected = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') + assert actual == expected def test_transform_socks5(self): """Test transform with a socks5 proxy.""" - proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') - val = NetworkProxy(self.t.transform('socks5://example.com')) - assert proxy == val + actual = NetworkProxy(self.t.transform('socks5://example.com')) + expected = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com') + assert actual == expected def test_transform_http_port(self): """Test transform with a http proxy with set port.""" - proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2342) - val = NetworkProxy(self.t.transform('socks5://example.com:2342')) - assert proxy == val + actual = NetworkProxy(self.t.transform('socks5://example.com:2342')) + expected = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2342) + assert actual == expected def test_transform_socks_user(self): """Test transform with a socks proxy with set user.""" - proxy = NetworkProxy( + actual = NetworkProxy(self.t.transform('socks5://foo@example.com')) + expected = NetworkProxy( QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo') - val = NetworkProxy(self.t.transform('socks5://foo@example.com')) - assert proxy == val + assert actual == expected def test_transform_socks_user_password(self): """Test transform with a socks proxy with set user/password.""" - proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, - 'foo', 'bar') - val = NetworkProxy(self.t.transform('socks5://foo:bar@example.com')) - assert proxy == val + actual = NetworkProxy(self.t.transform('socks5://foo:bar@example.com')) + expected = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, + 'foo', 'bar') + assert actual == expected def test_transform_socks_user_password_port(self): """Test transform with a socks proxy with set port/user/password.""" - proxy = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323, - 'foo', 'bar') - val = NetworkProxy( + actual = NetworkProxy( self.t.transform('socks5://foo:bar@example.com:2323')) - assert proxy == val + expected = NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323, + 'foo', 'bar') + assert actual == expected class TestSearchEngineName: + """Test SearchEngineName.""" @pytest.fixture(autouse=True) @@ -1784,6 +1822,7 @@ class TestSearchEngineName: class TestSearchEngineUrl: + """Test SearchEngineUrl.""" @pytest.fixture(autouse=True) @@ -1824,6 +1863,7 @@ class TestSearchEngineUrl: class TestFuzzyUrl: + """Test FuzzyUrl.""" @pytest.fixture(autouse=True) @@ -1868,6 +1908,7 @@ class TestFuzzyUrl: class TestUserStyleSheet: + """Test UserStyleSheet.""" @pytest.fixture(autouse=True) @@ -1906,8 +1947,12 @@ class TestUserStyleSheet: class TestAutoSearch: + """Test AutoSearch.""" + # https://bitbucket.org/logilab/pylint/issue/511/ + # pylint: disable=undefined-variable + TESTS = { 'naive': ['naive', 'NAIVE'] + TestBool.TESTS[True], 'dns': ['dns', 'DNS'], @@ -1929,9 +1974,8 @@ class TestAutoSearch: t = configtypes.AutoSearch(none_ok=True) t.validate('') - @pytest.mark.parametrize('val', - [val for vallist in TESTS.values() for val in - vallist]) + @pytest.mark.parametrize('val', [val for vallist in TESTS.values() + for val in vallist]) def test_validate_valid(self, val): """Test validate with valid values.""" self.t.validate(val) @@ -1943,9 +1987,8 @@ class TestAutoSearch: self.t.validate(val) @pytest.mark.parametrize('out, inp', - [(out, inp) for (out, inputs) in TESTS.items() for - inp in inputs] - ) + [(out, inp) for (out, inputs) in TESTS.items() + for inp in inputs]) def test_transform(self, out, inp): """Test transform with all values.""" assert self.t.transform(inp) == out @@ -1956,8 +1999,12 @@ class TestAutoSearch: class TestIgnoreCase: + """Test IgnoreCase.""" + # https://bitbucket.org/logilab/pylint/issue/511/ + # pylint: disable=undefined-variable + TESTS = { 'smart': ['smart', 'SMART'], True: TestBool.TESTS[True], @@ -1993,9 +2040,8 @@ class TestIgnoreCase: self.t.validate(val) @pytest.mark.parametrize('out, inp', - [(out, inp) for (out, inputs) in TESTS.items() for - inp in inputs] - ) + [(out, inp) for (out, inputs) in TESTS.items() + for inp in inputs]) def test_transform(self, out, inp): """Test transform with all values.""" assert self.t.transform(inp) == out @@ -2006,6 +2052,7 @@ class TestIgnoreCase: class TestEncoding: + """Test Encoding.""" @pytest.fixture(autouse=True) @@ -2042,6 +2089,7 @@ class TestEncoding: class TestUrlList: + """Test UrlList.""" @pytest.fixture(autouse=True) @@ -2079,14 +2127,15 @@ class TestUrlList: def test_transform_single(self): """Test transform with a single value.""" + actual = self.t.transform('http://qutebrowser.org/') expected = [QUrl('http://qutebrowser.org/')] - assert self.t.transform('http://qutebrowser.org/') == expected + assert actual == expected def test_transform_more(self): """Test transform with multiple values.""" - assert ( - self.t.transform('http://qutebrowser.org/,http://heise.de/') == - [QUrl('http://qutebrowser.org/'), QUrl('http://heise.de/')]) + actual = self.t.transform('http://qutebrowser.org/,http://heise.de/') + expected = [QUrl('http://qutebrowser.org/'), QUrl('http://heise.de/')] + assert actual == expected def test_transform_empty(self): """Test transform with an empty value.""" @@ -2094,6 +2143,7 @@ class TestUrlList: class TestFormatString: + """Test FormatString.""" @pytest.fixture(autouse=True) @@ -2134,6 +2184,7 @@ class TestFormatString: class TestUserAgent: + """Test UserAgent.""" @pytest.fixture(autouse=True) diff --git a/tests/conftest.py b/tests/conftest.py index 33bb99923..d3411694c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,27 +24,24 @@ import pytest @pytest.fixture(scope='session', autouse=True) def app_and_logging(qapp): - """ - Initializes our logging system and ensures that a QApplication is created - and used by all tests. + """Initialize a QApplication and logging. + + This ensures that a QApplication is created and used by all tests. """ from log import init - init() @pytest.fixture(scope='session') def stubs(): - """ - Provides access to stub objects useful for testing. - """ + """Provide access to stub objects useful for testing.""" import stubs - return stubs @pytest.fixture(scope='session') def unicode_encode_err(): + """Provide a fake UnicodeEncodeError exception.""" return UnicodeEncodeError('ascii', # codec '', # object 0, # start @@ -66,9 +63,7 @@ def webpage(): @pytest.fixture def fake_keyevent_factory(): - """ - Fixture that when called will return a mock instance of a QKeyEvent. - """ + """Fixture that when called will return a mock instance of a QKeyEvent.""" from unittest import mock from PyQt5.QtGui import QKeyEvent @@ -80,4 +75,4 @@ def fake_keyevent_factory(): evtmock.text.return_value = text return evtmock - return fake_keyevent \ No newline at end of file + return fake_keyevent diff --git a/tests/keyinput/test_basekeyparser.py b/tests/keyinput/test_basekeyparser.py index d28aceda2..7164bffbd 100644 --- a/tests/keyinput/test_basekeyparser.py +++ b/tests/keyinput/test_basekeyparser.py @@ -44,9 +44,7 @@ BINDINGS = {'test': {'': 'ctrla', @pytest.yield_fixture def fake_keyconfig(): - """ - Creates a mock of a KeyConfiguration and registers it into objreg. - """ + """Create a mock of a KeyConfiguration and register it into objreg.""" fake_keyconfig = mock.Mock(spec=['get_bindings_for']) fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] objreg.register('key-config', fake_keyconfig) @@ -56,9 +54,7 @@ def fake_keyconfig(): @pytest.fixture def mock_timer(mocker, stubs): - """Mocks the QTimer class used by the - "qutebrowser.keyinput.basekeyparser.usertypes" module with a stub version. - """ + """Mock the Timer class used by the usertypes module with a stub.""" mocker.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer', new=stubs.FakeTimer) @@ -211,12 +207,12 @@ class TestKeyChain: assert self.kp._keystring == '' def test_ambiguous_keychain(self, fake_keyevent_factory, mocker, stubs): - """Test ambigious keychain.""" + """Test ambiguous keychain.""" mocker.patch('qutebrowser.keyinput.basekeyparser.config', new=stubs.ConfigStub(CONFIG)) timer = self.kp._ambiguous_timer assert not timer.isActive() - # We start with 'a' where the keychain gives us an ambigious result. + # We start with 'a' where the keychain gives us an ambiguous result. # Then we check if the timer has been set up correctly self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a')) assert not self.kp.execute.called diff --git a/tests/keyinput/test_modeparsers.py b/tests/keyinput/test_modeparsers.py index c59bfe163..e177ad5a3 100644 --- a/tests/keyinput/test_modeparsers.py +++ b/tests/keyinput/test_modeparsers.py @@ -92,4 +92,3 @@ class TestsNormalKeyParser: assert not self.kp.execute.called assert self.kp._keystring == '' keystring_updated_mock.assert_called_once_with('') - diff --git a/tests/mainwindow/conftest.py b/tests/mainwindow/conftest.py index bfa84d337..47dcc5883 100644 --- a/tests/mainwindow/conftest.py +++ b/tests/mainwindow/conftest.py @@ -1,5 +1,23 @@ -""" -pytest fixtures and utilities for testing. +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + +"""pytest fixtures and utilities for testing. Fixtures defined here will be visible to all test files in this directory and below. @@ -16,8 +34,8 @@ def default_config(): """ Fixture that registers an empty config object into the objreg module. - Should be used by tests which create widgets that obtain their initial state - from the global config object. + Should be used by tests which create widgets that obtain their initial + state from the global config object. Note: @@ -31,4 +49,4 @@ def default_config(): config_obj = ConfigManager(configdir=None, fname=None, relaxed=True) objreg.register('config', config_obj) yield config_obj - objreg.delete('config') \ No newline at end of file + objreg.delete('config') diff --git a/tests/mainwindow/statusbar/test_percentage.py b/tests/mainwindow/statusbar/test_percentage.py index de1dbe03e..80de6b66f 100644 --- a/tests/mainwindow/statusbar/test_percentage.py +++ b/tests/mainwindow/statusbar/test_percentage.py @@ -19,6 +19,7 @@ """Test Percentage widget.""" + import pytest from qutebrowser.mainwindow.statusbar.percentage import Percentage @@ -37,7 +38,8 @@ def test_percentage_text(qtbot, y, expected): Args: qtbot: pytestqt.plugin.QtBot fixture - y: y position of the page as an int in the range [0, 100]. parametrized. + y: y position of the page as an int in the range [0, 100]. + parametrized. expected: expected text given y position. parametrized. """ percentage = Percentage() diff --git a/tests/mainwindow/statusbar/test_progress.py b/tests/mainwindow/statusbar/test_progress.py index 4a4038701..a3a9f1797 100644 --- a/tests/mainwindow/statusbar/test_progress.py +++ b/tests/mainwindow/statusbar/test_progress.py @@ -30,9 +30,7 @@ from qutebrowser.mainwindow.statusbar.progress import Progress @pytest.fixture def progress_widget(qtbot, default_config): - """ - Creates a Progress widget and checks it initial state. - """ + """Create a Progress widget and checks its initial state.""" widget = Progress() qtbot.add_widget(widget) assert not widget.isVisible() @@ -41,7 +39,8 @@ def progress_widget(qtbot, default_config): def test_load_started(progress_widget): - """ + """Ensure the Progress widget reacts properly when the page starts loading. + Args: progress_widget: Progress widget that will be tested. """ @@ -62,13 +61,14 @@ Tab = namedtuple('Tab', 'progress load_status') (Tab(100, webview.LoadStatus.none), False), ]) def test_tab_changed(progress_widget, tab, expected_visible): - """ - Test that progress widget value and visibility state match expectations, - using a dummy Tab object. + """Test that progress widget value and visibility state match expectations. + + This uses a dummy Tab object. Args: progress_widget: Progress widget that will be tested. """ progress_widget.on_tab_changed(tab) - assert (progress_widget.value(), progress_widget.isVisible()) == \ - (tab.progress, expected_visible) \ No newline at end of file + actual = progress_widget.value(), progress_widget.isVisible() + expected = tab.progress, expected_visible + assert actual == expected diff --git a/tests/mainwindow/statusbar/test_textbase.py b/tests/mainwindow/statusbar/test_textbase.py index 1c1082783..0abdf669b 100644 --- a/tests/mainwindow/statusbar/test_textbase.py +++ b/tests/mainwindow/statusbar/test_textbase.py @@ -24,18 +24,15 @@ from qutebrowser.mainwindow.statusbar.textbase import TextBase def test_elided_text(qtbot): - """ - Ensure that a widget that can't hold the entire label text will display - and elided version of the string instead. + """Ensure that a widget too small to hold the entire label text will elide. + + Note: + It is difficult to check what is actually being drawn in a portable + way, so at least we ensure our customized methods are being called and + the elided string contains the horizontal ellipsis character. Args: qtbot: pytestqt.plugin.QtBot fixture - - Note: - - It is difficult to check what is actually being drawn in a - portable way, so at least we ensure our customized methods are being - called and the elided string contains the horizontal ellipsis character. """ label = TextBase() qtbot.add_widget(label) @@ -43,4 +40,4 @@ def test_elided_text(qtbot): label.setText(long_string) label.resize(100, 50) label.show() - assert '…' in label._elided_text + assert '…' in label._elided_text # pylint: disable=protected-access diff --git a/tests/misc/test_editor.py b/tests/misc/test_editor.py index b560291e8..db6280339 100644 --- a/tests/misc/test_editor.py +++ b/tests/misc/test_editor.py @@ -23,7 +23,6 @@ import os import os.path -import unittest import logging from unittest import mock @@ -75,14 +74,15 @@ class TestArg: "bin", ["foo", filename, "bar"]) def test_in_arg_placeholder(self): + """Test starting editor with placeholder argument inside argument.""" self.config.data = {'general': {'editor': ['bin', 'foo{}bar'], 'editor-encoding': 'utf-8'}} - """Test starting editor with placeholder argument inside argument.""" self.editor.edit("") self.editor._proc.start.assert_called_with("bin", ["foo{}bar"]) class TestFileHandling(object): + """Test creation/deletion of tempfile. Attributes: @@ -132,6 +132,7 @@ class TestFileHandling(object): class TestModifyTests(object): + """Tests to test if the text gets saved/loaded correctly. Attributes: @@ -202,6 +203,7 @@ class TestModifyTests(object): class TestErrorMessage: + """Test if statusbar error messages get emitted correctly. Attributes: @@ -233,4 +235,3 @@ class TestErrorMessage: with caplog.atLevel(logging.ERROR, 'message'): self.editor.on_proc_closed(1, QProcess.NormalExit) assert len(caplog.records()) == 3 - diff --git a/tests/misc/test_readline.py b/tests/misc/test_readline.py index e1536edf4..9ace12b48 100644 --- a/tests/misc/test_readline.py +++ b/tests/misc/test_readline.py @@ -32,20 +32,18 @@ from qutebrowser.misc import readline @pytest.fixture def mocked_qapp(mocker, stubs): - """ - Fixture that mocks QApplication in the readline module and returns it. - """ + """Fixture that mocks readline.QApplication and returns it.""" return mocker.patch('qutebrowser.misc.readline.QApplication', new_callable=stubs.FakeQApplication) class TestNoneWidget: - """Tests when the focused widget is None.""" + """Test if there are no exceptions when the widget is None.""" def test_none(self, mocked_qapp): + """Call each rl_* method with a None focusWidget.""" self.bridge = readline.ReadlineBridge() - """Test if there are no exceptions when the widget is None.""" mocked_qapp.focusWidget = mock.Mock(return_value=None) for name, method in inspect.getmembers(self.bridge, inspect.ismethod): if name.startswith('rl_'): diff --git a/tests/stubs.py b/tests/stubs.py index 6345c7c00..95c6833b8 100644 --- a/tests/stubs.py +++ b/tests/stubs.py @@ -20,6 +20,7 @@ # pylint: disable=invalid-name """Fake objects/stubs.""" + import logging from unittest import mock @@ -272,6 +273,7 @@ class FakeTimer(QObject): class MessageModule: + """A drop-in replacement for qutebrowser.utils.message.""" def error(self, _win_id, message, _immediately=False): @@ -284,4 +286,4 @@ class MessageModule: def info(self, _win_id, message, _immediately=True): """Log an info message to the message logger.""" - logging.getLogger('message').info(message) \ No newline at end of file + logging.getLogger('message').info(message) diff --git a/tests/test_stubs.py b/tests/test_stubs.py index 9f340c1fd..aa7816f91 100644 --- a/tests/test_stubs.py +++ b/tests/test_stubs.py @@ -105,5 +105,3 @@ def test_interval(timer): assert timer.interval() == 0 timer.setInterval(1000) assert timer.interval() == 1000 - - diff --git a/tests/utils/debug/test_qenum_key.py b/tests/utils/debug/test_qenum_key.py index c111ff311..c43279a4a 100644 --- a/tests/utils/debug/test_qenum_key.py +++ b/tests/utils/debug/test_qenum_key.py @@ -21,7 +21,6 @@ import pytest -from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QStyle, QFrame from qutebrowser.utils import debug diff --git a/tests/utils/test_standarddir.py b/tests/utils/test_standarddir.py index a27b5b635..e98ab80f9 100644 --- a/tests/utils/test_standarddir.py +++ b/tests/utils/test_standarddir.py @@ -31,9 +31,10 @@ from qutebrowser.utils import standarddir @pytest.yield_fixture(autouse=True) def change_qapp_name(): - """ - Change the name of the QApplication instance for all tests in this module - to "qutebrowser_test". + """Change the name of the QApplication instance. + + This changes the applicationName for all tests in this module to + "qutebrowser_test". """ old_name = QApplication.instance().applicationName() QApplication.instance().setApplicationName('qutebrowser_test') @@ -44,23 +45,23 @@ def change_qapp_name(): @pytest.mark.skipif(not sys.platform.startswith("linux"), reason="requires Linux") class TestGetStandardDirLinux: - """Tests for standarddir under Linux. - """ + + """Tests for standarddir under Linux.""" def test_data_explicit(self, monkeypatch, tmpdir): - """Test data dir with XDG_DATA_HOME explicitely set.""" + """Test data dir with XDG_DATA_HOME explicitly set.""" monkeypatch.setenv('XDG_DATA_HOME', str(tmpdir)) standarddir.init(None) assert standarddir.data() == str(tmpdir / 'qutebrowser_test') def test_config_explicit(self, monkeypatch, tmpdir): - """Test config dir with XDG_CONFIG_HOME explicitely set.""" + """Test config dir with XDG_CONFIG_HOME explicitly set.""" monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir)) standarddir.init(None) assert standarddir.config() == str(tmpdir / 'qutebrowser_test') def test_cache_explicit(self, monkeypatch, tmpdir): - """Test cache dir with XDG_CACHE_HOME explicitely set.""" + """Test cache dir with XDG_CACHE_HOME explicitly set.""" monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir)) standarddir.init(None) assert standarddir.cache() == str(tmpdir / 'qutebrowser_test') @@ -93,8 +94,8 @@ class TestGetStandardDirLinux: @pytest.mark.skipif(not sys.platform.startswith("win"), reason="requires Windows") class TestGetStandardDirWindows: - """Tests for standarddir under Windows. - """ + + """Tests for standarddir under Windows.""" @pytest.fixture(autouse=True) def reset_standarddir(self): diff --git a/tests/utils/test_urlutils.py b/tests/utils/test_urlutils.py index bb5b330e4..501ce2dd1 100644 --- a/tests/utils/test_urlutils.py +++ b/tests/utils/test_urlutils.py @@ -21,9 +21,6 @@ """Tests for qutebrowser.utils.urlutils.""" -import unittest -from unittest import mock - from PyQt5.QtCore import QUrl import pytest @@ -46,6 +43,7 @@ def get_config_stub(auto_search=True): class TestSpecialURL: + """Test is_special_url. Attributes: @@ -78,10 +76,12 @@ class TestSpecialURL: class TestSearchUrl: + """Test _get_search_url.""" @pytest.fixture(autouse=True) def mock_config(self, stubs, mocker): + """Fixture to patch urlutils.config with a stub.""" mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(get_config_stub())) @@ -123,6 +123,7 @@ class TestSearchUrl: class TestIsUrl: + """Tests for is_url. Class attributes: @@ -179,43 +180,42 @@ class TestIsUrl: class TestQurlFromUserInput: + """Tests for qurl_from_user_input.""" def test_url(self): """Test a normal URL.""" - assert ( - urlutils.qurl_from_user_input('qutebrowser.org').toString() - == 'http://qutebrowser.org') + url = urlutils.qurl_from_user_input('qutebrowser.org') + assert url.toString() == 'http://qutebrowser.org' def test_url_http(self): """Test a normal URL with http://.""" - assert ( - urlutils.qurl_from_user_input('http://qutebrowser.org').toString() - == 'http://qutebrowser.org') + url = urlutils.qurl_from_user_input('http://qutebrowser.org') + assert url.toString() == 'http://qutebrowser.org' def test_ipv6_bare(self): """Test an IPv6 without brackets.""" - assert (urlutils.qurl_from_user_input('::1/foo').toString() - == 'http://[::1]/foo') + url = urlutils.qurl_from_user_input('::1/foo') + assert url.toString() == 'http://[::1]/foo' def test_ipv6(self): """Test an IPv6 with brackets.""" - assert (urlutils.qurl_from_user_input('[::1]/foo').toString() == - 'http://[::1]/foo') + url = urlutils.qurl_from_user_input('[::1]/foo') + assert url.toString() == 'http://[::1]/foo' def test_ipv6_http(self): """Test an IPv6 with http:// and brackets.""" - assert ( - urlutils.qurl_from_user_input('http://[::1]').toString() == - 'http://[::1]') + url = urlutils.qurl_from_user_input('http://[::1]') + assert url.toString() == 'http://[::1]' class TestFilenameFromUrl: + """Tests for filename_from_url.""" def test_invalid_url(self): """Test with an invalid QUrl.""" - assert urlutils.filename_from_url(QUrl()) == None + assert urlutils.filename_from_url(QUrl()) is None def test_url_path(self): """Test with an URL with path.""" diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index e82cee184..26dc1cab7 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -478,4 +478,3 @@ class TestNewestSlice: items = range(5) sliced = utils.newest_slice(items, 50) assert list(sliced) == list(items) - diff --git a/tox.ini b/tox.ini index 7bbe8ff7b..9eccaf18e 100644 --- a/tox.ini +++ b/tox.ini @@ -69,7 +69,7 @@ deps = pep257==0.5.0 # D102: Docstring missing, will be handled by others # D209: Blank line before closing """ (removed from PEP257) # D402: First line should not be function's signature (false-positives) -commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D209,D402 '--match=(?!resources|test_content_disposition).*\.py' +commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D103,D209,D402 '--match=(?!resources|test_content_disposition).*\.py' [testenv:flake8] skip_install = true From 75386e405101d983f6a30ae6e9c8a600446ed5a8 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:30:02 -0300 Subject: [PATCH 36/47] Remove "object" subclassing from Test classes missed initially --- tests/misc/test_editor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/misc/test_editor.py b/tests/misc/test_editor.py index db6280339..417253d21 100644 --- a/tests/misc/test_editor.py +++ b/tests/misc/test_editor.py @@ -81,7 +81,7 @@ class TestArg: self.editor._proc.start.assert_called_with("bin", ["foo{}bar"]) -class TestFileHandling(object): +class TestFileHandling: """Test creation/deletion of tempfile. @@ -131,7 +131,7 @@ class TestFileHandling(object): assert not os.path.exists(filename) -class TestModifyTests(object): +class TestModifyTests: """Tests to test if the text gets saved/loaded correctly. From 8702ac8a9861789d318f3d846eb0ee0da87e7043 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:30:29 -0300 Subject: [PATCH 37/47] Fix small docstring issues --- tests/browser/http/test_content_disposition.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/browser/http/test_content_disposition.py b/tests/browser/http/test_content_disposition.py index ae4b70b8e..576998bcf 100644 --- a/tests/browser/http/test_content_disposition.py +++ b/tests/browser/http/test_content_disposition.py @@ -76,7 +76,7 @@ class _HeaderChecker(object): @pytest.fixture def header_checker(caplog, stubs): - """Fixture that provides a _AttachmentChecker class for tests""" + """Fixture that provides a _HeaderChecker class for tests""" return _HeaderChecker(caplog, stubs) @@ -160,7 +160,7 @@ class TestAttachment: """'ATTACHMENT' only UA should offer to download the resource. - """ + """ header_checker.check_unnamed('ATTACHMENT') def test_attwithasciifilename(self, header_checker): From 26dc275db3c350c5e21e39d373c4e98a3a0c2bfb Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:39:34 -0300 Subject: [PATCH 38/47] Compare full lists instead of looping over items as suggested by @hackebrot --- tests/browser/test_tabhistory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/browser/test_tabhistory.py b/tests/browser/test_tabhistory.py index dcebe94f4..6a955a38b 100644 --- a/tests/browser/test_tabhistory.py +++ b/tests/browser/test_tabhistory.py @@ -81,8 +81,8 @@ class TestSerializeHistory: def test_userdata(self): """Check if all user data has been restored to self.user_data.""" - for item, user_data in zip(self.ITEMS, self.user_data): - assert user_data == item.user_data + userdata_items = [item.user_data for item in self.ITEMS] + assert userdata_items == self.user_data def test_currentitem(self): """Check if the current item index was loaded correctly.""" From abc2c2b087eed524f7311a080d89ad7fa2c68c53 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:41:51 -0300 Subject: [PATCH 39/47] Remove spurious dependencies from tox.ini as pointed out by @The-Compiler --- tox.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/tox.ini b/tox.ini index 9eccaf18e..74bade631 100644 --- a/tox.ini +++ b/tox.ini @@ -33,9 +33,6 @@ deps = {[testenv:unittests]deps} coverage==3.7.1 pytest-cov==1.8.1 - pytest-capturelog==0.7 - pytest-qt==1.3.0 - pytest-mock==0.4.2 cov-core==1.15.0 commands = {[testenv:mkvenv]commands} From a29b78e8ca3306840e33fb2e78546de80200870e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:46:02 -0300 Subject: [PATCH 40/47] Use mocker fixture instead of unittest.mock As pointed out by @hackebrot --- tests/browser/test_webelem.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/browser/test_webelem.py b/tests/browser/test_webelem.py index 33de396db..0461b0987 100644 --- a/tests/browser/test_webelem.py +++ b/tests/browser/test_webelem.py @@ -377,12 +377,12 @@ class TestIsEditable: yield webelem.config = old_config - @pytest.yield_fixture - def stub_config(self, stubs): + @pytest.fixture + def stub_config(self, stubs, mocker): """Fixture to create a config stub with an input section.""" config = stubs.ConfigStub({'input': {}}) - with mock.patch('qutebrowser.browser.webelem.config', new=config): - yield config + mocker.patch('qutebrowser.browser.webelem.config', new=config) + return config def test_input_plain(self): """Test with plain input element.""" From 6e3c3d7a70c1f9a28dbc203dc38d8f5e523cc9af Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:49:42 -0300 Subject: [PATCH 41/47] Use single-quoted string for consistency --- tests/config/test_configtypes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 90208244b..80fab1328 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -84,21 +84,21 @@ class TestValidValues: """Test __contains__ without a description.""" vv = configtypes.ValidValues('foo', 'bar') assert 'foo' in vv - assert "baz" not in vv + assert 'baz' not in vv def test_contains_with_desc(self): """Test __contains__ with a description.""" vv = configtypes.ValidValues(('foo', "foo desc"), ('bar', "bar desc")) assert 'foo' in vv assert 'bar' in vv - assert "baz" not in vv + assert 'baz' not in vv def test_contains_mixed_desc(self): """Test __contains__ with mixed description.""" vv = configtypes.ValidValues(('foo', "foo desc"), 'bar') assert 'foo' in vv assert 'bar' in vv - assert "baz" not in vv + assert 'baz' not in vv def test_iter_without_desc(self): """Test __iter__ without a description.""" From fd88311d9be77192cbf14833749dc76e475bba69 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 00:52:19 -0300 Subject: [PATCH 42/47] Use inline list comprehension for parametrize expression --- tests/config/test_configtypes.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 80fab1328..53970954a 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -337,12 +337,9 @@ class TestBool: def setup(self): self.t = configtypes.Bool() - _ALL_FORMS = [] - for out, inputs in TESTS.items(): - for inp in inputs: - _ALL_FORMS.append((out, inp)) - - @pytest.mark.parametrize('out, inp', _ALL_FORMS) + @pytest.mark.parametrize('out, inp', + [(out, inp) for out, inputs in TESTS.items() for + inp in inputs]) def test_transform(self, out, inp): """Test transform with all values.""" assert self.t.transform(inp) == out From fe696aeba50b0e004f2307b6d1202ed52bb6e601 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 8 Apr 2015 06:20:43 +0200 Subject: [PATCH 43/47] Fix string concatenation and indenting for INVALID. --- tests/config/test_configtypes.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 53970954a..7138c6b93 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -1146,9 +1146,14 @@ class TestFont: 'bold italic 10pt "Foobar Neue"': FontDesc(QFont.StyleItalic, QFont.Bold, 10, None, 'Foobar Neue'), } - INVALID = ['green "Foobar Neue"', 'italic green "Foobar Neue"', - 'bold bold "Foobar Neue"', 'bold italic "Foobar Neue"' - 'bold', '10pt 20px "Foobar Neue"'] + + INVALID = [ + 'green "Foobar Neue"', + 'italic green "Foobar Neue"', + 'bold bold "Foobar Neue"', + 'bold italic "Foobar Neue"', + '10pt 20px "Foobar Neue"' + ] @pytest.fixture(autouse=True) def setup(self): From 679ffa452a4158b69b6d8c25aceec72472fb0d12 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 8 Apr 2015 06:22:05 +0200 Subject: [PATCH 44/47] Add some more invalid testcases to TestFont. --- tests/config/test_configtypes.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 7138c6b93..7b356e5aa 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -1152,7 +1152,12 @@ class TestFont: 'italic green "Foobar Neue"', 'bold bold "Foobar Neue"', 'bold italic "Foobar Neue"', - '10pt 20px "Foobar Neue"' + '10pt 20px "Foobar Neue"', + 'bold', + 'italic', + 'green', + '10pt', + '10pt ""', ] @pytest.fixture(autouse=True) From 7d4e6dfd67b493f7e4a3c45f4bb652b35eb6605e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 8 Apr 2015 06:39:12 +0200 Subject: [PATCH 45/47] Another workaround for a pylint bug. --- tests/config/test_configtypes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index 7b356e5aa..0b6ea21b7 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -328,6 +328,9 @@ class TestBool: """Test Bool.""" + # https://bitbucket.org/logilab/pylint/issue/511/ + # pylint: disable=undefined-variable + TESTS = {True: ['1', 'yes', 'YES', 'true', 'TrUe', 'on'], False: ['0', 'no', 'NO', 'false', 'FaLsE', 'off']} From e584aa319f9930047864f06f47d716e6ed8f5f22 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Apr 2015 11:03:42 -0300 Subject: [PATCH 46/47] Using parametrization in test_textbase Also changed the wording a bit as suggested by @The-Compiler Conflicts: tests/mainwindow/statusbar/test_textbase.py --- tests/mainwindow/statusbar/test_textbase.py | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/mainwindow/statusbar/test_textbase.py b/tests/mainwindow/statusbar/test_textbase.py index 0abdf669b..eadf9c46a 100644 --- a/tests/mainwindow/statusbar/test_textbase.py +++ b/tests/mainwindow/statusbar/test_textbase.py @@ -19,25 +19,35 @@ """Test TextBase widget.""" +from PyQt5.QtCore import Qt +import pytest from qutebrowser.mainwindow.statusbar.textbase import TextBase -def test_elided_text(qtbot): +@pytest.mark.parametrize('elidemode, check', [ + (Qt.ElideRight, lambda s: s.endswith('…')), + (Qt.ElideLeft, lambda s: s.startswith('…')), + (Qt.ElideMiddle, lambda s: '…' in s), + (Qt.ElideNone, lambda s: '…' not in s), +]) +def test_elided_text(qtbot, elidemode, check): """Ensure that a widget too small to hold the entire label text will elide. - Note: - It is difficult to check what is actually being drawn in a portable - way, so at least we ensure our customized methods are being called and - the elided string contains the horizontal ellipsis character. + It is difficult to check what is actually being drawn in a portable way, so + at least we ensure our customized methods are being called and the elided + string contains the horizontal ellipsis character. Args: qtbot: pytestqt.plugin.QtBot fixture + elidemode: parametrized elide mode + check: function that receives the elided text and must return True + if the elipsis is placed correctly according to elidemode. """ - label = TextBase() + label = TextBase(elidemode=elidemode) qtbot.add_widget(label) long_string = 'Hello world! ' * 20 label.setText(long_string) label.resize(100, 50) label.show() - assert '…' in label._elided_text # pylint: disable=protected-access + assert check(label._elided_text) # pylint: disable=protected-access From 33dbed5624bc808c51e1e3b478eaaa93129bbb87 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Apr 2015 06:35:41 +0200 Subject: [PATCH 47/47] Update authors. --- README.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.asciidoc b/README.asciidoc index e686ea522..0c0bdad0e 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -134,6 +134,7 @@ Contributors, sorted by the number of commits in descending order: // QUTE_AUTHORS_START * Florian Bruhin +* Bruno Oliveira * Joel Torstensson * Raphael Pierzina * Claude