ef_tests/
suite.rs

1//! Abstractions for groups of tests.
2
3use crate::{
4    case::{Case, Cases},
5    result::assert_tests_pass,
6};
7use std::path::{Path, PathBuf};
8use walkdir::{DirEntry, WalkDir};
9
10/// A collection of tests.
11pub trait Suite {
12    /// The type of test cases in this suite.
13    type Case: Case;
14
15    /// The name of the test suite used to locate the individual test cases.
16    ///
17    /// # Example
18    ///
19    /// - `GeneralStateTests`
20    /// - `BlockchainTests/InvalidBlocks`
21    /// - `BlockchainTests/TransitionTests`
22    fn suite_name(&self) -> String;
23
24    /// Load an run each contained test case.
25    ///
26    /// # Note
27    ///
28    /// This recursively finds every test description in the resulting path.
29    fn run(&self) {
30        // Build the path to the test suite directory
31        let suite_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
32            .join("ethereum-tests")
33            .join(self.suite_name());
34
35        // Verify that the path exists
36        assert!(suite_path.exists(), "Test suite path does not exist: {suite_path:?}");
37
38        // Find all files with the ".json" extension in the test suite directory
39        let test_cases = find_all_files_with_extension(&suite_path, ".json")
40            .into_iter()
41            .map(|test_case_path| {
42                let case = Self::Case::load(&test_case_path).expect("test case should load");
43                (test_case_path, case)
44            })
45            .collect();
46
47        // Run the test cases and collect the results
48        let results = Cases { test_cases }.run();
49
50        // Assert that all tests in the suite pass
51        assert_tests_pass(&self.suite_name(), &suite_path, &results);
52    }
53}
54
55/// Recursively find all files with a given extension.
56fn find_all_files_with_extension(path: &Path, extension: &str) -> Vec<PathBuf> {
57    WalkDir::new(path)
58        .into_iter()
59        .filter_map(Result::ok)
60        .filter(|e| e.file_name().to_string_lossy().ends_with(extension))
61        .map(DirEntry::into_path)
62        .collect()
63}