-
-
Notifications
You must be signed in to change notification settings - Fork 646
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
) Add a `jvm_lockfile` pytest fixture and related custom mark `pytest.mark.jvm_lockfile` for defining lockfiles for use in tests. This allows avoiding having to replicate lockfile content in code using `TestCoursierWrapper`, which can become unwiedly once a resolve in complicated enough. Lockfiles are generated by running `./pants internal-generate-test-lockfile-fixtures ::` which collects all tests marked with `pytest.mark.jvm_lockfile`, resolves their requirements, and writes out the resulting lockfiles. A custom Pants goal is used so that the plugin code gets access to the Pants source tree and targets. (An earlier iteration of this PR tried to use a `RuleRunner` in a script under `build-support/bin`, but `RuleRunner` is used for testing in an isolated directory and has no access to the Pants repository sources.)
- Loading branch information
Tom Dyas
authored
May 9, 2022
1 parent
6e546bd
commit a95d9ce
Showing
12 changed files
with
448 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
python_sources() |
Empty file.
87 changes: 87 additions & 0 deletions
87
pants-plugins/internal_plugins/test_lockfile_fixtures/lockfile_fixture.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from pants.jvm.resolve.common import ArtifactRequirement, ArtifactRequirements, Coordinate | ||
from pants.jvm.resolve.coursier_fetch import CoursierResolvedLockfile | ||
from pants.jvm.resolve.lockfile_metadata import LockfileContext | ||
from pants.util.docutil import bin_name | ||
|
||
|
||
@dataclass(frozen=True) | ||
class JVMLockfileFixtureDefinition: | ||
lockfile_rel_path: Path | ||
coordinates: tuple[Coordinate, ...] | ||
|
||
@classmethod | ||
def from_kwargs(cls, kwargs) -> JVMLockfileFixtureDefinition: | ||
lockfile_rel_path = kwargs["path"] | ||
if not lockfile_rel_path: | ||
raise ValueError("`path` must be specified as a relative path to a lockfile") | ||
|
||
requirements = kwargs["requirements"] or [] | ||
coordinates: list[Coordinate] = [] | ||
for requirement in requirements: | ||
if isinstance(requirement, Coordinate): | ||
coordinates.append(requirement) | ||
elif isinstance(requirement, str): | ||
coordinate = Coordinate.from_coord_str(requirement) | ||
coordinates.append(coordinate) | ||
else: | ||
raise ValueError( | ||
f"Unsupported type `{type(requirement)}` for JVM coordinate. Expected `Coordinate` or `str`." | ||
) | ||
|
||
return cls( | ||
lockfile_rel_path=Path(lockfile_rel_path), | ||
coordinates=tuple(coordinates), | ||
) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class JVMLockfileFixture: | ||
lockfile: CoursierResolvedLockfile | ||
serialized_lockfile: str | ||
requirements: ArtifactRequirements | ||
|
||
|
||
class JvmLockfilePlugin: | ||
def pytest_configure(self, config): | ||
config.addinivalue_line( | ||
"markers", | ||
"jvm_lockfile(path, requirements): mark test to configure a `jvm_lockfile` fixture", | ||
) | ||
|
||
@pytest.fixture | ||
def jvm_lockfile(self, request) -> JVMLockfileFixture: | ||
mark = request.node.get_closest_marker("jvm_lockfile") | ||
|
||
definition = JVMLockfileFixtureDefinition.from_kwargs(mark.kwargs) | ||
|
||
# Load the lockfile. | ||
lockfile_path = request.node.path.parent / definition.lockfile_rel_path | ||
lockfile_contents = lockfile_path.read_bytes() | ||
lockfile = CoursierResolvedLockfile.from_serialized(lockfile_contents) | ||
|
||
# Check the lockfile's requirements against the requirements in the lockfile. | ||
# Fail the test if the lockfile needs to be regenerated. | ||
artifact_reqs = ArtifactRequirements( | ||
[ArtifactRequirement(coordinate) for coordinate in definition.coordinates] | ||
) | ||
if not lockfile.metadata: | ||
raise ValueError( | ||
f"Expected JVM lockfile {definition.lockfile_rel_path} to have metadata." | ||
) | ||
if not lockfile.metadata.is_valid_for(artifact_reqs, LockfileContext.TOOL): | ||
raise ValueError( | ||
f"Lockfile fixture {definition.lockfile_rel_path} is not valid. " | ||
"Please re-generate it using: " | ||
f"{bin_name()} internal-generate-test-lockfile-fixtures ::" | ||
) | ||
|
||
return JVMLockfileFixture(lockfile, lockfile_contents.decode(), artifact_reqs) |
26 changes: 26 additions & 0 deletions
26
pants-plugins/internal_plugins/test_lockfile_fixtures/register.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
from __future__ import annotations | ||
|
||
from internal_plugins.test_lockfile_fixtures.rules import rules as test_lockfile_fixtures_rules | ||
from pants.backend.python.register import rules as python_rules | ||
from pants.backend.python.register import target_types as python_target_types | ||
from pants.core.goals.test import rules as core_test_rules | ||
from pants.core.util_rules import config_files, source_files | ||
from pants.jvm.resolve import coursier_fetch, coursier_setup | ||
|
||
|
||
def target_types(): | ||
return python_target_types() | ||
|
||
|
||
def rules(): | ||
return ( | ||
*test_lockfile_fixtures_rules(), | ||
*python_rules(), # python backend | ||
*core_test_rules(), | ||
*config_files.rules(), | ||
*coursier_fetch.rules(), | ||
*coursier_setup.rules(), | ||
*source_files.rules(), | ||
) |
Oops, something went wrong.