diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 019facb4..00000000 --- a/.coveragerc +++ /dev/null @@ -1,6 +0,0 @@ -[run] -omit = - # The tests themselves - tests/* - # Any venv files - venv/* diff --git a/Contributing.md b/Contributing.md index 4d7fc9fc..29135804 100644 --- a/Contributing.md +++ b/Contributing.md @@ -244,6 +244,46 @@ handy if you want to step through the testing code to be sure of anything. Otherwise, see the [pytest documentation](https://docs.pytest.org/en/stable/contents.html). +### Test Coverage +As we work towards actually having tests for as much of the code as possible +it is useful to monitor the current test coverage. + +Running `pytest` will also produce the overall coverage report, see the +configured options in `pyproject.toml`. + +One issue you might run into is where there is code that only runs on one +platform. By default `pytest-cov`/`coverage` will count this code as not +tested when run on a different platform. We utilise the +`coverage-conditional-plugin` module so that `#pragma` comments can be used +to give hints to coverage about this. + +The pragmas are defined in the +`tool.coverage.coverage_conditional_plugin.rules` section of `pyproject.toml`, +e.g. + +```toml +[tool.coverage.coverage_conditional_plugin.rules] +sys-platform-win32 = "sys_platform != 'win32'" +... +``` +And are used as in: +```python +import sys + +if sys.platform == 'win32': # pragma: sys-platform-win32 + ... +else: # pragma: sys-platform-not-win32 + ... +``` +Note the inverted sense of the pragma definitions, as the comments cause +`coverage` to *not* consider that code block on this platform. + +As of 2022-10-02 and `coverage-conditional-plugin==0.7.0` there is no way to +signal that an entire file should be excluded from coverage reporting on the +current platform. See +[this GitHub issue comment](https://github.com/wemake-services/coverage-conditional-plugin/issues/2#issuecomment-1263918296) +. + --- ## Imports used only in core plugins diff --git a/config/__init__.py b/config/__init__.py index cc125129..56679019 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -454,19 +454,19 @@ def get_config(*args, **kwargs) -> AbstractConfig: :param kwargs: Args to be passed through to implementation. :return: Instance of the implementation. """ - if sys.platform == "darwin": + if sys.platform == "darwin": # pragma: sys-platform-darwin from .darwin import MacConfig return MacConfig(*args, **kwargs) - elif sys.platform == "win32": + elif sys.platform == "win32": # pragma: sys-platform-win32 from .windows import WinConfig return WinConfig(*args, **kwargs) - elif sys.platform == "linux": + elif sys.platform == "linux": # pragma: sys-platform-linux from .linux import LinuxConfig return LinuxConfig(*args, **kwargs) - else: + else: # pragma: sys-platform-not-known raise ValueError(f'Unknown platform: {sys.platform=}') diff --git a/config/darwin.py b/config/darwin.py index eb2b887f..895218a8 100644 --- a/config/darwin.py +++ b/config/darwin.py @@ -1,3 +1,4 @@ +"""Darwin/macOS implementation of AbstractConfig.""" import pathlib import sys from typing import Any, Dict, List, Union diff --git a/journal_lock.py b/journal_lock.py index 91a7895f..ef5cf983 100644 --- a/journal_lock.py +++ b/journal_lock.py @@ -94,7 +94,7 @@ class JournalLock: :return: LockResult - See the class Enum definition """ - if sys.platform == 'win32': + if sys.platform == 'win32': # pragma: sys-platform-win32 logger.trace_if('journal-lock', 'win32, using msvcrt') # win32 doesn't have fcntl, so we have to use msvcrt import msvcrt @@ -107,7 +107,7 @@ class JournalLock: f", assuming another process running: {e!r}") return JournalLockResult.ALREADY_LOCKED - else: # pytest coverage only sees this on !win32 + else: # pragma: sys-platform-not-win32 logger.trace_if('journal-lock', 'NOT win32, using fcntl') try: import fcntl @@ -143,7 +143,7 @@ class JournalLock: return True # We weren't locked, and still aren't unlocked = False - if sys.platform == 'win32': + if sys.platform == 'win32': # pragma: sys-platform-win32 logger.trace_if('journal-lock', 'win32, using msvcrt') # win32 doesn't have fcntl, so we have to use msvcrt import msvcrt @@ -160,7 +160,7 @@ class JournalLock: else: unlocked = True - else: # pytest coverage only sees this on !win32 + else: # pragma: sys-platform-not-win32 logger.trace_if('journal-lock', 'NOT win32, using fcntl') try: import fcntl diff --git a/pyproject.toml b/pyproject.toml index a7f41ad4..91c6e7e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,25 @@ line_length = 119 [tool.pytest.ini_options] testpaths = ["tests"] # Search for tests in tests/ +addopts = "--cov . --cov plugins --cov-report=term-missing --no-cov-on-fail" +# --cov-fail-under 80" [tool.coverage.run] -omit = ["venv/*"] # when running pytest --cov, dont report coverage in venv directories +omit = [ "tests/*", "venv/*", "dist.win32/*" ] +plugins = [ "coverage_conditional_plugin" ] + +[tool.coverage.coverage_conditional_plugin.rules] +# NB: The name versus content of all of these are inverted because of the way +# they're used. When a pragma cites one it causes that code block to +# **NOT** be considered for code coverage. +# See Contributing.md#test-coverage for more details. +sys-platform-win32 = "sys_platform != 'win32'" +sys-platform-not-win32 = "sys_platform == 'win32'" +sys-platform-darwin = "sys_platform != 'darwin'" +sys-platform-not-darwin = "sys_platform == 'darwin'" +sys-platform-linux = "sys_platform != 'linux'" +sys-platform-not-linux = "sys_platform == 'linux'" +sys-platform-not-known = "sys_platform in ('darwin', 'linux', 'win32')" [tool.pyright] # pythonPlatform = 'Darwin' diff --git a/requirements-dev.txt b/requirements-dev.txt index a9f569f5..2ae38e0a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -43,6 +43,7 @@ py2exe==0.12.0.1; sys_platform == 'win32' pytest==7.1.3 pytest-cov==4.0.0 # Pytest code coverage support coverage[toml]==6.5.0 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs +coverage-conditional-plugin==0.7.0 # For manipulating folder permissions and the like. pywin32==304; sys_platform == 'win32'