Add systemd timer and desktop notifications #3
1 changed files with 35 additions and 2 deletions
|
|
@ -1,21 +1,54 @@
|
|||
//! Test-only fixture helpers shared across modules' `#[cfg(test)]` code
|
||||
//! (`publisher`, `sanity`) — not production code, and not built outside
|
||||
//! (`publisher`, `sanity`, `notifier`) — not production code, and not built outside
|
||||
//! `cargo test`. See docs/ARCHITECTURE.md > "organize by pipeline stage, not
|
||||
//! by layer": this exists to remove one specific piece of duplication
|
||||
//! (two near-identical copies of "write an executable shell script"), not
|
||||
//! as a general test-utils dump.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Set by `wait_until_executable`'s probe so the script exits before
|
||||
/// running its real body.
|
||||
const PROBE_ENV: &str = "PKGWATCH_TEST_SCRIPT_PROBE";
|
||||
/// `ETXTBSY`: exec of a file some process still has open for writing.
|
||||
const TEXT_FILE_BUSY: i32 = 26;
|
||||
|
||||
/// Writes an executable `#!/bin/sh` script named `name` into `dir`,
|
||||
/// running `body` as its contents. Used to stand in for a real binary
|
||||
/// (`repo-add`, a package's own `--version` command) in tests, without
|
||||
/// needing the real tool installed or a mutated global `PATH`.
|
||||
///
|
||||
/// Doesn't return until the script can actually be exec'd. `cargo test`
|
||||
/// runs tests on parallel threads, and a `fork` on another thread between
|
||||
/// this function's write-open and close leaves the child holding a copy of
|
||||
/// the write fd until it execs, so an immediate exec of the new script can
|
||||
/// fail with `Text file busy`. Once no holder is left none can appear (our
|
||||
/// fd is closed), so a probe exec that succeeds proves later ones will.
|
||||
pub(crate) fn write_executable_script(dir: &Path, name: &str, body: &str) -> PathBuf {
|
||||
let path = dir.join(name);
|
||||
std::fs::write(&path, format!("#!/bin/sh\n{body}\n")).unwrap();
|
||||
std::fs::write(
|
||||
&path,
|
||||
format!("#!/bin/sh\n[ -z \"${PROBE_ENV}\" ] || exit 0\n{body}\n"),
|
||||
)
|
||||
.unwrap();
|
||||
let mut perms = std::fs::metadata(&path).unwrap().permissions();
|
||||
std::os::unix::fs::PermissionsExt::set_mode(&mut perms, 0o755);
|
||||
std::fs::set_permissions(&path, perms).unwrap();
|
||||
wait_until_executable(&path);
|
||||
path
|
||||
}
|
||||
|
||||
fn wait_until_executable(path: &Path) {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
loop {
|
||||
match Command::new(path).env(PROBE_ENV, "1").status() {
|
||||
Ok(_) => return,
|
||||
Err(err) if err.raw_os_error() == Some(TEXT_FILE_BUSY) && Instant::now() < deadline => {
|
||||
std::thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
Err(err) => panic!("probe-exec of {} failed: {err}", path.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue