1//! Basic operations useful for building a testsuite
2
3use color_eyre::eyre::Result;
4use crossbeam_channel::unbounded;
5use crossbeam_channel::Receiver;
6use crossbeam_channel::Sender;
7use regex::bytes::RegexSet;
8use std::num::NonZeroUsize;
9use std::path::Component;
10use std::path::Path;
11use std::path::Prefix;
12use std::sync::OnceLock;
13use std::thread;
14
15/// Remove the common prefix of this path and the `root_dir`.
16pub(crate) fn strip_path_prefix<'a>(
17 path: &'a Path,
18 prefix: &Path,
19) -> impl Iterator<Item = Component<'a>> {
20 let mut components: Components<'_> = path.components();
21 for c: Component<'_> in prefix.components() {
22 // Windows has some funky paths. This is probably wrong, but works well in practice.
23 let deverbatimize: impl Fn(Component<'_>) -> … = |c: Component<'_>| match c {
24 Component::Prefix(prefix: PrefixComponent<'_>) => Err(match prefix.kind() {
25 Prefix::VerbatimUNC(a: &OsStr, b: &OsStr) => Prefix::UNC(a, b),
26 Prefix::VerbatimDisk(d: u8) => Prefix::Disk(d),
27 other: Prefix<'_> => other,
28 }),
29 c: Component<'_> => Ok(c),
30 };
31 let c2: Option> = components.next();
32 if Some(deverbatimize(c)) == c2.map(deverbatimize) {
33 continue;
34 }
35 return c2.into_iter().chain(components);
36 }
37 None.into_iter().chain(components)
38}
39
40impl CrateType {
41 /// Heuristic:
42 /// * [`CrateType::ProcMacro`] if the file contains a [proc macro attribute]
43 /// * [`CrateType::Test`] if the file contains `#[test]`
44 /// * [`CrateType::Bin`] if the file contains `fn main()` or `#[start]`
45 /// * otherwise [`CrateType::Lib`]
46 ///
47 /// [proc macro attribute]: https://doc.rust-lang.org/reference/procedural-macros.html
48 pub fn from_file_contents(file_contents: &[u8]) -> CrateType {
49 static RE: OnceLock<RegexSet> = OnceLock::new();
50 let re = RE.get_or_init(|| {
51 RegexSet::new([
52 r"#\[proc_macro(_derive|_attribute)?[\](]",
53 r"#\[test\]",
54 r"fn main()|#\[start\]",
55 ])
56 .unwrap()
57 });
58
59 match re.matches(file_contents).iter().next() {
60 Some(0) => CrateType::ProcMacro,
61 Some(1) => CrateType::Test,
62 Some(2) => CrateType::Bin,
63 _ => CrateType::Lib,
64 }
65 }
66}
67
68/// The kind of crate we're building here. Corresponds to `--crate-type` flags of rustc
69pub enum CrateType {
70 /// A proc macro
71 ProcMacro,
72 /// A file containing unit tests
73 Test,
74 /// A binary file containing a main function or start function
75 Bin,
76 /// A library crate
77 Lib,
78}
79
80/// A generic multithreaded runner that has a thread for producing work,
81/// a thread for collecting work, and `num_threads` threads for doing the work.
82pub fn run_and_collect<const N: usize, SUBMISSION: Send, RESULT: Send>(
83 num_threads: NonZeroUsize,
84 submitter: impl FnOnce([Sender<SUBMISSION>; N]) + Send,
85 runner: impl Sync + Fn(&[Receiver<SUBMISSION>; N], Sender<RESULT>) -> Result<()>,
86 collector: impl FnOnce(Receiver<RESULT>) + Send,
87) -> Result<()> {
88 // A channel for files to process
89 let (submit, receive): (Vec<_>, Vec<_>) = std::iter::repeat_with(unbounded).take(N).unzip();
90 let receive = receive[..].try_into().unwrap();
91 let mut submit = submit.into_iter();
92 let submit = std::array::from_fn(|_| submit.next().unwrap());
93
94 thread::scope(|s| {
95 // Create a thread that is in charge of walking the directory and submitting jobs.
96 // It closes the channel when it is done.
97 s.spawn(|| submitter(submit));
98
99 // A channel for the messages emitted by the individual test threads.
100 // Used to produce live updates while running the tests.
101 let (finished_files_sender, finished_files_recv) = unbounded();
102
103 s.spawn(|| collector(finished_files_recv));
104
105 let mut threads = vec![];
106
107 // Create N worker threads that receive files to test.
108 for _ in 0..num_threads.get() {
109 let finished_files_sender = finished_files_sender.clone();
110 threads.push(s.spawn(|| runner(receive, finished_files_sender)));
111 }
112
113 for thread in threads {
114 thread.join().unwrap()?;
115 }
116 Ok(())
117 })
118}
119

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more