use super::*;
#[test]
fn a_drive_relative_path_carries_its_component_and_the_current_drive_uses_the_process_directory() {
let cwd = current_directory();
let cwd_drive = cwd.chars().next().filter(char::is_ascii_alphabetic);
let other = probe_drive_from(probe_drives::ROOTED_AT_THAT_DRIVE, None);
let _restore = BorrowedDriveEntry::take(other);
let resolved = resolve(&format!("{other}:foo"));
assert!(
resolved.ends_with(r"\foo"),
"the component is carried through whatever the entry holds: {resolved}"
);
if let Some(drive) = cwd_drive {
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{}\foo", cwd.trim_end_matches('\\')),
"on the current drive, the per-drive entry makes no difference to \
the result and the process directory is used"
);
}
}
#[test]
fn the_current_drives_entry_does_not_affect_resolution_and_is_not_rewritten() {
let cwd = current_directory();
let Some(drive) = cwd.chars().next().filter(char::is_ascii_alphabetic) else {
return;
};
let process_directory = format!(r"{}\foo", cwd.trim_end_matches('\\'));
let probe_dir = probe_directory("current-drive");
let probe = probe_dir.path.to_str().expect("the probe path is UTF-8");
let _restore = BorrowedDriveEntry::take(drive);
assert_ne!(
process_directory,
format!(r"{probe}\foo"),
"precondition: the entry must name somewhere other than the process \
directory, or honouring it and ignoring it look identical"
);
set_drive_entry(drive, Some(probe));
assert_eq!(
resolve(&format!("{drive}:foo")),
process_directory,
"the current drive's entry was set to {probe}, an entry the non-current \
arm honours verbatim, and the process directory won anyway"
);
let missing = probe_dir.path.join("no-such-child");
let missing = missing.to_str().expect("the probe path is UTF-8");
set_drive_entry(drive, Some(missing));
let _ = resolve(&format!("{drive}:foo"));
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(missing),
"an entry the non-current arm would have replaced with {drive}:\\ is \
left untouched on the current drive"
);
}
struct ProbeDir {
path: std::path::PathBuf,
created: bool,
_allocating: std::sync::RwLockReadGuard<'static, ()>,
}
impl ProbeDir {
fn drive(&self) -> Option<char> {
self.path
.as_os_str()
.to_string_lossy()
.chars()
.next()
.filter(char::is_ascii_alphabetic)
}
}
impl Drop for ProbeDir {
fn drop(&mut self) {
if self.created {
let _ = std::fs::remove_dir(&self.path);
}
}
}
fn probe_directory(tag: &str) -> ProbeDir {
let _allocating = handle_allocation()
.read()
.expect("the lock is not poisoned");
let canonical_drive_rooted = |p: &std::path::Path| {
let s = p.as_os_str().to_string_lossy().into_owned();
let mut chars = s.chars();
matches!(
(chars.next(), chars.next(), chars.next()),
(Some(d), Some(':'), Some('\\')) if d.is_ascii_alphabetic()
) && !s.contains('/')
&& !s.split('\\').any(|c| c == "." || c == "..")
};
let temp = std::env::temp_dir();
if canonical_drive_rooted(&temp) {
let path = temp.join(format!("wnrs-{}-{tag}", std::process::id()));
let created = match std::fs::create_dir(&path) {
Ok(()) => true,
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => false,
Err(e) => panic!("create the probe directory {}: {e}", path.display()),
};
return ProbeDir {
path,
created,
_allocating,
};
}
let system_root = std::path::PathBuf::from(
std::env::var("SystemRoot").expect("SystemRoot is set on Windows"),
);
let cwd = current_directory();
let cwd = cwd.trim_end_matches('\\');
let distinct = |p: &std::path::Path| {
!p.as_os_str()
.to_string_lossy()
.trim_end_matches('\\')
.eq_ignore_ascii_case(cwd)
};
let path = [system_root.clone(), system_root.join("System32")]
.into_iter()
.find(|p| canonical_drive_rooted(p) && distinct(p))
.unwrap_or_else(|| {
panic!(
"no fallback probe directory is both canonical and distinct \
from the current directory {cwd}"
)
});
ProbeDir {
path,
created: false,
_allocating,
}
}
mod probe_drives {
pub const ROOTED_AT_THAT_DRIVE: &[char] = &['X', 'Y', 'P'];
pub const VERBATIM_ENTRY: &[char] = &['W', 'U', 'N'];
pub const REJECTED_ENTRY: &[char] = &['V', 'T', 'M'];
pub const LONG_ENTRY: &[char] = &['R', 'S', 'K'];
pub const BORROW_GUARD: &[char] = &['G', 'H', 'J'];
pub const EMPTY_VS_ABSENT: &[char] = &['E', 'F', 'B'];
pub const ALL: &[(&str, &[char])] = &[
("ROOTED_AT_THAT_DRIVE", ROOTED_AT_THAT_DRIVE),
("VERBATIM_ENTRY", VERBATIM_ENTRY),
("REJECTED_ENTRY", REJECTED_ENTRY),
("LONG_ENTRY", LONG_ENTRY),
("BORROW_GUARD", BORROW_GUARD),
("EMPTY_VS_ABSENT", EMPTY_VS_ABSENT),
];
}
#[test]
fn the_probe_drive_candidate_lists_are_disjoint_and_large_enough() {
for (name, list) in probe_drives::ALL {
assert!(
list.len() >= 3,
"{name} has {} candidates, and at most two can be excluded, so \
fewer than three cannot guarantee a survivor",
list.len()
);
let mut seen = list.to_vec();
seen.sort_unstable();
seen.dedup();
assert_eq!(seen.len(), list.len(), "{name} repeats a letter");
}
for (a_name, a) in probe_drives::ALL {
for (b_name, b) in probe_drives::ALL {
if a_name == b_name {
continue;
}
let shared: Vec<char> = a
.iter()
.copied()
.filter(|c| b.iter().any(|d| d.eq_ignore_ascii_case(c)))
.collect();
assert!(
shared.is_empty(),
"{a_name} and {b_name} share {shared:?}, so the two tests can \
select the same drive and race under libtest's \
thread-per-test model"
);
}
}
}
fn probe_drive_from(candidates: &[char], avoid: Option<char>) -> char {
let cwd = current_directory();
let current = cwd.chars().next().filter(char::is_ascii_alphabetic);
let taken = |c: char| {
current.is_some_and(|d| d.eq_ignore_ascii_case(&c))
|| avoid.is_some_and(|d| d.eq_ignore_ascii_case(&c))
};
*candidates.iter().find(|&&c| !taken(c)).unwrap_or_else(|| {
panic!(
"every candidate of {candidates:?} is excluded by the current \
drive ({current:?}) or the probe drive ({avoid:?})"
)
})
}
fn drive_entry(drive: char) -> Option<Wtf16String> {
let name = Wtf16String::from(format!("={drive}:").as_str());
let mut buffer = vec![0u16; 256];
loop {
unsafe { windows_sys::Win32::Foundation::SetLastError(ERROR_SUCCESS) };
let written = unsafe {
windows_sys::Win32::System::Environment::GetEnvironmentVariableW(
name.as_terminated_ptr(),
buffer.as_mut_ptr(),
u32::try_from(buffer.len()).unwrap_or(u32::MAX),
)
};
let written = written as usize;
if written == 0 {
let last = unsafe { windows_sys::Win32::Foundation::GetLastError() };
return match last {
ERROR_SUCCESS => Some(Wtf16String::from_units(&[])),
ERROR_ENVVAR_NOT_FOUND => None,
other => panic!("reading ={drive}: failed with error {other}"),
};
}
if written < buffer.len() {
return Some(Wtf16String::from_units(&buffer[..written]));
}
buffer = vec![0u16; written];
}
}
fn set_drive_entry(drive: char, value: Option<&str>) {
set_drive_entry_units(drive, value.map(Wtf16String::from).as_ref());
}
fn set_drive_entry_units(drive: char, value: Option<&Wtf16String>) {
assert!(
try_set_drive_entry_units(drive, value),
"set ={drive}: entry"
);
}
fn try_set_drive_entry_units(drive: char, value: Option<&Wtf16String>) -> bool {
let name = Wtf16String::from(format!("={drive}:").as_str());
let value_ptr = value
.as_ref()
.map_or(core::ptr::null(), |v| v.as_terminated_ptr());
let ok = unsafe {
windows_sys::Win32::System::Environment::SetEnvironmentVariableW(
name.as_terminated_ptr(),
value_ptr,
)
};
ok != 0
}
struct BorrowedDriveEntry {
drive: char,
saved: Option<Wtf16String>,
}
impl BorrowedDriveEntry {
fn take(drive: char) -> Self {
Self {
drive,
saved: drive_entry(drive),
}
}
}
impl Drop for BorrowedDriveEntry {
fn drop(&mut self) {
let restored = try_set_drive_entry_units(self.drive, self.saved.as_ref());
assert!(
restored || std::thread::panicking(),
"restoring ={}: failed, leaving process-global state corrupted for \
every test that follows",
self.drive
);
}
}
#[test]
fn a_drive_relative_path_uses_that_drives_entry_verbatim_and_rewrites_a_bad_one() {
let probe_dir = probe_directory("verbatim");
let probe = probe_dir.path.to_str().expect("the probe path is UTF-8");
let drive = probe_drive_from(probe_drives::VERBATIM_ENTRY, probe_dir.drive());
let _restore = BorrowedDriveEntry::take(drive);
assert_ne!(
Some(drive.to_ascii_uppercase()),
probe_dir.drive().map(|d| d.to_ascii_uppercase()),
"the probe directory must be on a different drive, or the assertion \
below cannot show the entry is honoured ACROSS drives"
);
set_drive_entry(drive, Some(probe));
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{probe}\foo"),
"an entry naming an existing directory is honoured verbatim, even onto \
a different drive -- so \"that drive's own current directory\" is the \
convention the entry usually holds, not a guarantee about the result"
);
set_drive_entry(drive, Some(&format!(r"{probe}\")));
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{probe}\\foo"),
"an entry is accepted with a trailing separator and concatenated \
without normalising the join"
);
let missing = probe_dir.path.join("no-such-child");
assert!(
!missing.exists(),
"precondition: the rejected entry must name nothing: {}",
missing.display()
);
set_drive_entry(
drive,
Some(missing.to_str().expect("the probe path is UTF-8")),
);
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{drive}:\foo"),
"an entry that names nothing is rejected in favour of the drive root"
);
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(format!(r"{drive}:\").as_str()),
"and the call REWROTE the entry: this is a query that mutates the \
process environment block"
);
set_drive_entry(drive, None);
assert_eq!(drive_entry(drive), None, "precondition: entry cleared");
let _ = resolve(&format!("{drive}:foo"));
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(format!(r"{drive}:\").as_str()),
"resolving created the entry on a host that had none"
);
}
#[test]
fn a_rejected_drive_entry_is_replaced_by_the_drive_root() {
let probe_dir = probe_directory("shape");
let accepted = probe_dir.path.to_str().expect("the probe path is UTF-8");
let drive = probe_drive_from(probe_drives::REJECTED_ENTRY, probe_dir.drive());
let _restore = BorrowedDriveEntry::take(drive);
set_drive_entry(drive, Some(accepted));
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{accepted}\foo"),
"control: the same directory in canonical form is accepted"
);
for spelling in [
accepted.replace('\\', "/"),
format!(r"{accepted}\."),
format!(
r"{accepted}\..\{}",
accepted.rsplit('\\').next().unwrap_or("")
),
format!(r"\\?\{accepted}"),
] {
set_drive_entry(drive, Some(&spelling));
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{drive}:\foo"),
"{spelling:?} names an existing directory but is rejected on shape"
);
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(format!(r"{drive}:\").as_str()),
"and the rejected entry is written back as the drive root"
);
}
let system_file = std::path::PathBuf::from(
std::env::var("SystemRoot").expect("SystemRoot is set on Windows"),
)
.join("System32")
.join("kernel32.dll");
assert!(
system_file.is_file(),
"precondition: the rejected entry must name an existing FILE: {}",
system_file.display()
);
let system_file = system_file.to_str().expect("the system path is UTF-8");
set_drive_entry(drive, Some(system_file));
assert_eq!(
resolve(&format!("{drive}:foo")),
format!(r"{drive}:\foo"),
"{system_file} exists and is canonical, and is rejected anyway because \
it is not a directory -- so existence alone is not the gate"
);
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(format!(r"{drive}:\").as_str()),
"and an entry naming a file is written back as the drive root too"
);
}
#[test]
fn a_long_drive_entry_round_trips_through_the_reader() {
let drive = probe_drive_from(probe_drives::LONG_ENTRY, None);
let _restore = BorrowedDriveEntry::take(drive);
let long = format!(r"C:\{}", "a".repeat(1200));
set_drive_entry(drive, Some(&long));
let read_back = drive_entry(drive).expect("the entry was just set");
assert_eq!(
read_back.to_string_lossy(),
long,
"a long entry survives the read, so the buffer grew instead of truncating"
);
}
#[test]
fn a_borrowed_drive_entry_is_restored_even_when_the_borrower_panics() {
let drive = probe_drive_from(probe_drives::BORROW_GUARD, None);
let _outer = BorrowedDriveEntry::take(drive);
let sentinel = format!(r"C:\borrowed-entry-{}", std::process::id());
set_drive_entry(drive, Some(&sentinel));
let outcome = std::panic::catch_unwind(|| {
let _restore = BorrowedDriveEntry::take(drive);
set_drive_entry(drive, Some(r"C:\the-borrowed-value"));
panic!("expected: this panic exercises the restore-on-unwind path");
});
assert!(
outcome.is_err(),
"precondition: the borrower must actually panic, or the unwinding path \
is not the thing being measured"
);
assert_eq!(
drive_entry(drive).map(|v| v.to_string_lossy()).as_deref(),
Some(sentinel.as_str()),
"the guard put the entry back while unwinding, where an end-of-test \
restore would have been skipped"
);
}
#[test]
fn an_empty_drive_entry_is_distinguished_from_an_absent_one() {
let drive = probe_drive_from(probe_drives::EMPTY_VS_ABSENT, None);
let _outer = BorrowedDriveEntry::take(drive);
set_drive_entry(drive, Some(""));
let empty = drive_entry(drive);
assert_eq!(
empty.as_ref().map(|v| v.to_string_lossy()),
Some(String::new()),
"an entry set to the empty string reads back as PRESENT and empty"
);
set_drive_entry(drive, None);
assert_eq!(
drive_entry(drive),
None,
"and a deleted entry reads back as absent, which is the answer the \
empty one must not be confused with"
);
set_drive_entry_units(drive, empty.as_ref());
assert_eq!(
drive_entry(drive).as_ref().map(|v| v.to_string_lossy()),
Some(String::new()),
"restoring an empty entry puts back an empty entry, not an absent one"
);
}