use std::time::{Duration, Instant};
#[cfg(unix)]
use std::{
fs,
io::{self, Write as _},
os::unix::fs::PermissionsExt as _,
path::Path,
process::{Command, Stdio},
};
#[cfg(unix)]
use crate::child::{CleanupPolicy, OwnedChild};
#[cfg(unix)]
pub mod crash_barrier;
pub mod http;
pub mod net;
fn remaining_until(deadline: Instant) -> Option<Duration> {
deadline
.checked_duration_since(Instant::now())
.filter(|remaining| !remaining.is_zero())
}
#[cfg(unix)]
pub fn process_state(pid: u32) -> io::Result<Option<char>> {
use crate::tool::{OutputLimit, OutputLimits, capture_command};
let output = capture_command(
Command::new("/bin/ps").args(["-p", &pid.to_string(), "-o", "stat="]),
OutputLimits {
stdout: OutputLimit::Terminate(4096),
stderr: OutputLimit::Terminate(4096),
timeout: None,
},
)
.map_err(io::Error::other)?;
if output.status.code() == Some(1) && output.stdout.is_empty() && output.stderr.is_empty() {
return Ok(None);
}
if !output.status.success() {
return Err(io::Error::other(format!(
"process-state inspection failed: {}",
output.status
)));
}
let state = std::str::from_utf8(&output.stdout)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
Ok(state.trim().chars().next())
}
#[cfg(unix)]
pub fn write_executable_script(path: &Path, contents: impl AsRef<[u8]>) -> io::Result<()> {
let mut writer = OwnedChild::spawn(
Command::new("/bin/sh")
.args(["-c", "cat > \"$1\"", "write-test-script"])
.arg(path)
.stdin(Stdio::piped()),
CleanupPolicy::KillAndWait,
)?;
let written = writer
.take_stdin()
.ok_or_else(|| io::Error::other("script writer stdin missing"))?
.write_all(contents.as_ref());
let status = writer.wait();
written?;
let status = status?;
if !status.success() {
return Err(io::Error::other(format!("script writer failed: {status}")));
}
let mut permissions = fs::metadata(path)?.permissions();
permissions.set_mode(0o755);
fs::set_permissions(path, permissions)
}
#[cfg(all(test, unix))]
mod tests;
#[cfg(unix)]
#[must_use]
pub fn run_isolated(driver: &str, context: &str, limits: crate::tool::OutputLimits) -> bool {
let thread = std::thread::current();
let name = thread.name().expect("libtest names each test thread");
if std::env::var(driver).as_deref() == Ok(name) {
return false;
}
let mut child = OwnedChild::spawn(
Command::new(std::env::current_exe().expect("current test executable"))
.args([
"--exact",
name,
"--include-ignored",
"--test-threads=1",
"--nocapture",
])
.env(driver, name)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped()),
CleanupPolicy::KillAndWait,
)
.expect("spawn isolated test");
crate::tool::communicate_child(
&mut child,
None,
limits,
crate::tool::SuccessfulExit::Cleanup,
&mut || false,
)
.and_then(crate::tool::CompletedExecution::require_success)
.unwrap_or_else(|error| panic!("{}", crate::tool::format_failure_evidence(context, &error)));
true
}