Skip to content

Commit b3f2260

Browse files
committed
Add new functions to testing.py
1 parent 5647ea4 commit b3f2260

File tree

3 files changed

+146
-44
lines changed

3 files changed

+146
-44
lines changed

domdf_python_tools/testing.py

Lines changed: 112 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,21 @@
3535
import sys
3636
from functools import lru_cache
3737
from pathlib import Path
38-
from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union
38+
from textwrap import dedent
39+
from typing import Any, Callable, Iterator, List, Optional, Sequence, Tuple, Union
3940

4041
# 3rd party
4142
import pytest # nodep
4243
from _pytest.mark import MarkDecorator # nodep
44+
from jaraco.docker import is_docker # type: ignore # nodep
45+
from pytest_regressions.file_regression import FileRegressionFixture # nodep
4346

4447
# this package
4548
from domdf_python_tools.doctools import PYPY
4649
from domdf_python_tools.iterative import Len
4750
from domdf_python_tools.paths import PathPlus
51+
from domdf_python_tools.stringlist import StringList
52+
from domdf_python_tools.typing import PathLike
4853
from domdf_python_tools.versions import Version
4954

5055
__all__ = [
@@ -63,6 +68,13 @@
6368
"only_pypy",
6469
"pytest_report_header",
6570
"PEP_563",
71+
"platform_boolean_factory",
72+
"not_macos",
73+
"only_macos",
74+
"not_docker",
75+
"only_docker",
76+
"check_file_regression",
77+
"check_file_output",
6678
]
6779

