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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

100.0
/src/python/pants/backend/shell/lint/shfmt/rules_integration_test.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
3✔
5

6
from textwrap import dedent
3✔
7

8
import pytest
3✔
9

10
from pants.backend.shell.lint.shfmt.rules import ShfmtFieldSet, ShfmtRequest
3✔
11
from pants.backend.shell.lint.shfmt.rules import rules as shfmt_rules
3✔
12
from pants.backend.shell.target_types import ShellSourcesGeneratorTarget
3✔
13
from pants.backend.shell.target_types import rules as target_types_rules
3✔
14
from pants.core.goals.fmt import FmtResult
3✔
15
from pants.core.util_rules import config_files, external_tool, source_files
3✔
16
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
3✔
17
from pants.engine.addresses import Address
3✔
18
from pants.engine.target import Target
3✔
19
from pants.testutil.rule_runner import QueryRule, RuleRunner
3✔
20

21

22
@pytest.fixture
3✔
23
def rule_runner() -> RuleRunner:
3✔
24
    return RuleRunner(
3✔
25
        rules=[
26
            *shfmt_rules(),
27
            *config_files.rules(),
28
            *external_tool.rules(),
29
            *source_files.rules(),
30
            *target_types_rules(),
31
            QueryRule(FmtResult, [ShfmtRequest.Batch]),
32
            QueryRule(SourceFiles, [SourceFilesRequest]),
33
        ],
34
        target_types=[ShellSourcesGeneratorTarget],
35
    )
36

37

38
GOOD_FILE = "! foo bar >a &\n"
3✔
39
BAD_FILE = "!    foo bar >a  &\n"
3✔
40

41
# If config is loaded correctly, shfmt will indent the case statements.
42
NEEDS_CONFIG_FILE = dedent(
3✔
43
    """\
44
    case foo in
45
    PATTERN_1)
46
    \tbar
47
    \t;;
48
    *)
49
    \tbaz
50
    \t;;
51
    esac
52
    """
53
)
54
FIXED_NEEDS_CONFIG_FILE = dedent(
3✔
55
    """\
56
    case foo in
57
    \tPATTERN_1)
58
    \t\tbar
59
    \t\t;;
60
    \t*)
61
    \t\tbaz
62
    \t\t;;
63
    esac
64
    """
65
)
66

67

68
def run_shfmt(
3✔
69
    rule_runner: RuleRunner,
70
    targets: list[Target],
71
    *,
72
    extra_args: list[str] | None = None,
73
) -> FmtResult:
74
    rule_runner.set_options(
3✔
75
        ["--backend-packages=pants.backend.shell.lint.shfmt", *(extra_args or ())],
76
        env_inherit={"PATH"},
77
    )
78
    field_sets = [ShfmtFieldSet.create(tgt) for tgt in targets]
3✔
79
    input_sources = rule_runner.request(
3✔
80
        SourceFiles,
81
        [
82
            SourceFilesRequest(field_set.sources for field_set in field_sets),
83
        ],
84
    )
85
    fmt_result = rule_runner.request(
3✔
86
        FmtResult,
87
        [
88
            ShfmtRequest.Batch(
89
                "",
90
                input_sources.snapshot.files,
91
                partition_metadata=None,
92
                snapshot=input_sources.snapshot,
93
            ),
94
        ],
95
    )
96
    return fmt_result
3✔
97

98

99
def test_passing(rule_runner: RuleRunner) -> None:
3✔
UNCOV
100
    rule_runner.write_files({"f.sh": GOOD_FILE, "BUILD": "shell_sources(name='t')"})
1✔
UNCOV
101
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.sh"))
1✔
UNCOV
102
    fmt_result = run_shfmt(rule_runner, [tgt])
1✔
UNCOV
103
    assert fmt_result.stdout == ""
1✔
UNCOV
104
    assert fmt_result.output == rule_runner.make_snapshot({"f.sh": GOOD_FILE})
1✔
UNCOV
105
    assert fmt_result.did_change is False
1✔
106

107

108
@pytest.mark.platform_specific_behavior
3✔
109
def test_failing(rule_runner: RuleRunner) -> None:
3✔
110
    rule_runner.write_files({"f.sh": BAD_FILE, "BUILD": "shell_sources(name='t')"})
3✔
111
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.sh"))
3✔
112
    fmt_result = run_shfmt(rule_runner, [tgt])
3✔
113
    assert fmt_result.stdout == "f.sh\n"
3✔
114
    assert fmt_result.output == rule_runner.make_snapshot({"f.sh": GOOD_FILE})
3✔
115
    assert fmt_result.did_change is True
3✔
116

117

118
def test_multiple_targets(rule_runner: RuleRunner) -> None:
3✔
UNCOV
119
    rule_runner.write_files(
1✔
120
        {"good.sh": GOOD_FILE, "bad.sh": BAD_FILE, "BUILD": "shell_sources(name='t')"}
121
    )
UNCOV
122
    tgts = [
1✔
123
        rule_runner.get_target(Address("", target_name="t", relative_file_path="good.sh")),
124
        rule_runner.get_target(Address("", target_name="t", relative_file_path="bad.sh")),
125
    ]
UNCOV
126
    fmt_result = run_shfmt(rule_runner, tgts)
1✔
UNCOV
127
    assert "bad.sh\n" == fmt_result.stdout
1✔
UNCOV
128
    assert fmt_result.output == rule_runner.make_snapshot(
1✔
129
        {"good.sh": GOOD_FILE, "bad.sh": GOOD_FILE}
130
    )
UNCOV
131
    assert fmt_result.did_change is True
1✔
132

133

134
def test_config_files(rule_runner: RuleRunner) -> None:
3✔
UNCOV
135
    rule_runner.write_files(
1✔
136
        {
137
            "a/f.sh": NEEDS_CONFIG_FILE,
138
            "a/BUILD": "shell_sources()",
139
            "a/.editorconfig": "[*.sh]\nswitch_case_indent = true\n",
140
            "b/f.sh": NEEDS_CONFIG_FILE,
141
            "b/BUILD": "shell_sources()",
142
        }
143
    )
UNCOV
144
    tgts = [
1✔
145
        rule_runner.get_target(Address("a", relative_file_path="f.sh")),
146
        rule_runner.get_target(Address("b", relative_file_path="f.sh")),
147
    ]
UNCOV
148
    fmt_result = run_shfmt(rule_runner, tgts)
1✔
UNCOV
149
    assert fmt_result.stdout == "a/f.sh\n"
1✔
UNCOV
150
    assert fmt_result.output == rule_runner.make_snapshot(
1✔
151
        {"a/f.sh": FIXED_NEEDS_CONFIG_FILE, "b/f.sh": NEEDS_CONFIG_FILE}
152
    )
UNCOV
153
    assert fmt_result.did_change is True
1✔
154

155

156
def test_passthrough_args(rule_runner: RuleRunner) -> None:
3✔
UNCOV
157
    rule_runner.write_files({"f.sh": NEEDS_CONFIG_FILE, "BUILD": "shell_sources(name='t')"})
1✔
UNCOV
158
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.sh"))
1✔
UNCOV
159
    fmt_result = run_shfmt(rule_runner, [tgt], extra_args=["--shfmt-args=-ci"])
1✔
UNCOV
160
    assert fmt_result.stdout == "f.sh\n"
1✔
UNCOV
161
    assert fmt_result.output == rule_runner.make_snapshot({"f.sh": FIXED_NEEDS_CONFIG_FILE})
1✔
UNCOV
162
    assert fmt_result.did_change is True
1✔
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