use std::os::unix::process::CommandExt as _;
const CHILD_VARIABLE: &str = "CLI_GIT_NO_THREADS_CHILD";
fn apply_limit() -> std::io::Result<()> {
let mut found = libc::rlimit { rlim_cur: 0, rlim_max: 0 };
unsafe {
if libc::getrlimit(libc::RLIMIT_NPROC, &mut found) != 0 {
return Err(std::io::Error::last_os_error());
}
let lowered = libc::rlimit { rlim_cur: 1, rlim_max: found.rlim_max };
if libc::setrlimit(libc::RLIMIT_NPROC, &lowered) != 0 {
return Err(std::io::Error::last_os_error());
}
}
return Ok(());
}
fn child_command(path: &str) -> std::process::Command {
let mut command: std::process::Command = std::process::Command::new(crate::test_support::this_test_binary().expect("the test binary"));
command.args(["--exact", path, "--nocapture", "--test-threads=1"]).env(CHILD_VARIABLE, "1").stdin(std::process::Stdio::null());
unsafe {
command.pre_exec(apply_limit);
}
return command;
}
#[cfg(test)]
#[path = "no_threads_child_test_support_tests.rs"]
mod tests;
pub(crate) fn ran_in_child(module: &str, name: &str) -> bool {
if std::env::var_os(CHILD_VARIABLE).is_some() {
return false;
}
let strip = |full: &str| -> String { return full.split_once("::").map_or(String::from(full), |(_, rest)| { return String::from(rest); }); };
let probe_path: String = format!("{}::tests::thread_probe", strip(module_path!()));
let probe = child_command(probe_path.as_str()).stderr(std::process::Stdio::null()).output().expect("the probe runs");
if String::from_utf8_lossy(probe.stdout.as_slice()).contains("thread-started") {
eprintln!("skipped: this user may exceed RLIMIT_NPROC, so no thread start was refused");
return true;
}
let path: String = format!("{}::{name}", strip(module));
let output = child_command(path.as_str()).output().expect("the child test runs");
let said: String = String::from_utf8_lossy(output.stdout.as_slice()).into_owned();
assert!(output.status.success(), "the child test {path} without a thread to start failed:\n{said}\n{}", String::from_utf8_lossy(output.stderr.as_slice()));
assert!(said.contains("running 1 test") && said.contains("1 passed"), "the child ran no test named {path}:\n{said}");
return true;
}