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

pantsbuild / pants / 22206482340

20 Feb 2026 12:44AM UTC coverage: 80.376% (+0.05%) from 80.324%
22206482340

Pull #23066

github

web-flow
Merge ddfa8ddd7 into 56952304a
Pull Request #23066: Extract JavaScript backend test resources to files

73 of 73 new or added lines in 2 files covered. (100.0%)

1093 existing lines in 48 files now uncovered.

78880 of 98139 relevant lines covered (80.38%)

3.35 hits per line

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

67.86
/src/python/pants/backend/scala/test/scalatest.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
1✔
4

5
import logging
1✔
6
from dataclasses import dataclass
1✔
7
from typing import Any
1✔
8

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

50
logger = logging.getLogger(__name__)
1✔
51

52

53
@dataclass(frozen=True)
1✔
54
class ScalatestTestFieldSet(TestFieldSet):
1✔
55
    required_fields = (
1✔
56
        ScalatestTestSourceField,
57
        JvmJdkField,
58
    )
59

60
    sources: ScalatestTestSourceField
1✔
61
    timeout: ScalatestTestTimeoutField
1✔
62
    jdk_version: JvmJdkField
1✔
63
    dependencies: JvmDependenciesField
1✔
64
    extra_env_vars: ScalatestTestExtraEnvVarsField
1✔
65

66

67
class ScalatestTestRequest(TestRequest):
1✔
68
    tool_subsystem = Scalatest  # type: ignore[assignment]
1✔
69
    field_set_type = ScalatestTestFieldSet
1✔
70
    supports_debug = True
1✔
71

72

73
@dataclass(frozen=True)
1✔
74
class TestSetupRequest:
1✔
75
    field_set: ScalatestTestFieldSet
1✔
76
    is_debug: bool
1✔
77

78

79
@dataclass(frozen=True)
1✔
80
class TestSetup:
1✔
81
    process: JvmProcess
1✔
82
    reports_dir_prefix: str
1✔
83

84

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

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

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

113
    toolcp_relpath = "__toolcp"
×
114
    extra_immutable_input_digests = {
×
115
        toolcp_relpath: scalatest_classpath.digest,
116
    }
117

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

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

124
    # Cache test runs only if they are successful, or not at all if `--test-force`.
125
    cache_scope = test_subsystem.default_process_cache_scope
×
126

UNCOV
127
    extra_jvm_args: list[str] = []
×
UNCOV
128
    if request.is_debug:
×
129
        extra_jvm_args.extend(jvm.debug_args)
×
130

131
    field_set_extra_env = await environment_vars_subset(
×
132
        EnvironmentVarsRequest(request.field_set.extra_env_vars.value or ()), **implicitly()
133
    )
134

UNCOV
135
    process = JvmProcess(
×
136
        jdk=jdk,
137
        classpath_entries=[
138
            *classpath.args(),
139
            *scalatest_classpath.classpath_entries(toolcp_relpath),
140
        ],
141
        argv=[
142
            *extra_jvm_args,
143
            "org.scalatest.tools.Runner",
144
            # TODO: We currently give the entire user classpath to the JVM for startup (which
145
            # mixes it with the user classpath), and then only specify the roots to run here.
146
            #   see https://github.com/pantsbuild/pants/issues/13871
147
            *(("-R", user_classpath_arg) if user_classpath_arg else ()),
148
            "-o",
149
            "-u",
150
            reports_dir,
151
            *scalatest.args,
152
        ],
153
        input_digest=input_digest,
154
        extra_env={**test_extra_env.env, **field_set_extra_env},
155
        extra_jvm_options=scalatest.jvm_options,
156
        extra_immutable_input_digests=extra_immutable_input_digests,
157
        output_directories=(reports_dir,),
158
        description=f"Run Scalatest runner for {request.field_set.address}",
159
        timeout_seconds=request.field_set.timeout.calculate_from_global_options(test_subsystem),
160
        level=LogLevel.DEBUG,
161
        cache_scope=cache_scope,
162
        use_nailgun=False,
163
    )
UNCOV
164
    return TestSetup(process=process, reports_dir_prefix=reports_dir_prefix)
×
165

166

167
@rule(desc="Run Scalatest", level=LogLevel.DEBUG)
1✔
168
async def run_scalatest_test(
1✔
169
    test_subsystem: TestSubsystem,
170
    batch: ScalatestTestRequest.Batch[ScalatestTestFieldSet, Any],
171
) -> TestResult:
UNCOV
172
    field_set = batch.single_element
×
173

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

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

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

197

198
@rule(level=LogLevel.DEBUG)
1✔
199
async def setup_scalatest_debug_request(
1✔
200
    batch: ScalatestTestRequest.Batch[ScalatestTestFieldSet, Any],
201
) -> TestDebugRequest:
UNCOV
202
    setup = await setup_scalatest_for_target(
×
203
        TestSetupRequest(batch.single_element, is_debug=True), **implicitly()
204
    )
UNCOV
205
    process = await jvm_process(**implicitly(setup.process))
×
UNCOV
206
    return TestDebugRequest(
×
207
        InteractiveProcess.from_process(process, forward_signals_to_process=False, restartable=True)
208
    )
209

210

211
def rules():
1✔
212
    return [
1✔
213
        *collect_rules(),
214
        *lockfile.rules(),
215
        UnionRule(ExportableTool, Scalatest),
216
        *ScalatestTestRequest.rules(),
217
    ]
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