Skip to content

Commit 5cd45c0

Browse files
committed
refactor(infra): improve conformance runner
1 parent 13060c8 commit 5cd45c0

File tree

3 files changed

+570
-27
lines changed

3 files changed

+570
-27
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//! File discovery layer for conformance test suites
2+
//!
3+
//! This module provides centralized file discovery to avoid redundant directory traversals.
4+
//! Instead of each Suite walking the directory tree independently, file discovery is performed
5+
//! once per test root directory and the results are shared across multiple tool runs.
6+
7+
use std::{
8+
fs,
9+
io::Read,
10+
path::{Path, PathBuf},
11+
process::{Command, Stdio},
12+
};
13+
14+
use encoding_rs::UTF_16LE;
15+
use encoding_rs_io::DecodeReaderBytesBuilder;
16+
use walkdir::WalkDir;
17+
18+
use crate::workspace_root;
19+
20+
/// Discovered test files from a single test root directory
21+
#[derive(Debug, Clone)]
22+
pub struct DiscoveredFiles {
23+
/// Root directory that was scanned
24+
#[expect(dead_code)]
25+
pub test_root: PathBuf,
26+
/// List of discovered file paths (relative to workspace root)
27+
pub files: Vec<DiscoveredFile>,
28+
}
29+
30+
/// A single discovered test file
31+
#[derive(Debug, Clone)]
32+
pub struct DiscoveredFile {
33+
/// Path relative to workspace root
34+
pub path: PathBuf,
35+
/// Source code content
36+
pub code: String,
37+
}
38+
39+
/// File discovery configuration
40+
pub struct FileDiscoveryConfig<'a> {
41+
/// Test root directory relative to workspace
42+
pub test_root: &'a Path,
43+
/// Optional filter string to match against file paths
44+
pub filter: Option<&'a str>,
45+
/// Function to determine if a path should be skipped
46+
pub skip_test_path: Box<dyn Fn(&Path) -> bool + 'a>,
47+
/// Whether to skip directory crawl entirely
48+
pub skip_test_crawl: bool,
49+
/// Name of the test suite (for submodule initialization messages)
50+
pub suite_name: &'a str,
51+
}
52+
53+
impl DiscoveredFiles {
54+
/// Discover files from a test root directory
55+
pub fn discover(config: &FileDiscoveryConfig<'_>) -> Self {
56+
let test_path = workspace_root();
57+
let test_root = config.test_root.to_path_buf();
58+
59+
let paths = if config.skip_test_crawl {
60+
vec![]
61+
} else {
62+
let cases_path = test_path.join(&test_root);
63+
64+
let get_paths = || {
65+
WalkDir::new(&cases_path)
66+
.into_iter()
67+
.filter_map(Result::ok)
68+
.filter(|e| !e.file_type().is_dir())
69+
.filter(|e| e.file_name() != ".DS_Store")
70+
.map(|e| e.path().to_owned())
71+
.filter(|path| !(config.skip_test_path)(path))
72+
.filter(|path| {
73+
config.filter.is_none_or(|query| path.to_string_lossy().contains(query))
74+
})
75+
.collect::<Vec<_>>()
76+
};
77+
78+
let mut paths = get_paths();
79+
80+
// Initialize git submodule if it is empty and no filter is provided
81+
if paths.is_empty() && config.filter.is_none() {
82+
println!("-------------------------------------------------------");
83+
println!("git submodule is empty for {}", config.suite_name);
84+
println!("Running `just submodules` to clone the submodules");
85+
println!("This may take a while.");
86+
println!("-------------------------------------------------------");
87+
Command::new("just")
88+
.args(["submodules"])
89+
.stdout(Stdio::inherit())
90+
.stderr(Stdio::inherit())
91+
.output()
92+
.expect("failed to execute `just submodules`");
93+
paths = get_paths();
94+
}
95+
paths
96+
};
97+
98+
// Read all files in parallel
99+
let files = paths
100+
.into_iter()
101+
.map(|path| {
102+
let mut code = fs::read_to_string(&path).unwrap_or_else(|_| {
103+
// TypeScript tests may contain utf_16 encoding files
104+
let file = fs::File::open(&path).unwrap();
105+
let mut content = String::new();
106+
DecodeReaderBytesBuilder::new()
107+
.encoding(Some(UTF_16LE))
108+
.build(file)
109+
.read_to_string(&mut content)
110+
.unwrap();
111+
content
112+
});
113+
114+
let path = path.strip_prefix(&test_path).unwrap().to_owned();
115+
// Remove the Byte Order Mark in some of the TypeScript files
116+
if code.starts_with('\u{feff}') {
117+
code.remove(0);
118+
}
119+
120+
DiscoveredFile { path, code }
121+
})
122+
.collect();
123+
124+
Self { test_root, files }
125+
}
126+
}

0 commit comments

Comments
 (0)