ef_tests/
result.rs

1//! Test results and errors
2
3use crate::Case;
4use reth_db::DatabaseError;
5use reth_provider::ProviderError;
6use std::path::{Path, PathBuf};
7use thiserror::Error;
8
9/// Test errors
10///
11/// # Note
12///
13/// `Error::Skipped` should not be treated as a test failure.
14#[derive(Debug, Error)]
15#[non_exhaustive]
16pub enum Error {
17    /// The test was skipped
18    #[error("test was skipped")]
19    Skipped,
20    /// No post state found in test
21    #[error("no post state found for validation")]
22    MissingPostState,
23    /// An IO error occurred
24    #[error("an error occurred interacting with the file system at {path}: {error}")]
25    Io {
26        /// The path to the file or directory
27        path: PathBuf,
28        /// The specific error
29        #[source]
30        error: std::io::Error,
31    },
32    /// A deserialization error occurred
33    #[error("an error occurred deserializing the test at {path}: {error}")]
34    CouldNotDeserialize {
35        /// The path to the file we wanted to deserialize
36        path: PathBuf,
37        /// The specific error
38        #[source]
39        error: serde_json::Error,
40    },
41    /// A database error occurred.
42    #[error(transparent)]
43    Database(#[from] DatabaseError),
44    /// A test assertion failed.
45    #[error("test failed: {0}")]
46    Assertion(String),
47    /// An error internally in reth occurred.
48    #[error("test failed: {0}")]
49    Provider(#[from] ProviderError),
50    /// An error occurred while decoding RLP.
51    #[error("an error occurred deserializing RLP: {0}")]
52    RlpDecodeError(#[from] alloy_rlp::Error),
53}
54
55/// The result of running a test.
56#[derive(Debug)]
57pub struct CaseResult {
58    /// A description of the test.
59    pub desc: String,
60    /// The full path to the test.
61    pub path: PathBuf,
62    /// The result of the test.
63    pub result: Result<(), Error>,
64}
65
66impl CaseResult {
67    /// Create a new test result.
68    pub fn new(path: &Path, case: &impl Case, result: Result<(), Error>) -> Self {
69        Self { desc: case.description(), path: path.into(), result }
70    }
71}
72
73/// Assert that all the given tests passed and print the results to stdout.
74pub(crate) fn assert_tests_pass(suite_name: &str, path: &Path, results: &[CaseResult]) {
75    let (passed, failed, skipped) = categorize_results(results);
76
77    print_results(suite_name, path, &passed, &failed, &skipped);
78
79    assert!(failed.is_empty(), "Some tests failed (see above)");
80}
81
82/// Categorize test results into `(passed, failed, skipped)`.
83pub(crate) fn categorize_results(
84    results: &[CaseResult],
85) -> (Vec<&CaseResult>, Vec<&CaseResult>, Vec<&CaseResult>) {
86    let mut passed = Vec::new();
87    let mut failed = Vec::new();
88    let mut skipped = Vec::new();
89
90    for case in results {
91        match case.result.as_ref().err() {
92            Some(Error::Skipped) => skipped.push(case),
93            Some(_) => failed.push(case),
94            None => passed.push(case),
95        }
96    }
97
98    (passed, failed, skipped)
99}
100
101/// Display the given test results to stdout.
102pub(crate) fn print_results(
103    suite_name: &str,
104    path: &Path,
105    passed: &[&CaseResult],
106    failed: &[&CaseResult],
107    skipped: &[&CaseResult],
108) {
109    println!("Suite: {suite_name} (at {})", path.display());
110    println!(
111        "Ran {} tests ({} passed, {} failed, {} skipped)",
112        passed.len() + failed.len() + skipped.len(),
113        passed.len(),
114        failed.len(),
115        skipped.len()
116    );
117
118    for case in skipped {
119        println!("[S] Case {} skipped", case.path.display());
120    }
121
122    for case in failed {
123        let error = case.result.as_ref().unwrap_err();
124        println!("[!] Case {} failed (description: {}): {}", case.path.display(), case.desc, error);
125    }
126}