1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
// Licensed under the MIT License
// SPDX-License-Identifier: MIT
// How much of the report is printed, when a first run against a large codebase
// can find a thousand offences.
//
// The cap is on what is shown and never on what is counted. Truncating the
// summary as well would produce a report that says a codebase has a hundred
// problems when it has a thousand -- the precise shape of quiet wrongness this
// tool exists to catch, and it would be this tool doing it.
use stern4rust::offence::Offence;
use stern4rust::offence_threshold::OffenceThreshold;
fn offences(count: usize) -> Vec<Offence> {
(1..=count)
.map(|line| {
Offence::new(
"src/a.rs",
line,
"header",
"wrong".to_string(),
"fix it".to_string(),
)
})
.collect()
}
#[test]
fn default_is_one_hundred() {
// Arrange & Act
let threshold = OffenceThreshold::default();
// Assert
assert_eq!(threshold.limit(), 100);
}
#[test]
fn is_unlimited_of_a_positive_limit_is_false() {
// Arrange & Act
let threshold = OffenceThreshold::new(1);
// Assert
assert!(!threshold.is_unlimited());
}
// Zero is the escape hatch rather than a way to silence the report entirely --
// a limit of nothing would be a tool that finds problems and refuses to say
// which.
#[test]
fn is_unlimited_of_zero_is_true() {
// Arrange & Act
let threshold = OffenceThreshold::new(0);
// Assert
assert!(threshold.is_unlimited());
}
#[test]
fn kept_of_fewer_offences_than_the_limit_returns_all_of_them() {
// Arrange
let found = offences(3);
// Act
let kept = OffenceThreshold::new(10).kept(&found);
// Assert
assert_eq!(kept.len(), 3);
}
#[test]
fn kept_of_more_offences_than_the_limit_returns_the_limit() {
// Arrange
let found = offences(7);
// Act
let kept = OffenceThreshold::new(2).kept(&found);
// Assert
assert_eq!(kept.len(), 2);
}
// The offences are already sorted by file then line, so the ones kept are whole
// files from the top rather than a scattering across the tree. A reader fixes
// what is shown, re-runs, and gets the next file.
#[test]
fn kept_returns_the_offences_in_the_order_it_was_given() {
// Arrange
let found = offences(7);
// Act
let kept = OffenceThreshold::new(2).kept(&found);
// Assert
assert_eq!(kept[0].line, 1);
assert_eq!(kept[1].line, 2);
}
#[test]
fn kept_with_no_limit_returns_all_of_them() {
// Arrange
let found = offences(500);
// Act
let kept = OffenceThreshold::new(0).kept(&found);
// Assert
assert_eq!(kept.len(), 500);
}
#[test]
fn omitted_of_fewer_offences_than_the_limit_is_none() {
// Arrange & Act
let omitted = OffenceThreshold::new(10).omitted(&offences(3));
// Assert
assert_eq!(omitted, 0);
}
#[test]
fn omitted_of_more_offences_than_the_limit_is_the_remainder() {
// Arrange & Act
let omitted = OffenceThreshold::new(2).omitted(&offences(7));
// Assert
assert_eq!(omitted, 5);
}
#[test]
fn omitted_with_no_limit_is_none() {
// Arrange & Act
let omitted = OffenceThreshold::new(0).omitted(&offences(500));
// Assert
assert_eq!(omitted, 0);
}