1use std::{io::BufRead, sync::OnceLock, time::Instant};
2
3use unittest_report::{Report, ReportInfo, TestResult};
4
5static RESULT: OnceLock<Report> = OnceLock::new();
6
7fn try_bench(path: &str) {
8 let Ok(file) = std::fs::File::open(path) else {
9 return;
10 };
11 println!("starting benchmarking ({})", path);
12 let start = Instant::now();
13 for line in std::io::BufReader::new(file).lines() {
14 if let Ok(line) = &line {
15 if line.contains("\u{0000}") {
16 continue;
17 }
18 if !line.is_ascii() {
19 continue;
20 }
21 println!("STARTING {}", line);
22 let mut possibles = Vec::new();
23 for exe in std::fs::read_dir("/initrd").unwrap() {
24 if exe
25 .as_ref()
26 .unwrap()
27 .file_name()
28 .to_string_lossy()
29 .starts_with(line)
30 {
31 possibles.push(format!(
32 "/initrd/{}",
33 exe.as_ref().unwrap().file_name().to_string_lossy()
34 ));
35 }
36 if exe
37 .as_ref()
38 .unwrap()
39 .file_name()
40 .to_string_lossy()
41 .starts_with(&line.replace("-", "_"))
42 {
43 possibles.push(format!(
44 "/initrd/{}",
45 exe.as_ref().unwrap().file_name().to_string_lossy()
46 ));
47 }
48 }
49 for (i, exe) in possibles.iter().enumerate() {
50 if let Ok(test_comp) = monitor_api::CompartmentLoader::new(
51 &exe,
52 &exe,
53 monitor_api::NewCompartmentFlags::empty(),
54 )
55 .args(&[exe.as_str(), "--bench"])
56 .load()
57 {
58 let mut flags = test_comp.info().flags;
59 while !flags.contains(monitor_api::CompartmentFlags::EXITED) {
60 flags = test_comp.wait(flags);
61 }
62 } else {
63 if i == possibles.len() - 1 {
64 eprintln!("failed to start {}", line);
65 }
66 }
67 }
68 }
69 }
70 let dur = Instant::now() - start;
71 println!("unittest: benches finished in {:?}", dur);
72}
73
74fn main() {
75 try_bench("/initrd/bench_bins");
76 try_bench("/initrd/bench_bin");
77 let Ok(file) = std::fs::File::open("/initrd/test_bins")
78 .inspect_err(|e| eprintln!("failed to open test bins: {}", e))
79 else {
80 return;
81 };
82
83 let heartbeat_thread = std::thread::spawn(|| io_heartbeat());
84
85 let mut reports = vec![];
86 let start = Instant::now();
87 for line in std::io::BufReader::new(file).lines() {
88 if let Ok(line) = &line {
89 if line.contains("\u{0000}") {
90 continue;
91 }
92 if !line.is_ascii() {
93 continue;
94 }
95 println!("STARTING {}", line);
96 if let Ok(test_comp) = monitor_api::CompartmentLoader::new(
97 line,
98 line,
99 monitor_api::NewCompartmentFlags::empty(),
100 )
101 .args(&[line.as_str(), "--test"])
102 .load()
103 {
104 let mut flags = test_comp.info().flags;
105 while !flags.contains(monitor_api::CompartmentFlags::EXITED) {
106 flags = test_comp.wait(flags);
107 }
108 reports.push(TestResult {
109 name: line.clone(),
110 passed: true,
111 });
112 } else {
113 reports.push(TestResult {
114 name: line.clone(),
115 passed: false,
116 });
117 }
118 }
119 }
120 let dur = Instant::now() - start;
121 println!("unittest: tests finished, waiting for status request");
122 RESULT
123 .set(Report::ready(ReportInfo {
124 time: dur,
125 tests: reports,
126 }))
127 .unwrap();
128 heartbeat_thread.join().unwrap();
129}
130
131fn io_heartbeat() {
132 let mut buf = String::new();
133 while let Ok(_) = std::io::stdin().read_line(&mut buf) {
134 match buf.as_str().trim() {
135 "status" => {
136 if let Some(report) = RESULT.get() {
137 println!("unittest: creating report");
138 println!("REPORT {}", serde_json::to_string(report).unwrap());
139 return;
140 } else {
141 println!(
142 "REPORT {}",
143 serde_json::to_string(&Report::pending()).unwrap()
144 );
145 }
146 }
147 _ => {
148 println!("!! unknown command: {}", buf);
149 }
150 }
151 buf.clear();
152 }
153}