Skip to main content

ef_tests/
case.rs

1//! Test case definitions
2
3use crate::result::{CaseResult, Error};
4use rayon::{prelude::*, ThreadPool, ThreadPoolBuilder};
5use std::{
6    fmt::Debug,
7    path::{Path, PathBuf},
8    sync::LazyLock,
9};
10
11/// Stack size of the worker threads that run test cases.
12///
13/// The EVM recurses natively per call frame, so a 1024-deep call chain needs more than the 2 MiB
14/// default.
15const WORKER_STACK_SIZE: usize = 8 * 1024 * 1024;
16
17static POOL: LazyLock<ThreadPool> = LazyLock::new(|| {
18    ThreadPoolBuilder::new()
19        .stack_size(WORKER_STACK_SIZE)
20        .build()
21        .expect("failed to build the test case thread pool")
22});
23
24/// A single test case, capable of loading a JSON description of itself and running it.
25///
26/// See <https://ethereum-tests.readthedocs.io/> for test specs.
27pub trait Case: Debug + Send + Sync + Sized + 'static {
28    /// A description of the test.
29    fn description(&self) -> String {
30        "no description".to_string()
31    }
32
33    /// Load the test from the given file path.
34    ///
35    /// The file can be assumed to be a valid EF test case as described on <https://ethereum-tests.readthedocs.io/>.
36    fn load(path: &Path) -> Result<Self, Error>;
37
38    /// Run the test.
39    fn run(self) -> Result<(), Error>;
40}
41
42/// A container for multiple test cases.
43#[derive(Debug)]
44pub struct Cases<T> {
45    /// The contained test cases and the path to each test.
46    pub test_cases: Vec<(PathBuf, T)>,
47}
48
49impl<T: Case> Cases<T> {
50    /// Run the contained test cases.
51    pub fn run(self) -> Vec<CaseResult> {
52        POOL.install(|| {
53            self.test_cases
54                .into_par_iter()
55                .map(|(path, case)| CaseResult::new(&path, case.description(), case.run()))
56                .collect()
57        })
58    }
59}