1use tester as test;
4
5use std::{
6 fs,
7 path::{Path, PathBuf},
8 process::Command,
9 sync::Arc,
10};
11use test::{ShouldPanic, TestDesc, TestDescAndFn, TestFn, TestName, TestType};
12use walkdir::{DirEntry, WalkDir};
13
14pub fn run_tests(cmd: &'static Path) -> i32 {
16 let args = std::env::args().collect::<Vec<_>>();
17 let mut opts = match test::test::parse_opts(&args) {
18 Some(Ok(o)) => o,
19 Some(Err(msg)) => {
20 eprintln!("error: {msg}");
21 return 101;
22 }
23 None => return 0,
24 };
25
26 if opts.test_threads.is_none() {
36 opts.test_threads = std::thread::available_parallelism().map(|x| x.get()).ok();
37 }
38
39 let mut tests = Vec::new();
40 make_tests(cmd, &mut tests);
41 tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice()));
42
43 if opts.list {
44 test::test_main(&args, tests, Some(opts.options));
46 return 0;
47 }
48
49 match test::run_tests_console(&opts, tests) {
50 Ok(true) => 0,
51 Ok(false) => {
52 eprintln!("Some tests failed");
53 1
54 }
55 Err(e) => {
56 eprintln!("I/O failure during tests: {e}");
57 101
58 }
59 }
60}
61
62fn make_tests(cmd: &'static Path, tests: &mut Vec<TestDescAndFn>) {
63 let config = Arc::new(Config::new(cmd));
64
65 let codegen = config.root.join("tests/codegen");
66 for entry in collect_tests(&codegen) {
67 let config = config.clone();
68 let path = entry.path().to_path_buf();
69 let stripped = path.strip_prefix(config.root).unwrap();
70 let name = stripped.display().to_string();
71 tests.push(TestDescAndFn {
72 desc: TestDesc {
73 name: TestName::DynTestName(name),
74 allow_fail: false,
75 ignore: false,
76 should_panic: ShouldPanic::No,
77 test_type: TestType::Unknown,
78 },
79 testfn: TestFn::DynTestFn(Box::new(move || run_test(&config, &path))),
80 });
81 }
82}
83
84fn collect_tests(root: &Path) -> impl Iterator<Item = DirEntry> {
85 WalkDir::new(root)
86 .sort_by_file_name()
87 .into_iter()
88 .map(Result::unwrap)
89 .filter(|e| e.file_type().is_file())
90}
91
92fn run_test(config: &Config, path: &Path) {
93 let test_name = path.file_stem().unwrap().to_str().unwrap();
94
95 let build_dir = &config.build_base;
105
106 let mut compiler = Command::new(config.cmd);
107 fs::create_dir_all(build_dir).unwrap();
108 compiler.arg(path).arg("-o").arg(build_dir);
109 let output = compiler.output().expect("failed to run test");
111 assert!(
112 output.status.success(),
113 "compiler failed with {}:\n{}",
114 output.status,
115 String::from_utf8_lossy(&output.stderr)
116 );
117 let out_dir = build_dir.join(test_name);
118 assert!(out_dir.exists(), "no output produced");
119
120 let input_path = out_dir.join("opt.ll");
121
122 let mut filecheck = Command::new(config.filecheck.as_deref().unwrap_or("FileCheck".as_ref()));
123 filecheck.arg(path).arg("--input-file").arg(&input_path);
124 let output = filecheck.output().expect("failed to run FileCheck");
126 assert!(
127 output.status.success(),
128 "FileCheck failed with {}:\n{}",
129 output.status,
130 String::from_utf8_lossy(&output.stderr)
131 );
132}
133
134struct Config {
135 cmd: &'static Path,
136 root: &'static Path,
137 build_base: PathBuf,
138 filecheck: Option<PathBuf>,
139}
140
141impl Config {
142 fn new(cmd: &'static Path) -> Self {
143 let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
144 let build_base = root.join("target/tester");
145 fs::create_dir_all(&build_base).unwrap();
146 Self { root, cmd, build_base, filecheck: None }
147 }
148}