trellis_testing/
conformance.rs1use std::collections::{BTreeMap, BTreeSet};
2
3mod runner;
4
5pub use runner::{
6 ConformanceCheckReport, ConformanceCheckResult, ConformanceFailure, ConformanceRunner,
7 conformance,
8};
9
10#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
12pub enum ConformanceLevel {
13 DeterministicTrace = 1,
15 ScopeResourceLifecycle = 2,
17 MaterializedOutput = 3,
19 FullRecomputeOracle = 4,
21 GeneratedModelSequences = 5,
23 PerformanceSmoke = 6,
25}
26
27impl ConformanceLevel {
28 pub const ALL: [Self; 6] = [
30 Self::DeterministicTrace,
31 Self::ScopeResourceLifecycle,
32 Self::MaterializedOutput,
33 Self::FullRecomputeOracle,
34 Self::GeneratedModelSequences,
35 Self::PerformanceSmoke,
36 ];
37}
38
39#[derive(Clone, Debug, Default, Eq, PartialEq)]
41pub struct ConformanceReport {
42 supported: BTreeSet<ConformanceLevel>,
43 unsupported: BTreeSet<ConformanceLevel>,
44 unsupported_reasons: BTreeMap<ConformanceLevel, Vec<String>>,
45 checks: Vec<ConformanceCheckReport>,
46}
47
48impl ConformanceReport {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn support(mut self, level: ConformanceLevel) -> Self {
56 self.unsupported.remove(&level);
57 self.unsupported_reasons.remove(&level);
58 self.supported.insert(level);
59 self
60 }
61
62 pub fn unsupported(mut self, level: ConformanceLevel) -> Self {
64 self.unsupported_reasons
65 .entry(level)
66 .or_default()
67 .push("explicitly unsupported".to_owned());
68 self.supported.remove(&level);
69 self.unsupported.insert(level);
70 self
71 }
72
73 pub fn unsupported_with_reason(
75 mut self,
76 level: ConformanceLevel,
77 reason: impl Into<String>,
78 ) -> Self {
79 self.supported.remove(&level);
80 self.unsupported.insert(level);
81 self.unsupported_reasons
82 .entry(level)
83 .or_default()
84 .push(reason.into());
85 self
86 }
87
88 pub fn record_check(mut self, check: ConformanceCheckReport) -> Self {
90 self.checks.push(check);
91 self
92 }
93
94 pub fn supported_levels(&self) -> &BTreeSet<ConformanceLevel> {
96 &self.supported
97 }
98
99 pub fn unsupported_levels(&self) -> &BTreeSet<ConformanceLevel> {
101 &self.unsupported
102 }
103
104 pub fn unsupported_reasons(&self) -> &BTreeMap<ConformanceLevel, Vec<String>> {
106 &self.unsupported_reasons
107 }
108
109 pub fn check_results(&self) -> &[ConformanceCheckReport] {
111 &self.checks
112 }
113
114 pub fn supports(&self, level: ConformanceLevel) -> bool {
116 self.supported.contains(&level)
117 }
118}
119
120#[derive(Clone, Debug, Eq, PartialEq)]
122pub struct ConformanceSuite {
123 required: BTreeSet<ConformanceLevel>,
124}
125
126impl ConformanceSuite {
127 pub fn new() -> Self {
129 Self {
130 required: BTreeSet::new(),
131 }
132 }
133
134 pub fn all() -> Self {
136 let mut suite = Self::new();
137 for level in ConformanceLevel::ALL {
138 suite = suite.require(level);
139 }
140 suite
141 }
142
143 pub fn require(mut self, level: ConformanceLevel) -> Self {
145 self.required.insert(level);
146 self
147 }
148
149 pub fn required_levels(&self) -> &BTreeSet<ConformanceLevel> {
151 &self.required
152 }
153
154 pub fn report(&self, supported: &[ConformanceLevel]) -> ConformanceReport {
156 let mut report = ConformanceReport::new();
157 let supported = supported.iter().copied().collect::<BTreeSet<_>>();
158 for level in &self.required {
159 if supported.contains(level) {
160 report = report.support(*level);
161 } else {
162 report = report.unsupported(*level);
163 }
164 }
165 report
166 }
167
168 pub fn runner(self) -> ConformanceRunner {
170 ConformanceRunner::new(self)
171 }
172}
173
174impl Default for ConformanceSuite {
175 fn default() -> Self {
176 Self::new()
177 }
178}