refactor: move embedded tests into dedicated test directory

This commit is contained in:
Valdemar Lorenzen
2026-10-16 16:30:07 +02:00
parent de6a57e67d
commit 2772e6878a
55 changed files with 45752 additions and 45858 deletions
+3 -279
View File
@@ -324,91 +324,7 @@ mod macos {
#[cfg(test)]
mod tests {
use super::*;
unsafe extern "C" {
fn PasteboardSynchronize(pasteboard: Object) -> u32;
fn PasteboardGetItemIdentifier(
pasteboard: Object,
index: isize,
item: *mut usize,
) -> i32;
fn PasteboardCopyItemFlavorData(
pasteboard: Object,
item: usize,
flavor: Object,
data: *mut Object,
) -> i32;
fn CFDataGetBytePtr(data: Object) -> *const u8;
fn CFDataGetLength(data: Object) -> isize;
}
#[test]
fn native_publication_preserves_plain_utf8_bytes_on_an_isolated_pasteboard() {
// A private pasteboard leaves the user's clipboard untouched.
let _pool = unsafe { AutoreleasePool(objc_autoreleasePoolPush()) };
let name = unsafe {
Owned::new(CFStringCreateWithCString(
ptr::null(),
c"vamp-native-clipboard-regression".as_ptr(),
0x0800_0100,
))
.unwrap()
};
let board = unsafe {
let mut board = ptr::null();
check(PasteboardCreate(name.0, &mut board)).unwrap();
Owned::new(board).unwrap()
};
let reader = unsafe {
let send: unsafe extern "C" fn(Object, Object, Object) -> Object =
std::mem::transmute(objc_msgSend as unsafe extern "C" fn());
send(
objc_getClass(c"NSPasteboard".as_ptr()),
sel_registerName(c"pasteboardWithName:".as_ptr()),
name.0,
)
};
for text in [
"",
"é e\u{301} 👨‍💻\n\0tail",
"{\\rtf1 plain text}",
"%!PS-Adobe-3.0\nplain",
] {
write_to(name.0, text).unwrap();
assert_eq!(read_from(reader).unwrap(), text);
unsafe {
PasteboardSynchronize(board.0);
let mut item = 0;
check(PasteboardGetItemIdentifier(board.0, 1, &mut item)).unwrap();
let flavor = Owned::new(CFStringCreateWithCString(
ptr::null(),
c"public.utf8-plain-text".as_ptr(),
0x0800_0100,
))
.unwrap();
let mut data = ptr::null();
check(PasteboardCopyItemFlavorData(
board.0, item, flavor.0, &mut data,
))
.unwrap();
let data = Owned::new(data).unwrap();
let length = CFDataGetLength(data.0) as usize;
let bytes = if length == 0 {
&[]
} else {
std::slice::from_raw_parts(CFDataGetBytePtr(data.0), length)
};
assert_eq!(bytes, text.as_bytes());
}
}
let maximum = "x".repeat(super::super::MAX_CLIPBOARD_BYTES);
write_to(name.0, &maximum).unwrap();
assert_eq!(read_from(reader).unwrap(), maximum);
write_to(name.0, &(maximum + "x")).unwrap();
assert_eq!(
read_from(reader).unwrap_err(),
"System clipboard text exceeds 4 MiB"
);
}
include!("../tests/unit/clipboard_macos.rs");
}
}
@@ -625,197 +541,5 @@ unsafe extern "C" {
}
#[cfg(all(test, any(target_os = "macos", target_os = "linux")))]
mod tests {
use super::*;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::sync::atomic::{AtomicU64, Ordering};
static FIXTURE_ID: AtomicU64 = AtomicU64::new(0);
struct Fixture(PathBuf);
impl Fixture {
fn new() -> Self {
let path = std::env::temp_dir().join(format!(
"vamp-clipboard-{}-{}",
std::process::id(),
FIXTURE_ID.fetch_add(1, Ordering::Relaxed)
));
fs::create_dir(&path).unwrap();
Self(path)
}
fn command(&self, name: &str, body: &str) -> PathBuf {
let path = self.0.join(name);
fs::write(&path, format!("#!/bin/sh\n{body}\n")).unwrap();
fs::set_permissions(&path, fs::Permissions::from_mode(0o700)).unwrap();
path
}
fn clipboard(&self, text: &[u8]) -> SystemClipboard {
let state = self.0.join("state");
fs::write(&state, text).unwrap();
let state = format!("'{}'", state.to_str().unwrap().replace('\'', "'\\''"));
SystemClipboard::with_commands(
self.command("read", &format!("exec /bin/cat {state}")),
self.command("write", &format!("exec /bin/cat > {state}")),
)
}
}
impl Drop for Fixture {
fn drop(&mut self) {
fs::remove_dir_all(&self.0).unwrap();
}
}
#[test]
fn construction_does_not_probe_or_spawn() {
assert!(SystemClipboard::new().commands.is_none());
}
#[test]
fn round_trips_unicode_whitespace_and_large_pipes_exactly() {
let fixture = Fixture::new();
let mut clipboard = fixture.clipboard(b"");
for text in ["", " héllo\t世界🙂\r\n\n", &"é\n".repeat(300_000)] {
clipboard.write(text).unwrap();
assert_eq!(fs::read(fixture.0.join("state")).unwrap(), text.as_bytes());
assert_eq!(clipboard.read().unwrap(), text);
}
}
#[test]
fn rejects_oversize_and_invalid_utf8_without_leaking_contents() {
let fixture = Fixture::new();
let mut clipboard = fixture.clipboard(&[0xff, b'\n']);
assert_eq!(
clipboard.read().unwrap_err(),
"System clipboard is not valid UTF-8"
);
let maximum = "é".repeat(MAX_CLIPBOARD_BYTES / 2);
clipboard.write(&maximum).unwrap();
assert_eq!(clipboard.read().unwrap(), maximum);
let too_large = format!("{maximum}x");
assert_eq!(
clipboard.write(&too_large).unwrap_err(),
"System clipboard text exceeds 4 MiB"
);
assert_eq!(
fs::read(fixture.0.join("state")).unwrap(),
maximum.as_bytes()
);
fs::write(fixture.0.join("state"), &too_large).unwrap();
assert_eq!(
clipboard.read().unwrap_err(),
"System clipboard text exceeds 4 MiB"
);
let mut incomplete = vec![b'x'; MAX_CLIPBOARD_BYTES - 1];
incomplete.push(0xc3);
fs::write(fixture.0.join("state"), incomplete).unwrap();
assert_eq!(
clipboard.read().unwrap_err(),
"System clipboard is not valid UTF-8"
);
}
#[test]
fn command_failures_and_stderr_cannot_leak_clipboard_text() {
let fixture = Fixture::new();
let failed = fixture.command(
"failed",
"printf 'private clipboard payload' >&2\nprintf 'private clipboard payload'\nexit 23",
);
let mut clipboard = SystemClipboard::with_commands(failed.clone(), failed);
let error = clipboard.read().unwrap_err();
assert!(error.contains("failed"));
assert!(!error.contains("private clipboard payload"));
let error = clipboard.write("private clipboard payload").unwrap_err();
assert!(!error.contains("private clipboard payload"));
let missing = fixture.0.join("not-an-executable");
let mut clipboard = SystemClipboard::with_commands(missing.clone(), missing);
assert!(clipboard.read().unwrap_err().contains("Cannot start"));
assert!(
clipboard
.write("secret")
.unwrap_err()
.contains("Cannot start")
);
}
#[test]
fn ignored_stderr_does_not_block_large_writes() {
let fixture = Fixture::new();
let mut clipboard = fixture.clipboard(b"");
let state = fixture.0.join("state");
let state = format!("'{}'", state.to_str().unwrap().replace('\'', "'\\''"));
clipboard
.commands
.as_mut()
.unwrap()
.as_mut()
.unwrap()
.writer
.as_mut()
.unwrap()
.program = fixture.command(
"noisy",
&format!("/bin/dd if=/dev/zero bs=65536 count=8 >&2\nexec /bin/cat > {state}"),
);
let text = "λ\n".repeat(300_000);
clipboard.write(&text).unwrap();
assert_eq!(clipboard.read().unwrap(), text);
}
#[test]
fn failed_process_cleanup_kills_and_reaps_a_running_child() {
use std::io::Read;
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};
unsafe extern "C" {
fn waitpid(pid: i32, status: *mut i32, options: i32) -> i32;
}
let fixture = Fixture::new();
let hung = fixture.command("running", "printf R\nwhile :; do :; done");
let mut command = Command::new(hung);
command
.process_group(0)
.stdout(Stdio::piped())
.stderr(Stdio::null());
let mut child = command.spawn().unwrap();
let pid = child.id() as i32;
let mut ready = [0];
child
.stdout
.as_mut()
.unwrap()
.read_exact(&mut ready)
.unwrap();
assert_eq!(ready, [b'R']);
drop(ClipboardProcess(Some(child)));
let mut status = 0;
// SAFETY: waitpid's output pointer is valid; WNOHANG cannot block this check.
assert_eq!(unsafe { waitpid(pid, &mut status, 1) }, -1);
assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(10));
}
#[test]
fn immediate_injected_timeout_terminates_nonterminating_commands() {
let fixture = Fixture::new();
let hung = fixture.command("hung", "while :; do :; done");
let mut clipboard = SystemClipboard::with_commands(hung.clone(), hung);
// No assertion depends on wall-clock speed or a deadline remaining in the future.
clipboard.timeout = Duration::ZERO;
assert_eq!(
clipboard.read().unwrap_err(),
"System clipboard command timed out"
);
assert_eq!(
clipboard.write("text").unwrap_err(),
"System clipboard command timed out"
);
}
}
#[path = "../tests/unit/clipboard.rs"]
mod tests;