From 63ce7d6e02330d9de69c8bdc728ca9dd0d100c18 Mon Sep 17 00:00:00 2001 From: Raphael Pierzina Date: Wed, 8 Apr 2015 23:55:44 +0200 Subject: [PATCH 01/40] Remove unittest methods in favor of pytest assert statements --- tests/utils/test_jinja.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/utils/test_jinja.py b/tests/utils/test_jinja.py index eee703cdf..2d2c287c3 100644 --- a/tests/utils/test_jinja.py +++ b/tests/utils/test_jinja.py @@ -20,7 +20,6 @@ """Tests for qutebrowser.utils.jinja.""" import os.path -import unittest import unittest.mock from qutebrowser.utils import jinja @@ -35,7 +34,7 @@ def _read_file(path): @unittest.mock.patch('qutebrowser.utils.jinja.utils.read_file') -class JinjaTests(unittest.TestCase): +class JinjaTests(object): """Tests for getting template via jinja.""" @@ -45,7 +44,7 @@ class JinjaTests(unittest.TestCase): template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='World') # pylint: disable=no-member - self.assertEqual(data, "Hello World") + assert data == "Hello World" def test_utf8(self, readfile_mock): """Test rendering with an UTF8 template. @@ -59,8 +58,4 @@ class JinjaTests(unittest.TestCase): template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='\u2603') # pylint: disable=no-member - self.assertEqual(data, "Hello \u2603") - - -if __name__ == '__main__': - unittest.main() + assert data == "Hello \u2603" From 894a2a4e7bea10b8594a2d3e4fe6b6ac0c6f3046 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 19:14:06 -0300 Subject: [PATCH 02/40] Add custom "gui" marker to tests which use qtbot fixture Fixes #15 --- tests/conftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index d3411694c..286fd9597 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -76,3 +76,24 @@ def fake_keyevent_factory(): return evtmock return fake_keyevent + + +def pytest_collection_modifyitems(items): + """ + pytest hook called after collection has been performed, adds a marker + named "gui" which can be used to filter gui tests from the command line. + For example: + + py.test -m "not gui" # run all tests except gui tests + py.test -m "gui" # run only gui tests + + Args: + items: list of _pytest.main.Node items, where each item represents + a python test that will be executed. + + Reference: + http://pytest.org/latest/plugins.html + """ + for item in items: + if 'qtbot' in item.fixturenames: + item.add_marker('gui') \ No newline at end of file From d375ddebea5a92bc370fd6ae42652decda174c04 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 19:16:45 -0300 Subject: [PATCH 03/40] Add new-line at the end of conftest.py --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 286fd9597..b428559d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -96,4 +96,4 @@ def pytest_collection_modifyitems(items): """ for item in items: if 'qtbot' in item.fixturenames: - item.add_marker('gui') \ No newline at end of file + item.add_marker('gui') From d91400c3beaef80ce3dd9bce32e17108d7763717 Mon Sep 17 00:00:00 2001 From: Raphael Pierzina Date: Thu, 9 Apr 2015 00:32:24 +0200 Subject: [PATCH 04/40] Use pytest monkeypatch instead of unittest.mock.patch --- tests/utils/test_jinja.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/utils/test_jinja.py b/tests/utils/test_jinja.py index 2d2c287c3..b56d1cf83 100644 --- a/tests/utils/test_jinja.py +++ b/tests/utils/test_jinja.py @@ -20,7 +20,6 @@ """Tests for qutebrowser.utils.jinja.""" import os.path -import unittest.mock from qutebrowser.utils import jinja @@ -33,20 +32,22 @@ def _read_file(path): raise ValueError("Invalid path {}!".format(path)) -@unittest.mock.patch('qutebrowser.utils.jinja.utils.read_file') class JinjaTests(object): """Tests for getting template via jinja.""" - def test_simple_template(self, readfile_mock): + def test_simple_template(self, monkeypatch): """Test with a simple template.""" - readfile_mock.side_effect = _read_file + monkeypatch.setattr( + 'qutebrowser.utils.jinja.utils.read_file', + _read_file + ) template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='World') # pylint: disable=no-member assert data == "Hello World" - def test_utf8(self, readfile_mock): + def test_utf8(self, monkeypatch): """Test rendering with an UTF8 template. This was an attempt to get a failing test case for #127 but it seems @@ -54,7 +55,10 @@ class JinjaTests(object): https://github.com/The-Compiler/qutebrowser/issues/127 """ - readfile_mock.side_effect = _read_file + monkeypatch.setattr( + 'qutebrowser.utils.jinja.utils.read_file', + _read_file + ) template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='\u2603') # pylint: disable=no-member From c3e615dfa3b1aadf5702a970226bd783368d53cc Mon Sep 17 00:00:00 2001 From: Raphael Pierzina Date: Thu, 9 Apr 2015 00:38:57 +0200 Subject: [PATCH 05/40] Remove the test class from test_jinja.py --- tests/utils/test_jinja.py | 51 ++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/tests/utils/test_jinja.py b/tests/utils/test_jinja.py index b56d1cf83..ad04061e4 100644 --- a/tests/utils/test_jinja.py +++ b/tests/utils/test_jinja.py @@ -32,34 +32,31 @@ def _read_file(path): raise ValueError("Invalid path {}!".format(path)) -class JinjaTests(object): +def test_simple_template(monkeypatch): + """Test with a simple template.""" + monkeypatch.setattr( + 'qutebrowser.utils.jinja.utils.read_file', + _read_file + ) + template = jinja.env.get_template('test.html') + # https://bitbucket.org/logilab/pylint/issue/490/ + data = template.render(var='World') # pylint: disable=no-member + assert data == "Hello World" - """Tests for getting template via jinja.""" - def test_simple_template(self, monkeypatch): - """Test with a simple template.""" - monkeypatch.setattr( - 'qutebrowser.utils.jinja.utils.read_file', - _read_file - ) - template = jinja.env.get_template('test.html') - # https://bitbucket.org/logilab/pylint/issue/490/ - data = template.render(var='World') # pylint: disable=no-member - assert data == "Hello World" +def test_utf8(monkeypatch): + """Test rendering with an UTF8 template. - def test_utf8(self, monkeypatch): - """Test rendering with an UTF8 template. + This was an attempt to get a failing test case for #127 but it seems + the issue is elsewhere. - This was an attempt to get a failing test case for #127 but it seems - the issue is elsewhere. - - https://github.com/The-Compiler/qutebrowser/issues/127 - """ - monkeypatch.setattr( - 'qutebrowser.utils.jinja.utils.read_file', - _read_file - ) - template = jinja.env.get_template('test.html') - # https://bitbucket.org/logilab/pylint/issue/490/ - data = template.render(var='\u2603') # pylint: disable=no-member - assert data == "Hello \u2603" + https://github.com/The-Compiler/qutebrowser/issues/127 + """ + monkeypatch.setattr( + 'qutebrowser.utils.jinja.utils.read_file', + _read_file + ) + template = jinja.env.get_template('test.html') + # https://bitbucket.org/logilab/pylint/issue/490/ + data = template.render(var='\u2603') # pylint: disable=no-member + assert data == "Hello \u2603" From b18c1254a46208c295e64a90ac9d32d94d4f6fc8 Mon Sep 17 00:00:00 2001 From: Raphael Pierzina Date: Thu, 9 Apr 2015 00:46:48 +0200 Subject: [PATCH 06/40] Use an autofixture that monkeypatches read_file for both tests --- tests/utils/test_jinja.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/utils/test_jinja.py b/tests/utils/test_jinja.py index ad04061e4..27d2bdaee 100644 --- a/tests/utils/test_jinja.py +++ b/tests/utils/test_jinja.py @@ -21,30 +21,32 @@ import os.path +import pytest + from qutebrowser.utils import jinja -def _read_file(path): - """Mocked utils.read_file.""" - if path == os.path.join('html', 'test.html'): - return """Hello {{var}}""" - else: - raise ValueError("Invalid path {}!".format(path)) +@pytest.fixture(autouse=True) +def patch_read_file(monkeypatch): + def _read_file(path): + """Mocked utils.read_file.""" + if path == os.path.join('html', 'test.html'): + return """Hello {{var}}""" + else: + raise ValueError("Invalid path {}!".format(path)) + + monkeypatch.setattr('qutebrowser.utils.jinja.utils.read_file', _read_file) -def test_simple_template(monkeypatch): +def test_simple_template(): """Test with a simple template.""" - monkeypatch.setattr( - 'qutebrowser.utils.jinja.utils.read_file', - _read_file - ) template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='World') # pylint: disable=no-member assert data == "Hello World" -def test_utf8(monkeypatch): +def test_utf8(): """Test rendering with an UTF8 template. This was an attempt to get a failing test case for #127 but it seems @@ -52,10 +54,6 @@ def test_utf8(monkeypatch): https://github.com/The-Compiler/qutebrowser/issues/127 """ - monkeypatch.setattr( - 'qutebrowser.utils.jinja.utils.read_file', - _read_file - ) template = jinja.env.get_template('test.html') # https://bitbucket.org/logilab/pylint/issue/490/ data = template.render(var='\u2603') # pylint: disable=no-member From 6037fd74cd300bb44a69c6bdbfb3cd712552e243 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 20:06:52 -0300 Subject: [PATCH 07/40] Convert test_split to pytest --- tests/misc/test_split.py | 66 ++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/tests/misc/test_split.py b/tests/misc/test_split.py index c2c930b50..340fe6fb1 100644 --- a/tests/misc/test_split.py +++ b/tests/misc/test_split.py @@ -19,7 +19,7 @@ """Tests for qutebrowser.misc.split.""" -import unittest +import pytest from qutebrowser.misc import split @@ -103,38 +103,35 @@ foo\ bar/foo bar/foo\ bar/ áéíóú/áéíóú/áéíóú/ """ +test_data_lines = test_data.strip().splitlines() -class SplitTests(unittest.TestCase): +class TestSplit: """Test split.""" - def test_split(self): + @pytest.mark.parametrize('cmd, out', + [case.split('/')[:-2] for case in test_data_lines]) + def test_split(self, cmd, out): """Test splitting.""" - for case in test_data.strip().splitlines(): - cmd, out = case.split('/')[:-2] - with self.subTest(cmd=cmd): - items = split.split(cmd) - self.assertEqual(items, out.split('|')) + items = split.split(cmd) + assert items == out.split('|') - def test_split_keep_original(self): + @pytest.mark.parametrize('cmd', + [case.split('/')[0] for case in test_data_lines]) + def test_split_keep_original(self, cmd): """Test if splitting with keep=True yields the original string.""" - for case in test_data.strip().splitlines(): - cmd = case.split('/')[0] - with self.subTest(cmd=cmd): - items = split.split(cmd, keep=True) - self.assertEqual(''.join(items), cmd) + items = split.split(cmd, keep=True) + assert ''.join(items) == cmd - def test_split_keep(self): + @pytest.mark.parametrize('cmd, _mid, out', + [case.split('/')[:-1] for case in test_data_lines]) + def test_split_keep(self, cmd, _mid, out): """Test splitting with keep=True.""" - for case in test_data.strip().splitlines(): - cmd, _mid, out = case.split('/')[:-1] - with self.subTest(cmd=cmd): - items = split.split(cmd, keep=True) - self.assertEqual(items, out.split('|')) + items = split.split(cmd, keep=True) + assert items == out.split('|') -class SimpleSplitTests(unittest.TestCase): - +class TestSimpleSplit: """Test simple_split.""" TESTS = { @@ -145,27 +142,22 @@ class SimpleSplitTests(unittest.TestCase): 'foo\nbar': ['foo', '\nbar'], } - def test_str_split(self): + @pytest.mark.parametrize('test', TESTS) + def test_str_split(self, test): """Test if the behavior matches str.split.""" - for test in self.TESTS: - with self.subTest(string=test): - self.assertEqual(split.simple_split(test), - test.rstrip().split()) + assert split.simple_split(test) == test.rstrip().split() def test_str_split_maxsplit_1(self): """Test if the behavior matches str.split with maxsplit=1.""" - string = "foo bar baz" - self.assertEqual(split.simple_split(string, maxsplit=1), - string.rstrip().split(maxsplit=1)) + s = "foo bar baz" + assert split.simple_split(s, maxsplit=1) == s.rstrip().split(maxsplit=1) def test_str_split_maxsplit_0(self): """Test if the behavior matches str.split with maxsplit=0.""" - string = " foo bar baz " - self.assertEqual(split.simple_split(string, maxsplit=0), - string.rstrip().split(maxsplit=0)) + s = " foo bar baz " + assert split.simple_split(s, maxsplit=0) == s.rstrip().split(maxsplit=0) - def test_split_keep(self): + @pytest.mark.parametrize('test, expected', TESTS.items()) + def test_split_keep(self, test, expected): """Test splitting with keep=True.""" - for test, expected in self.TESTS.items(): - with self.subTest(string=test): - self.assertEqual(split.simple_split(test, keep=True), expected) + assert split.simple_split(test, keep=True) == expected From 853280feeb989baa61a1df59c88725f5852716f0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Apr 2015 20:23:52 -0300 Subject: [PATCH 08/40] Convert test_qtutils to pytest --- tests/utils/test_qtutils.py | 93 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/tests/utils/test_qtutils.py b/tests/utils/test_qtutils.py index 582abbf07..b88caada6 100644 --- a/tests/utils/test_qtutils.py +++ b/tests/utils/test_qtutils.py @@ -20,14 +20,14 @@ """Tests for qutebrowser.utils.qtutils.""" import sys -import unittest + +import pytest from qutebrowser import qutebrowser from qutebrowser.utils import qtutils -class CheckOverflowTests(unittest.TestCase): - +class TestCheckOverflow: """Test check_overflow. Class attributes: @@ -58,69 +58,60 @@ class CheckOverflowTests(unittest.TestCase): (float(INT64_MAX + 1), INT64_MAX)], } - def test_good_values(self): + @pytest.mark.parametrize('ctype, val', [(ctype, val) for ctype, vals in + GOOD_VALUES.items() for val in + vals]) + def test_good_values(self, ctype, val): """Test values which are inside bounds.""" - for ctype, vals in self.GOOD_VALUES.items(): - for val in vals: - with self.subTest(ctype=ctype, val=val): - qtutils.check_overflow(val, ctype) + qtutils.check_overflow(val, ctype) - def test_bad_values_fatal(self): + @pytest.mark.parametrize('ctype, val', + [(ctype, val) for ctype, vals in BAD_VALUES.items() + for (val, _) in vals]) + def test_bad_values_fatal(self, ctype, val): """Test values which are outside bounds with fatal=True.""" - for ctype, vals in self.BAD_VALUES.items(): - for (val, _) in vals: - with self.subTest(ctype=ctype, val=val): - with self.assertRaises(OverflowError): - qtutils.check_overflow(val, ctype) + with pytest.raises(OverflowError): + qtutils.check_overflow(val, ctype) - def test_bad_values_nonfatal(self): + @pytest.mark.parametrize('ctype, val, repl', + [(ctype, val, repl) for ctype, vals in + BAD_VALUES.items() for (val, repl) in vals]) + def test_bad_values_nonfatal(self, ctype, val, repl): """Test values which are outside bounds with fatal=False.""" - for ctype, vals in self.BAD_VALUES.items(): - for (val, replacement) in vals: - with self.subTest(ctype=ctype, val=val): - newval = qtutils.check_overflow(val, ctype, fatal=False) - self.assertEqual(newval, replacement) + newval = qtutils.check_overflow(val, ctype, fatal=False) + assert newval == repl -def argparser_exit(status=0, message=None): # pylint: disable=unused-argument - """Function to monkey-patch .exit() of the argparser so it doesn't exit.""" - raise Exception - - -class GetQtArgsTests(unittest.TestCase): - +class TestGetQtArgs: """Tests for get_args.""" - def setUp(self): - self.parser = qutebrowser.get_argparser() - self.parser.exit = argparser_exit + @pytest.fixture + def parser(self, mocker): + parser = qutebrowser.get_argparser() + # monkey-patch .exit() of the argparser so it doesn't exit. + mocker.patch.object(parser, 'exit', side_effect=Exception) + return parser - def test_no_qt_args(self): + def test_no_qt_args(self, parser): """Test commandline with no Qt arguments given.""" - args = self.parser.parse_args(['--debug']) - self.assertEqual(qtutils.get_args(args), [sys.argv[0]]) + args = parser.parse_args(['--debug']) + assert qtutils.get_args(args) == [sys.argv[0]] - def test_qt_flag(self): + def test_qt_flag(self, parser): """Test commandline with a Qt flag.""" - args = self.parser.parse_args(['--debug', '--qt-reverse', '--nocolor']) - self.assertEqual(qtutils.get_args(args), [sys.argv[0], '-reverse']) + args = parser.parse_args(['--debug', '--qt-reverse', '--nocolor']) + assert qtutils.get_args(args) == [sys.argv[0], '-reverse'] - def test_qt_arg(self): + def test_qt_arg(self, parser): """Test commandline with a Qt argument.""" - args = self.parser.parse_args(['--qt-stylesheet', 'foobar']) - self.assertEqual(qtutils.get_args(args), [sys.argv[0], '-stylesheet', - 'foobar']) + args = parser.parse_args(['--qt-stylesheet', 'foobar']) + assert qtutils.get_args(args) == [sys.argv[0], '-stylesheet', 'foobar'] - def test_qt_both(self): + def test_qt_both(self, parser): """Test commandline with a Qt argument and flag.""" - args = self.parser.parse_args(['--qt-stylesheet', 'foobar', - '--qt-reverse']) + args = parser.parse_args(['--qt-stylesheet', 'foobar', '--qt-reverse']) qt_args = qtutils.get_args(args) - self.assertEqual(qt_args[0], sys.argv[0]) - self.assertIn('-reverse', qt_args) - self.assertIn('-stylesheet', qt_args) - self.assertIn('foobar', qt_args) - - -if __name__ == '__main__': - unittest.main() + assert qt_args[0] == sys.argv[0] + assert '-reverse' in qt_args + assert '-stylesheet' in qt_args + assert 'foobar' in qt_args From 343a091aee9725b1967d012bb1c024d550012496 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Apr 2015 06:42:34 +0200 Subject: [PATCH 09/40] Small docstring cleanup. --- tests/conftest.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b428559d3..865cb22f9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -79,7 +79,8 @@ def fake_keyevent_factory(): def pytest_collection_modifyitems(items): - """ + """Automatically add a 'gui' marker to all gui-related tests. + pytest hook called after collection has been performed, adds a marker named "gui" which can be used to filter gui tests from the command line. For example: @@ -89,7 +90,7 @@ def pytest_collection_modifyitems(items): Args: items: list of _pytest.main.Node items, where each item represents - a python test that will be executed. + a python test that will be executed. Reference: http://pytest.org/latest/plugins.html From 44a6617184a5f46a942ee68d8852920d0b5ff12f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Apr 2015 06:53:21 +0200 Subject: [PATCH 10/40] Add docstring for patch_read_file. --- tests/utils/test_jinja.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/utils/test_jinja.py b/tests/utils/test_jinja.py index 27d2bdaee..174a44df2 100644 --- a/tests/utils/test_jinja.py +++ b/tests/utils/test_jinja.py @@ -28,8 +28,9 @@ from qutebrowser.utils import jinja @pytest.fixture(autouse=True) def patch_read_file(monkeypatch): + """pytest fixture to patch utils.read_file.""" def _read_file(path): - """Mocked utils.read_file.""" + """A read_file which returns a simple template if the path is right.""" if path == os.path.join('html', 'test.html'): return """Hello {{var}}""" else: From 74f4642a2c03ed7bcc28fd09e717c719bff360b6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Apr 2015 07:35:33 +0200 Subject: [PATCH 11/40] Fix lint. --- tests/misc/test_split.py | 19 ++++++++++++++----- tests/utils/test_qtutils.py | 11 ++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/misc/test_split.py b/tests/misc/test_split.py index 340fe6fb1..e926368e0 100644 --- a/tests/misc/test_split.py +++ b/tests/misc/test_split.py @@ -107,24 +107,28 @@ test_data_lines = test_data.strip().splitlines() class TestSplit: + """Test split.""" @pytest.mark.parametrize('cmd, out', - [case.split('/')[:-2] for case in test_data_lines]) + [case.split('/')[:-2] + for case in test_data_lines]) def test_split(self, cmd, out): """Test splitting.""" items = split.split(cmd) assert items == out.split('|') @pytest.mark.parametrize('cmd', - [case.split('/')[0] for case in test_data_lines]) + [case.split('/')[0] + for case in test_data_lines]) def test_split_keep_original(self, cmd): """Test if splitting with keep=True yields the original string.""" items = split.split(cmd, keep=True) assert ''.join(items) == cmd @pytest.mark.parametrize('cmd, _mid, out', - [case.split('/')[:-1] for case in test_data_lines]) + [case.split('/')[:-1] + for case in test_data_lines]) def test_split_keep(self, cmd, _mid, out): """Test splitting with keep=True.""" items = split.split(cmd, keep=True) @@ -132,6 +136,7 @@ class TestSplit: class TestSimpleSplit: + """Test simple_split.""" TESTS = { @@ -150,12 +155,16 @@ class TestSimpleSplit: def test_str_split_maxsplit_1(self): """Test if the behavior matches str.split with maxsplit=1.""" s = "foo bar baz" - assert split.simple_split(s, maxsplit=1) == s.rstrip().split(maxsplit=1) + actual = split.simple_split(s, maxsplit=1) + expected = s.rstrip().split(maxsplit=1) + assert actual == expected def test_str_split_maxsplit_0(self): """Test if the behavior matches str.split with maxsplit=0.""" s = " foo bar baz " - assert split.simple_split(s, maxsplit=0) == s.rstrip().split(maxsplit=0) + actual = split.simple_split(s, maxsplit=0) + expected = s.rstrip().split(maxsplit=0) + assert actual == expected @pytest.mark.parametrize('test, expected', TESTS.items()) def test_split_keep(self, test, expected): diff --git a/tests/utils/test_qtutils.py b/tests/utils/test_qtutils.py index b88caada6..e3c5478f8 100644 --- a/tests/utils/test_qtutils.py +++ b/tests/utils/test_qtutils.py @@ -28,6 +28,7 @@ from qutebrowser.utils import qtutils class TestCheckOverflow: + """Test check_overflow. Class attributes: @@ -66,8 +67,8 @@ class TestCheckOverflow: qtutils.check_overflow(val, ctype) @pytest.mark.parametrize('ctype, val', - [(ctype, val) for ctype, vals in BAD_VALUES.items() - for (val, _) in vals]) + [(ctype, val) for ctype, vals in + BAD_VALUES.items() for (val, _) in vals]) def test_bad_values_fatal(self, ctype, val): """Test values which are outside bounds with fatal=True.""" with pytest.raises(OverflowError): @@ -83,12 +84,16 @@ class TestCheckOverflow: class TestGetQtArgs: + """Tests for get_args.""" @pytest.fixture def parser(self, mocker): + """Fixture to provide an argparser. + + Monkey-patches .exit() of the argparser so it doesn't exit on errors. + """ parser = qutebrowser.get_argparser() - # monkey-patch .exit() of the argparser so it doesn't exit. mocker.patch.object(parser, 'exit', side_effect=Exception) return parser From 96ddfd5b65d19f1aff1d1e80553bccd73dbd1e86 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 9 Apr 2015 07:55:59 -0300 Subject: [PATCH 12/40] Parametrize TestSplitCount in test_basekeyparser As pointed out by @The-Compiler --- tests/keyinput/test_basekeyparser.py | 50 +++++++++------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/tests/keyinput/test_basekeyparser.py b/tests/keyinput/test_basekeyparser.py index 7164bffbd..8206a8194 100644 --- a/tests/keyinput/test_basekeyparser.py +++ b/tests/keyinput/test_basekeyparser.py @@ -63,44 +63,26 @@ class TestSplitCount: """Test the _split_count method. - Attributes: - kp: The BaseKeyParser we're testing. + Class Attributes: + TESTS: list of parameters for the tests, as tuples of + (input_key, supports_count, expected) """ - @pytest.fixture(autouse=True) - def setup(self): - self.kp = basekeyparser.BaseKeyParser(0, supports_count=True) + TESTS = [ + ('10', True, (10, '')), + ('10foo', True, (10, 'foo')), + ('-1foo', True, (None, '-1foo')), + ('10e4foo', True, (10, 'e4foo')), + ('foo', True, (None, 'foo')), + ('10foo', False, (None, '10foo')), + ] - def test_onlycount(self): + @pytest.mark.parametrize('input_key, supports_count, expected', TESTS) + def test_splitcount(self, input_key, supports_count, expected): """Test split_count with only a count.""" - self.kp._keystring = '10' - assert self.kp._split_count() == (10, '') - - def test_normalcount(self): - """Test split_count with count and text.""" - self.kp._keystring = '10foo' - assert self.kp._split_count() == (10, 'foo') - - def test_minuscount(self): - """Test split_count with a negative count.""" - self.kp._keystring = '-1foo' - assert self.kp._split_count() == (None, '-1foo') - - def test_expcount(self): - """Test split_count with an exponential count.""" - self.kp._keystring = '10e4foo' - assert self.kp._split_count() == (10, 'e4foo') - - def test_nocount(self): - """Test split_count with only a command.""" - self.kp._keystring = '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' - assert self.kp._split_count() == (None, '10foo') + kp = basekeyparser.BaseKeyParser(0, supports_count=supports_count) + kp._keystring = input_key + assert kp._split_count() == expected @pytest.mark.usefixtures('fake_keyconfig', 'mock_timer') From 91b72ef29289ace1b323786dbb7743044f9dd35d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Apr 2015 21:19:37 +0200 Subject: [PATCH 13/40] Add a test to validate the default key config. --- tests/config/test_config.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 6bee98ee4..37ab94c26 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -31,7 +31,9 @@ from PyQt5.QtCore import QObject from PyQt5.QtGui import QColor import pytest -from qutebrowser.config import config, configexc +from qutebrowser.config import config, configexc, configdata +from qutebrowser.config.parsers import keyconf +from qutebrowser.commands import runners from qutebrowser.utils import objreg, standarddir @@ -164,6 +166,16 @@ class TestDefaultConfig: conf = config.ConfigManager(None, None) conf._validate_all() + def test_default_key_config(self): + """Test validating of the default key config.""" + # We import qutebrowser.app so the cmdutils.register decorators run. + import qutebrowser.app + conf = keyconf.KeyConfigParser(None, None) + runner = runners.CommandRunner(win_id=0) + for sectname in configdata.KEY_DATA: + for cmd in conf.get_bindings_for(sectname).values(): + runner.parse(cmd, aliases=False) + class TestConfigInit: From 55e3645131fcc1cb117d7b9e17e72c655e56f2ba Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 9 Apr 2015 18:13:13 -0300 Subject: [PATCH 14/40] Add comment to test samples in test_basekeyparser --- tests/keyinput/test_basekeyparser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/keyinput/test_basekeyparser.py b/tests/keyinput/test_basekeyparser.py index 8206a8194..29d556ac5 100644 --- a/tests/keyinput/test_basekeyparser.py +++ b/tests/keyinput/test_basekeyparser.py @@ -69,6 +69,7 @@ class TestSplitCount: """ TESTS = [ + # (input_key, supports_count, expected) ('10', True, (10, '')), ('10foo', True, (10, 'foo')), ('-1foo', True, (None, '-1foo')), From 253f3b2cd7e99776aa33c3e2ad165554fa5942cd Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 9 Apr 2015 18:40:56 -0300 Subject: [PATCH 15/40] Use namedtuple and parametrized fixture for TestSplit As discussed in the PR, this greatly improves legibility --- tests/misc/test_split.py | 58 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/tests/misc/test_split.py b/tests/misc/test_split.py index e926368e0..284a406be 100644 --- a/tests/misc/test_split.py +++ b/tests/misc/test_split.py @@ -18,6 +18,7 @@ # along with qutebrowser. If not, see . """Tests for qutebrowser.misc.split.""" +import collections import pytest @@ -29,7 +30,7 @@ from qutebrowser.misc import split # Format: input/split|output|without|keep/split|output|with|keep/ -test_data = r""" +test_data_str = r""" one two/one|two/one| two/ one "two three" four/one|two three|four/one| "two three"| four/ one 'two three' four/one|two three|four/one| 'two three'| four/ @@ -103,36 +104,53 @@ foo\ bar/foo bar/foo\ bar/ áéíóú/áéíóú/áéíóú/ """ -test_data_lines = test_data.strip().splitlines() + +def _parse_split_test_data_str(): + """ + Parse the test data set into a namedtuple to use in tests. + + Returns: + A list of namedtuples with str attributes: input, keep, no_keep + """ + tuple_class = collections.namedtuple('TestCase', 'input, keep, no_keep') + + result = [] + for line in test_data_str.splitlines(): + if not line: + continue + data = line.split('/') + item = tuple_class(input=data[0], keep=data[1].split('|'), + no_keep=data[2].split('|')) + result.append(item) + return result class TestSplit: """Test split.""" - @pytest.mark.parametrize('cmd, out', - [case.split('/')[:-2] - for case in test_data_lines]) - def test_split(self, cmd, out): + @pytest.fixture(params=_parse_split_test_data_str()) + def split_test_case(self, request): + """ + Fixture that will automatically parametrize all tests the depend on it + using the parsed test case data. + """ + return request.param + + def test_split(self, split_test_case): """Test splitting.""" - items = split.split(cmd) - assert items == out.split('|') + items = split.split(split_test_case.input) + assert items == split_test_case.keep - @pytest.mark.parametrize('cmd', - [case.split('/')[0] - for case in test_data_lines]) - def test_split_keep_original(self, cmd): + def test_split_keep_original(self, split_test_case): """Test if splitting with keep=True yields the original string.""" - items = split.split(cmd, keep=True) - assert ''.join(items) == cmd + items = split.split(split_test_case.input, keep=True) + assert ''.join(items) == split_test_case.input - @pytest.mark.parametrize('cmd, _mid, out', - [case.split('/')[:-1] - for case in test_data_lines]) - def test_split_keep(self, cmd, _mid, out): + def test_split_keep(self, split_test_case): """Test splitting with keep=True.""" - items = split.split(cmd, keep=True) - assert items == out.split('|') + items = split.split(split_test_case.input, keep=True) + assert items == split_test_case.no_keep class TestSimpleSplit: From 6f1e830aba5c58b7863f942ba003a05e97526734 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 9 Apr 2015 18:44:40 -0300 Subject: [PATCH 16/40] Parametrize test_str_split_maxsplit As suggested by @hackebrot --- tests/misc/test_split.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/misc/test_split.py b/tests/misc/test_split.py index 284a406be..75c485937 100644 --- a/tests/misc/test_split.py +++ b/tests/misc/test_split.py @@ -170,18 +170,12 @@ class TestSimpleSplit: """Test if the behavior matches str.split.""" assert split.simple_split(test) == test.rstrip().split() - def test_str_split_maxsplit_1(self): - """Test if the behavior matches str.split with maxsplit=1.""" - s = "foo bar baz" - actual = split.simple_split(s, maxsplit=1) - expected = s.rstrip().split(maxsplit=1) - assert actual == expected - - def test_str_split_maxsplit_0(self): - """Test if the behavior matches str.split with maxsplit=0.""" - s = " foo bar baz " - actual = split.simple_split(s, maxsplit=0) - expected = s.rstrip().split(maxsplit=0) + @pytest.mark.parametrize('s, maxsplit', + [("foo bar baz", 1), (" foo bar baz ", 0)]) + def test_str_split_maxsplit(self, s, maxsplit): + """Test if the behavior matches str.split with given maxsplit.""" + actual = split.simple_split(s, maxsplit=maxsplit) + expected = s.rstrip().split(maxsplit=maxsplit) assert actual == expected @pytest.mark.parametrize('test, expected', TESTS.items()) From 29c51c288b01b50062483ac96a90d569f7cad68c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 9 Apr 2015 18:47:25 -0300 Subject: [PATCH 17/40] Fix small typo in docstring --- qutebrowser/misc/split.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/misc/split.py b/qutebrowser/misc/split.py index bd1904763..b763d8246 100644 --- a/qutebrowser/misc/split.py +++ b/qutebrowser/misc/split.py @@ -127,7 +127,7 @@ def split(s, keep=False): """Split a string via ShellLexer. Args: - keep: Whether to keep are special chars in the split output. + keep: Whether to keep special chars in the split output. """ lexer = ShellLexer(s) lexer.keep = keep From f4c46ec1c5b699f1b9c2bf9be247ba1a35a6e143 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 10 Apr 2015 18:22:02 -0300 Subject: [PATCH 18/40] Improve test legibility in TestCheckOverflow Created OverflowTestCases which is responsible to provide data for the tests --- tests/utils/test_qtutils.py | 44 +++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/tests/utils/test_qtutils.py b/tests/utils/test_qtutils.py index e3c5478f8..290f6dad3 100644 --- a/tests/utils/test_qtutils.py +++ b/tests/utils/test_qtutils.py @@ -27,9 +27,9 @@ from qutebrowser import qutebrowser from qutebrowser.utils import qtutils -class TestCheckOverflow: - - """Test check_overflow. +class OverflowTestCases: + """ + Provides test data for overflow checking. Class attributes: INT32_MIN: Minimum valid value for a signed int32. @@ -59,24 +59,47 @@ class TestCheckOverflow: (float(INT64_MAX + 1), INT64_MAX)], } - @pytest.mark.parametrize('ctype, val', [(ctype, val) for ctype, vals in - GOOD_VALUES.items() for val in - vals]) + @classmethod + def iter_good_values(cls): + """ + Yields pairs of (c data type, value) which should pass overflow + checking. + """ + for ctype, values in cls.GOOD_VALUES.items(): + for value in values: + yield ctype, value + + @classmethod + def iter_bad_values(cls): + """ + Yields pairs of (c type, value, repl) for values which don't pass + overflow checking, and value they should be replaced with if overflow + checking should not be fatal. + """ + for ctype, values in cls.BAD_VALUES.items(): + for value, repl in values: + yield ctype, value, repl + + +class TestCheckOverflow: + """Test check_overflow. + """ + + @pytest.mark.parametrize('ctype, val', OverflowTestCases.iter_good_values()) def test_good_values(self, ctype, val): """Test values which are inside bounds.""" qtutils.check_overflow(val, ctype) @pytest.mark.parametrize('ctype, val', - [(ctype, val) for ctype, vals in - BAD_VALUES.items() for (val, _) in vals]) + [(ctype, val) for (ctype, val, _) in + OverflowTestCases.iter_bad_values()]) def test_bad_values_fatal(self, ctype, val): """Test values which are outside bounds with fatal=True.""" with pytest.raises(OverflowError): qtutils.check_overflow(val, ctype) @pytest.mark.parametrize('ctype, val, repl', - [(ctype, val, repl) for ctype, vals in - BAD_VALUES.items() for (val, repl) in vals]) + OverflowTestCases.iter_bad_values()) def test_bad_values_nonfatal(self, ctype, val, repl): """Test values which are outside bounds with fatal=False.""" newval = qtutils.check_overflow(val, ctype, fatal=False) @@ -84,7 +107,6 @@ class TestCheckOverflow: class TestGetQtArgs: - """Tests for get_args.""" @pytest.fixture From 3433a1ec7a28d10629e15c57f005e03478841bb7 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 13 Apr 2015 07:47:09 +0200 Subject: [PATCH 19/40] Add tests for CommandRunner/KeyConfigParser. --- tests/commands/test_runners.py | 44 ++++++++++++++++++++++++++++++++++ tests/config/test_config.py | 21 ++++++++++++++++ tests/conftest.py | 41 +++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 tests/commands/test_runners.py diff --git a/tests/commands/test_runners.py b/tests/commands/test_runners.py new file mode 100644 index 000000000..a03eab9d8 --- /dev/null +++ b/tests/commands/test_runners.py @@ -0,0 +1,44 @@ +# 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 qutebrowser.commands.runners.""" + +import pytest + +from qutebrowser.commands import runners, cmdexc + + +class TestCommandRunner: + + """Tests for CommandRunner.""" + + def test_parse_all(self, cmdline_test): + """Test parsing of commands. + + See https://github.com/The-Compiler/qutebrowser/issues/615 + + Args: + cmdline_test: A pytest fixture which provides testcases. + """ + cr = runners.CommandRunner(0) + if cmdline_test.valid: + list(cr.parse_all(cmdline_test.cmd, aliases=False)) + else: + with pytest.raises(cmdexc.NoSuchCommandError): + list(cr.parse_all(cmdline_test.cmd, aliases=False)) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 37ab94c26..c8937671f 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -157,6 +157,27 @@ class TestConfigParser: self.cfg.get('general', 'bar') # pylint: disable=bad-config-call +class TestKeyConfigParser: + + """Test config.parsers.keyconf.KeyConfigParser.""" + + def test_cmd_binding(self, cmdline_test): + """Test various command bindings. + + See https://github.com/The-Compiler/qutebrowser/issues/615 + + Args: + cmdline_test: A pytest fixture which provides testcases. + """ + kcp = keyconf.KeyConfigParser(None, None) + kcp._cur_section = 'normal' + if cmdline_test.valid: + kcp._read_command(cmdline_test.cmd) + else: + with pytest.raises(keyconf.KeyConfigError): + kcp._read_command(cmdline_test.cmd) + + class TestDefaultConfig: """Test validating of the default config.""" diff --git a/tests/conftest.py b/tests/conftest.py index 865cb22f9..3f3fc7ba0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,9 @@ """The qutebrowser test suite contest file.""" +import collections +import itertools + import pytest @@ -98,3 +101,41 @@ def pytest_collection_modifyitems(items): for item in items: if 'qtbot' in item.fixturenames: item.add_marker('gui') + + +def _generate_cmdline_tests(): + """Generate testcases for test_split_binding.""" + # pylint: disable=invalid-name + TestCase = collections.namedtuple('TestCase', 'cmd, valid') + separators = [';;', ' ;; ', ';; ', ' ;;'] + invalid = ['foo', ''] + valid = ['leave-mode', 'hint all'] + # Valid command only -> valid + for item in valid: + yield TestCase(''.join(item), True) + # Invalid command only -> invalid + for item in valid: + yield TestCase(''.join(item), True) + # Invalid command combined with invalid command -> invalid + for item in itertools.product(invalid, separators, invalid): + yield TestCase(''.join(item), False) + # Valid command combined with valid command -> valid + for item in itertools.product(valid, separators, valid): + yield TestCase(''.join(item), True) + # Valid command combined with invalid command -> invalid + for item in itertools.product(valid, separators, invalid): + yield TestCase(''.join(item), False) + # Invalid command combined with valid command -> invalid + for item in itertools.product(invalid, separators, valid): + yield TestCase(''.join(item), False) + # Command with no_cmd_split combined with an "invalid" command -> valid + for item in itertools.product(['bind x open'], separators, invalid): + yield TestCase(''.join(item), True) + + +@pytest.fixture(params=_generate_cmdline_tests()) +def cmdline_test(request): + """Fixture which generates tests for things validating commandlines.""" + # Import qutebrowser.app so all cmdutils.register decorators get run. + import qutebrowser.app # pylint: disable=unused-variable + return request.param From e8ddd9397dc3b781bc7d37b2bf2a88e1a3b5ac72 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 13 Apr 2015 22:34:30 +0200 Subject: [PATCH 20/40] Use a single QNetworkAccessManager per session. --- tests/conftest.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 865cb22f9..a32e9cfd0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -49,15 +49,23 @@ def unicode_encode_err(): 'fake exception') # reason +@pytest.fixture(scope='session') +def qnam(): + """Session-wide QNetworkAccessManager.""" + from PyQt5.QtNetwork import QNetworkAccessManager + nam = QNetworkAccessManager() + nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) + return nam + + @pytest.fixture -def webpage(): +def webpage(qnam): """Get a new QWebPage object.""" from PyQt5.QtWebKitWidgets import QWebPage - from PyQt5.QtNetwork import QNetworkAccessManager page = QWebPage() - nam = page.networkAccessManager() - nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) + page.networkAccessManager().deleteLater() + page.setNetworkAccessManager(qnam) return page From 6ae94d6f49273cac07cdefbec69968c691803a99 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 13 Apr 2015 18:20:40 -0300 Subject: [PATCH 21/40] Create module overflow_test_cases As suggested by @The-Compiler --- tests/utils/overflow_test_cases.py | 70 ++++++++++++++++++++++++++++++ tests/utils/test_qtutils.py | 62 +++----------------------- 2 files changed, 75 insertions(+), 57 deletions(-) create mode 100644 tests/utils/overflow_test_cases.py diff --git a/tests/utils/overflow_test_cases.py b/tests/utils/overflow_test_cases.py new file mode 100644 index 000000000..2801c1b72 --- /dev/null +++ b/tests/utils/overflow_test_cases.py @@ -0,0 +1,70 @@ +# 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 . + +""" +Provides test data for overflow checking. + +Module attributes: + INT32_MIN: Minimum valid value for a signed int32. + INT32_MAX: Maximum valid value for a signed int32. + INT64_MIN: Minimum valid value for a signed int64. + INT64_MAX: Maximum valid value for a signed int64. + GOOD_VALUES: A dict of types mapped to a list of good values. + BAD_VALUES: A dict of types mapped to a list of bad values. +""" + +INT32_MIN = -(2 ** 31) +INT32_MAX = 2 ** 31 - 1 +INT64_MIN = -(2 ** 63) +INT64_MAX = 2 ** 63 - 1 + +GOOD_VALUES = { + 'int': [-1, 0, 1, 23.42, INT32_MIN, INT32_MAX], + 'int64': [-1, 0, 1, 23.42, INT64_MIN, INT64_MAX], +} + +BAD_VALUES = { + 'int': [(INT32_MIN - 1, INT32_MIN), + (INT32_MAX + 1, INT32_MAX), + (float(INT32_MAX + 1), INT32_MAX)], + 'int64': [(INT64_MIN - 1, INT64_MIN), + (INT64_MAX + 1, INT64_MAX), + (float(INT64_MAX + 1), INT64_MAX)], +} + + +def iter_good_values(): + """ + Yields pairs of (c data type, value) which should pass overflow + checking. + """ + for ctype, values in GOOD_VALUES.items(): + for value in values: + yield ctype, value + + +def iter_bad_values(): + """ + Yields pairs of (c type, value, repl) for values which don't pass + overflow checking, and a value they should be replaced with if overflow + checking should not be fatal. + """ + for ctype, values in BAD_VALUES.items(): + for value, repl in values: + yield ctype, value, repl diff --git a/tests/utils/test_qtutils.py b/tests/utils/test_qtutils.py index 290f6dad3..07633e46a 100644 --- a/tests/utils/test_qtutils.py +++ b/tests/utils/test_qtutils.py @@ -25,81 +25,29 @@ import pytest from qutebrowser import qutebrowser from qutebrowser.utils import qtutils - - -class OverflowTestCases: - """ - Provides test data for overflow checking. - - Class attributes: - INT32_MIN: Minimum valid value for a signed int32. - INT32_MAX: Maximum valid value for a signed int32. - INT64_MIN: Minimum valid value for a signed int64. - INT64_MAX: Maximum valid value for a signed int64. - GOOD_VALUES: A dict of types mapped to a list of good values. - BAD_VALUES: A dict of types mapped to a list of bad values. - """ - - INT32_MIN = -(2 ** 31) - INT32_MAX = 2 ** 31 - 1 - INT64_MIN = -(2 ** 63) - INT64_MAX = 2 ** 63 - 1 - - GOOD_VALUES = { - 'int': [-1, 0, 1, 23.42, INT32_MIN, INT32_MAX], - 'int64': [-1, 0, 1, 23.42, INT64_MIN, INT64_MAX], - } - - BAD_VALUES = { - 'int': [(INT32_MIN - 1, INT32_MIN), - (INT32_MAX + 1, INT32_MAX), - (float(INT32_MAX + 1), INT32_MAX)], - 'int64': [(INT64_MIN - 1, INT64_MIN), - (INT64_MAX + 1, INT64_MAX), - (float(INT64_MAX + 1), INT64_MAX)], - } - - @classmethod - def iter_good_values(cls): - """ - Yields pairs of (c data type, value) which should pass overflow - checking. - """ - for ctype, values in cls.GOOD_VALUES.items(): - for value in values: - yield ctype, value - - @classmethod - def iter_bad_values(cls): - """ - Yields pairs of (c type, value, repl) for values which don't pass - overflow checking, and value they should be replaced with if overflow - checking should not be fatal. - """ - for ctype, values in cls.BAD_VALUES.items(): - for value, repl in values: - yield ctype, value, repl +import overflow_test_cases class TestCheckOverflow: """Test check_overflow. """ - @pytest.mark.parametrize('ctype, val', OverflowTestCases.iter_good_values()) + @pytest.mark.parametrize('ctype, val', + overflow_test_cases.iter_good_values()) def test_good_values(self, ctype, val): """Test values which are inside bounds.""" qtutils.check_overflow(val, ctype) @pytest.mark.parametrize('ctype, val', [(ctype, val) for (ctype, val, _) in - OverflowTestCases.iter_bad_values()]) + overflow_test_cases.iter_bad_values()]) def test_bad_values_fatal(self, ctype, val): """Test values which are outside bounds with fatal=True.""" with pytest.raises(OverflowError): qtutils.check_overflow(val, ctype) @pytest.mark.parametrize('ctype, val, repl', - OverflowTestCases.iter_bad_values()) + overflow_test_cases.iter_bad_values()) def test_bad_values_nonfatal(self, ctype, val, repl): """Test values which are outside bounds with fatal=False.""" newval = qtutils.check_overflow(val, ctype, fatal=False) From ba678e29fbba687c7eb7dae05f934d203dd18c3a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 14 Apr 2015 07:00:56 +0200 Subject: [PATCH 22/40] Fix lint. --- tests/misc/test_split.py | 7 ++++--- tests/utils/overflow_test_cases.py | 14 +++++++------- tests/utils/test_qtutils.py | 5 +++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/misc/test_split.py b/tests/misc/test_split.py index 75c485937..65fa218b1 100644 --- a/tests/misc/test_split.py +++ b/tests/misc/test_split.py @@ -131,9 +131,10 @@ class TestSplit: @pytest.fixture(params=_parse_split_test_data_str()) def split_test_case(self, request): - """ - Fixture that will automatically parametrize all tests the depend on it - using the parsed test case data. + """Fixture to automatically parametrize all depending tests. + + It will use the test data from test_data_str, parsed using + _parse_split_test_data_str(). """ return request.param diff --git a/tests/utils/overflow_test_cases.py b/tests/utils/overflow_test_cases.py index 2801c1b72..08e6ae7a8 100644 --- a/tests/utils/overflow_test_cases.py +++ b/tests/utils/overflow_test_cases.py @@ -50,9 +50,9 @@ BAD_VALUES = { def iter_good_values(): - """ - Yields pairs of (c data type, value) which should pass overflow - checking. + """Yield "good" (C data type, value) tuples. + + Those should pass overflow checking. """ for ctype, values in GOOD_VALUES.items(): for value in values: @@ -60,10 +60,10 @@ def iter_good_values(): def iter_bad_values(): - """ - Yields pairs of (c type, value, repl) for values which don't pass - overflow checking, and a value they should be replaced with if overflow - checking should not be fatal. + """Yield pairs of "bad" (C type, value, repl) tuples. + + Theose should not pass overflow checking. The third value is the value they + should be replaced with if overflow checking should not be fatal. """ for ctype, values in BAD_VALUES.items(): for value, repl in values: diff --git a/tests/utils/test_qtutils.py b/tests/utils/test_qtutils.py index 07633e46a..85a99c2f4 100644 --- a/tests/utils/test_qtutils.py +++ b/tests/utils/test_qtutils.py @@ -29,8 +29,8 @@ import overflow_test_cases class TestCheckOverflow: - """Test check_overflow. - """ + + """Test check_overflow.""" @pytest.mark.parametrize('ctype, val', overflow_test_cases.iter_good_values()) @@ -55,6 +55,7 @@ class TestCheckOverflow: class TestGetQtArgs: + """Tests for get_args.""" @pytest.fixture From f5e6091ff654838143a52e7b996ce60c75b9d4a0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 15 Apr 2015 20:16:57 -0300 Subject: [PATCH 23/40] Add tests for CommandLineEdit --- tests/misc/test_miscwidgets.py | 98 ++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/misc/test_miscwidgets.py diff --git a/tests/misc/test_miscwidgets.py b/tests/misc/test_miscwidgets.py new file mode 100644 index 000000000..b73bab050 --- /dev/null +++ b/tests/misc/test_miscwidgets.py @@ -0,0 +1,98 @@ +# 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 widgets in miscwidgets module.""" +from unittest import mock +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QApplication +import pytest + +from qutebrowser.misc import lineparser +from qutebrowser.misc.miscwidgets import CommandLineEdit +from qutebrowser.utils import objreg + + +class TestCommandLineEdit: + """Tests for CommandLineEdit widget.""" + + @pytest.yield_fixture + def cmd_edit(self, qtbot): + """ + Fixture to initialize a CommandLineEdit and its dependencies, + cleaning up afterwards. + """ + command_history = lineparser.LimitLineParser('', '', limit=None) + objreg.register('command-history', command_history) + cmd_edit = CommandLineEdit(None) + cmd_edit.set_prompt(':') + qtbot.add_widget(cmd_edit) + assert cmd_edit.text() == '' + + yield cmd_edit + + objreg.delete('command-history') + + @pytest.yield_fixture + def mock_clipboard(self): + """ + Fixture installs a MagicMock into QApplication.clipboard() and + returns it. + """ + with mock.patch.object(QApplication, 'clipboard'): + clipboard = mock.MagicMock() + clipboard.supportsSelection.return_value = True + QApplication.clipboard.return_value = clipboard + yield clipboard + + def test_position(self, qtbot, cmd_edit): + """Test cursor position based on the prompt.""" + qtbot.keyClicks(cmd_edit, ':hello') + assert cmd_edit.text() == ':hello' + assert cmd_edit.cursorPosition() == len(':hello') + + cmd_edit.home(mark=True) + assert cmd_edit.cursorPosition() == len(':hello') + qtbot.keyClick(cmd_edit, Qt.Key_Delete) + assert cmd_edit.text() == ':' + qtbot.keyClick(cmd_edit, Qt.Key_Backspace) + assert cmd_edit.text() == ':' + + qtbot.keyClicks(cmd_edit, 'hey again') + assert cmd_edit.text() == ':hey again' + + def test_invalid_prompt(self, qtbot, cmd_edit): + """Test prevent invalid prompt to be entered.""" + qtbot.keyClicks(cmd_edit, '$hello') + assert cmd_edit.text() == '' + + def test_clipboard_paste(self, qtbot, cmd_edit, mock_clipboard): + """Test pasting commands from clipboard.""" + mock_clipboard.text.return_value = ':command' + qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier) + assert cmd_edit.text() == ':command' + + mock_clipboard.text.return_value = ' param1' + qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier) + assert cmd_edit.text() == ':command param1' + + cmd_edit.clear() + mock_clipboard.text.return_value = '$ command' + qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier) + assert cmd_edit.text() == ':command param1' + From 6c97a4a6e0e642d83fd600662b749aa2545e3dba Mon Sep 17 00:00:00 2001 From: Raphael Pierzina Date: Sun, 19 Apr 2015 21:10:27 +0200 Subject: [PATCH 24/40] Remove blank line at end of file to fix flake8 --- tests/misc/test_miscwidgets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/misc/test_miscwidgets.py b/tests/misc/test_miscwidgets.py index b73bab050..ad94f2613 100644 --- a/tests/misc/test_miscwidgets.py +++ b/tests/misc/test_miscwidgets.py @@ -95,4 +95,3 @@ class TestCommandLineEdit: mock_clipboard.text.return_value = '$ command' qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier) assert cmd_edit.text() == ':command param1' - From 2d19708a419fff804e6395ba9d7041818ef80345 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 19 Apr 2015 17:11:29 -0300 Subject: [PATCH 25/40] Play nice with other plugins in conftest.py Some plugins might create their own Item subclasses without a `fixturenames` attribute. Discovered while taking pytest-flakes for a spin. --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index cf34f75d5..892a91912 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -107,7 +107,7 @@ def pytest_collection_modifyitems(items): http://pytest.org/latest/plugins.html """ for item in items: - if 'qtbot' in item.fixturenames: + if 'qtbot' in getattr(item, 'fixturenames', ()): item.add_marker('gui') From f55242ad93c9927a50611b666aa9bcdb1b0bafad Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 19 Apr 2015 17:13:47 -0300 Subject: [PATCH 26/40] Use pytest-mock to install QApplication.clipboard mock --- tests/misc/test_miscwidgets.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/misc/test_miscwidgets.py b/tests/misc/test_miscwidgets.py index ad94f2613..1bd19541a 100644 --- a/tests/misc/test_miscwidgets.py +++ b/tests/misc/test_miscwidgets.py @@ -48,17 +48,17 @@ class TestCommandLineEdit: objreg.delete('command-history') - @pytest.yield_fixture - def mock_clipboard(self): + @pytest.fixture + def mock_clipboard(self, mocker): """ Fixture installs a MagicMock into QApplication.clipboard() and returns it. """ - with mock.patch.object(QApplication, 'clipboard'): - clipboard = mock.MagicMock() - clipboard.supportsSelection.return_value = True - QApplication.clipboard.return_value = clipboard - yield clipboard + mocker.patch.object(QApplication, 'clipboard') + clipboard = mock.MagicMock() + clipboard.supportsSelection.return_value = True + QApplication.clipboard.return_value = clipboard + return clipboard def test_position(self, qtbot, cmd_edit): """Test cursor position based on the prompt.""" From 69061c5629c15e0b5b0d21790f42d14c55bd1739 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 20 Apr 2015 12:51:36 -0300 Subject: [PATCH 27/40] Remove LimitLineParser from test As suggested by @The-Compiler, this is not really necessary --- tests/misc/test_miscwidgets.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/misc/test_miscwidgets.py b/tests/misc/test_miscwidgets.py index 1bd19541a..ec7a6bf15 100644 --- a/tests/misc/test_miscwidgets.py +++ b/tests/misc/test_miscwidgets.py @@ -37,17 +37,12 @@ class TestCommandLineEdit: Fixture to initialize a CommandLineEdit and its dependencies, cleaning up afterwards. """ - command_history = lineparser.LimitLineParser('', '', limit=None) - objreg.register('command-history', command_history) cmd_edit = CommandLineEdit(None) cmd_edit.set_prompt(':') qtbot.add_widget(cmd_edit) assert cmd_edit.text() == '' - yield cmd_edit - objreg.delete('command-history') - @pytest.fixture def mock_clipboard(self, mocker): """ From c098d0de37c92954f9db717f037dfe4b53d8c06c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 20 Apr 2015 18:02:04 +0200 Subject: [PATCH 28/40] Register the gui marker in tox.ini. --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 101d2b02c..0f943e8d3 100644 --- a/tox.ini +++ b/tox.ini @@ -109,3 +109,5 @@ commands = [pytest] norecursedirs = .tox .venv +markers = + gui: Tests using the GUI (e.g. spawning widgets) From 1b13b0c385e9297bc9b74223d65216c731b1445c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 20 Apr 2015 18:02:59 +0200 Subject: [PATCH 29/40] Add --strict to pytest invocation. --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 0f943e8d3..d1e337271 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ deps = # on Ubuntu Trusty. commands = {envpython} scripts/link_pyqt.py --tox {envdir} - {envpython} -m py.test {posargs} + {envpython} -m py.test --strict {posargs} [testenv:coverage] deps = @@ -36,7 +36,7 @@ deps = cov-core==1.15.0 commands = {[testenv:mkvenv]commands} - {envpython} -m py.test --cov qutebrowser --cov-report term --cov-report html {posargs} + {envpython} -m py.test --strict --cov qutebrowser --cov-report term --cov-report html {posargs} [testenv:misc] commands = From a7dfdd48e01718024d797b7c50ce9f16f09cd30d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 20 Apr 2015 22:59:35 +0200 Subject: [PATCH 30/40] Fix lint. --- tests/misc/test_miscwidgets.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/misc/test_miscwidgets.py b/tests/misc/test_miscwidgets.py index ec7a6bf15..bd316935f 100644 --- a/tests/misc/test_miscwidgets.py +++ b/tests/misc/test_miscwidgets.py @@ -18,25 +18,22 @@ # along with qutebrowser. If not, see . """Test widgets in miscwidgets module.""" + from unittest import mock from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication import pytest -from qutebrowser.misc import lineparser from qutebrowser.misc.miscwidgets import CommandLineEdit -from qutebrowser.utils import objreg class TestCommandLineEdit: + """Tests for CommandLineEdit widget.""" @pytest.yield_fixture def cmd_edit(self, qtbot): - """ - Fixture to initialize a CommandLineEdit and its dependencies, - cleaning up afterwards. - """ + """Fixture to initialize a CommandLineEdit.""" cmd_edit = CommandLineEdit(None) cmd_edit.set_prompt(':') qtbot.add_widget(cmd_edit) @@ -45,9 +42,10 @@ class TestCommandLineEdit: @pytest.fixture def mock_clipboard(self, mocker): - """ - Fixture installs a MagicMock into QApplication.clipboard() and - returns it. + """Fixture to mock QApplication.clipboard. + + Return: + The mocked QClipboard object. """ mocker.patch.object(QApplication, 'clipboard') clipboard = mock.MagicMock() @@ -72,7 +70,7 @@ class TestCommandLineEdit: assert cmd_edit.text() == ':hey again' def test_invalid_prompt(self, qtbot, cmd_edit): - """Test prevent invalid prompt to be entered.""" + """Test preventing of an invalid prompt being entered.""" qtbot.keyClicks(cmd_edit, '$hello') assert cmd_edit.text() == '' From 9f443d026a96c382fa3edbf7b89a27991adda50b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 20 Apr 2015 23:12:00 +0200 Subject: [PATCH 31/40] Make pylint shut up. --- tests/config/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index c8937671f..383fed232 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -190,7 +190,7 @@ class TestDefaultConfig: def test_default_key_config(self): """Test validating of the default key config.""" # We import qutebrowser.app so the cmdutils.register decorators run. - import qutebrowser.app + import qutebrowser.app # pylint: disable=unused-variable conf = keyconf.KeyConfigParser(None, None) runner = runners.CommandRunner(win_id=0) for sectname in configdata.KEY_DATA: From 6ca39dd851e3d451000c511df69f4ea621600a86 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 21 Apr 2015 22:48:45 +0200 Subject: [PATCH 32/40] Handle --relaxed-config for keys.conf as well. --- qutebrowser/config/config.py | 2 ++ qutebrowser/config/parsers/keyconf.py | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 15872004b..e1942667b 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -161,7 +161,9 @@ def _init_key_config(parent): parent: The parent to use for the KeyConfigParser. """ try: + args = objreg.get('args') key_config = keyconf.KeyConfigParser(standarddir.config(), 'keys.conf', + args.relaxed_config, parent=parent) except (keyconf.KeyConfigError, UnicodeDecodeError) as e: log.init.exception(e) diff --git a/qutebrowser/config/parsers/keyconf.py b/qutebrowser/config/parsers/keyconf.py index 1fdc24025..b15a9ae4a 100644 --- a/qutebrowser/config/parsers/keyconf.py +++ b/qutebrowser/config/parsers/keyconf.py @@ -75,12 +75,13 @@ class KeyConfigParser(QObject): config_dirty = pyqtSignal() UNBOUND_COMMAND = '' - def __init__(self, configdir, fname, parent=None): + def __init__(self, configdir, fname, relaxed=False, parent=None): """Constructor. Args: configdir: The directory to save the configs in. fname: The filename of the config. + relaxed: If given, unknwon commands are ignored. """ super().__init__(parent) self.is_dirty = False @@ -95,7 +96,7 @@ class KeyConfigParser(QObject): if self._configfile is None or not os.path.exists(self._configfile): self._load_default() else: - self._read() + self._read(relaxed) self._load_default(only_new=True) log.init.debug("Loaded bindings: {}".format(self.keybindings)) @@ -267,8 +268,12 @@ class KeyConfigParser(QObject): else: return True - def _read(self): - """Read the config file from disk and parse it.""" + def _read(self, relaxed=False): + """Read the config file from disk and parse it. + + Args: + relaxed: Ignore unknown commands. + """ try: with open(self._configfile, 'r', encoding='utf-8') as f: for i, line in enumerate(f): @@ -287,8 +292,11 @@ class KeyConfigParser(QObject): line = line.strip() self._read_command(line) except KeyConfigError as e: - e.lineno = i - raise + if relaxed: + continue + else: + e.lineno = i + raise except OSError: log.keyboard.exception("Failed to read key bindings!") for sectname in self.keybindings: From c21ae0b65155123d5d1cbae78ef588cbe10c505d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 22 Apr 2015 07:13:56 +0200 Subject: [PATCH 33/40] Add a :debug-webaction command. --- doc/help/commands.asciidoc | 15 +++++++++++++++ qutebrowser/browser/commands.py | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index d6340b35a..491e9ac10 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -947,6 +947,7 @@ These commands are mainly intended for debugging. They are hidden if qutebrowser |<>|Crash for debugging purposes. |<>|Evaluate a python string and display the results as a web page. |<>|Trace executed code via hunter. +|<>|Execute a webaction. |============== [[debug-all-objects]] === debug-all-objects @@ -995,3 +996,17 @@ Trace executed code via hunter. * This command does not split arguments after the last argument and handles quotes literally. * With this command, +;;+ is interpreted literally instead of splitting off a second command. +[[debug-webaction]] +=== debug-webaction +Syntax: +:debug-webaction 'action'+ + +Execute a webaction. + +See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the available actions. + +==== positional arguments +* +'action'+: The action to execute, e.g. MoveToNextChar. + +==== count +How many times to repeat the action. + diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 04408cdcf..2cc30e668 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1150,3 +1150,23 @@ class CommandDispatcher: flags |= QWebPage.FindBackward for _ in range(count): view.search(view.search_text, flags) + + @cmdutils.register(instance='command-dispatcher', scope='window', + count='count', debug=True) + def debug_webaction(self, action, count=1): + """Execute a webaction. + + See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the + available actions. + + Args: + action: The action to execute, e.g. MoveToNextChar. + count: How many times to repeat the action. + """ + member = getattr(QWebPage, action, None) + if not isinstance(member, QWebPage.WebAction): + raise cmdexc.CommandError("{} is not a valid web action!".format( + acction)) + view = self._current_widget() + for _ in range(count): + view.triggerPageAction(member) From e1f2259e980976e38fe8206619c9cc28facf4e72 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 22 Apr 2015 07:46:01 +0200 Subject: [PATCH 34/40] Fix typo. --- qutebrowser/browser/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2cc30e668..94afb0dc2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1166,7 +1166,7 @@ class CommandDispatcher: member = getattr(QWebPage, action, None) if not isinstance(member, QWebPage.WebAction): raise cmdexc.CommandError("{} is not a valid web action!".format( - acction)) + action)) view = self._current_widget() for _ in range(count): view.triggerPageAction(member) From 8e0ef128c90fb3f44f854cdb35ccf42f6e4436ce Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 22 Apr 2015 15:46:26 +0200 Subject: [PATCH 35/40] Regenerate authors. --- README.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.asciidoc b/README.asciidoc index 0c0bdad0e..741dd95c6 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -135,8 +135,8 @@ Contributors, sorted by the number of commits in descending order: // QUTE_AUTHORS_START * Florian Bruhin * Bruno Oliveira -* Joel Torstensson * Raphael Pierzina +* Joel Torstensson * Claude * ZDarian * Peter Vilim From 71608af4867f61b7bb9e22318680c78383d793fc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 22 Apr 2015 18:11:45 +0200 Subject: [PATCH 36/40] tox.ini: Use pytest-qt from git. See https://github.com/pytest-dev/pytest-qt/pull/38. --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d1e337271..b11817280 100644 --- a/tox.ini +++ b/tox.ini @@ -16,11 +16,13 @@ usedevelop = true [testenv:unittests] setenv = QT_QPA_PLATFORM_PLUGIN_PATH={envsitepackagesdir}/PyQt5/plugins/platforms +# Using pytest-qt from git because of +# https://github.com/pytest-dev/pytest-qt/pull/38 deps = py==1.4.26 pytest==2.7.0 pytest-capturelog==0.7 - pytest-qt==1.3.0 + git+https://github.com/pytest-dev/pytest-qt.git pytest-mock==0.4.3 # We don't use {[testenv:mkvenv]commands} here because that seems to be broken # on Ubuntu Trusty. From 844473e47ab62c327a574f40257354d3478705de Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 24 Apr 2015 17:25:53 +0200 Subject: [PATCH 37/40] Fix /-foo searches. --- qutebrowser/mainwindow/statusbar/command.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py index 0803cfd48..1d8105c2f 100644 --- a/qutebrowser/mainwindow/statusbar/command.py +++ b/qutebrowser/mainwindow/statusbar/command.py @@ -163,8 +163,8 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): """Execute the command currently in the commandline.""" prefixes = { ':': '', - '/': 'search ', - '?': 'search -r ', + '/': 'search -- ', + '?': 'search -r -- ', } text = self.text() self.history.append(text) From f5227ef9824e175d974a8e802a4295dd7eb0723e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 24 Apr 2015 17:33:59 +0200 Subject: [PATCH 38/40] Update changelog. --- CHANGELOG.asciidoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 950679b90..d5daad877 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -28,6 +28,14 @@ Changed - `QUTE_HTML` and `QUTE_TEXT` for userscripts now don't store the contents directly, and instead contain a filename. - `:spawn` now shows the command being executed in the statusbar, use `-q`/`--quiet` for the old behavior. +v0.2.2 (unreleased) +------------------- + +Fixed +~~~~~ + +- Fixed searching for terms starting with a hyphen (e.g. `/-foo`) + https://github.com/The-Compiler/qutebrowser/releases/tag/v0.2.1[v0.2.1] ----------------------------------------------------------------------- From 8edfa4281e7c860f297f2be3057c1be1b2b453f8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 24 Apr 2015 17:34:10 +0200 Subject: [PATCH 39/40] Revert "tox.ini: Use pytest-qt from git." This reverts commit 71608af4867f61b7bb9e22318680c78383d793fc. --- tox.ini | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index b11817280..d1e337271 100644 --- a/tox.ini +++ b/tox.ini @@ -16,13 +16,11 @@ usedevelop = true [testenv:unittests] setenv = QT_QPA_PLATFORM_PLUGIN_PATH={envsitepackagesdir}/PyQt5/plugins/platforms -# Using pytest-qt from git because of -# https://github.com/pytest-dev/pytest-qt/pull/38 deps = py==1.4.26 pytest==2.7.0 pytest-capturelog==0.7 - git+https://github.com/pytest-dev/pytest-qt.git + pytest-qt==1.3.0 pytest-mock==0.4.3 # We don't use {[testenv:mkvenv]commands} here because that seems to be broken # on Ubuntu Trusty. From 1903792239156094c8864321af2c2424490edde4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 27 Apr 2015 12:22:03 +0200 Subject: [PATCH 40/40] tox: Update pyroma to 1.8.1. Changelog: - More robust rating. [Jeff Quast] - Closed #24. ("pyroma some_pypi_package" fails) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d1e337271..0e5654d03 100644 --- a/tox.ini +++ b/tox.ini @@ -82,7 +82,7 @@ commands = [testenv:pyroma] skip_install = true deps = - pyroma==1.7 + pyroma==1.8.1 docutils==0.12 commands = {[testenv:mkvenv]commands}