plexus_conformance/
runner.rs1use plexus_engine::QueryResult;
2
3use crate::merge::normalize_rows;
4use crate::types::{
5 ConformanceCase, ConformanceError, ConformanceRunCaseReport, ConformanceRunReport,
6};
7
8pub fn assert_case_outcome(
9 case: &ConformanceCase,
10 outcome: Result<QueryResult, String>,
11) -> Result<(), ConformanceError> {
12 match (&case.expected_error_contains, outcome) {
13 (Some(expected), Ok(_)) => Err(ConformanceError::ExpectedErrorButSucceeded {
14 case: case.name.clone(),
15 expected: expected.clone(),
16 }),
17 (Some(expected), Err(err)) => {
18 if err
19 .to_ascii_lowercase()
20 .contains(&expected.to_ascii_lowercase())
21 {
22 Ok(())
23 } else {
24 Err(ConformanceError::ErrorMismatch {
25 case: case.name.clone(),
26 expected: expected.clone(),
27 actual: err,
28 })
29 }
30 }
31 (None, Err(err)) => Err(ConformanceError::ExpectedSuccessButFailed {
32 case: case.name.clone(),
33 actual: err,
34 }),
35 (None, Ok(out)) => {
36 if case.any_order {
37 if normalize_rows(&out.rows) == normalize_rows(&case.expected_rows) {
38 Ok(())
39 } else {
40 Err(ConformanceError::RowMismatchAnyOrder {
41 case: case.name.clone(),
42 expected: case.expected_rows.clone(),
43 actual: out.rows,
44 })
45 }
46 } else if out.rows == case.expected_rows {
47 Ok(())
48 } else {
49 Err(ConformanceError::RowMismatchOrdered {
50 case: case.name.clone(),
51 expected: case.expected_rows.clone(),
52 actual: out.rows,
53 })
54 }
55 }
56 }
57}
58
59pub fn run_conformance_cases<F>(
60 cases: &[ConformanceCase],
61 mut execute: F,
62) -> Vec<(String, Result<(), ConformanceError>)>
63where
64 F: FnMut(&ConformanceCase) -> Result<QueryResult, String>,
65{
66 cases
67 .iter()
68 .map(|case| {
69 let outcome = execute(case);
70 (case.name.clone(), assert_case_outcome(case, outcome))
71 })
72 .collect()
73}
74
75pub fn run_conformance_suite<F>(cases: &[ConformanceCase], mut execute: F) -> ConformanceRunReport
76where
77 F: FnMut(&ConformanceCase) -> Result<QueryResult, String>,
78{
79 let mut reports = Vec::with_capacity(cases.len());
80 let mut passed = 0usize;
81 for case in cases {
82 let outcome = assert_case_outcome(case, execute(case));
83 if outcome.is_ok() {
84 passed += 1;
85 }
86 reports.push(ConformanceRunCaseReport {
87 case: case.name.clone(),
88 tags: case.tags.clone(),
89 outcome,
90 });
91 }
92 ConformanceRunReport {
93 total: reports.len(),
94 passed,
95 failed: reports.len().saturating_sub(passed),
96 cases: reports,
97 }
98}