103 lines
3.6 KiB
Rust
103 lines
3.6 KiB
Rust
//! A fake `podman` for the runtime tests: a shell script that records every call's arguments and,
|
|
//! for `run`, does what the test says. Do not edit.
|
|
//!
|
|
//! Included with `#[path = "support/fake_podman.rs"] mod fake_podman;`.
|
|
|
|
#![allow(dead_code)] // each test file uses a different part of this module
|
|
|
|
use std::os::unix::fs::PermissionsExt;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
use std::sync::{Arc, Mutex, MutexGuard};
|
|
|
|
use brokerd::config::{Config, Runner};
|
|
|
|
static NEXT: AtomicU32 = AtomicU32::new(0);
|
|
static SERIAL: Mutex<()> = Mutex::new(());
|
|
|
|
/// Tests that write a script and run it take turns. Otherwise another test's fork can hold the
|
|
/// script open for writing at the moment it is run, and running it fails with "text file busy"
|
|
/// (ETXTBSY), which has nothing to do with the code under test.
|
|
pub fn serial() -> MutexGuard<'static, ()> {
|
|
SERIAL.lock().unwrap_or_else(|p| p.into_inner())
|
|
}
|
|
|
|
pub const IMAGE: &str = "localhost/boxmaker-tools@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
|
|
|
pub struct Fake {
|
|
pub dir: PathBuf,
|
|
pub script: PathBuf,
|
|
}
|
|
|
|
impl Drop for Fake {
|
|
fn drop(&mut self) {
|
|
let _ = std::fs::remove_dir_all(&self.dir);
|
|
}
|
|
}
|
|
|
|
impl Fake {
|
|
/// A fake whose `run` does `run_body` (a shell fragment; `$D` is the fake's directory). Every
|
|
/// other command (`kill`, `rm`) is recorded and succeeds.
|
|
pub fn new(tag: &str, run_body: &str) -> Fake {
|
|
let n = NEXT.fetch_add(1, Ordering::SeqCst);
|
|
let dir = std::env::temp_dir().join(format!("bx-fp-{tag}-{}-{n}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let script = dir.join("podman");
|
|
let text = format!(
|
|
"#!/bin/sh\nD='{}'\nfor a in \"$@\"; do printf '%s\\n' \"$a\"; done >> \"$D/calls\"\necho --- >> \"$D/calls\"\n[ \"$1\" = run ] || exit 0\n{run_body}\n",
|
|
dir.display()
|
|
);
|
|
std::fs::write(&script, text).unwrap();
|
|
std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755)).unwrap();
|
|
Fake { dir, script }
|
|
}
|
|
|
|
/// Every call so far, each as its arguments.
|
|
pub fn calls(&self) -> Vec<Vec<String>> {
|
|
let text = std::fs::read_to_string(self.dir.join("calls")).unwrap_or_default();
|
|
let mut calls = Vec::new();
|
|
let mut current = Vec::new();
|
|
for line in text.lines() {
|
|
if line == "---" {
|
|
calls.push(std::mem::take(&mut current));
|
|
} else {
|
|
current.push(line.to_string());
|
|
}
|
|
}
|
|
calls
|
|
}
|
|
|
|
/// What the last `run` read on standard input, if the body saved it to `$D/stdin`.
|
|
pub fn stdin(&self) -> String {
|
|
std::fs::read_to_string(self.dir.join("stdin")).unwrap_or_default()
|
|
}
|
|
|
|
/// A `[runner]` using this fake, with `extra` lines added.
|
|
pub fn runner(&self, extra: &str) -> Runner {
|
|
let text = format!(
|
|
"[runner]\npodman = \"{}\"\nimage = \"{IMAGE}\"\n{extra}\n",
|
|
self.script.display()
|
|
);
|
|
Config::parse(&text).unwrap().runner.unwrap()
|
|
}
|
|
}
|
|
|
|
/// A log that keeps its lines.
|
|
#[derive(Clone, Default)]
|
|
pub struct Lines(pub Arc<Mutex<Vec<String>>>);
|
|
|
|
impl Lines {
|
|
pub fn sink(&self) -> Arc<dyn Fn(&str) + Send + Sync> {
|
|
let lines = Arc::clone(&self.0);
|
|
Arc::new(move |l: &str| lines.lock().unwrap().push(l.to_string()))
|
|
}
|
|
pub fn all(&self) -> String {
|
|
self.0.lock().unwrap().join("\n")
|
|
}
|
|
}
|
|
|
|
pub fn path(p: &Path) -> String {
|
|
p.to_str().unwrap().to_string()
|
|
}
|