Skip to main content

plexus_conformance/
runner.rs

1use 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}