Merge pull request #19 from hackebrot/parametrize-sub-tests

Parametrize sub tests
This commit is contained in:
Florian Bruhin 2015-04-14 07:01:53 +02:00
commit 987bab9960
5 changed files with 189 additions and 155 deletions

View File

@ -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

View File

@ -63,44 +63,27 @@ 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 = [
# (input_key, supports_count, expected)
('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')

View File

@ -18,8 +18,9 @@
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Tests for qutebrowser.misc.split."""
import collections
import unittest
import pytest
from qutebrowser.misc import split
@ -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/
@ -104,36 +105,56 @@ foo\ bar/foo bar/foo\ bar/
"""
class SplitTests(unittest.TestCase):
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."""
def test_split(self):
@pytest.fixture(params=_parse_split_test_data_str())
def split_test_case(self, request):
"""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
def test_split(self, split_test_case):
"""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(split_test_case.input)
assert items == split_test_case.keep
def test_split_keep_original(self):
def test_split_keep_original(self, split_test_case):
"""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(split_test_case.input, keep=True)
assert ''.join(items) == split_test_case.input
def test_split_keep(self):
def test_split_keep(self, split_test_case):
"""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(split_test_case.input, keep=True)
assert items == split_test_case.no_keep
class SimpleSplitTests(unittest.TestCase):
class TestSimpleSplit:
"""Test simple_split."""
@ -145,27 +166,20 @@ 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))
@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
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))
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

View File

@ -0,0 +1,70 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""
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():
"""Yield "good" (C data type, value) tuples.
Those should pass overflow checking.
"""
for ctype, values in GOOD_VALUES.items():
for value in values:
yield ctype, value
def iter_bad_values():
"""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:
yield ctype, value, repl

View File

@ -20,107 +20,74 @@
"""Tests for qutebrowser.utils.qtutils."""
import sys
import unittest
import pytest
from qutebrowser import qutebrowser
from qutebrowser.utils import qtutils
import overflow_test_cases
class CheckOverflowTests(unittest.TestCase):
class TestCheckOverflow:
"""Test check_overflow.
"""Test check_overflow."""
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)],
}
def test_good_values(self):
@pytest.mark.parametrize('ctype, val',
overflow_test_cases.iter_good_values())
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)
def test_bad_values_fatal(self):
@pytest.mark.parametrize('ctype, val',
[(ctype, val) for (ctype, val, _) in
overflow_test_cases.iter_bad_values()])
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):
with pytest.raises(OverflowError):
qtutils.check_overflow(val, ctype)
def test_bad_values_nonfatal(self):
@pytest.mark.parametrize('ctype, val, repl',
overflow_test_cases.iter_bad_values())
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)
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):
"""Fixture to provide an argparser.
def test_no_qt_args(self):
Monkey-patches .exit() of the argparser so it doesn't exit on errors.
"""
parser = qutebrowser.get_argparser()
mocker.patch.object(parser, 'exit', side_effect=Exception)
return parser
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