Skip to content

Commit 3ded42b

Browse files
committed
Added new "testing" module.
1 parent e86cbf1 commit 3ded42b

File tree

4 files changed

+217
-51
lines changed

4 files changed

+217
-51
lines changed

domdf_python_tools/testing.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/usr/bin/env python
2+
#
3+
# testing.py
4+
"""
5+
Handy functions for testing code.
6+
7+
Requires `pytest <https://docs.pytest.org/en/stable/>`_ to be installed.
8+
"""
9+
#
10+
# Copyright © 2020 Dominic Davis-Foster <[email protected]>
11+
#
12+
# This program is free software; you can redistribute it and/or modify
13+
# it under the terms of the GNU Lesser General Public License as published by
14+
# the Free Software Foundation; either version 3 of the License, or
15+
# (at your option) any later version.
16+
#
17+
# This program is distributed in the hope that it will be useful,
18+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
# GNU Lesser General Public License for more details.
21+
#
22+
# You should have received a copy of the GNU Lesser General Public License
23+
# along with this program; if not, write to the Free Software
24+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25+
# MA 02110-1301, USA.
26+
#
27+
28+
# stdlib
29+
import itertools
30+
import random
31+
from functools import lru_cache
32+
from typing import List, Sequence
33+
34+
# 3rd party
35+
import pytest
36+
from _pytest.mark import MarkDecorator
37+
38+
# this package
39+
from domdf_python_tools.utils import Len
40+
41+
42+
def testing_boolean_values(
43+
extra_truthy: Sequence = (),
44+
extra_falsy: Sequence = (),
45+
) -> MarkDecorator:
46+
"""
47+
Returns a `pytest.mark.parametrize <https://docs.pytest.org/en/stable/parametrize.html>`_
48+
decorator that provides a list of strings, integers and booleans, and the boolean representations of them.
49+
50+
The parametrized arguments are ``boolean_string`` for the input value,
51+
and ``expected_boolean`` for the expected output.
52+
53+
:param extra_truthy: Additional values that should be considered :py:obj:`True`.
54+
:param extra_falsy: Additional values that should be considered :py:obj:`False`.
55+
"""
56+
57+
truthy = [
58+
True,
59+
"True",
60+
"true",
61+
"tRUe",
62+
'y',
63+
'Y',
64+
"YES",
65+
"yes",
66+
"Yes",
67+
"yEs",
68+
"ON",
69+
"on",
70+
'1',
71+
1,
72+
*extra_truthy,
73+
]
74+
75+
falsy = [
76+
False,
77+
"False",
78+
"false",
79+
"falSE",
80+
'n',
81+
'N',
82+
"NO",
83+
"no",
84+
"nO",
85+
"OFF",
86+
"off",
87+
"oFF",
88+
'0',
89+
0,
90+
*extra_falsy,
91+
]
92+
93+
boolean_strings = [
94+
*itertools.zip_longest(truthy, [], fillvalue=True),
95+
*itertools.zip_longest(falsy, [], fillvalue=False),
96+
]
97+
98+
return pytest.mark.parametrize("boolean_string, expected_boolean", boolean_strings)
99+
100+
101+
whitespace = " \t\n\r"
102+
103+
104+
@lru_cache(1)
105+
def whitespace_perms_list() -> List[str]:
106+
chain = itertools.chain.from_iterable(itertools.permutations(whitespace, n) for n in Len(whitespace))
107+
return list("".join(x) for x in chain)
108+
109+
110+
def whitespace_perms(ratio: float = 0.5) -> MarkDecorator:
111+
r"""
112+
Returns a `pytest.mark.parametrize <https://docs.pytest.org/en/stable/parametrize.html>`_
113+
decorator that provides permutations of whitespace (strictly only ``␣\n\t\r``).
114+
Not all permutations are returned, as there are a lot of them;
115+
instead a random selection of the permutations is returned.
116+
By default ½ of the permutations are returned, but this can be configured using the ``ratio`` argument.
117+
118+
The single parametrized argument is ``char``.
119+
120+
:param ratio: The ratio of the number of permutations to select to the total number of permutations.
121+
"""
122+
123+
perms = whitespace_perms_list()
124+
return pytest.mark.parametrize("char", random.sample(perms, int(len(perms) * ratio)))
125+
126+
127+
def count(stop: int, start: int = 0, step: int = 1) -> MarkDecorator:
128+
"""
129+
Returns a `pytest.mark.parametrize <https://docs.pytest.org/en/stable/parametrize.html>`_
130+
decorator that provides a list of numbers between ``start`` and ``stop`` with an interval of ``step``.
131+
132+
The single parametrized argument is ``count``.
133+
134+
:param stop: The stop value passed to :class:`range`.
135+
:param start: The start value passed to :class:`range`.
136+
:param step: The step passed to :class:`range`.
137+
"""
138+
139+
return pytest.mark.parametrize("count", range(start, stop, step))