6880
MarkDecorator.__module__ = "_pytest.mark"
@@ -286,64 +298,71 @@ def max_version(
286298
return pytest.mark.skipif(condition=sys.version_info[:3] > version_, reason=reason)
287299

288300

289-
def not_windows(reason: str = "Not required on Windows.", ) -> MarkDecorator:
301+
def platform_boolean_factory(
302+
condition: bool,
303+
platform: str,
304+
versionadded: Optional[str] = None,
305+
) -> Tuple[Callable[[str], MarkDecorator], Callable[[str], MarkDecorator]]:
290306
"""
291-
Factory function to return a ``@pytest.mark.skipif`` decorator that will
292-
skip a test if the current platform is Windows.
293-
294-
:param reason: The reason to display when skipping.
295-
296-
:rtype:
297-
298-
.. versionadded:: 0.9.0
299-
""" # noqa D400
307+
Factory function to return decorators such as :func:`~.not_pypy` and :func:`~.only_windows`.
300308
301-
return pytest.mark.skipif(condition=sys.platform == "win32", reason=reason)
309+
:param condition: Should evaluate to :py:obj:`True` if the test should be skipped.
310+
:param platform:
311+
:param versionadded:
302312
313+
:return: 2-element tuple of ``not_function``, ``only_function``.
303314
304-
def only_windows(reason: str = "Only required on Windows.", ) -> MarkDecorator:
315+
.. versionadded: 1.5.0
305316
"""
306-
Factory function to return a ``@pytest.mark.skipif`` decorator that will
307-
skip a test if the current platform is **not** Windows.
308317

309-
:param reason: The reason to display when skipping.
318+
default_reason = "{} required on Windows"
310319

311-
:rtype:
320+
def not_function(reason: str = default_reason.format("Not")) -> MarkDecorator:
321+
return pytest.mark.skipif(condition=condition, reason=reason)
312322

313-
.. versionadded:: 0.9.0
314-
""" # noqa D400
323+
def only_function(reason: str = default_reason.format("Only")) -> MarkDecorator:
324+
return pytest.mark.skipif(condition=not condition, reason=reason)
315325

316-
return pytest.mark.skipif(condition=sys.platform != "win32", reason=reason)
326+
docstring = dedent(
327+
"""\
328+
Factory function to return a ``@pytest.mark.skipif`` decorator that will
329+
skip a test {why} the current platform is {platform}.
317330
331+
:param reason: The reason to display when skipping.
332+
"""
333+
)
318334

319-
def not_pypy(reason: str = "Not required on PyPy.") -> MarkDecorator:
320-
"""
321-
Factory function to return a ``@pytest.mark.skipif`` decorator that will
322-
skip a test if the current Python implementation is PyPy.
335+
if versionadded:
336+
docstring += f"\n\n:rtype:\n\n.. versionadded:: {versionadded}"
323337

324-
:param reason: The reason to display when skipping.
338+
not_function.__name__ = f"not_{platform.lower()}"
339+
not_function.__doc__ = docstring.format(why="if", platform=platform)
325340

326-
:rtype:
341+
only_function.__name__ = f"only_{platform.lower()}"
342+
only_function.__doc__ = docstring.format(why="unless", platform=platform)
327343

328-
.. versionadded:: 0.9.0
329-
""" # noqa D400
344+
return not_function, only_function
330345

331-
return pytest.mark.skipif(condition=PYPY, reason=reason)
332346

347+
not_windows, only_windows = platform_boolean_factory(
348+
condition=sys.platform == "win32",
349+
platform="Windows",
350+
versionadded="0.9.0",
351+
)
333352

334-
def only_pypy(reason: str = "Only required on PyPy.") -> MarkDecorator:
335-
"""
336-
Factory function to return a ``@pytest.mark.skipif`` decorator that will
337-
skip a test if the current Python implementation is not PyPy.
353+
not_macos, only_macos = platform_boolean_factory(
354+
condition=sys.platform == "darwin",
355+
platform="macOS",
356+
versionadded="1.5.0",
357+
)
338358

339-
:param reason: The reason to display when skipping.
359+
not_docker, only_docker = platform_boolean_factory(condition=is_docker(), platform="Docker", versionadded="1.5.0")
360+
not_docker.__doc__ = not_docker.__doc__.replace("the current platform is", "running on")
361+
only_docker.__doc__ = only_docker.__doc__.replace("the current platform is", "running on")
340362

341-
:rtype:
342-
343-
.. versionadded:: 0.9.0
344-
""" # noqa D400
345-
346-
return pytest.mark.skipif(condition=not PYPY, reason=reason)
363+
not_pypy, only_pypy = platform_boolean_factory(condition=PYPY, platform="Docker", versionadded="0.9.0")
364+
not_pypy.__doc__ = not_pypy.__doc__.replace("current platform", "current Python implementation")
365+
only_pypy.__doc__ = only_pypy.__doc__.replace("current platform", "current Python implementation")
347366

348367

349368
@pytest.fixture()
@@ -388,3 +407,56 @@ def pytest_report_header(config, startdir):
388407
389408
.. versionadded:: 1.4.2
390409
"""
410+
411+
412+
def check_file_regression(
413+
data: Union[str, StringList],
414+
file_regression: FileRegressionFixture,
415+
extension: str = ".txt",
416+
**kwargs,
417+
):
418+
r"""
419+
Check the given data against that in the reference file.
420+
421+
:param data:
422+
:param file_regression: The file regression fixture for the test.
423+
:param extension: The extension of the reference file.
424+
:param \*\*kwargs: Additional keyword arguments passed to :meth:`.FileRegressionFixture.check`.
425+
426+
.. versionadded:: 1.5.0
427+
"""
428+
429+
if isinstance(data, StringList):
430+
data = str(data)
431+
432+
file_regression.check(data, encoding="UTF-8", extension=extension, **kwargs)
433+
434+
return True
435+
436+
437+
def check_file_output(
438+
filename: PathLike,
439+
file_regression: FileRegressionFixture,
440+
extension: Optional[str] = None,
441+
**kwargs,
442+
):
443+
r"""
444+
Check the content of the given file against the reference file.
445+
446+
:param filename:
447+
:param file_regression: The file regression fixture for the test.
448+
:param extension: The extension of the reference file.
449+
:param \*\*kwargs: Additional keyword arguments passed to :meth:`.FileRegressionFixture.check`.
450+
451+
.. versionadded:: 1.5.0
452+
"""
453+
454+
filename = PathPlus(filename)
455+
456+
data = filename.read_text(encoding="UTF-8")
457+
extension = extension or filename.suffix
458+
459+
if extension == ".py":
460+
extension = "._py_"
461+
462+
return check_file_regression(data, file_regression, extension, **kwargs)

repo_helper.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ short_desc: 'Helpful functions for Python 🐍 🛠️'
3030

3131
classifiers:
3232
- 'Development Status :: 4 - Beta'
33-
# - "Development Status :: 5 - Production/Stable"
34-
# - "Development Status :: 6 - Mature"
35-
# - "Development Status :: 7 - Inactive"
3633
- 'Intended Audience :: Developers'
3734
- 'Topic :: Software Development :: Libraries :: Python Modules'
3835

@@ -41,6 +38,8 @@ extras_require:
4138
- pytz>=2019.1
4239
testing:
4340
- pytest>=6.0.0
41+
- pytest-regressions>=2.0.2
42+
- jaraco.docker>=2.0
4443

4544
keywords:
4645
- utilities

tests/test_testing.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
import platform
33
import random
44
import re
5+
import sys
56

67
# 3rd party
78
from _pytest.mark import Mark, MarkDecorator
89

910
# this package
1011
from domdf_python_tools import testing
1112
from domdf_python_tools.paths import PathPlus
12-
from domdf_python_tools.testing import not_pypy
13+
from domdf_python_tools.testing import not_macos, not_pypy, not_windows, only_macos, only_pypy, only_windows
1314
from domdf_python_tools.utils import strtobool
1415

1516

@@ -149,6 +150,36 @@ def test_not_pypy():
149150
assert False # noqa: PT015
150151

151152

153+
@only_pypy("Success")
154+
def test_only_pypy():
155+
if platform.python_implementation() != "PyPy":
156+
assert False # noqa: PT015
157+
158+
159+
@not_windows("Success")
160+
def test_not_windows():
161+
if sys.platform == "win32":
162+
assert False # noqa: PT015
163+
164+
165+
@only_windows("Success")
166+
def test_only_windows():
167+
if sys.platform != "win32":
168+
assert False # noqa: PT015
169+
170+
171+
@not_macos("Success")
172+
def test_not_macos():
173+
if sys.platform == "darwin":
174+
assert False # noqa: PT015
175+
176+
177+
@only_macos("Success")
178+
def test_only_macos():
179+
if sys.platform != "darwin":
180+
assert False # noqa: PT015
181+
182+
152183
pytest_plugins = ("domdf_python_tools.testing", )
153184

154185

0 commit comments

Comments
 (0)