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

pantsbuild / pants / 18252174847

05 Oct 2025 01:36AM UTC coverage: 43.382% (-36.9%) from 80.261%
18252174847

push

github

web-flow
run tests on mac arm (#22717)

Just doing the minimal to pull forward the x86_64 pattern.

ref #20993

25776 of 59416 relevant lines covered (43.38%)

1.3 hits per line

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

0.0
/src/python/pants/jvm/test/junit.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
×
5

6
import logging
×
7
from dataclasses import dataclass
×
8
from typing import Any
×
9

10
from pants.backend.java.subsystems.junit import JUnit
×
11
from pants.core.goals.resolves import ExportableTool
×
12
from pants.core.goals.test import (
×
13
    TestDebugRequest,
14
    TestExtraEnv,
15
    TestExtraEnvVarsField,
16
    TestFieldSet,
17
    TestRequest,
18
    TestResult,
19
    TestSubsystem,
20
)
21
from pants.core.target_types import FileSourceField
×
22
from pants.core.util_rules.env_vars import environment_vars_subset
×
23
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
×
24
from pants.engine.addresses import Addresses
×
25
from pants.engine.env_vars import EnvironmentVarsRequest
×
26
from pants.engine.fs import DigestSubset, MergeDigests, PathGlobs, RemovePrefix
×
27
from pants.engine.internals.graph import transitive_targets
×
28
from pants.engine.intrinsics import (
×
29
    digest_subset_to_digest,
30
    digest_to_snapshot,
31
    execute_process_with_retry,
32
    merge_digests,
33
)
34
from pants.engine.process import InteractiveProcess, ProcessCacheScope, ProcessWithRetries
×
35
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
×
36
from pants.engine.target import SourcesField, TransitiveTargetsRequest
×
37
from pants.engine.unions import UnionRule
×
38
from pants.jvm.classpath import classpath as classpath_get
×
39
from pants.jvm.goals import lockfile
×
40
from pants.jvm.jdk_rules import JdkRequest, JvmProcess, jvm_process, prepare_jdk_environment
×
41
from pants.jvm.resolve.coursier_fetch import ToolClasspathRequest, materialize_classpath_for_tool
×
42
from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool
×
43
from pants.jvm.subsystems import JvmSubsystem
×
44
from pants.jvm.target_types import (
×
45
    JunitTestSourceField,
46
    JunitTestTimeoutField,
47
    JvmDependenciesField,
48
    JvmJdkField,
49
)
50
from pants.util.logging import LogLevel
×
51

52
logger = logging.getLogger(__name__)
×
53

54

55
@dataclass(frozen=True)
×
56
class JunitTestFieldSet(TestFieldSet):
×
57
    required_fields = (
×
58
        JunitTestSourceField,
59
        JvmJdkField,
60
    )
61

62
    sources: JunitTestSourceField
×
63
    timeout: JunitTestTimeoutField
×
64
    jdk_version: JvmJdkField
×
65
    dependencies: JvmDependenciesField
×
66
    extra_env_vars: TestExtraEnvVarsField
×
67

68

69
class JunitTestRequest(TestRequest):
×
70
    tool_subsystem = JUnit
×
71
    field_set_type = JunitTestFieldSet
×
72
    supports_debug = True
×
73

74

75
@dataclass(frozen=True)
×
76
class TestSetupRequest:
×
77
    field_set: JunitTestFieldSet
×
78
    is_debug: bool
×
79

80

81
@dataclass(frozen=True)
×
82
class TestSetup:
×
83
    process: JvmProcess
×
84
    reports_dir_prefix: str
×
85

86

87
@rule(level=LogLevel.DEBUG)
×
88
async def setup_junit_for_target(
×
89
    request: TestSetupRequest,
90
    jvm: JvmSubsystem,
91
    junit: JUnit,
92
    test_subsystem: TestSubsystem,
93
    test_extra_env: TestExtraEnv,
94
) -> TestSetup:
95
    jdk, transitive_tgts = await concurrently(
×
96
        prepare_jdk_environment(**implicitly(JdkRequest.from_field(request.field_set.jdk_version))),
97
        transitive_targets(TransitiveTargetsRequest([request.field_set.address]), **implicitly()),
98
    )
99

100
    lockfile_request = GenerateJvmLockfileFromTool.create(junit)
×
101
    classpath, junit_classpath, files = await concurrently(
×
102
        classpath_get(**implicitly(Addresses([request.field_set.address]))),
103
        materialize_classpath_for_tool(ToolClasspathRequest(lockfile=lockfile_request)),
104
        determine_source_files(
105
            SourceFilesRequest(
106
                (dep.get(SourcesField) for dep in transitive_tgts.dependencies),
107
                for_sources_types=(FileSourceField,),
108
                enable_codegen=True,
109
            )
110
        ),
111
    )
112

113
    input_digest = await merge_digests(MergeDigests((*classpath.digests(), files.snapshot.digest)))
×
114

115
    toolcp_relpath = "__toolcp"
×
116
    extra_immutable_input_digests = {
×
117
        toolcp_relpath: junit_classpath.digest,
118
    }
119

120
    reports_dir_prefix = "__reports_dir"
×
121
    reports_dir = f"{reports_dir_prefix}/{request.field_set.address.path_safe_spec}"
×
122

123
    # Classfiles produced by the root `junit_test` targets are the only ones which should run.
124
    user_classpath_arg = ":".join(classpath.root_args())
×
125

126
    # Cache test runs only if they are successful, or not at all if `--test-force`.
127
    cache_scope = (
×
128
        ProcessCacheScope.PER_SESSION if test_subsystem.force else ProcessCacheScope.SUCCESSFUL
129
    )
130

131
    extra_jvm_args: list[str] = []
×
132
    if request.is_debug:
×
133
        extra_jvm_args.extend(jvm.debug_args)
×
134

135
    field_set_extra_env = await environment_vars_subset(
×
136
        EnvironmentVarsRequest(request.field_set.extra_env_vars.value or ()), **implicitly()
137
    )
138

139
    process = JvmProcess(
×
140
        jdk=jdk,
141
        classpath_entries=[
142
            *classpath.args(),
143
            *junit_classpath.classpath_entries(toolcp_relpath),
144
        ],
145
        argv=[
146
            *extra_jvm_args,
147
            "org.junit.platform.console.ConsoleLauncher",
148
            *(("--classpath", user_classpath_arg) if user_classpath_arg else ()),
149
            *(("--scan-class-path", user_classpath_arg) if user_classpath_arg else ()),
150
            "--reports-dir",
151
            reports_dir,
152
            *junit.args,
153
        ],
154
        input_digest=input_digest,
155
        extra_env={**test_extra_env.env, **field_set_extra_env},
156
        extra_jvm_options=junit.jvm_options,
157
        extra_immutable_input_digests=extra_immutable_input_digests,
158
        output_directories=(reports_dir,),
159
        description=f"Run JUnit 5 ConsoleLauncher against {request.field_set.address}",
160
        timeout_seconds=request.field_set.timeout.calculate_from_global_options(test_subsystem),
161
        level=LogLevel.DEBUG,
162
        cache_scope=cache_scope,
163
        use_nailgun=False,
164
    )
165
    return TestSetup(process=process, reports_dir_prefix=reports_dir_prefix)
×
166

167

168
@rule(desc="Run JUnit", level=LogLevel.DEBUG)
×
169
async def run_junit_test(
×
170
    test_subsystem: TestSubsystem,
171
    batch: JunitTestRequest.Batch[JunitTestFieldSet, Any],
172
) -> TestResult:
173
    field_set = batch.single_element
×
174

175
    test_setup = await setup_junit_for_target(
×
176
        TestSetupRequest(field_set, is_debug=False), **implicitly()
177
    )
178
    process = await jvm_process(**implicitly(test_setup.process))
×
179
    process_results = await execute_process_with_retry(
×
180
        ProcessWithRetries(process, test_subsystem.attempts_default)
181
    )
182
    reports_dir_prefix = test_setup.reports_dir_prefix
×
183

184
    xml_result_subset = await digest_subset_to_digest(
×
185
        DigestSubset(process_results.last.output_digest, PathGlobs([f"{reports_dir_prefix}/**"]))
186
    )
187
    xml_results = await digest_to_snapshot(
×
188
        **implicitly(RemovePrefix(xml_result_subset, reports_dir_prefix))
189
    )
190

191
    return TestResult.from_fallible_process_result(
×
192
        process_results=process_results.results,
193
        address=field_set.address,
194
        output_setting=test_subsystem.output,
195
        xml_results=xml_results,
196
    )
197

198

199
@rule(level=LogLevel.DEBUG)
×
200
async def setup_junit_debug_request(
×
201
    batch: JunitTestRequest.Batch[JunitTestFieldSet, Any],
202
) -> TestDebugRequest:
203
    setup = await setup_junit_for_target(
×
204
        TestSetupRequest(batch.single_element, is_debug=True), **implicitly()
205
    )
206
    process = await jvm_process(**implicitly(setup.process))
×
207
    return TestDebugRequest(
×
208
        InteractiveProcess.from_process(process, forward_signals_to_process=False, restartable=True)
209
    )
210

211

212
def rules():
×
213
    return [
×
214
        *collect_rules(),
215
        *lockfile.rules(),
216
        UnionRule(ExportableTool, JUnit),
217
        *JunitTestRequest.rules(),
218
    ]
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

© 2025 Coveralls, Inc