domdf_python_tools/utils.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@
88
#
99
# Copyright © 2018-2020 Dominic Davis-Foster <[email protected]>
1010
#
11-
# check_dependencies based on https://stackoverflow.com/a/29044693/3092681
12-
# Copyright © 2015 TehTechGuy
13-
# Licensed under CC-BY-SA
14-
#
15-
# as_text from https://stackoverflow.com/a/40935194
16-
# Copyright © 2016 User3759685
17-
# Available under the MIT License
18-
#
19-
# chunks from https://stackoverflow.com/a/312464/3092681
20-
# Copytight © 2008 Ned Batchelder
21-
# Licensed under CC-BY-SA
22-
#
2311
# This program is free software; you can redistribute it and/or modify
2412
# it under the terms of the GNU Lesser General Public License as published by
2513
# the Free Software Foundation; either version 3 of the License, or
@@ -35,6 +23,25 @@
3523
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
3624
# MA 02110-1301, USA.
3725
#
26+
# check_dependencies based on https://stackoverflow.com/a/29044693/3092681
27+
# Copyright © 2015 TehTechGuy
28+
# Licensed under CC-BY-SA
29+
#
30+
# as_text from https://stackoverflow.com/a/40935194
31+
# Copyright © 2016 User3759685
32+
# Available under the MIT License
33+
#
34+
# chunks from https://stackoverflow.com/a/312464/3092681
35+
# Copytight © 2008 Ned Batchelder
36+
# Licensed under CC-BY-SA
37+
#
38+
# strtobool based on the "distutils" module from CPython
39+
# Licensed under the Python Software Foundation License Version 2.
40+
# Copyright © 2001-2020 Python Software Foundation. All rights reserved.
41+
# Copyright © 2000 BeOpen.com. All rights reserved.
42+
# Copyright © 1995-2000 Corporation for National Research Initiatives. All rights reserved.
43+
# Copyright © 1991-1995 Stichting Mathematisch Centrum. All rights reserved.
44+
#
3845

3946
# stdlib
4047
import itertools
@@ -250,8 +257,6 @@ def strtobool(val: Union[str, bool]) -> bool:
250257
:py:obj:`False` values are ``'n'``, ``'no'``, ``'f'``, ``'false'``, ``'off'``, ``'0'``, and ``0``.
251258
252259
:raises: :py:exc:`ValueError` if 'val' is anything else.
253-
254-
Based on distutils
255260
"""
256261

257262
if isinstance(val, int):

tests/test_testing.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# stdlib
2+
import random
3+
4+
# 3rd party
5+
from _pytest.mark import Mark, MarkDecorator
6+
7+
# this package
8+
from domdf_python_tools.testing import count, testing_boolean_values, whitespace_perms
9+
from domdf_python_tools.utils import strtobool
10+
11+
12+
def test_count():
13+
assert isinstance(count(100), MarkDecorator)
14+
assert isinstance(count(100).mark, Mark)
15+
assert "count" in count(100).mark.args
16+
assert count(100).mark.args[0] == "count"
17+
assert count(100).mark.args[1] == range(0, 100)
18+
19+
assert count(10).mark.args[1] == range(0, 10)
20+
assert count(10, 5).mark.args[1] == range(5, 10) # order of count is "stop, start, step"
21+
assert count(10, 5, 2).mark.args[1] == range(5, 10, 2) # order of count is "stop, start, step"
22+
23+
24+
def test_whitespace_perms():
25+
random.seed(1234)
26+
27+
assert isinstance(whitespace_perms(), MarkDecorator)
28+
assert isinstance(whitespace_perms().mark, Mark)
29+
assert "char" in whitespace_perms().mark.args
30+
assert whitespace_perms().mark.args[0] == "char"
31+
assert len(whitespace_perms().mark.args[1]) == 20
32+
assert len(whitespace_perms(1).mark.args[1]) == 41
33+
assert len(whitespace_perms(.1).mark.args[1]) == 4
34+
35+
assert isinstance(whitespace_perms(.1).mark.args[1], list)
36+
assert isinstance(whitespace_perms(.1).mark.args[1][0], str)
37+
38+
assert whitespace_perms(.1).mark.args[1] == ['\n\t\r', '\r\t', '\t \n', '\n\r']
39+
# TODO: some test that they're all whitespace
40+
41+
42+
def test_testing_boolean_strings():
43+
assert isinstance(testing_boolean_values(), MarkDecorator)
44+
assert isinstance(testing_boolean_values().mark, Mark)
45+
assert "boolean_string, expected_boolean" in testing_boolean_values().mark.args
46+
assert testing_boolean_values().mark.args[0] == "boolean_string, expected_boolean"
47+
assert len(testing_boolean_values().mark.args[1]) == 28
48+
assert isinstance(testing_boolean_values().mark.args[1], list)
49+
assert isinstance(testing_boolean_values().mark.args[1][0], tuple)
50+
assert len(testing_boolean_values().mark.args[1][0]) == 2
51+
assert isinstance(testing_boolean_values().mark.args[1][0][0], bool)
52+
assert isinstance(testing_boolean_values().mark.args[1][0][1], bool)
53+
54+
for value, expects in testing_boolean_values().mark.args[1]:
55+
assert strtobool(value) is expects

tests/test_utils.py

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
# this package
2121
from domdf_python_tools import utils
22+
from domdf_python_tools.testing import testing_boolean_values
2223
from domdf_python_tools.utils import Len, chunks, double_chain, list2str, list2string, pyversion, str2tuple
2324

2425

@@ -283,43 +284,9 @@ def test_str2tuple_semicolon(value, expects):
283284
assert str2tuple(value, sep=';') == expects
284285

285286

286-
@pytest.mark.parametrize(
287-
"obj, expects",
288-
[
289-
(True, True),
290-
("True", True),
291-
("true", True),
292-
("tRUe", True),
293-
('y', True),
294-
('Y', True),
295-
("YES", True),
296-
("yes", True),
297-
("Yes", True),
298-
("yEs", True),
299-
("ON", True),
300-
("on", True),
301-
('1', True),
302-
(1, True),
303-
(50, True),
304-
(-1, True),
305-
(False, False),
306-
("False", False),
307-
("false", False),
308-
("falSE", False),
309-
('n', False),
310-
('N', False),
311-
("NO", False),
312-
("no", False),
313-
("nO", False),
314-
("OFF", False),
315-
("off", False),
316-
("oFF", False),
317-
('0', False),
318-
(0, False),
319-
],
320-
)
321-
def test_strtobool(obj, expects):
322-
assert utils.strtobool(obj) == expects
287+
@testing_boolean_values(extra_truthy=[50, -1])
288+
def test_strtobool(boolean_string, expected_boolean):
289+
assert utils.strtobool(boolean_string) == expected_boolean
323290

324291

325292
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)