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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
"""The values the timeout contract compares, and the terms it derives.
Separated from the nextest reading in ``nextest_budgets`` and the
workflow reading in ``coverage_lanes`` so each stays legible on its own
and none outgrows the 400-line limit ``AGENTS.md`` sets.
See "Test timeouts: four tiers, outermost last" in
``docs/developers-guide.md``.
"""
: = .
: = / /
: = / /
#: The environment variable the shared coverage action reads for its
#: wall-clock cap on one `cargo` invocation.
: =
#: The action whose steps run under that watchdog.
: =
#: Everything in a coverage job that is not a `cargo` invocation the
#: watchdog bounds: the checkout and toolchain setup, the cache save and
#: restore, and here also the suite's own `cargo nextest` step and the
#: Loom models, which run outside the coverage step. The job timer
#: covers all of it; the watchdog does not.
#:
#: Measured from the worst of many runs rather than one, and across runs
#: of every conclusion rather than successful ones only. Across the last
#: 115 `ci.yml` coverage jobs, 44 successful, 55 failed, and 16
#: cancelled, the widest gap between the coverage step and its job was
#: 969 s on run 30024924292. Across all 19 `coverage-main.yml` runs it
#: was 42 s on run 29354687551. No cancelled run came near the ceiling,
#: the worst reaching 727 s of a 3,600 s budget, so none was ended by
#: any of these four timers. Twenty minutes covers the worst gap seen
#: with eleven minutes to spare, and none of those runs was genuinely
#: cold.
: =
#: The watchdog every coverage step is required to declare, as a value
#: rather than as a floor.
#:
#: The ordering contract elsewhere derives a floor from the nextest
#: budgets and asserts each watchdog clears it. That is what catches a
#: watchdog set too low, and it is not a substitute for this: a floor is
#: satisfied by any larger number, so a lane raised to six hours by a
#: paste would pass every derived comparison while quietly letting a
#: wedged `cargo` burn the job's whole ceiling. This constant says what
#: the value is. The two together mean a change has to be deliberate in
#: both places and still has to clear the arithmetic.
#:
#: 1,800 s is also the shared action's own default, which is why writing
#: it down matters: deleting the variable changes nothing observable
#: until the run it kills.
: =
#: What nextest allows a test between `SIGTERM` and `SIGKILL` when a
#: `slow-timeout` table names no `grace-period`. The default is per
#: declaration rather than per file: a table omitting the field gets
#: this, whatever another table names. Every table in this repository's
#: `.config/nextest.toml` names 5 s, so the value in force here is five
#: seconds and this is what a new table omitting the field would allow.
: =
#: Added to that grace period to cover the teardown and report writing
#: that follow it. Kept a separate term rather than folded into a single
#: floor, so raising the grace period raises the requirement instead of
#: being absorbed silently.
: =
#: Build time inside a `cargo` invocation before nextest starts its own
#: clock.
: =
#: The whole-run budget `.config/nextest.toml` must declare, as
#: `docs/developers-guide.md` records it. Pinned as well as ordered: the
#: ordering below holds for a wide range of values and would not notice
#: the budget drifting away from the guide.
: =
#: How far a ceiling must sit above the sum it contains, rather than
#: merely reaching it. A ceiling equal to that sum cancels the job at
#: the moment the watchdog would have reported the overrun, and the
#: report is the only thing that makes an overrun actionable.
: =
#: The ceiling every coverage job must carry, likewise from the guide.
#: The requirement is 50 minutes, the margin takes it to 65, and the
#: estate's comparison is strict, so the ceiling is the next whole
#: minute above that: a ceiling equal to the requirement it contains
#: cancels the job at the moment the watchdog would have reported.
: =
"""Raised when a configured budget cannot be read as a bound.
Attributes
----------
field : str
The configuration key at fault.
value : object
What the configuration said, so the message names the text an
author has to change rather than only the rule it broke.
"""
"""Record the failing key and its value alongside the message.
Parameters
----------
message : str
The human-readable explanation.
field : str
The configuration key at fault.
value : object
What the configuration said.
"""
=
=
"""Raised when the nextest configuration cannot be read at all.
Separate from a budget that reads as unbounded. A file that is not
TOML, a ``slow-timeout`` table with no ``period``, or a file with no
``slow-timeout`` anywhere, is a configuration this contract cannot
reason about rather than one whose tiers are in the wrong order.
"""
"""Raised when a ``slow-timeout`` terminates no test.
``terminate-after`` is optional, and without it nextest marks a test
slow and lets it run on, so the configuration parses, reads as
deliberate, and bounds nothing. Reporting that as a period-long
budget would put a number on the tier that is missing.
"""
"""Return a YAML value as a mapping, or an empty one.
Structural matching rather than a chain of `isinstance` checks, per
this repository's own guidance, and with an explicit fallback so a
scalar or a list where a mapping was expected yields nothing rather
than raising from an attribute access several frames away.
Parameters
----------
value : object
Any value the YAML parser produced.
Returns
-------
dict[str, object]
The mapping, or an empty one when the value is not one.
"""
:
:
return
:
return
"""Return a YAML value as a list, or an empty one.
Parameters
----------
value : object
Any value the YAML parser produced.
Returns
-------
list[object]
The list, or an empty one when the value is not one.
"""
:
:
return
:
return
"""Return the smallest acceptable ceiling for one job, in seconds.
Three terms. Each coverage step may legitimately spend its whole
watchdog, so the sum is the floor. The measured work outside those
windows is added because the job timer covers it and the watchdogs
do not. The margin is added because a ceiling equal to that sum
cancels the job at the moment the watchdog would have reported the
overrun, and the report is the only thing that makes an overrun
actionable.
Parameters
----------
budgets : cabc.Sequence[Fraction]
One watchdog budget per coverage step in the job.
allowance : Fraction
The measured work outside those windows, in seconds.
Returns
-------
Fraction
The smallest acceptable ceiling, in seconds, exactly. Every term
is a ``Fraction`` so that the sum stays exact: a single ``float``
among them would convert the whole ceiling back, and the two
comparisons that read it are the ones a lost second would pass.
"""
return + +
"""Convert a nextest duration to seconds.
The grammar and the arithmetic live in ``nextest_durations``, which
mirrors ``humantime`` 2.3.0 exactly: integer seconds and
nanoseconds, kept apart the way ``humantime`` keeps them, with every
intermediate checked against the range of a ``u64`` and a fraction
refused unless it divides into its unit exactly. This wrapper exists
so the refusal arrives as the `NextestConfigurationError` the rest
of these readings report faults with, rather than as a second error
type callers would have to catch.
Parameters
----------
duration : str
A duration as nextest spells it, such as ``"120s"``, the
multi-component ``"1m 30s"`` or the fractional ``"1.5m"``.
Returns
-------
Fraction
The duration in seconds, exactly.
Raises
------
NextestConfigurationError
If the text is not a duration nextest would accept.
The value is exact rather than a float. Above two to the fifty-third
a float no longer holds every integer second, so two budgets nextest
reads as different would compare equal and an ordering that must
hold strictly would pass on a configuration that violates it.
Examples
--------
>>> seconds("120s") == 120
True
>>> seconds("1m 30s") == 90
True
>>> seconds("1.5m") == 90
True
"""
return