Skip to content

Commit 3f2c8fc

Browse files
hsegwoodruffw
andauthored
test-cache: Fix XDG tests, make tests more consistent (#880)
Co-authored-by: William Woodruff <[email protected]> Co-authored-by: William Woodruff <[email protected]>
1 parent 06bfcdc commit 3f2c8fc

File tree

6 files changed

+119
-114
lines changed

6 files changed

+119
-114
lines changed

.github/workflows/ci.yml

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,83 @@ jobs:
3535
- name: test
3636
run: make test PIP_AUDIT_EXTRA=test
3737

38+
- name: Upload coverage data
39+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
40+
with:
41+
name: coverage-data-${{ matrix.python }}
42+
path: .coverage.*
43+
include-hidden-files: true
44+
if-no-files-found: ignore
45+
46+
test-windows:
47+
runs-on: windows-latest
48+
steps:
49+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
50+
with:
51+
persist-credentials: false
52+
53+
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
54+
with:
55+
# Always test with latest Python on Windows.
56+
python-version: "3.x"
57+
cache: "pip"
58+
cache-dependency-path: pyproject.toml
59+
60+
- name: test
61+
run: make test PIP_AUDIT_EXTRA=test
62+
63+
- name: Upload coverage data
64+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
65+
with:
66+
name: coverage-data-windows
67+
path: .coverage.*
68+
include-hidden-files: true
69+
if-no-files-found: ignore
70+
71+
coverage:
72+
name: Combine & check coverage
73+
if: always()
74+
needs: [test, test-windows]
75+
runs-on: ubuntu-latest
76+
77+
steps:
78+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
79+
with:
80+
persist-credentials: false
81+
82+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
83+
with:
84+
python-version: "3.x"
85+
86+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
87+
with:
88+
pattern: coverage-data-*
89+
merge-multiple: true
90+
91+
- name: Combine coverage & fail if it's <100%
92+
run: |
93+
make dev PIP_AUDIT_EXTRA=cov
94+
95+
./env/bin/python -Im coverage combine
96+
./env/bin/python -Im coverage html --skip-covered --skip-empty
97+
98+
# Report and write to summary.
99+
./env/bin/python -Im coverage report --format=markdown >> "${GITHUB_STEP_SUMMARY}"
100+
101+
# Report again and fail if under 100%.
102+
./env/bin/python -Im coverage report --fail-under=100
103+
104+
- name: Upload HTML report if check failed
105+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
106+
with:
107+
name: html-report
108+
path: htmlcov
109+
if: ${{ failure() }}
110+
38111
all-tests-pass:
39112
if: always()
40113

41-
needs:
42-
- test
114+
needs: [coverage]
43115

44116
runs-on: ubuntu-latest
45117

Makefile

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,10 @@ PIP_AUDIT_EXTRA := dev
2323

2424
# If the user selects a specific test pattern to run, set `pytest` to fail fast
2525
# and only run tests that match the pattern.
26-
# Otherwise, run all tests and enable coverage assertions, since we expect
27-
# complete test coverage.
2826
ifneq ($(TESTS),)
2927
TEST_ARGS := -x -k $(TESTS)
30-
COV_ARGS :=
3128
else
3229
TEST_ARGS :=
33-
COV_ARGS := --fail-under 100
3430
endif
3531

3632
.PHONY: all
@@ -67,8 +63,7 @@ reformat:
6763
.PHONY: test tests
6864
test tests: $(VENV)/pyvenv.cfg
6965
. $(VENV_BIN)/activate && \
70-
pytest --cov=$(PY_MODULE) $(T) $(TEST_ARGS) && \
71-
python -m coverage report -m $(COV_ARGS)
66+
coverage run -m pytest $(T) $(TEST_ARGS)
7267

7368
.PHONY: doc
7469
doc: $(VENV)/pyvenv.cfg

pip_audit/_cli.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@
3434
)
3535
from pip_audit._service import EcosystemsService, OsvService, PyPIService
3636
from pip_audit._service.interface import ConnectionError as VulnServiceConnectionError
37-
from pip_audit._service.interface import ResolvedDependency, SkippedDependency, VulnerabilityService
37+
from pip_audit._service.interface import (
38+
Dependency,
39+
ResolvedDependency,
40+
SkippedDependency,
41+
VulnerabilityResult,
42+
VulnerabilityService,
43+
)
3844
from pip_audit._state import AuditSpinner, AuditState
3945
from pip_audit._util import assert_never
4046

@@ -538,7 +544,7 @@ def audit() -> None: # pragma: no cover
538544
# wants to dry-run the "fix" step instead of the "audit" step
539545
auditor = Auditor(service, options=AuditOptions(dry_run=args.dry_run and not args.fix))
540546

