Skip to main content

ef_tests/
result.rs

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