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

pantsbuild / pants / 21594612670

02 Feb 2026 02:45PM UTC coverage: 79.722% (-0.6%) from 80.273%
21594612670

push

github

web-flow
Delete more Get-related cruft (#23068)

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

9 existing lines in 1 file now uncovered.

73874 of 92665 relevant lines covered (79.72%)

3.54 hits per line

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

94.74
/src/python/pants/core/goals/lint_test.py
1
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
from abc import ABCMeta, abstractmethod
1✔
7
from collections.abc import Iterable
1✔
8
from dataclasses import dataclass
1✔
9
from pathlib import Path
1✔
10
from textwrap import dedent
1✔
11
from typing import Any, TypeVar
1✔
12

13
import pytest
1✔
14

15
from pants.base.specs import Specs
1✔
16
from pants.core.goals.fix import FixFilesRequest, FixTargetsRequest
1✔
17
from pants.core.goals.fmt import FmtFilesRequest, FmtTargetsRequest
1✔
18
from pants.core.goals.lint import (
1✔
19
    AbstractLintRequest,
20
    Lint,
21
    LintFilesRequest,
22
    LintResult,
23
    LintSubsystem,
24
    LintTargetsRequest,
25
    Partitions,
26
)
27
from pants.core.goals.lint_goal import lint
1✔
28
from pants.core.util_rules.distdir import DistDir
1✔
29
from pants.core.util_rules.partitions import PartitionerType, _EmptyMetadata
1✔
30
from pants.engine.addresses import Address
1✔
31
from pants.engine.fs import SpecsPaths, Workspace
1✔
32
from pants.engine.internals.native_engine import EMPTY_SNAPSHOT
1✔
33
from pants.engine.rules import QueryRule
1✔
34
from pants.engine.target import Field, FieldSet, FilteredTargets, MultipleSourcesField, Target
1✔
35
from pants.engine.unions import UnionMembership, UnionRule
1✔
36
from pants.option.option_types import SkipOption
1✔
37
from pants.option.subsystem import Subsystem
1✔
38
from pants.testutil.option_util import create_goal_subsystem
1✔
39
from pants.testutil.rule_runner import RuleRunner, mock_console, run_rule_with_mocks
1✔
40
from pants.util.logging import LogLevel
1✔
41
from pants.util.meta import classproperty
1✔
42

43
_LintRequestT = TypeVar("_LintRequestT", bound=AbstractLintRequest)
1✔
44

45

46
class MockMultipleSourcesField(MultipleSourcesField):
1✔
47
    pass
1✔
48

49

50
class MockRequiredField(Field):
1✔
51
    alias = "required"
1✔
52
    required = True
1✔
53

54

55
class MockTarget(Target):
1✔
56
    alias = "mock_target"
1✔
57
    core_fields = (MockMultipleSourcesField, MockRequiredField)
1✔
58

59

60
@dataclass(frozen=True)
1✔
61
class MockLinterFieldSet(FieldSet):
1✔
62
    required_fields = (MultipleSourcesField,)
1✔
63
    sources: MultipleSourcesField
1✔
64
    required: MockRequiredField
1✔
65

66

67
class MockLintRequest(AbstractLintRequest, metaclass=ABCMeta):
1✔
68
    @staticmethod
1✔
69
    @abstractmethod
1✔
70
    def exit_code(_: Iterable[Address]) -> int:
1✔
71
        pass
×
72

73
    @classmethod
1✔
74
    @abstractmethod
1✔
75
    def get_lint_result(cls, elements: Iterable) -> LintResult:
1✔
76
        pass
×
77

78

79
class MockLintTargetsRequest(MockLintRequest, LintTargetsRequest):
1✔
80
    field_set_type = MockLinterFieldSet
1✔
81

82
    @classmethod
1✔
83
    def get_lint_result(cls, field_sets: Iterable[MockLinterFieldSet]) -> LintResult:
1✔
84
        addresses = [field_set.address for field_set in field_sets]
1✔
85
        return LintResult(cls.exit_code(addresses), "", "", cls.tool_name)
1✔
86

87

88
class SuccessfulRequest(MockLintTargetsRequest):
1✔
89
    @classproperty
1✔
90
    def tool_name(cls) -> str:
1✔
91
        return "Successful Linter"
1✔
92

93
    @classproperty
1✔
94
    def tool_id(cls) -> str:
1✔
95
        return "successfullinter"
1✔
96

97
    @staticmethod
1✔
98
    def exit_code(_: Iterable[Address]) -> int:
1✔
99
        return 0
1✔
100

101

102
class FailingRequest(MockLintTargetsRequest):
1✔
103
    @classproperty
1✔
104
    def tool_name(cls) -> str:
1✔
105
        return "Failing Linter"
1✔
106

107
    @classproperty
1✔
108
    def tool_id(cls) -> str:
1✔
109
        return "failinglinter"
1✔
110

111
    @staticmethod
1✔
112
    def exit_code(_: Iterable[Address]) -> int:
1✔
113
        return 1
1✔
114

115

116
class ConditionallySucceedsRequest(MockLintTargetsRequest):
1✔
117
    @classproperty
1✔
118
    def tool_name(cls) -> str:
1✔
119
        return "Conditionally Succeeds Linter"
1✔
120

121
    @classproperty
1✔
122
    def tool_id(cls) -> str:
1✔
123
        return "conditionallysucceedslinter"
1✔
124

125
    @staticmethod
1✔
126
    def exit_code(addresses: Iterable[Address]) -> int:
1✔
127
        if any(address.target_name == "bad" for address in addresses):
1✔
128
            return 127
1✔
129
        return 0
1✔
130

131

132
class SkippedRequest(MockLintTargetsRequest):
1✔
133
    @classproperty
1✔
134
    def tool_name(cls) -> str:
1✔
135
        return "Skipped Linter"
×
136

137
    @classproperty
1✔
138
    def tool_id(cls) -> str:
1✔
139
        return "skippedlinter"
1✔
140

141
    @staticmethod
1✔
142
    def exit_code(_) -> int:
1✔
143
        return 0
×
144

145

146
class InvalidField(MultipleSourcesField):
1✔
147
    pass
1✔
148

149

150
class InvalidFieldSet(MockLinterFieldSet):
1✔
151
    required_fields = (InvalidField,)
1✔
152

153

154
class InvalidRequest(MockLintTargetsRequest):
1✔
155
    field_set_type = InvalidFieldSet
1✔
156

157
    @classproperty
1✔
158
    def tool_name(cls) -> str:
1✔
159
        return "Invalid Linter"
×
160

161
    @classproperty
1✔
162
    def tool_id(cls) -> str:
1✔
163
        return "invalidlinter"
1✔
164

165
    @staticmethod
1✔
166
    def exit_code(_: Iterable[Address]) -> int:
1✔
167
        return -1
×
168

169

170
def _all_lint_requests() -> Iterable[type[MockLintRequest]]:
1✔
171
    classes = [MockLintRequest]
1✔
172
    while classes:
1✔
173
        cls = classes.pop()
1✔
174
        subclasses = cls.__subclasses__()
1✔
175
        classes.extend(subclasses)
1✔
176
        yield from subclasses
1✔
177

178

179
def mock_target_partitioner(__implicitly: tuple) -> Partitions[MockLinterFieldSet, Any]:
1✔
180
    request, typ = next(iter(__implicitly[0].items()))
1✔
181
    assert typ == LintTargetsRequest.PartitionRequest
1✔
182
    if type(request) is SkippedRequest.PartitionRequest:
1✔
183
        return Partitions()
1✔
184

185
    operates_on_paths = {
1✔
186
        getattr(cls, "PartitionRequest"): cls._requires_snapshot for cls in _all_lint_requests()
187
    }[type(request)]
188
    if operates_on_paths:
1✔
189
        return Partitions.single_partition(fs.sources.globs for fs in request.field_sets)
1✔
190

191
    return Partitions.single_partition(request.field_sets)
1✔
192

193

194
class MockFilesRequest(MockLintRequest, LintFilesRequest):
1✔
195
    @classproperty
1✔
196
    def tool_name(cls) -> str:
1✔
197
        return "Files Linter"
1✔
198

199
    @classproperty
1✔
200
    def tool_id(cls) -> str:
1✔
201
        return "fileslinter"
1✔
202

203
    @classmethod
1✔
204
    def get_lint_result(cls, files: Iterable[str]) -> LintResult:
1✔
205
        return LintResult(0, "", "", cls.tool_name)
1✔
206

207

208
def mock_file_partitioner(__implicitly: dict) -> Partitions[str, Any]:
1✔
209
    request, typ = next(iter(__implicitly[0].items()))
1✔
210
    assert typ == LintFilesRequest.PartitionRequest
1✔
211
    return Partitions.single_partition(request.files)
1✔
212

213

214
def mock_lint_partition(__implicitly: dict) -> LintResult:
1✔
215
    request, typ = next(iter(__implicitly[0].items()))
1✔
216
    assert typ == AbstractLintRequest.Batch
1✔
217
    request_type = {cls.Batch: cls for cls in _all_lint_requests()}[type(request)]
1✔
218
    return request_type.get_lint_result(request.elements)
1✔
219

220

221
class MockFmtRequest(MockLintRequest, FmtTargetsRequest):
1✔
222
    field_set_type = MockLinterFieldSet
1✔
223

224

225
class SuccessfulFormatter(MockFmtRequest):
1✔
226
    @classproperty
1✔
227
    def tool_name(cls) -> str:
1✔
228
        return "Successful Formatter"
1✔
229

230
    @classproperty
1✔
231
    def tool_id(cls) -> str:
1✔
232
        return "successfulformatter"
1✔
233

234
    @classmethod
1✔
235
    def get_lint_result(cls, field_sets: Iterable[MockLinterFieldSet]) -> LintResult:
1✔
236
        return LintResult(0, "", "", cls.tool_name)
1✔
237

238

239
class FailingFormatter(MockFmtRequest):
1✔
240
    @classproperty
1✔
241
    def tool_name(cls) -> str:
1✔
242
        return "Failing Formatter"
1✔
243

244
    @classproperty
1✔
245
    def tool_id(cls) -> str:
1✔
246
        return "failingformatter"
1✔
247

248
    @classmethod
1✔
249
    def get_lint_result(cls, field_sets: Iterable[MockLinterFieldSet]) -> LintResult:
1✔
250
        return LintResult(1, "", "", cls.tool_name)
1✔
251

252

253
class BuildFileFormatter(MockLintRequest, FmtFilesRequest):
1✔
254
    @classproperty
1✔
255
    def tool_name(cls) -> str:
1✔
256
        return "Bob The BUILDer"
1✔
257

258
    @classproperty
1✔
259
    def tool_id(cls) -> str:
1✔
260
        return "bob"
1✔
261

262
    @classmethod
1✔
263
    def get_lint_result(cls, files: Iterable[str]) -> LintResult:
1✔
264
        return LintResult(0, "", "", cls.tool_name)
1✔
265

266

267
class MockFixRequest(MockLintRequest, FixTargetsRequest):
1✔
268
    field_set_type = MockLinterFieldSet
1✔
269

270

271
class SuccessfulFixer(MockFixRequest):
1✔
272
    @classproperty
1✔
273
    def tool_name(cls) -> str:
1✔
274
        return "Successful Fixer"
1✔
275

276
    @classproperty
1✔
277
    def tool_id(cls) -> str:
1✔
278
        return "successfulfixer"
1✔
279

280
    @classmethod
1✔
281
    def get_lint_result(cls, field_sets: Iterable[MockLinterFieldSet]) -> LintResult:
1✔
282
        return LintResult(0, "", "", cls.tool_name)
1✔
283

284

285
class FailingFixer(MockFixRequest):
1✔
286
    @classproperty
1✔
287
    def tool_name(cls) -> str:
1✔
288
        return "Failing Fixer"
1✔
289

290
    @classproperty
1✔
291
    def tool_id(cls) -> str:
1✔
292
        return "failingfixer"
1✔
293

294
    @classmethod
1✔
295
    def get_lint_result(cls, field_sets: Iterable[MockLinterFieldSet]) -> LintResult:
1✔
296
        return LintResult(1, "", "", cls.tool_name)
1✔
297

298

299
class BuildFileFixer(MockLintRequest, FixFilesRequest):
1✔
300
    @classproperty
1✔
301
    def tool_name(cls) -> str:
1✔
302
        return "BUILD Annually"
1✔
303

304
    @classproperty
1✔
305
    def tool_id(cls) -> str:
1✔
306
        return "buildannually"
1✔
307

308
    @classmethod
1✔
309
    def get_lint_result(cls, files: Iterable[str]) -> LintResult:
1✔
310
        return LintResult(0, "", "", cls.tool_name)
1✔
311

312

313
def mock_fix_partition_as_linter(request) -> LintResult:
1✔
314
    request_type = {cls.Batch: cls for cls in _all_lint_requests()}[type(request)]
1✔
315
    return request_type.get_lint_result(request.elements)
1✔
316

317

318
@pytest.fixture
1✔
319
def rule_runner() -> RuleRunner:
1✔
320
    return RuleRunner()
1✔
321

322

323
def make_target(address: Address | None = None) -> Target:
1✔
324
    return MockTarget(
1✔
325
        {MockRequiredField.alias: "present"}, address or Address("", target_name="tests")
326
    )
327

328

329
def run_lint_rule(
1✔
330
    rule_runner: RuleRunner,
331
    *,
332
    lint_request_types: Iterable[type[_LintRequestT]],
333
    targets: list[Target],
334
    batch_size: int = 128,
335
    only: list[str] | None = None,
336
    skip_formatters: bool = False,
337
    skip_fixers: bool = False,
338
) -> tuple[int, str]:
339
    union_membership = UnionMembership.from_rules(
1✔
340
        {
341
            *[UnionRule(AbstractLintRequest, t) for t in lint_request_types],
342
            *[UnionRule(AbstractLintRequest.Batch, rt.Batch) for rt in lint_request_types],
343
            *[
344
                UnionRule(LintTargetsRequest.PartitionRequest, rt.PartitionRequest)
345
                for rt in lint_request_types
346
                if issubclass(rt, LintTargetsRequest)
347
            ],
348
            *[
349
                UnionRule(LintFilesRequest.PartitionRequest, rt.PartitionRequest)
350
                for rt in lint_request_types
351
                if issubclass(rt, LintFilesRequest)
352
            ],
353
        }
354
    )
355
    lint_subsystem = create_goal_subsystem(
1✔
356
        LintSubsystem,
357
        batch_size=batch_size,
358
        only=only or [],
359
        skip_formatters=skip_formatters,
360
        skip_fixers=skip_fixers,
361
    )
362

363
    with mock_console(rule_runner.options_bootstrapper) as (console, stdio_reader):
1✔
364
        result: Lint = run_rule_with_mocks(
1✔
365
            lint,
366
            rule_args=[
367
                console,
368
                Workspace(rule_runner.scheduler, _enforce_effects=False),
369
                Specs.empty(),
370
                lint_subsystem,
371
                union_membership,
372
                DistDir(relpath=Path("dist")),
373
            ],
374
            mock_calls={
375
                "pants.engine.internals.graph.filter_targets": lambda __implicitly: FilteredTargets(
376
                    tuple(targets)
377
                ),
378
                "pants.engine.internals.specs_rules.resolve_specs_paths": lambda _: SpecsPaths(
379
                    ("f.txt", "BUILD"), ()
380
                ),
381
                "pants.core.goals.lint.partition_targets": mock_target_partitioner,
382
                "pants.core.goals.lint.partition_files": mock_file_partitioner,
383
                "pants.core.goals.lint.lint_batch": mock_lint_partition,
384
                "pants.core.goals.lint_goal.run_fixer_or_formatter_as_linter": mock_fix_partition_as_linter,
385
                "pants.engine.intrinsics.digest_to_snapshot": lambda __implicitly: EMPTY_SNAPSHOT,
386
            },
387
            union_membership=union_membership,
388
            # We don't want temporary warnings to interfere with our expected output.
389
            show_warnings=False,
390
        )
391
        assert not stdio_reader.get_stdout()
1✔
392
        return result.exit_code, stdio_reader.get_stderr()
1✔
393

394

395
def test_duplicate_files_in_fixer(rule_runner: RuleRunner) -> None:
1✔
396
    assert MockFixRequest.Batch(
1✔
397
        "tool_name",
398
        ("a", "a"),
399
        _EmptyMetadata(),
400
        rule_runner.make_snapshot({"a": ""}),
401
    ).files == ("a",)
402

403

404
def test_invalid_target_noops(rule_runner: RuleRunner) -> None:
1✔
405
    exit_code, stderr = run_lint_rule(
1✔
406
        rule_runner, lint_request_types=[InvalidRequest], targets=[make_target()]
407
    )
408
    assert exit_code == 0
1✔
409
    assert stderr == ""
1✔
410

411

412
def test_summary(rule_runner: RuleRunner) -> None:
1✔
413
    """Test that we render the summary correctly.
414

415
    This tests that we:
416
    * Merge multiple results belonging to the same linter (`--per-file-caching`).
417
    * Decide correctly between skipped, failed, and succeeded.
418
    """
419
    good_address = Address("", target_name="good")
1✔
420
    bad_address = Address("", target_name="bad")
1✔
421

422
    request_types = [
1✔
423
        ConditionallySucceedsRequest,
424
        FailingRequest,
425
        SkippedRequest,
426
        SuccessfulRequest,
427
        SuccessfulFormatter,
428
        FailingFormatter,
429
        BuildFileFormatter,
430
        SuccessfulFixer,
431
        FailingFixer,
432
        BuildFileFixer,
433
        MockFilesRequest,
434
    ]
435
    targets = [make_target(good_address), make_target(bad_address)]
1✔
436

437
    exit_code, stderr = run_lint_rule(
1✔
438
        rule_runner,
439
        lint_request_types=request_types,
440
        targets=targets,
441
    )
442
    assert exit_code == FailingRequest.exit_code([bad_address])
1✔
UNCOV
443
    assert stderr == dedent(
×
444
        """\
445

446
        ✓ BUILD Annually succeeded.
447
        ✓ Bob The BUILDer succeeded.
448
        ✕ Conditionally Succeeds Linter failed.
449
        ✕ Failing Fixer failed.
450
        ✕ Failing Formatter failed.
451
        ✕ Failing Linter failed.
452
        ✓ Files Linter succeeded.
453
        ✓ Successful Fixer succeeded.
454
        ✓ Successful Formatter succeeded.
455
        ✓ Successful Linter succeeded.
456

457
        (One or more formatters failed. Run `pants fmt` to fix.)
458
        (One or more fixers failed. Run `pants fix` to fix.)
459
        """
460
    )
461

UNCOV
462
    exit_code, stderr = run_lint_rule(
×
463
        rule_runner,
464
        lint_request_types=request_types,
465
        targets=targets,
466
        only=[
467
            FailingRequest.tool_id,
468
            MockFilesRequest.tool_id,
469
            FailingFormatter.tool_id,
470
            FailingFixer.tool_id,
471
            BuildFileFormatter.tool_id,
472
            BuildFileFixer.tool_id,
473
        ],
474
    )
UNCOV
475
    assert stderr == dedent(
×
476
        """\
477

478
        ✓ BUILD Annually succeeded.
479
        ✓ Bob The BUILDer succeeded.
480
        ✕ Failing Fixer failed.
481
        ✕ Failing Formatter failed.
482
        ✕ Failing Linter failed.
483
        ✓ Files Linter succeeded.
484

485
        (One or more formatters failed. Run `pants fmt` to fix.)
486
        (One or more fixers failed. Run `pants fix` to fix.)
487
        """
488
    )
489

UNCOV
490
    exit_code, stderr = run_lint_rule(
×
491
        rule_runner,
492
        lint_request_types=request_types,
493
        targets=targets,
494
        skip_formatters=True,
495
        skip_fixers=True,
496
    )
UNCOV
497
    assert stderr == dedent(
×
498
        """\
499

500
        ✕ Conditionally Succeeds Linter failed.
501
        ✕ Failing Linter failed.
502
        ✓ Files Linter succeeded.
503
        ✓ Successful Linter succeeded.
504
        """
505
    )
506

UNCOV
507
    exit_code, stderr = run_lint_rule(
×
508
        rule_runner,
509
        lint_request_types=request_types,
510
        targets=targets,
511
        skip_fixers=True,
512
    )
UNCOV
513
    assert stderr == dedent(
×
514
        """\
515

516
        ✓ Bob The BUILDer succeeded.
517
        ✕ Conditionally Succeeds Linter failed.
518
        ✕ Failing Formatter failed.
519
        ✕ Failing Linter failed.
520
        ✓ Files Linter succeeded.
521
        ✓ Successful Formatter succeeded.
522
        ✓ Successful Linter succeeded.
523

524
        (One or more formatters failed. Run `pants fmt` to fix.)
525
        """
526
    )
527

UNCOV
528
    exit_code, stderr = run_lint_rule(
×
529
        rule_runner,
530
        lint_request_types=request_types,
531
        targets=targets,
532
        skip_formatters=True,
533
    )
UNCOV
534
    assert stderr == dedent(
×
535
        """\
536

537
        ✓ BUILD Annually succeeded.
538
        ✕ Conditionally Succeeds Linter failed.
539
        ✕ Failing Fixer failed.
540
        ✕ Failing Linter failed.
541
        ✓ Files Linter succeeded.
542
        ✓ Successful Fixer succeeded.
543
        ✓ Successful Linter succeeded.
544

545
        (One or more fixers failed. Run `pants fix` to fix.)
546
        """
547
    )
548

549

550
def test_default_single_partition_partitioner() -> None:
1✔
551
    class KitchenSubsystem(Subsystem):
1✔
552
        options_scope = "kitchen"
1✔
553
        help = "a cookbook might help"
1✔
554
        name = "The Kitchen"
1✔
555
        skip = SkipOption("lint")
1✔
556

557
    class LintKitchenRequest(LintTargetsRequest):
1✔
558
        field_set_type = MockLinterFieldSet
1✔
559
        tool_subsystem = KitchenSubsystem  # type: ignore[assignment]
1✔
560
        partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
1✔
561

562
    rules = [
1✔
563
        *LintKitchenRequest._get_rules(),
564
        QueryRule(Partitions, [LintKitchenRequest.PartitionRequest]),
565
    ]
566
    rule_runner = RuleRunner(rules=rules)
1✔
567
    field_sets = (
1✔
568
        MockLinterFieldSet(
569
            Address("knife"),
570
            MultipleSourcesField(["knife"], Address("knife")),
571
            MockRequiredField("present", Address("")),
572
        ),
573
        MockLinterFieldSet(
574
            Address("bowl"),
575
            MultipleSourcesField(["bowl"], Address("bowl")),
576
            MockRequiredField("present", Address("")),
577
        ),
578
    )
579
    partitions = rule_runner.request(Partitions, [LintKitchenRequest.PartitionRequest(field_sets)])
1✔
580
    assert len(partitions) == 1
1✔
581
    assert partitions[0].elements == field_sets
1✔
582

583
    rule_runner.set_options(["--kitchen-skip"])
1✔
584
    partitions = rule_runner.request(Partitions, [LintKitchenRequest.PartitionRequest(field_sets)])
1✔
585
    assert partitions == Partitions([])
1✔
586

587

588
@pytest.mark.parametrize("batch_size", [1, 32, 128, 1024])
1✔
589
def test_batched(rule_runner: RuleRunner, batch_size: int) -> None:
1✔
590
    exit_code, stderr = run_lint_rule(
1✔
591
        rule_runner,
592
        lint_request_types=[
593
            ConditionallySucceedsRequest,
594
            FailingRequest,
595
            SkippedRequest,
596
            SuccessfulRequest,
597
        ],
598
        targets=[make_target(Address("", target_name=f"good{i}")) for i in range(0, 512)],
599
        batch_size=batch_size,
600
    )
601
    assert exit_code == FailingRequest.exit_code([])
1✔
602
    assert stderr == dedent(
1✔
603
        """\
604

605
        ✓ Conditionally Succeeds Linter succeeded.
606
        ✕ Failing Linter failed.
607
        ✓ Successful Linter succeeded.
608
        """
609
    )
610

611

612
def test_streaming_output_success() -> None:
1✔
613
    result = LintResult(0, "stdout", "stderr", linter_name="linter")
1✔
614
    assert result.level() == LogLevel.INFO
1✔
615
    assert result.message() == dedent(
1✔
616
        """\
617
        linter succeeded.
618
        stdout
619
        stderr
620

621
        """
622
    )
623

624

625
def test_streaming_output_failure() -> None:
1✔
626
    result = LintResult(18, "stdout", "stderr", linter_name="linter")
1✔
627
    assert result.level() == LogLevel.ERROR
1✔
628
    assert result.message() == dedent(
1✔
629
        """\
630
        linter failed (exit code 18).
631
        stdout
632
        stderr
633

634
        """
635
    )
636

637

638
def test_streaming_output_partitions() -> None:
1✔
639
    result = LintResult(
1✔
640
        21, "stdout", "stderr", linter_name="linter", partition_description="ghc9.2"
641
    )
642
    assert result.level() == LogLevel.ERROR
1✔
643
    assert result.message() == dedent(
1✔
644
        """\
645
        linter failed (exit code 21).
646
        Partition: ghc9.2
647
        stdout
648
        stderr
649

650
        """
651
    )
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