1use crate::{
4 case::{Case, Cases},
5 result::assert_tests_pass,
6};
7use std::path::{Path, PathBuf};
8use walkdir::{DirEntry, WalkDir};
9
10pub trait Suite {
12 type Case: Case;
14
15 fn suite_path(&self) -> &Path;
17
18 fn run(&self) {
20 let suite_path = self.suite_path();
21 for entry in WalkDir::new(suite_path).min_depth(1).max_depth(1) {
22 let entry = entry.expect("Failed to read directory");
23 if entry.file_type().is_dir() {
24 self.run_only(entry.file_name().to_string_lossy().as_ref());
25 }
26 }
27 }
28
29 fn run_only(&self, name: &str) {
35 let suite_path = self.suite_path().join(name);
37
38 assert!(suite_path.exists(), "Test suite path does not exist: {suite_path:?}");
40
41 let test_cases = find_all_files_with_extension(&suite_path, ".json")
43 .into_iter()
44 .map(|test_case_path| {
45 let case = Self::Case::load(&test_case_path).expect("test case should load");
46 (test_case_path, case)
47 })
48 .collect();
49
50 let results = Cases { test_cases }.run();
52
53 assert_tests_pass(name, &suite_path, &results);
55 }
56}
57
58fn find_all_files_with_extension(path: &Path, extension: &str) -> Vec<PathBuf> {
60 WalkDir::new(path)
61 .into_iter()
62 .filter_map(Result::ok)
63 .filter(|e| e.file_name().to_string_lossy().ends_with(extension))
64 .map(DirEntry::into_path)
65 .collect()
66}