1use crate::Case;
4use reth_db::DatabaseError;
5use reth_ethereum_primitives::Block;
6use reth_primitives_traits::RecoveredBlock;
7use reth_provider::ProviderError;
8use reth_stateless::ExecutionWitness;
9use std::path::{Path, PathBuf};
10use thiserror::Error;
11
12#[derive(Debug, Error)]
18#[non_exhaustive]
19pub enum Error {
20 #[error("test was skipped")]
22 Skipped,
23 #[error("block {block_number} failed to process: {err}")]
27 BlockProcessingFailed {
28 block_number: u64,
30 partial_program_inputs: Vec<(RecoveredBlock<Block>, ExecutionWitness)>,
33 #[source]
35 err: Box<dyn std::error::Error + Send + Sync>,
36 },
37 #[error("an error occurred interacting with the file system at {path}: {error}")]
39 Io {
40 path: PathBuf,
42 #[source]
44 error: std::io::Error,
45 },
46 #[error("an error occurred deserializing the test at {path}: {error}")]
48 CouldNotDeserialize {
49 path: PathBuf,
51 #[source]
53 error: serde_json::Error,
54 },
55 #[error(transparent)]
57 Database(#[from] DatabaseError),
58 #[error("test failed: {0}")]
60 Assertion(String),
61 #[error("test failed: {0}")]
63 Provider(#[from] ProviderError),
64 #[error("an error occurred deserializing RLP: {0}")]
66 RlpDecodeError(#[from] alloy_rlp::Error),
67 #[error("an error occurred during consensus checks: {0}")]
69 ConsensusError(#[from] reth_consensus::ConsensusError),
70}
71
72impl Error {
73 pub fn block_failed(
75 block_number: u64,
76 partial_program_inputs: Vec<(RecoveredBlock<Block>, ExecutionWitness)>,
77 err: impl std::error::Error + Send + Sync + 'static,
78 ) -> Self {
79 Self::BlockProcessingFailed { block_number, partial_program_inputs, err: Box::new(err) }
80 }
81}
82
83#[derive(Debug)]
85pub struct CaseResult {
86 pub desc: String,
88 pub path: PathBuf,
90 pub result: Result<(), Error>,
92}
93
94impl CaseResult {
95 pub fn new(path: &Path, case: &impl Case, result: Result<(), Error>) -> Self {
97 Self { desc: case.description(), path: path.into(), result }
98 }
99}
100
101pub(crate) fn assert_tests_pass(suite_name: &str, path: &Path, results: &[CaseResult]) {
103 let (passed, failed, skipped) = categorize_results(results);
104
105 print_results(suite_name, path, &passed, &failed, &skipped);
106
107 assert!(failed.is_empty(), "Some tests failed (see above)");
108}
109
110pub(crate) fn categorize_results(
112 results: &[CaseResult],
113) -> (Vec<&CaseResult>, Vec<&CaseResult>, Vec<&CaseResult>) {
114 let mut passed = Vec::new();
115 let mut failed = Vec::new();
116 let mut skipped = Vec::new();
117
118 for case in results {
119 match case.result.as_ref().err() {
120 Some(Error::Skipped) => skipped.push(case),
121 Some(_) => failed.push(case),
122 None => passed.push(case),
123 }
124 }
125
126 (passed, failed, skipped)
127}
128
129pub(crate) fn print_results(
131 suite_name: &str,
132 path: &Path,
133 passed: &[&CaseResult],
134 failed: &[&CaseResult],
135 skipped: &[&CaseResult],
136) {
137 println!("Suite: {suite_name} (at {})", path.display());
138 println!(
139 "Ran {} tests ({} passed, {} failed, {} skipped)",
140 passed.len() + failed.len() + skipped.len(),
141 passed.len(),
142 failed.len(),
143 skipped.len()
144 );
145
146 for case in skipped {
147 println!("[S] Case {} skipped", case.path.display());
148 }
149
150 for case in failed {
151 let error = case.result.as_ref().unwrap_err();
152 println!("[!] Case {} failed (description: {}): {}", case.path.display(), case.desc, error);
153 }
154}