541-
result = {}
547+
result: dict[Dependency, list[VulnerabilityResult]] = {}
542548
pkg_count = 0
543549
vuln_count = 0
544550
skip_count = 0

pyproject.toml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,10 @@ dependencies = [
4040
requires-python = ">=3.9"
4141

4242
[project.optional-dependencies]
43-
test = [
43+
cov = [
4444
"coverage[toml] ~= 7.0, != 7.3.3", # https://github.com/nedbat/coveragepy/issues/1713
45-
"pretend",
46-
"pytest",
47-
"pytest-cov",
4845
]
46+
test = ["pretend", "pytest", "pip-audit[cov]"]
4947
lint = [
5048
"ruff >= 0.11",
5149
"interrogate ~= 1.6",
@@ -64,6 +62,19 @@ Homepage = "https://pypi.org/project/pip-audit/"
6462
Issues = "https://github.com/pypa/pip-audit/issues"
6563
Source = "https://github.com/pypa/pip-audit"
6664

65+
[tool.coverage.paths]
66+
# This is used for path mapping when combining coverage data
67+
# from multiple machines. The first entry is the local path,
68+
# and subsequent entries are the remote paths that get remapped
69+
# to the local path.
70+
# See: https://coverage.readthedocs.io/en/latest/config.html#paths
71+
source = ["pip_audit", "*/pip_audit", "*\\pip_audit"]
72+
73+
[tool.coverage.run]
74+
source = ["pip_audit"]
75+
parallel = true
76+
relative_files = true
77+
6778
[tool.interrogate]
6879
# don't enforce documentation coverage for packaging, testing, the virtual
6980
# environment, or the CLI (which is documented separately).

test/dependency_source/test_pip.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ def test_pip_source_warns_about_old_pip(monkeypatch):
5454
monkeypatch.setattr(pip, "logger", logger)
5555

5656
pip.PipSource()
57-
assert logger.warning.calls == [
57+
assert (
5858
pretend.call(
5959
"pip 1.0.0 is very old, and may not provide reliable dependency information! "
6060
"You are STRONGLY encouraged to upgrade to a newer version of pip."
6161
)
62-
]
62+
in logger.warning.calls
63+
)
6364

6465

6566
def test_pip_source_pip_api_failure(monkeypatch):
@@ -75,7 +76,9 @@ def explode():
7576

7677

7778
def test_pip_source_invalid_version(monkeypatch):
78-
logger = pretend.stub(debug=pretend.call_recorder(lambda s: None))
79+
logger = pretend.stub(
80+
debug=pretend.call_recorder(lambda s: None), warning=pretend.call_recorder(lambda s: None)
81+
)
7982
monkeypatch.setattr(pip, "logger", logger)
8083

8184
source = pip.PipSource()

test/test_cache.py

Lines changed: 15 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,24 @@
1-
import importlib
2-
import sys
31
from pathlib import Path
42

5-
import platformdirs
63
import pretend # type: ignore
7-
import pytest
84
from packaging.version import Version
9-
from pytest import MonkeyPatch
5+
from platformdirs import user_cache_path
106

117
import pip_audit._cache as cache
128
from pip_audit._cache import _get_cache_dir, _get_pip_cache
139

1410

15-
def _patch_platformdirs(monkeypatch: MonkeyPatch, sys_platform: str) -> None:
16-
"""Utility function to patch `platformdirs` in order to test cross-platforms."""
17-
# Mocking OS host
18-
monkeypatch.setattr(sys, "platform", sys_platform)
19-
# We are forced to reload `platformdirs` to get the correct cache directory
20-
# as cache definition is stored in the top level `__init__.py` file of the
21-
# `platformdirs` package
22-
importlib.reload(platformdirs)
23-
if sys_platform == "win32":
24-
monkeypatch.setenv("LOCALAPPDATA", "/tmp/AppData/Local")
25-
26-
2711
def test_get_cache_dir(monkeypatch):
2812
# When we supply a cache directory, always use that
29-
cache_dir = _get_cache_dir(Path("/tmp/foo/cache_dir"))
30-
assert cache_dir.as_posix() == "/tmp/foo/cache_dir"
13+
cache_dir = Path("/tmp/foo/cache_dir")
14+
assert _get_cache_dir(cache_dir) == cache_dir
3115

32-
get_pip_cache = pretend.call_recorder(lambda: Path("/fake/pip/cache/dir"))
16+
cache_dir = Path("/fake/pip/cache/dir")
17+
get_pip_cache = pretend.call_recorder(lambda: cache_dir)
3318
monkeypatch.setattr(cache, "_get_pip_cache", get_pip_cache)
3419

3520
# When `pip cache dir` works, we use it. In this case, it's mocked.
36-
cache_dir = _get_cache_dir(None, use_pip=True)
37-
assert cache_dir.as_posix() == "/fake/pip/cache/dir"
21+
assert _get_cache_dir(None, use_pip=True) == cache_dir
3822

3923

4024
def test_get_pip_cache():
@@ -43,96 +27,30 @@ def test_get_pip_cache():
4327
assert cache_dir.stem == "http"
4428

4529

46-
@pytest.mark.parametrize(
47-
"sys_platform,expected",
48-
[
49-
pytest.param(
50-
"linux",
51-
Path.home() / ".cache" / "pip-audit",
52-
id="on Linux",
53-
),
54-
pytest.param(
55-
"win32",
56-
Path("/tmp") / "AppData" / "Local" / "pip-audit" / "Cache",
57-
id="on Windows",
58-
),
59-
pytest.param(
60-
"darwin",
61-
Path.home() / "Library" / "Caches" / "pip-audit",
62-
id="on MacOS",
63-
),
64-
],
65-
)
66-
def test_get_cache_dir_do_not_use_pip(monkeypatch, sys_platform, expected):
67-
# Check cross-platforms
68-
_patch_platformdirs(monkeypatch, sys_platform)
30+
def test_get_cache_dir_do_not_use_pip():
31+
expected = user_cache_path("pip-audit", appauthor=False)
32+
6933
# Even with None, we never use the pip cache if we're told not to.
70-
cache_dir = _get_cache_dir(None, use_pip=False)
71-
assert cache_dir == expected
34+
assert _get_cache_dir(None, use_pip=False) == expected
7235

7336

74-
@pytest.mark.parametrize(
75-
"sys_platform,expected",
76-
[
77-
pytest.param(
78-
"linux",
79-
Path.home() / ".cache" / "pip-audit",
80-
id="on Linux",
81-
),
82-
pytest.param(
83-
"win32",
84-
Path("/tmp") / "AppData" / "Local" / "pip-audit" / "Cache",
85-
id="on Windows",
86-
),
87-
pytest.param(
88-
"darwin",
89-
Path.home() / "Library" / "Caches" / "pip-audit",
90-
id="on MacOS",
91-
),
92-
],
93-
)
94-
def test_get_cache_dir_pip_disabled_in_environment(monkeypatch, sys_platform, expected):
37+
def test_get_cache_dir_pip_disabled_in_environment(monkeypatch):
9538
monkeypatch.setenv("PIP_NO_CACHE_DIR", "1")
96-
# Check cross-platforms
97-
_patch_platformdirs(monkeypatch, sys_platform)
39+
40+
expected = user_cache_path("pip-audit", appauthor=False)
9841

9942
# Even with use_pip=True, we avoid pip's cache if the environment tells us to.
10043
assert _get_cache_dir(None, use_pip=True) == expected
10144

10245

103-
@pytest.mark.parametrize(
104-
"sys_platform,expected",
105-
[
106-
pytest.param(
107-
"linux",
108-
Path.home() / ".cache" / "pip-audit",
109-
id="on Linux",
110-
),
111-
pytest.param(
112-
"win32",
113-
Path("/tmp") / "AppData" / "Local" / "pip-audit" / "Cache",
114-
id="on Windows",
115-
),
116-
pytest.param(
117-
"darwin",
118-
Path.home() / "Library" / "Caches" / "pip-audit",
119-
id="on MacOS",
120-
),
121-
],
122-
)
123-
def test_get_cache_dir_old_pip(monkeypatch, sys_platform, expected):
46+
def test_get_cache_dir_old_pip(monkeypatch):
12447
# Check the case where we have an old `pip`
12548
monkeypatch.setattr(cache, "_PIP_VERSION", Version("1.0.0"))
126-
# Check cross-platforms
127-
_patch_platformdirs(monkeypatch, sys_platform)
128-
129-
# When we supply a cache directory, always use that
130-
cache_dir = _get_cache_dir(Path("/tmp/foo/cache_dir"))
131-
assert cache_dir.as_posix() == "/tmp/foo/cache_dir"
13249

13350
# In this case, we can't query `pip` to figure out where its HTTP cache is
13451
# Instead, we use `~/.pip-audit-cache`
13552
cache_dir = _get_cache_dir(None)
53+
expected = user_cache_path("pip-audit", appauthor=False)
13654
assert cache_dir == expected
13755

13856

0 commit comments

Comments
 (0)