rto_graph/compile_claim.rs
1//! When a green check refutes "this will not compile" — and when it does not
2//! (Stage 35).
3//!
4//! # The measurement this exists to spend
5//!
6//! On the adjudicated corpus ([`crate::review_corpus`]) *every* false positive was
7//! a claim that the code would not build, and *every* claim that the code would not
8//! build was a false positive — with no real defect anywhere in the class. That is
9//! the filter's whole licence, so it is **asserted against the data** rather than
10//! recorded here as a number that could go stale: see
11//! `the_compile_claim_class_is_still_the_only_false_one_and_wholly_false` in
12//! `tests/review_corpus.rs`, which fails the build if a real defect ever joins the
13//! class.
14//!
15//! CI already computed the refutation each time: the `msrv` job had gone **green at
16//! the very commit the comment was left on**, by 65 seconds on one and 83 on
17//! another. So withholding such a claim while the relevant check is green costs no
18//! extra compute and, on this evidence, discards nothing true. Every investigation
19//! those comments triggered was avoidable by reading a status that already existed.
20//!
21//! # Why "the build is green" is the wrong rule
22//!
23//! `docs/REVIEW_CHECKLIST.md` records the trap, and it is not hypothetical: this
24//! repository has already shipped a defect that a green build was structurally
25//! blind to. The `GGML_ASSERT` engine-teardown abort of #291 was **macOS-only**,
26//! and every compiling job here runs on `ubuntu-latest`. A filter keyed on "was
27//! the build green" would have suppressed a report of it.
28//!
29//! So a check refutes a claim only when it **ran at that commit** and **covered
30//! the configuration the claim is about**. Three axes decide coverage, each one a
31//! way this project's CI is narrower than "the build":
32//!
33//! - **Platform** — every job is `ubuntu-latest`, so nothing here compiles
34//! `cfg(target_os = "macos")` code, of which this repo has a good deal (Metal,
35//! the engine teardown path, the sandbox backend).
36//! - **Features** — `msrv` and `checks` are `--all-features`; `default-features`
37//! is the default set. Neither covers the other: turning features *on* cannot
38//! find a defect in code being cfg'd *out*, which is exactly why the
39//! `default-features` job exists.
40//! - **Targets** — `msrv` is `cargo check --workspace --all-features`, with **no
41//! `--all-targets`**. It therefore never compiles `#[cfg(test)]` modules or
42//! `tests/` integration targets. A claim that *test* code will not build on the
43//! MSRV toolchain is refuted by no job in this repository: the jobs that compile
44//! test targets do so on `stable`. That gap falls out of the model here rather
45//! than being asserted, and [`Suppression::Unrefuted`] reports it.
46//!
47//! Deliberately conservative on every axis: an unknown site is never refuted, and
48//! coverage is exact match rather than subsumption, because the cost of the two
49//! errors is not symmetric. A claim wrongly suppressed is a defect shipped
50//! silently — the #291 shape. A claim wrongly kept costs a human one look at a CI
51//! page.
52//!
53//! # Deciding, not fetching
54//!
55//! Everything here is a pure function of evidence a caller supplies. This crate
56//! cannot reach the network (its `gix` is pinned without transports), and a
57//! suppression rule is precisely the code that would otherwise acquire a "just ask
58//! the API" call. Whoever holds a GitHub token turns check runs into
59//! [`CheckRun`]s; the policy lives here where it can be tested exhaustively and
60//! offline.
61
62use std::collections::BTreeSet;
63
64use serde::{Deserialize, Serialize};
65
66/// The platform a compilation covers, or that a code site requires.
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68#[serde(rename_all = "kebab-case")]
69pub enum TargetOs {
70 /// Linux — every compiling job in this repository's CI.
71 Linux,
72 /// macOS. Nothing in CI compiles it; see the module docs.
73 MacOs,
74 /// Windows.
75 Windows,
76}
77
78/// Which Cargo feature set a compilation used, or that a code site needs to be
79/// compiled at all.
80///
81/// Not ordered by "more features": `--all-features` does not subsume the default
82/// set, because code behind `cfg(not(feature = …))` is compiled by exactly one of
83/// them. The `default-features` CI job exists because three `-D warnings` errors
84/// had rotted in code that `--all-features` structurally cannot see.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "kebab-case")]
87pub enum Features {
88 /// The crate's default feature set.
89 Default,
90 /// `--all-features`.
91 All,
92 /// `--no-default-features`.
93 None,
94}
95
96/// Which targets a compilation built.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
98#[serde(rename_all = "kebab-case")]
99pub enum Targets {
100 /// Libraries and binaries only — `cargo check` with no `--all-targets`. Does
101 /// **not** compile `#[cfg(test)]` modules or `tests/` integration targets.
102 LibsAndBins,
103 /// `--all-targets`: tests, benches and examples too.
104 AllTargets,
105}
106
107impl Targets {
108 /// Whether this scope compiles test code.
109 #[must_use]
110 pub fn compiles_tests(self) -> bool {
111 self == Self::AllTargets
112 }
113}
114
115/// How a check run finished. Only [`Conclusion::Success`] can refute anything; the
116/// rest are spelled out so that "no check run at all" and "a check run that
117/// failed" cannot be confused for each other by a caller mapping an API response.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
119#[serde(rename_all = "kebab-case")]
120pub enum Conclusion {
121 /// Green.
122 Success,
123 /// Red.
124 Failure,
125 /// Cancelled, timed out, skipped, or still running — anything that is not a
126 /// statement about whether the code compiles.
127 Inconclusive,
128}
129
130/// One compiling CI job, as it ran.
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132pub struct CheckRun {
133 /// Job name, for the message a suppression prints (`msrv`, `checks`, …). A
134 /// human told *which* job refutes the claim can go and look; a human told
135 /// "CI is green" cannot.
136 pub job: String,
137 /// The commit this job ran on. Compared for equality with the claim's
138 /// `reviewed_sha`: a green run on a *later* commit says nothing about the tree
139 /// the reviewer saw.
140 pub sha: String,
141 /// How it finished.
142 pub conclusion: Conclusion,
143 /// The toolchain it used (`1.94`, `stable`), verbatim.
144 pub toolchain: String,
145 /// The platform it ran on.
146 pub platform: TargetOs,
147 /// The feature set it compiled.
148 pub features: Features,
149 /// The targets it compiled.
150 pub targets: Targets,
151}
152
153/// The code a compile claim is about, in the terms that decide whether a job
154/// compiled it.
155///
156/// Every field is a *requirement*, and `None` means "not established". An
157/// unestablished requirement is never satisfied, so a claim whose site is unknown
158/// is never suppressed — the default is to let the human look.
159#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
160pub struct ClaimSite {
161 /// The commit the claim was made against — the corpus's `reviewed_sha`.
162 pub sha: String,
163 /// Path the claim is anchored to, for the suppression message.
164 pub path: String,
165 /// The platform whose `cfg` gates this code, or `None` if it is compiled on
166 /// every platform.
167 ///
168 /// `Some(MacOs)` is the #291 shape: no CI job compiles it, so no CI job can
169 /// refute a claim about it.
170 pub platform: Option<TargetOs>,
171 /// The feature set that compiles this code. `None` means unconditional — any
172 /// feature set compiles it.
173 ///
174 /// `Some(Features::All)` covers a module behind a non-default feature, like
175 /// `rto-exec`'s `#[cfg(feature = "exec-boxlite")] pub mod boxlite`.
176 pub features: Option<Features>,
177 /// Whether the site is test code (`#[cfg(test)]` or a `tests/` target). Test
178 /// code needs a job that passed `--all-targets`.
179 pub is_test_code: bool,
180 /// The toolchain the claim is about, if it names one — a claim of the form
181 /// "this is not on MSRV 1.94" is only refuted by a job that used that
182 /// toolchain, not by a green `stable` build.
183 pub toolchain: Option<String>,
184}
185
186impl ClaimSite {
187 /// A site at `sha`/`path` with nothing else established — the conservative
188 /// default, which no check run refutes.
189 #[must_use]
190 pub fn unknown(sha: impl Into<String>, path: impl Into<String>) -> Self {
191 Self {
192 sha: sha.into(),
193 path: path.into(),
194 ..Self::default()
195 }
196 }
197
198 /// Whether `run` compiled this site's code, ignoring the run's conclusion and
199 /// commit — the coverage half of the decision.
200 #[must_use]
201 pub fn covered_by(&self, run: &CheckRun) -> bool {
202 // Platform: an unconditional site is compiled by every platform; a gated
203 // site only by its own.
204 if self.platform.is_some_and(|p| p != run.platform) {
205 return false;
206 }
207 // Features: exact match, not subsumption. See `Features`.
208 if self.features.is_some_and(|f| f != run.features) {
209 return false;
210 }
211 // Targets: test code needs `--all-targets`.
212 if self.is_test_code && !run.targets.compiles_tests() {
213 return false;
214 }
215 // Toolchain: only a claim that names one constrains this.
216 if self
217 .toolchain
218 .as_deref()
219 .is_some_and(|t| t != run.toolchain)
220 {
221 return false;
222 }
223 true
224 }
225}
226
227/// Whether a compile claim may be withheld, and why — or why not.
228///
229/// The negative variants carry their reason because that is the actionable half:
230/// "unrefuted, because no green job compiled `cfg(target_os = "macos")` code at
231/// that commit" tells a reviewer it owes the claim a real look, which is exactly
232/// what the #291 teardown abort needed and did not get.
233#[derive(Debug, Clone, PartialEq, Eq)]
234pub enum Suppression {
235 /// A green job compiled this code at this commit. Withhold the claim.
236 Refuted {
237 /// The job that refutes it.
238 job: String,
239 /// One sentence naming the job, the commit and the configuration.
240 reason: String,
241 },
242 /// No green job covered this configuration at this commit. Keep the claim.
243 Unrefuted {
244 /// Why the evidence falls short.
245 reason: String,
246 },
247}
248
249impl Suppression {
250 /// Whether the claim should be withheld.
251 #[must_use]
252 pub fn is_refuted(&self) -> bool {
253 matches!(self, Self::Refuted { .. })
254 }
255
256 /// The explanation, in either case.
257 #[must_use]
258 pub fn reason(&self) -> &str {
259 match self {
260 Self::Refuted { reason, .. } | Self::Unrefuted { reason } => reason,
261 }
262 }
263}
264
265/// Decide whether `checks` refute a compile claim about `site`.
266///
267/// Refuted only by a run that is [`Conclusion::Success`], ran at **exactly**
268/// `site.sha`, and covered the site's configuration ([`ClaimSite::covered_by`]).
269/// Among several qualifying runs the first in job-name order is reported, so the
270/// answer does not depend on the order a caller happened to collect them in.
271#[must_use]
272pub fn suppression(site: &ClaimSite, checks: &[CheckRun]) -> Suppression {
273 let at_sha: Vec<&CheckRun> = checks.iter().filter(|c| c.sha == site.sha).collect();
274 if at_sha.is_empty() {
275 return Suppression::Unrefuted {
276 reason: format!(
277 "no check run recorded at {} — a green run on any other commit says \
278 nothing about the tree the claim was made against",
279 short(&site.sha)
280 ),
281 };
282 }
283
284 let mut green_covering: Vec<&CheckRun> = at_sha
285 .iter()
286 .copied()
287 .filter(|c| c.conclusion == Conclusion::Success && site.covered_by(c))
288 .collect();
289 green_covering.sort_by(|a, b| a.job.cmp(&b.job));
290 if let Some(run) = green_covering.first() {
291 return Suppression::Refuted {
292 job: run.job.clone(),
293 reason: format!(
294 "`{}` was green at {} and compiled {} ({}), so the claim that it \
295 does not build is already refuted",
296 run.job,
297 short(&site.sha),
298 site.path,
299 configuration(run),
300 ),
301 };
302 }
303
304 // Something ran at this commit, so say which axis fell short — a reviewer
305 // reading "unrefuted" needs to know whether to look at the code or at CI.
306 let covering: Vec<&CheckRun> = at_sha
307 .iter()
308 .copied()
309 .filter(|c| site.covered_by(c))
310 .collect();
311 if covering.is_empty() {
312 return Suppression::Unrefuted {
313 reason: format!(
314 "no check run at {} compiled {} ({}) — {}",
315 short(&site.sha),
316 site.path,
317 requirement(site),
318 "turning features on cannot find a defect in code cfg'd out, and \
319 no job here compiles another platform's code, so this claim is \
320 unrefuted and owes a real look",
321 ),
322 };
323 }
324 Suppression::Unrefuted {
325 reason: format!(
326 "the check run(s) covering {} at {} did not conclude green ({}), so \
327 nothing refutes the claim",
328 site.path,
329 short(&site.sha),
330 covering
331 .iter()
332 .map(|c| format!("{}: {:?}", c.job, c.conclusion))
333 .collect::<Vec<_>>()
334 .join(", "),
335 ),
336 }
337}
338
339/// The distinct job names that could ever refute a claim about `site`, given
340/// `checks` — what to tell an operator whose CI does not cover a configuration.
341#[must_use]
342pub fn jobs_covering(site: &ClaimSite, checks: &[CheckRun]) -> BTreeSet<String> {
343 checks
344 .iter()
345 .filter(|c| site.covered_by(c))
346 .map(|c| c.job.clone())
347 .collect()
348}
349
350/// A run's configuration, as one readable phrase.
351fn configuration(run: &CheckRun) -> String {
352 let features = match run.features {
353 Features::Default => "default features",
354 Features::All => "--all-features",
355 Features::None => "--no-default-features",
356 };
357 let targets = match run.targets {
358 Targets::LibsAndBins => "libs and bins",
359 Targets::AllTargets => "--all-targets",
360 };
361 format!(
362 "{:?}, {}, {}, toolchain {}",
363 run.platform, features, targets, run.toolchain
364 )
365}
366
367/// What a site needs compiled, as one readable phrase.
368fn requirement(site: &ClaimSite) -> String {
369 let mut parts = Vec::new();
370 if let Some(p) = site.platform {
371 parts.push(format!("needs {p:?}"));
372 }
373 if let Some(f) = site.features {
374 parts.push(format!("needs {f:?} features"));
375 }
376 if site.is_test_code {
377 parts.push("is test code, so needs --all-targets".to_owned());
378 }
379 if let Some(t) = &site.toolchain {
380 parts.push(format!("the claim names toolchain {t}"));
381 }
382 if parts.is_empty() {
383 "unconditional code".to_owned()
384 } else {
385 parts.join("; ")
386 }
387}
388
389/// Short form of a sha for a message, without assuming it is 40 characters.
390fn short(sha: &str) -> &str {
391 sha.get(..8).unwrap_or(sha)
392}
393
394#[cfg(test)]
395mod tests {
396 use super::{
397 CheckRun, ClaimSite, Conclusion, Features, Suppression, TargetOs, Targets, jobs_covering,
398 suppression,
399 };
400
401 /// This repository's compiling jobs at a commit, as `.github/workflows/ci.yml`
402 /// defines them. Written out because the whole filter turns on their exact
403 /// narrowness: all three are `ubuntu-latest`, only `msrv` is on the MSRV
404 /// toolchain, and only `msrv` omits `--all-targets`.
405 fn ci_at(sha: &str) -> Vec<CheckRun> {
406 vec![
407 CheckRun {
408 job: "msrv".to_owned(),
409 sha: sha.to_owned(),
410 conclusion: Conclusion::Success,
411 toolchain: "1.94".to_owned(),
412 platform: TargetOs::Linux,
413 features: Features::All,
414 targets: Targets::LibsAndBins,
415 },
416 CheckRun {
417 job: "checks".to_owned(),
418 sha: sha.to_owned(),
419 conclusion: Conclusion::Success,
420 toolchain: "stable".to_owned(),
421 platform: TargetOs::Linux,
422 features: Features::All,
423 targets: Targets::AllTargets,
424 },
425 CheckRun {
426 job: "default-features".to_owned(),
427 sha: sha.to_owned(),
428 conclusion: Conclusion::Success,
429 toolchain: "stable".to_owned(),
430 platform: TargetOs::Linux,
431 features: Features::Default,
432 targets: Targets::AllTargets,
433 },
434 ]
435 }
436
437 /// **The four corpus rows this filter is licensed by.** Each is unconditional
438 /// library code — verified at its `reviewed_sha` — except `boxlite.rs`, whose
439 /// module is `#[cfg(feature = "exec-boxlite")]`, so it needs an
440 /// `--all-features` job. None is test code. Every one is refuted, which is the
441 /// measured claim: the filter discards nothing true on this corpus.
442 #[test]
443 fn every_known_false_compile_claim_is_refuted() {
444 let rows = [
445 ("2b761ce7", "crates/rto-llama/src/slot.rs", None),
446 ("5e25f921", "crates/rto-graph/src/media.rs", None),
447 ("add397f2", "crates/roteiro/src/main.rs", None),
448 (
449 "c1481836",
450 "crates/rto-exec/src/boxlite.rs",
451 Some(Features::All),
452 ),
453 ];
454 for (sha, path, features) in rows {
455 let site = ClaimSite {
456 features,
457 ..ClaimSite::unknown(sha, path)
458 };
459 let verdict = suppression(&site, &ci_at(sha));
460 assert!(
461 verdict.is_refuted(),
462 "{path} at {sha} should be refuted: {}",
463 verdict.reason()
464 );
465 assert!(
466 verdict.reason().contains(sha) && verdict.reason().contains(path),
467 "the reason names the commit and the file: {}",
468 verdict.reason()
469 );
470 }
471 }
472
473 /// The #352 claim named a toolchain — "not on MSRV 1.94". Only the `msrv` job
474 /// can refute that; a green `stable` build cannot. The filter must pick the
475 /// right job rather than any green one.
476 #[test]
477 fn a_claim_naming_the_msrv_toolchain_is_refuted_only_by_the_msrv_job() {
478 let sha = "c1481836";
479 let site = ClaimSite {
480 features: Some(Features::All),
481 toolchain: Some("1.94".to_owned()),
482 ..ClaimSite::unknown(sha, "crates/rto-exec/src/boxlite.rs")
483 };
484 let Suppression::Refuted { ref job, .. } = suppression(&site, &ci_at(sha)) else {
485 panic!("the msrv job compiled it");
486 };
487 assert_eq!(job, "msrv");
488
489 // Strip the MSRV job and the same claim stands: the remaining green jobs
490 // are `stable`, which says nothing about 1.94.
491 let stable_only: Vec<CheckRun> =
492 ci_at(sha).into_iter().filter(|c| c.job != "msrv").collect();
493 let verdict = suppression(&site, &stable_only);
494 assert!(!verdict.is_refuted(), "{}", verdict.reason());
495 assert!(
496 verdict.reason().contains("1.94"),
497 "says which toolchain went uncovered: {}",
498 verdict.reason()
499 );
500 }
501
502 /// **The #291 case, and the reason this is not a "green build" check.** The
503 /// `GGML_ASSERT` teardown abort was `cfg(target_os = "macos")`. Every CI job
504 /// here is `ubuntu-latest`, so a wholly green CI must leave a claim about that
505 /// code standing.
506 #[test]
507 fn a_macos_only_site_is_never_refuted_by_ci_here() {
508 let sha = "0123456789abcdef0123456789abcdef01234567";
509 let site = ClaimSite {
510 platform: Some(TargetOs::MacOs),
511 ..ClaimSite::unknown(sha, "crates/rto-llama/src/backend.rs")
512 };
513 let verdict = suppression(&site, &ci_at(sha));
514 assert!(
515 !verdict.is_refuted(),
516 "a green ubuntu CI must not refute macOS-only code: {}",
517 verdict.reason()
518 );
519 assert!(
520 verdict.reason().contains("MacOs"),
521 "names the uncovered platform: {}",
522 verdict.reason()
523 );
524 assert!(
525 jobs_covering(&site, &ci_at(sha)).is_empty(),
526 "no job in this repository compiles macOS code"
527 );
528 }
529
530 /// A `--no-default-features` claim is unrefuted: no job here builds that set,
531 /// and `--all-features` cannot cover it because the two compile different code.
532 #[test]
533 fn a_no_default_features_site_is_unrefuted() {
534 let sha = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
535 let site = ClaimSite {
536 features: Some(Features::None),
537 ..ClaimSite::unknown(sha, "crates/rto-graph/src/lib.rs")
538 };
539 assert!(!suppression(&site, &ci_at(sha)).is_refuted());
540 }
541
542 /// Turning features *on* cannot find a defect in code being cfg'd *out*: a
543 /// default-set site is not covered by the `--all-features` jobs, only by
544 /// `default-features`.
545 #[test]
546 fn a_default_features_site_is_covered_only_by_the_default_features_job() {
547 let sha = "1111111111111111111111111111111111111111";
548 let site = ClaimSite {
549 features: Some(Features::Default),
550 ..ClaimSite::unknown(sha, "crates/rto-llama/src/lib.rs")
551 };
552 assert_eq!(
553 jobs_covering(&site, &ci_at(sha)),
554 ["default-features".to_owned()].into_iter().collect()
555 );
556 }
557
558 /// **The gap the model exposes.** `msrv` is `cargo check --workspace
559 /// --all-features` with no `--all-targets`, so it never compiles test code;
560 /// the jobs that do compile test code run on `stable`. A claim that test code
561 /// will not build on the MSRV toolchain is therefore refuted by no job in this
562 /// repository, and the filter must say so rather than suppress it.
563 #[test]
564 fn an_msrv_claim_about_test_code_is_refuted_by_no_job_here() {
565 let sha = "2222222222222222222222222222222222222222";
566 let site = ClaimSite {
567 is_test_code: true,
568 toolchain: Some("1.94".to_owned()),
569 ..ClaimSite::unknown(sha, "crates/rto-graph/tests/review_corpus.rs")
570 };
571 let verdict = suppression(&site, &ci_at(sha));
572 assert!(
573 !verdict.is_refuted(),
574 "no job compiles test code on the MSRV toolchain: {}",
575 verdict.reason()
576 );
577 assert!(
578 jobs_covering(&site, &ci_at(sha)).is_empty(),
579 "msrv omits --all-targets; the --all-targets jobs are stable"
580 );
581 // The same site without a toolchain claim *is* covered — by the stable
582 // jobs that pass `--all-targets`. So the gap is specifically MSRV-and-test,
583 // not test code in general.
584 let stable_claim = ClaimSite {
585 toolchain: None,
586 ..site
587 };
588 assert!(suppression(&stable_claim, &ci_at(sha)).is_refuted());
589 }
590
591 /// A green run on a different commit refutes nothing. This is the sibling of
592 /// the corpus's `reviewed_sha` rule: the tree that matters is the one the
593 /// reviewer saw.
594 #[test]
595 fn a_green_run_on_another_commit_refutes_nothing() {
596 let site = ClaimSite::unknown(
597 "3333333333333333333333333333333333333333",
598 "crates/rto-graph/src/lib.rs",
599 );
600 let elsewhere = ci_at("4444444444444444444444444444444444444444");
601 let verdict = suppression(&site, &elsewhere);
602 assert!(!verdict.is_refuted());
603 assert!(
604 verdict.reason().contains("no check run recorded"),
605 "{}",
606 verdict.reason()
607 );
608 }
609
610 /// A failing or still-running job is not a refutation, and the message says
611 /// which it was — "unrefuted" alone would leave a reviewer unsure whether to
612 /// look at the code or wait for CI.
613 #[test]
614 fn a_non_green_conclusion_is_not_a_refutation() {
615 let sha = "5555555555555555555555555555555555555555";
616 for conclusion in [Conclusion::Failure, Conclusion::Inconclusive] {
617 let runs: Vec<CheckRun> = ci_at(sha)
618 .into_iter()
619 .map(|c| CheckRun { conclusion, ..c })
620 .collect();
621 let site = ClaimSite::unknown(sha, "crates/roteiro/src/main.rs");
622 let verdict = suppression(&site, &runs);
623 assert!(!verdict.is_refuted(), "{conclusion:?}");
624 assert!(
625 verdict.reason().contains(&format!("{conclusion:?}")),
626 "names the conclusion: {}",
627 verdict.reason()
628 );
629 }
630 }
631
632 /// With no evidence at all, nothing is suppressed. The filter is opt-in on
633 /// evidence, so a caller that cannot reach CI loses the filter rather than
634 /// gaining a blanket suppression.
635 #[test]
636 fn no_evidence_suppresses_nothing() {
637 let site = ClaimSite::unknown(
638 "6666666666666666666666666666666666666666",
639 "crates/rto-graph/src/lib.rs",
640 );
641 assert!(!suppression(&site, &[]).is_refuted());
642 }
643
644 /// The reported job does not depend on the order the caller collected runs in.
645 #[test]
646 fn the_reported_job_is_order_independent() {
647 let sha = "7777777777777777777777777777777777777777";
648 let site = ClaimSite::unknown(sha, "crates/roteiro/src/main.rs");
649 let mut reversed = ci_at(sha);
650 reversed.reverse();
651 let forward = suppression(&site, &ci_at(sha));
652 let backward = suppression(&site, &reversed);
653 assert_eq!(forward, backward);
654 let Suppression::Refuted { ref job, .. } = forward else {
655 panic!("unconditional code is refuted by a green all-features job");
656 };
657 assert_eq!(job, "checks", "job-name order, not collection order");
658 }
659}