|
35 | 35 | import sys
|
36 | 36 | from functools import lru_cache
|
37 | 37 | 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 |
39 | 40 |
|
40 | 41 | # 3rd party
|
41 | 42 | import pytest # nodep
|
42 | 43 | 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 |
43 | 46 |
|
44 | 47 | # this package
|
45 | 48 | from domdf_python_tools.doctools import PYPY
|
46 | 49 | from domdf_python_tools.iterative import Len
|
47 | 50 | from domdf_python_tools.paths import PathPlus
|
| 51 | +from domdf_python_tools.stringlist import StringList |
| 52 | +from domdf_python_tools.typing import PathLike |
48 | 53 | from domdf_python_tools.versions import Version
|
49 | 54 |
|
50 | 55 | __all__ = [
|
|
63 | 68 | "only_pypy",
|
64 | 69 | "pytest_report_header",
|
65 | 70 | "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", |
66 | 78 | ]
|
67 | 79 |
|
68 | 80 | MarkDecorator.__module__ = "_pytest.mark"
|
@@ -286,64 +298,71 @@ def max_version(
|
286 | 298 | return pytest.mark.skipif(condition=sys.version_info[:3] > version_, reason=reason)
|
287 | 299 |
|
288 | 300 |
|
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]]: |
290 | 306 | """
|
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`. |
300 | 308 |
|
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: |
302 | 312 |
|
| 313 | + :return: 2-element tuple of ``not_function``, ``only_function``. |
303 | 314 |
|
304 |
| -def only_windows(reason: str = "Only required on Windows.", ) -> MarkDecorator: |
| 315 | + .. versionadded: 1.5.0 |
305 | 316 | """
|
306 |
| - Factory function to return a ``@pytest.mark.skipif`` decorator that will |
307 |
| - skip a test if the current platform is **not** Windows. |
308 | 317 |
|
309 |
| - :param reason: The reason to display when skipping. |
| 318 | + default_reason = "{} required on Windows" |
310 | 319 |
|
311 |
| - :rtype: |
| 320 | + def not_function(reason: str = default_reason.format("Not")) -> MarkDecorator: |
| 321 | + return pytest.mark.skipif(condition=condition, reason=reason) |
312 | 322 |
|
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) |
315 | 325 |
|
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}. |
317 | 330 |
|
| 331 | +:param reason: The reason to display when skipping. |
| 332 | +""" |
| 333 | + ) |
318 | 334 |
|
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}" |
323 | 337 |
|
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) |
325 | 340 |
|
326 |
| - :rtype: |
| 341 | + only_function.__name__ = f"only_{platform.lower()}" |
| 342 | + only_function.__doc__ = docstring.format(why="unless", platform=platform) |
327 | 343 |
|
328 |
| - .. versionadded:: 0.9.0 |
329 |
| - """ # noqa D400 |
| 344 | + return not_function, only_function |
330 | 345 |
|
331 |
| - return pytest.mark.skipif(condition=PYPY, reason=reason) |
332 | 346 |
|
| 347 | +not_windows, only_windows = platform_boolean_factory( |
| 348 | + condition=sys.platform == "win32", |
| 349 | + platform="Windows", |
| 350 | + versionadded="0.9.0", |
| 351 | + ) |
333 | 352 |
|
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 | + ) |
338 | 358 |
|
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") |
340 | 362 |
|
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") |
347 | 366 |
|
348 | 367 |
|
349 | 368 | @pytest.fixture()
|
@@ -388,3 +407,56 @@ def pytest_report_header(config, startdir):
|
388 | 407 |
|
389 | 408 | .. versionadded:: 1.4.2
|
390 | 409 | """
|
| 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) |
0 commit comments