• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 20328535594

18 Dec 2025 06:46AM UTC coverage: 57.969% (-22.3%) from 80.295%
20328535594

Pull #22954

github

web-flow
Merge ccc9c5409 into 407284c67
Pull Request #22954: free up disk space in runner image

39083 of 67421 relevant lines covered (57.97%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/src/python/pants/jvm/testutil.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
2✔
5

6
import ast
2✔
7
import dataclasses
2✔
8
import os
2✔
9
from dataclasses import dataclass
2✔
10

11
import pytest
2✔
12

13
from pants.build_graph.address import Address
2✔
14
from pants.core.util_rules import system_binaries
2✔
15
from pants.core.util_rules.archive import ExtractedArchive, MaybeExtractArchiveRequest
2✔
16
from pants.core.util_rules.system_binaries import UnzipBinary
2✔
17
from pants.engine.addresses import Addresses
2✔
18
from pants.engine.fs import CreateDigest, DigestContents, PathGlobs, RemovePrefix
2✔
19
from pants.engine.internals.native_engine import Digest, Snapshot
2✔
20
from pants.engine.intrinsics import digest_to_snapshot
2✔
21
from pants.engine.process import Process, execute_process_or_raise
2✔
22
from pants.engine.rules import QueryRule, collect_rules, concurrently, implicitly, rule
2✔
23
from pants.engine.target import CoarsenedTarget, CoarsenedTargets, Targets
2✔
24
from pants.jvm.classpath import Classpath
2✔
25
from pants.jvm.compile import ClasspathEntry
2✔
26
from pants.jvm.resolve.key import CoursierResolveKey
2✔
27
from pants.testutil.rule_runner import RuleRunner
2✔
28

29

30
def maybe_skip_jdk_test(func):
2✔
31
    run_jdk_tests = bool(ast.literal_eval(os.environ.get("PANTS_RUN_JDK_TESTS", "True")))
2✔
32
    return pytest.mark.skipif(not run_jdk_tests, reason="Skip JDK tests")(func)
2✔
33

34

35
def expect_single_expanded_coarsened_target(
2✔
36
    rule_runner: RuleRunner, address: Address
37
) -> CoarsenedTarget:
38
    expanded_target = rule_runner.request(Targets, [Addresses([address])]).expect_single()
2✔
39
    coarsened_targets = rule_runner.request(
2✔
40
        CoarsenedTargets, [Addresses([expanded_target.address])]
41
    )
42
    assert len(coarsened_targets) == 1
2✔
43
    return coarsened_targets[0]
2✔
44

45

46
def make_resolve(
2✔
47
    rule_runner: RuleRunner,
48
    resolve_name: str = "jvm-default",
49
    resolve_path: str = "3rdparty/jvm/default.lock",
50
) -> CoursierResolveKey:
51
    digest = rule_runner.request(Digest, [PathGlobs([resolve_path])])
2✔
52
    return CoursierResolveKey(name=resolve_name, path=resolve_path, digest=digest)
2✔
53

54

55
@dataclass(frozen=True)
2✔
56
class RenderedClasspath:
2✔
57
    """The contents of a classpath, organized as a key per entry with its contained classfiles."""
58

59
    content: dict[str, set[str]]
2✔
60

61

62
@rule
2✔
63
async def render_classpath_entry(
2✔
64
    classpath_entry: ClasspathEntry, unzip_binary: UnzipBinary
65
) -> RenderedClasspath:
66
    dest_dir = "dest"
×
67
    process_results = await concurrently(
×
68
        execute_process_or_raise(
69
            **implicitly(
70
                Process(
71
                    argv=[
72
                        unzip_binary.path,
73
                        "-d",
74
                        dest_dir,
75
                        filename,
76
                    ],
77
                    input_digest=classpath_entry.digest,
78
                    output_directories=(dest_dir,),
79
                    description=f"Extract {filename}",
80
                ),
81
            )
82
        )
83
        for filename in classpath_entry.filenames
84
    )
85

86
    listing_snapshots = await concurrently(
×
87
        digest_to_snapshot(**implicitly(RemovePrefix(pr.output_digest, dest_dir)))
88
        for pr in process_results
89
    )
90

91
    return RenderedClasspath(
×
92
        {
93
            path: set(listing.files)
94
            for path, listing in zip(classpath_entry.filenames, listing_snapshots)
95
        }
96
    )
97

98

99
@rule
2✔
100
async def render_classpath(classpath: Classpath) -> RenderedClasspath:
2✔
101
    rendered_classpaths = await concurrently(
×
102
        render_classpath_entry(cpe, **implicitly())
103
        for cpe in ClasspathEntry.closure(classpath.entries)
104
    )
105
    return RenderedClasspath({k: v for rc in rendered_classpaths for k, v in rc.content.items()})
×
106

107

108
def rules():
2✔
109
    return [
2✔
110
        *collect_rules(),
111
        *system_binaries.rules(),
112
        QueryRule(CoarsenedTargets, (Addresses,)),
113
        QueryRule(RenderedClasspath, (Classpath,)),
114
        QueryRule(RenderedClasspath, (ClasspathEntry,)),
115
        QueryRule(Targets, (Addresses,)),
116
    ]
117

118

119
def _get_jar_contents_snapshot(
2✔
120
    rule_runner: RuleRunner, *, filename: str, digest: Digest
121
) -> Snapshot:
122
    contents = rule_runner.request(DigestContents, [digest])
×
123
    files_content = [content for content in contents if content.path == filename]
×
124
    assert len(files_content) == 1
×
125

126
    renamed_digest = rule_runner.request(
×
127
        Digest, [CreateDigest([dataclasses.replace(files_content[0], path=f"{filename}.zip")])]
128
    )
129
    extracted_jar = rule_runner.request(
×
130
        ExtractedArchive, [MaybeExtractArchiveRequest(renamed_digest)]
131
    )
132
    return rule_runner.request(Snapshot, [extracted_jar.digest])
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc