tests: every temporary path is removed by a guard; no leftovers in /tmp
About 10,000 test leftovers had built up in titan's /tmp. loopd's Home and FakeServer, its fake broker's socket, bxctl's temp sockets, fake loopd and command homes, inferproxy's socket, and a few brokerd and toolkit tests created uniquely named directories or sockets and never removed them, or removed them by hand at the end, which a failing test skips. Each is now a guard removed on drop (bxctl gains tests/support/temp.rs). Two races also left gatewayd homes behind: the test Running only signalled its server to stop, and two Stage structs declared the home first, so it was removed while the server could still write into it. Running's drop now waits for the thread (with a 5-second limit), and every home is declared last. Checked: make gate twice, then the gate and three rounds of gatewayd and loopd tests; no new entry in /tmp; 944 tests pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
//! A `brokerd` that accepts and never answers does not hang `bxctl` (M3a review finding 9).
|
||||
|
||||
#[path = "support/temp.rs"]
|
||||
mod temp;
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -13,9 +15,7 @@ fn the_default_is_thirty_seconds() {
|
||||
|
||||
#[test]
|
||||
fn a_silent_brokerd_is_an_error_after_the_timeout() {
|
||||
let dir = std::env::temp_dir().join(format!("bx-admin-timeout-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let dir = temp::TempDir::new("bx-admin-timeout");
|
||||
let socket = dir.join("admin.sock");
|
||||
let listener = UnixListener::bind(&socket).unwrap();
|
||||
let held = std::thread::spawn(move || {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Tests for `bxctl chat`, against a fake `loopd` that speaks the channel protocol. Do not edit.
|
||||
|
||||
#[path = "support/temp.rs"]
|
||||
mod temp;
|
||||
use bxctl::chat::{ChatError, Printer, new_session_id, run_turn};
|
||||
use proto::{
|
||||
DataClass, Envelope, ErrorCode, Message, PROTOCOL_VERSION, SessionId, Turn, TurnDone,
|
||||
@@ -8,12 +10,9 @@ use proto::{
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
static NEXT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// What the fake `loopd` sends back for one turn, after the events: done or an error.
|
||||
#[derive(Clone)]
|
||||
enum End {
|
||||
@@ -24,6 +23,7 @@ enum End {
|
||||
struct FakeLoopd {
|
||||
socket: PathBuf,
|
||||
turns: Arc<Mutex<Vec<Turn>>>,
|
||||
_dir: temp::TempDir,
|
||||
}
|
||||
|
||||
fn usage() -> Usage {
|
||||
@@ -39,9 +39,7 @@ fn usage() -> Usage {
|
||||
/// Serves every connection with the same script: the given events, then `end`. Records the
|
||||
/// turns it received.
|
||||
fn fake_loopd(events: Vec<TurnEvent>, end: End) -> FakeLoopd {
|
||||
let n = NEXT.fetch_add(1, Ordering::SeqCst);
|
||||
let dir = std::env::temp_dir().join(format!("bxctl-test-{}-{n}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let dir = temp::TempDir::new("bxctl-test");
|
||||
let socket = dir.join("loop.sock");
|
||||
let listener = UnixListener::bind(&socket).unwrap();
|
||||
let turns = Arc::new(Mutex::new(Vec::new()));
|
||||
@@ -93,7 +91,11 @@ fn fake_loopd(events: Vec<TurnEvent>, end: End) -> FakeLoopd {
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
FakeLoopd { socket, turns }
|
||||
FakeLoopd {
|
||||
socket,
|
||||
turns,
|
||||
_dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
fn events() -> Vec<TurnEvent> {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! `bxctl reindex` and `bxctl memory`: parsing, what is sent to `memoryd`, and what is printed
|
||||
//! (M5a spec, section 10). Do not edit.
|
||||
|
||||
#[path = "support/temp.rs"]
|
||||
mod temp;
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc;
|
||||
@@ -112,11 +114,8 @@ fn fake(socket: &Path, answer: Message) -> mpsc::Receiver<Message> {
|
||||
rx
|
||||
}
|
||||
|
||||
fn dir(tag: &str) -> PathBuf {
|
||||
let path = std::env::temp_dir().join(format!("bx-mem-{tag}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&path);
|
||||
std::fs::create_dir_all(&path).unwrap();
|
||||
path
|
||||
fn dir(tag: &str) -> temp::TempDir {
|
||||
temp::TempDir::new(&format!("bx-mem-{tag}"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -15,12 +15,38 @@ use std::thread;
|
||||
|
||||
static NEXT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// A socket path in a fresh temporary directory.
|
||||
pub fn temp_socket(name: &str) -> PathBuf {
|
||||
/// A socket path in a fresh temporary directory, which is removed when the value is dropped
|
||||
/// (tip T44).
|
||||
pub struct TempSocket {
|
||||
dir: PathBuf,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TempSocket {
|
||||
type Target = std::path::Path;
|
||||
fn deref(&self) -> &std::path::Path {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<std::path::Path> for TempSocket {
|
||||
fn as_ref(&self) -> &std::path::Path {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempSocket {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::remove_dir_all(&self.dir);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn temp_socket(name: &str) -> TempSocket {
|
||||
let n = NEXT.fetch_add(1, Ordering::SeqCst);
|
||||
let dir = std::env::temp_dir().join(format!("bxctl-m3a-{}-{n}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
dir.join(name)
|
||||
let path = dir.join(name);
|
||||
TempSocket { dir, path }
|
||||
}
|
||||
|
||||
pub fn ts(text: &str) -> Timestamp {
|
||||
@@ -28,7 +54,7 @@ pub fn ts(text: &str) -> Timestamp {
|
||||
}
|
||||
|
||||
pub struct FakeBrokerd {
|
||||
pub socket: PathBuf,
|
||||
pub socket: TempSocket,
|
||||
/// Every request message received, in order.
|
||||
pub requests: Arc<Mutex<Vec<Message>>>,
|
||||
}
|
||||
@@ -124,7 +150,7 @@ pub fn brokerd_with(items: Vec<PendingApproval>, outcome: proto::DecisionRecord)
|
||||
}
|
||||
|
||||
pub struct FakeLoopd {
|
||||
pub socket: PathBuf,
|
||||
pub socket: TempSocket,
|
||||
/// The content of every turn received, in order.
|
||||
pub turns: Arc<Mutex<Vec<String>>>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
//! A temporary directory for a test, removed with everything in it when the value is dropped
|
||||
//! (tip T44). Included with `#[path = "support/temp.rs"] mod temp;`.
|
||||
|
||||
#![allow(dead_code)] // each test file uses a different part of this module
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
static NEXT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub struct TempDir(PathBuf);
|
||||
|
||||
impl TempDir {
|
||||
/// A fresh, empty directory `<temp>/<tag>-<pid>-<n>`.
|
||||
pub fn new(tag: &str) -> TempDir {
|
||||
let n = NEXT.fetch_add(1, Ordering::SeqCst);
|
||||
let path = std::env::temp_dir().join(format!("{tag}-{}-{n}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&path);
|
||||
std::fs::create_dir_all(&path).unwrap();
|
||||
TempDir(path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TempDir {
|
||||
type Target = Path;
|
||||
fn deref(&self) -> &Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for TempDir {
|
||||
fn as_ref(&self) -> &Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for TempDir {
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
self.0.as_os_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempDir {
|
||||
fn drop(&mut self) {
|
||||
// Put back the permissions a test may have taken away, or the removal fails.
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let _ = std::fs::set_permissions(&self.0, std::fs::Permissions::from_mode(0o700));
|
||||
let _ = std::fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
//! `bxctl audit verify` says which files in `audit/` it did not read: a file that looks like a
|
||||
//! log but is not named as one is ignored by `brokerd` too, and "ok" must not seem to cover it.
|
||||
|
||||
#[path = "support/temp.rs"]
|
||||
mod temp;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn home(tag: &str) -> PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!("bx-verify-skip-{tag}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
fn home(tag: &str) -> temp::TempDir {
|
||||
let dir = temp::TempDir::new(&format!("bx-verify-skip-{tag}"));
|
||||
std::fs::create_dir_all(dir.join("audit")).unwrap();
|
||||
let fixture =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto/tests/fixtures/audit/good");
|
||||
|
||||
Reference in New Issue
Block a user