unittest/
main.rs

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}