1#[derive(Debug, Clone)]
12pub struct ParityResult {
13 pub test_name: &'static str,
14 pub interpreter: ExecutionResult,
15 pub vm: ExecutionResult,
16 pub jit: ExecutionResult,
17}
18
19#[derive(Debug, Clone)]
21pub enum ExecutionResult {
22 Success(String),
24 Error(String),
26 NotSupported(&'static str),
28 Skipped(&'static str),
30}
31
32impl ExecutionResult {
33 pub fn is_success(&self) -> bool {
34 matches!(self, ExecutionResult::Success(_))
35 }
36
37 pub fn is_not_supported(&self) -> bool {
38 matches!(self, ExecutionResult::NotSupported(_))
39 }
40
41 pub fn is_skipped(&self) -> bool {
42 matches!(self, ExecutionResult::Skipped(_))
43 }
44
45 pub fn output(&self) -> Option<&str> {
47 match self {
48 ExecutionResult::Success(s) => Some(s),
49 _ => None,
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum ParityStatus {
57 AllMatch,
59 InterpreterVmMismatch { interpreter: String, vm: String },
61 InterpreterJitMismatch { interpreter: String, jit: String },
63 VmJitMismatch { vm: String, jit: String },
65 PartialSkipped { reason: String },
67 AllFailed,
69}
70
71impl ParityResult {
72 pub fn check_parity(&self) -> bool {
74 matches!(
75 self.parity_status(),
76 ParityStatus::AllMatch | ParityStatus::PartialSkipped { .. }
77 )
78 }
79
80 pub fn parity_status(&self) -> ParityStatus {
82 let i_out = self.interpreter.output();
83 let v_out = self.vm.output();
84 let j_out = self.jit.output();
85
86 match (i_out, v_out, j_out) {
87 (Some(i), Some(v), Some(j)) => {
89 if i == v && v == j {
90 ParityStatus::AllMatch
91 } else if i != v {
92 ParityStatus::InterpreterVmMismatch {
93 interpreter: i.to_string(),
94 vm: v.to_string(),
95 }
96 } else if i != j {
97 ParityStatus::InterpreterJitMismatch {
98 interpreter: i.to_string(),
99 jit: j.to_string(),
100 }
101 } else {
102 ParityStatus::VmJitMismatch {
103 vm: v.to_string(),
104 jit: j.to_string(),
105 }
106 }
107 }
108 (Some(i), Some(v), None) => {
110 if i == v {
111 ParityStatus::PartialSkipped {
112 reason: "JIT skipped/not supported".to_string(),
113 }
114 } else {
115 ParityStatus::InterpreterVmMismatch {
116 interpreter: i.to_string(),
117 vm: v.to_string(),
118 }
119 }
120 }
121 (Some(i), None, Some(j)) => {
122 if i == j {
123 ParityStatus::PartialSkipped {
124 reason: "VM skipped/not supported".to_string(),
125 }
126 } else {
127 ParityStatus::InterpreterJitMismatch {
128 interpreter: i.to_string(),
129 jit: j.to_string(),
130 }
131 }
132 }
133 (None, Some(v), Some(j)) => {
134 if v == j {
135 ParityStatus::PartialSkipped {
136 reason: "Interpreter skipped/not supported".to_string(),
137 }
138 } else {
139 ParityStatus::VmJitMismatch {
140 vm: v.to_string(),
141 jit: j.to_string(),
142 }
143 }
144 }
145 (Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => {
147 ParityStatus::PartialSkipped {
148 reason: "Only one backend succeeded".to_string(),
149 }
150 }
151 (None, None, None) => ParityStatus::AllFailed,
153 }
154 }
155
156 pub fn is_passing(&self) -> bool {
158 matches!(
159 self.parity_status(),
160 ParityStatus::AllMatch | ParityStatus::PartialSkipped { .. }
161 )
162 }
163
164 pub fn format_diff(&self) -> String {
166 match self.parity_status() {
167 ParityStatus::AllMatch => "All backends match".to_string(),
168 ParityStatus::InterpreterVmMismatch { interpreter, vm } => {
169 format!(
170 "Interpreter vs VM mismatch:\n Interpreter: {}\n VM: {}",
171 interpreter, vm
172 )
173 }
174 ParityStatus::InterpreterJitMismatch { interpreter, jit } => {
175 format!(
176 "Interpreter vs JIT mismatch:\n Interpreter: {}\n JIT: {}",
177 interpreter, jit
178 )
179 }
180 ParityStatus::VmJitMismatch { vm, jit } => {
181 format!("VM vs JIT mismatch:\n VM: {}\n JIT: {}", vm, jit)
182 }
183 ParityStatus::PartialSkipped { reason } => {
184 format!("Partial: {}", reason)
185 }
186 ParityStatus::AllFailed => "All backends failed".to_string(),
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_all_match() {
197 let result = ParityResult {
198 test_name: "test",
199 interpreter: ExecutionResult::Success("42".to_string()),
200 vm: ExecutionResult::Success("42".to_string()),
201 jit: ExecutionResult::Success("42".to_string()),
202 };
203 assert!(result.is_passing());
204 assert_eq!(result.parity_status(), ParityStatus::AllMatch);
205 }
206
207 #[test]
208 fn test_interpreter_vm_mismatch() {
209 let result = ParityResult {
210 test_name: "test",
211 interpreter: ExecutionResult::Success("42".to_string()),
212 vm: ExecutionResult::Success("43".to_string()),
213 jit: ExecutionResult::Success("42".to_string()),
214 };
215 assert!(!result.is_passing());
216 assert!(matches!(
217 result.parity_status(),
218 ParityStatus::InterpreterVmMismatch { .. }
219 ));
220 }
221
222 #[test]
223 fn test_partial_skipped() {
224 let result = ParityResult {
225 test_name: "test",
226 interpreter: ExecutionResult::Success("42".to_string()),
227 vm: ExecutionResult::Success("42".to_string()),
228 jit: ExecutionResult::NotSupported("pattern_def"),
229 };
230 assert!(result.is_passing());
231 assert!(matches!(
232 result.parity_status(),
233 ParityStatus::PartialSkipped { .. }
234 ));
235 }
236
237 #[test]
238 fn test_not_supported_variant() {
239 let result = ExecutionResult::NotSupported("async_block");
240 assert!(result.is_not_supported());
241 assert!(!result.is_success());
242 }
243}