Skip to main content

trellis_testing/
conformance.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3mod runner;
4
5pub use runner::{
6    ConformanceCheckReport, ConformanceCheckResult, ConformanceFailure, ConformanceRunner,
7    conformance,
8};
9
10/// Opt-in conformance levels for application graph tests.
11#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
12pub enum ConformanceLevel {
13    /// Deterministic transaction trace and phase order.
14    DeterministicTrace = 1,
15    /// Scope and resource lifecycle.
16    ScopeResourceLifecycle = 2,
17    /// Materialized output coherence.
18    MaterializedOutput = 3,
19    /// Full-recompute oracle equivalence.
20    FullRecomputeOracle = 4,
21    /// Generated/model sequence checks.
22    GeneratedModelSequences = 5,
23    /// Performance/allocation smoke checks.
24    PerformanceSmoke = 6,
25}
26
27impl ConformanceLevel {
28    /// All currently defined conformance levels in ascending order.
29    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/// Report of supported and unsupported conformance levels.
40#[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    /// Creates an empty report.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Marks a level as supported by the test target.
55    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    /// Marks a level as explicitly unsupported by the test target.
63    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    /// Marks a level as explicitly unsupported by the target with a reason.
74    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    /// Records an executed check in this report.
89    pub fn record_check(mut self, check: ConformanceCheckReport) -> Self {
90        self.checks.push(check);
91        self
92    }
93
94    /// Returns supported levels.
95    pub fn supported_levels(&self) -> &BTreeSet<ConformanceLevel> {
96        &self.supported
97    }
98
99    /// Returns explicitly unsupported levels.
100    pub fn unsupported_levels(&self) -> &BTreeSet<ConformanceLevel> {
101        &self.unsupported
102    }
103
104    /// Returns unsupported reasons by conformance level.
105    pub fn unsupported_reasons(&self) -> &BTreeMap<ConformanceLevel, Vec<String>> {
106        &self.unsupported_reasons
107    }
108
109    /// Returns executed check summaries.
110    pub fn check_results(&self) -> &[ConformanceCheckReport] {
111        &self.checks
112    }
113
114    /// Returns true if a level is supported.
115    pub fn supports(&self, level: ConformanceLevel) -> bool {
116        self.supported.contains(&level)
117    }
118}
119
120/// Opt-in set of conformance levels an application wants to exercise.
121#[derive(Clone, Debug, Eq, PartialEq)]
122pub struct ConformanceSuite {
123    required: BTreeSet<ConformanceLevel>,
124}
125
126impl ConformanceSuite {
127    /// Creates an empty conformance suite.
128    pub fn new() -> Self {
129        Self {
130            required: BTreeSet::new(),
131        }
132    }
133
134    /// Creates a suite requiring all currently defined levels.
135    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    /// Adds a required level to this suite.
144    pub fn require(mut self, level: ConformanceLevel) -> Self {
145        self.required.insert(level);
146        self
147    }
148
149    /// Returns required levels.
150    pub fn required_levels(&self) -> &BTreeSet<ConformanceLevel> {
151        &self.required
152    }
153
154    /// Builds a report, marking required but unsupported levels explicitly.
155    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    /// Creates an executable runner for this conformance suite.
169    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}