diff --git a/crates/brokerd/tests/config_runner.rs b/crates/brokerd/tests/config_runner.rs index a437cb1..2a71e40 100644 --- a/crates/brokerd/tests/config_runner.rs +++ b/crates/brokerd/tests/config_runner.rs @@ -1,5 +1,8 @@ //! `[runner]` in `brokerd.toml` (M3b spec, section 6). Do not edit. +#[path = "support/tmp.rs"] +mod tmp; + use std::path::{Path, PathBuf}; use std::time::Duration; @@ -88,8 +91,7 @@ fn unknown_runner_keys_are_errors() { #[test] fn bad_values_are_errors_that_name_them() { - let dir = std::env::temp_dir().join(format!("bx-runner-cfg-{}", std::process::id())); - std::fs::create_dir_all(&dir).unwrap(); + let dir = tmp::TempDir::new("runner-cfg"); let cases = [ ( format!("image = \"x@sha256:{}\"", "0".repeat(63)), @@ -123,14 +125,13 @@ fn bad_values_are_errors_that_name_them() { (format!("image = \"{DIGEST}\"\npodman = \"\""), "podman"), ]; for (n, (body, word)) in cases.iter().enumerate() { - let path = dir.join(format!("c{n}.toml")); + let path = dir.path().join(format!("c{n}.toml")); std::fs::write(&path, format!("[runner]\n{body}\n")).unwrap(); match Config::load(&path) { Err(ConfigError::Invalid(_, why)) => assert!(why.contains(word), "{body}: {why}"), other => panic!("{body}: expected Invalid, got {other:?}"), } } - let _ = std::fs::remove_dir_all(&dir); } #[test] diff --git a/crates/brokerd/tests/notices.rs b/crates/brokerd/tests/notices.rs index 65aae3e..2c3bb27 100644 --- a/crates/brokerd/tests/notices.rs +++ b/crates/brokerd/tests/notices.rs @@ -1,6 +1,9 @@ //! Two small texts from the M3b review: every line `brokerd serve` prints about its runtime starts //! with `brokerd:`, and a bad `[runner]` value is quoted in its error. Do not edit. +#[path = "support/tmp.rs"] +mod tmp; + #[path = "support/fake_podman.rs"] mod fake_podman; @@ -59,8 +62,7 @@ fn the_runtime_notice_starts_with_brokerd() { #[test] fn a_bad_runner_value_is_quoted() { - let dir = std::env::temp_dir().join(format!("bx-notice-cfg-{}", std::process::id())); - std::fs::create_dir_all(&dir).unwrap(); + let dir = tmp::TempDir::new("notice-cfg"); let cases = [ ("image = \"not by digest\"", "\"not by digest\""), ( @@ -69,12 +71,11 @@ fn a_bad_runner_value_is_quoted() { ), ]; for (n, (body, quoted)) in cases.iter().enumerate() { - let path = dir.join(format!("q{n}.toml")); + let path = dir.path().join(format!("q{n}.toml")); std::fs::write(&path, format!("[runner]\n{body}\n")).unwrap(); match Config::load(&path) { Err(ConfigError::Invalid(_, why)) => assert!(why.contains(quoted), "{why}"), other => panic!("{body}: {other:?}"), } } - let _ = std::fs::remove_dir_all(&dir); } diff --git a/crates/bxctl/tests/admin_timeout.rs b/crates/bxctl/tests/admin_timeout.rs index 1b3e69e..073ab1e 100644 --- a/crates/bxctl/tests/admin_timeout.rs +++ b/crates/bxctl/tests/admin_timeout.rs @@ -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 || { diff --git a/crates/bxctl/tests/chat.rs b/crates/bxctl/tests/chat.rs index 061a097..e8cab87 100644 --- a/crates/bxctl/tests/chat.rs +++ b/crates/bxctl/tests/chat.rs @@ -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>>, + _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, 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, end: End) -> FakeLoopd { .unwrap(); } }); - FakeLoopd { socket, turns } + FakeLoopd { + socket, + turns, + _dir: dir, + } } fn events() -> Vec { diff --git a/crates/bxctl/tests/cli_memory.rs b/crates/bxctl/tests/cli_memory.rs index 7fd3785..16b31e7 100644 --- a/crates/bxctl/tests/cli_memory.rs +++ b/crates/bxctl/tests/cli_memory.rs @@ -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 { 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] diff --git a/crates/bxctl/tests/support/mod.rs b/crates/bxctl/tests/support/mod.rs index fd1886b..b9cf501 100644 --- a/crates/bxctl/tests/support/mod.rs +++ b/crates/bxctl/tests/support/mod.rs @@ -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 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>>, } @@ -124,7 +150,7 @@ pub fn brokerd_with(items: Vec, outcome: proto::DecisionRecord) } pub struct FakeLoopd { - pub socket: PathBuf, + pub socket: TempSocket, /// The content of every turn received, in order. pub turns: Arc>>, } diff --git a/crates/bxctl/tests/support/temp.rs b/crates/bxctl/tests/support/temp.rs new file mode 100644 index 0000000..f28a29e --- /dev/null +++ b/crates/bxctl/tests/support/temp.rs @@ -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 `/--`. + 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 for TempDir { + fn as_ref(&self) -> &Path { + &self.0 + } +} + +impl AsRef 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); + } +} diff --git a/crates/bxctl/tests/verify_skipped.rs b/crates/bxctl/tests/verify_skipped.rs index 8706928..a604f83 100644 --- a/crates/bxctl/tests/verify_skipped.rs +++ b/crates/bxctl/tests/verify_skipped.rs @@ -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"); diff --git a/crates/gatewayd/tests/serve_answers.rs b/crates/gatewayd/tests/serve_answers.rs index eaacc17..514e40c 100644 --- a/crates/gatewayd/tests/serve_answers.rs +++ b/crates/gatewayd/tests/serve_answers.rs @@ -59,13 +59,15 @@ fn held(home: &TempDir, before: Vec) -> (mpsc::Receiver, mpsc:: } struct Stage { - _home: TempDir, mm: FakeMm, _running: Running, ws: WsPeer, go: mpsc::Sender<()>, socket: PathBuf, root: String, + // Declared last so it is dropped last: fields drop in order, and the home must outlive + // everything that may still write into it (tip T44). + _home: TempDir, } /// gatewayd up, a direct message from KYLE whose turn is held, and the approval's socket. diff --git a/crates/gatewayd/tests/serve_redirect.rs b/crates/gatewayd/tests/serve_redirect.rs index 8355874..9f4ee18 100644 --- a/crates/gatewayd/tests/serve_redirect.rs +++ b/crates/gatewayd/tests/serve_redirect.rs @@ -50,13 +50,15 @@ fn held(home: &TempDir) -> (mpsc::Receiver, mpsc::Sender<()>) { } struct Stage { - _home: TempDir, mm: FakeMm, _running: Running, ws: WsPeer, go: mpsc::Sender<()>, asking: Asking, root: String, + // Declared last so it is dropped last: fields drop in order, and the home must outlive + // everything that may still write into it (tip T44). + _home: TempDir, } /// gatewayd up, EVE's message `text` in `channel` (of `channel_type`), its turn held, and diff --git a/crates/gatewayd/tests/support/gateway.rs b/crates/gatewayd/tests/support/gateway.rs index 0e2c94e..a735208 100644 --- a/crates/gatewayd/tests/support/gateway.rs +++ b/crates/gatewayd/tests/support/gateway.rs @@ -67,6 +67,17 @@ impl Running { impl Drop for Running { fn drop(&mut self) { self.stop.store(true, Ordering::SeqCst); + // Wait for `run` to finish before the test's home is removed, or it can recreate its + // socket directory there afterwards (tip T44). The wait has a limit (T27). + if let Some(handle) = self.handle.take() { + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + while !handle.is_finished() && std::time::Instant::now() < deadline { + std::thread::sleep(std::time::Duration::from_millis(10)); + } + if handle.is_finished() { + let _ = handle.join(); + } + } } } diff --git a/crates/inferproxy/tests/forward.rs b/crates/inferproxy/tests/forward.rs index 9e2cbab..b628125 100644 --- a/crates/inferproxy/tests/forward.rs +++ b/crates/inferproxy/tests/forward.rs @@ -14,11 +14,37 @@ use std::time::Duration; static NEXT: AtomicU32 = AtomicU32::new(0); -fn socket_path() -> PathBuf { +/// A socket path in a fresh directory of its own, removed when the value is dropped (tip T44). +struct TempSocket { + dir: PathBuf, + path: PathBuf, +} + +impl std::ops::Deref for TempSocket { + type Target = PathBuf; + fn deref(&self) -> &PathBuf { + &self.path + } +} + +impl AsRef 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); + } +} + +fn socket_path() -> TempSocket { let n = NEXT.fetch_add(1, Ordering::SeqCst); let dir = std::env::temp_dir().join(format!("inferproxy-test-{}-{n}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); - dir.join("infer.sock") + let path = dir.join("infer.sock"); + TempSocket { dir, path } } /// An upstream that reads until the client stops sending, then answers with what it read, @@ -52,7 +78,7 @@ fn silent_upstream() -> String { addr } -fn start(upstream: String, limits: Limits) -> PathBuf { +fn start(upstream: String, limits: Limits) -> TempSocket { let path = socket_path(); let listener = UnixListener::bind(&path).unwrap(); thread::spawn(move || serve(listener, upstream, limits)); diff --git a/crates/loopd/tests/broker_port.rs b/crates/loopd/tests/broker_port.rs index 2ad285d..32807a5 100644 --- a/crates/loopd/tests/broker_port.rs +++ b/crates/loopd/tests/broker_port.rs @@ -135,7 +135,7 @@ fn an_expiry_that_has_already_passed_still_leaves_the_timeout() { fn no_socket_is_unavailable() { let socket = socket_path(); let _ = std::fs::remove_file(&socket); - let got = call(socket.clone(), 2_000, &request()); + let got = call(socket.to_path_buf(), 2_000, &request()); assert_unavailable(&got, "no socket file"); assert!( got.lines[0].contains(&socket.display().to_string()), @@ -260,7 +260,7 @@ fn a_request_too_large_for_a_frame_is_its_own_failure() { let _listener = UnixListener::bind(&path).unwrap(); let mut big = request(); big.arguments = format!(r#"{{"path":"/{}"}}"#, "a".repeat(proto::MAX_FRAME)); - let got = call(path, 1_000, &big); + let got = call(path.to_path_buf(), 1_000, &big); assert_eq!( got.response, ToolResponse::Failed { @@ -291,7 +291,7 @@ fn every_call_is_its_own_connection() { ); } }); - let port = BrokerPort::new(path, Duration::from_millis(2_000)); + let port = BrokerPort::new(path.to_path_buf(), Duration::from_millis(2_000)); for n in 0..3 { let got = port.call(&request(), &mut |_| {}); assert_eq!(got, result(&format!("answer {n}"))); diff --git a/crates/loopd/tests/channel.rs b/crates/loopd/tests/channel.rs index c25cedf..cbbd213 100644 --- a/crates/loopd/tests/channel.rs +++ b/crates/loopd/tests/channel.rs @@ -23,9 +23,11 @@ use support::{FakeServer, Home, Reply, ScriptedPort}; const CHAT: &str = "/v1/chat/completions"; struct Loopd { - home: Home, server: FakeServer, socket: PathBuf, + // Declared last so it is dropped last: fields drop in order, and the home must outlive + // everything that may still write into it (tip T44). + home: Home, } fn start(replies: Vec) -> Loopd { diff --git a/crates/loopd/tests/device.rs b/crates/loopd/tests/device.rs index f43731d..f5eff47 100644 --- a/crates/loopd/tests/device.rs +++ b/crates/loopd/tests/device.rs @@ -59,6 +59,11 @@ impl Proxy { impl Drop for Proxy { fn drop(&mut self) { self.kill(); + // `socket_path` made a directory of its own for the socket; remove it with the proxy + // (tip T44). + if let Some(dir) = self.socket.parent() { + let _ = std::fs::remove_dir_all(dir); + } } } diff --git a/crates/loopd/tests/memory.rs b/crates/loopd/tests/memory.rs index e1a6372..35dbac5 100644 --- a/crates/loopd/tests/memory.rs +++ b/crates/loopd/tests/memory.rs @@ -44,10 +44,12 @@ impl MemorySource for Shared { } struct Loopd { - _home: Home, server: FakeServer, socket: PathBuf, memory: Arc, + // Declared last so it is dropped last: fields drop in order, and the home must outlive + // everything that may still write into it (tip T44). + _home: Home, } fn start(block: Result<&str, &str>) -> Loopd { diff --git a/crates/loopd/tests/support/broker.rs b/crates/loopd/tests/support/broker.rs index c48ec00..501955d 100644 --- a/crates/loopd/tests/support/broker.rs +++ b/crates/loopd/tests/support/broker.rs @@ -6,7 +6,7 @@ #![allow(dead_code)] // each test file uses a different part of this module use std::os::unix::net::{UnixListener, UnixStream}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; @@ -21,9 +21,31 @@ use proto::{ static NEXT: AtomicU32 = AtomicU32::new(0); -pub fn socket_path() -> PathBuf { +/// A fresh socket path, removed when the value is dropped (tip T44). +pub struct SocketPath(PathBuf); + +impl std::ops::Deref for SocketPath { + type Target = Path; + fn deref(&self) -> &Path { + &self.0 + } +} + +impl AsRef for SocketPath { + fn as_ref(&self) -> &Path { + &self.0 + } +} + +impl Drop for SocketPath { + fn drop(&mut self) { + let _ = std::fs::remove_file(&self.0); + } +} + +pub fn socket_path() -> SocketPath { let n = NEXT.fetch_add(1, Ordering::SeqCst); - std::env::temp_dir().join(format!("loopd-broker-{}-{n}.sock", std::process::id())) + SocketPath(std::env::temp_dir().join(format!("loopd-broker-{}-{n}.sock", std::process::id()))) } /// Accepts one connection, reads the request frame and hands both to `script`. The connection @@ -35,13 +57,16 @@ where let path = socket_path(); let _ = std::fs::remove_file(&path); let listener = UnixListener::bind(&path).unwrap(); + let returned = path.to_path_buf(); + // The thread owns the path, so the socket file goes once the exchange is over. let handle = thread::spawn(move || { + let _path = path; let (mut stream, _) = listener.accept().unwrap(); let request = read_frame(&mut stream).unwrap(); script(&mut stream, &request); request }); - (path, handle) + (returned, handle) } pub fn frame(id: u64, r#final: bool, msg: Message) -> Envelope { diff --git a/crates/loopd/tests/support/mod.rs b/crates/loopd/tests/support/mod.rs index 5a62324..2e3dfc7 100644 --- a/crates/loopd/tests/support/mod.rs +++ b/crates/loopd/tests/support/mod.rs @@ -281,12 +281,14 @@ struct State { pub struct FakeServer { pub socket: PathBuf, state: Arc, + dir: PathBuf, } impl FakeServer { pub fn start() -> FakeServer { let n = NEXT.fetch_add(1, Ordering::SeqCst); let dir = std::env::temp_dir().join(format!("loopd-fake-{}-{n}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); let socket = dir.join("infer.sock"); let _ = std::fs::remove_file(&socket); @@ -303,7 +305,7 @@ impl FakeServer { thread::spawn(move || serve(stream, &state)); } }); - FakeServer { socket, state } + FakeServer { socket, state, dir } } /// Replies for `path` (the query is ignored), served in order. The last one repeats. @@ -401,3 +403,23 @@ fn serve(mut stream: UnixStream, state: &State) { } thread::sleep(Duration::from_millis(reply.hang_ms)); } + +/// Removes a test's directory, first putting back the permissions a test may have taken away, +/// or the removal fails (tip T44). +fn remove_test_dir(dir: &Path) { + use std::os::unix::fs::PermissionsExt; + let _ = std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700)); + let _ = std::fs::remove_dir_all(dir); +} + +impl Drop for Home { + fn drop(&mut self) { + remove_test_dir(&self.dir); + } +} + +impl Drop for FakeServer { + fn drop(&mut self) { + remove_test_dir(&self.dir); + } +} diff --git a/crates/toolkit/tests/egress_form.rs b/crates/toolkit/tests/egress_form.rs index 033168f..d36e20d 100644 --- a/crates/toolkit/tests/egress_form.rs +++ b/crates/toolkit/tests/egress_form.rs @@ -1,15 +1,16 @@ //! `toolkit egress-proxy` takes exactly `--socket --allow `: with anything after //! them it is not the proxy, and exits 2 at once rather than listening (M3b review). Do not edit. +#[path = "support/mod.rs"] +mod support; + use std::process::{Command, Stdio}; use std::time::{Duration, Instant}; #[test] fn trailing_arguments_are_not_the_proxy_form() { - let dir = std::env::temp_dir().join(format!("tk-egress-form-{}", std::process::id())); - let _ = std::fs::remove_dir_all(&dir); - std::fs::create_dir_all(&dir).unwrap(); - let socket = dir.join("egress.sock"); + let dir = support::TempDir::new("egress-form"); + let socket = dir.path().join("egress.sock"); let mut child = Command::new(env!("CARGO_BIN_EXE_toolkit")) .args([ "egress-proxy", @@ -38,5 +39,4 @@ fn trailing_arguments_are_not_the_proxy_form() { }; assert_eq!(status.code(), Some(2)); assert!(!socket.exists(), "no socket was made"); - let _ = std::fs::remove_dir_all(&dir); }