use std::ptr;
use windows_sys::Win32::Foundation::{
ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, ERROR_INVALID_HANDLE, FALSE, GetLastError, HANDLE,
INVALID_HANDLE_VALUE, SetLastError, TRUE,
};
use super::{
Win32Error, perform, perform_bool, perform_handle, perform_nonnull_handle, perform_nonzero,
};
fn fails_with<T>(code: u32, result: T) -> impl FnOnce() -> T {
move || {
unsafe { SetLastError(code) };
result
}
}
fn clobber_last_error() {
unsafe { SetLastError(ERROR_ACCESS_DENIED) };
}
#[test]
fn a_succeeding_bool_call_yields_no_error() {
let outcome = perform_bool(|| TRUE);
assert!(outcome.is_ok());
}
#[test]
fn a_failing_bool_call_reports_the_raw_code() {
let outcome = perform_bool(fails_with(ERROR_FILE_NOT_FOUND, FALSE));
assert_eq!(
outcome.expect_err("FALSE is a failure").code(),
ERROR_FILE_NOT_FOUND
);
}
#[test]
fn success_is_not_second_guessed_by_a_stale_last_error() {
clobber_last_error();
let outcome = perform_bool(|| TRUE);
assert!(
outcome.is_ok(),
"the return value decides success, not the thread's error slot"
);
}
#[test]
fn the_code_survives_cleanup_that_would_overwrite_it() {
let naive = {
unsafe { SetLastError(ERROR_FILE_NOT_FOUND) };
let _failed = FALSE;
clobber_last_error();
unsafe { GetLastError() }
};
assert_eq!(
naive, ERROR_ACCESS_DENIED,
"the trap must be real, or the guarantee below proves nothing"
);
let outcome = perform_bool(fails_with(ERROR_FILE_NOT_FOUND, FALSE));
clobber_last_error();
assert_eq!(
outcome.expect_err("FALSE is a failure").code(),
ERROR_FILE_NOT_FOUND,
"the code is snapshotted before anything else can run"
);
}
#[test]
fn error_file_not_found_is_passed_through_rather_than_interpreted() {
let from_open = perform_handle(fails_with(ERROR_FILE_NOT_FOUND, INVALID_HANDLE_VALUE));
let from_query = perform_bool(fails_with(ERROR_FILE_NOT_FOUND, FALSE));
assert_eq!(
from_open
.expect_err("an invalid handle is a failure")
.code(),
ERROR_FILE_NOT_FOUND
);
assert_eq!(
from_query.expect_err("FALSE is a failure").code(),
ERROR_FILE_NOT_FOUND,
"two calls, one code, and the crate distinguishes neither"
);
}
#[test]
fn a_handle_call_uses_the_invalid_handle_value_convention() {
let sentinel = 0x1234_usize as HANDLE;
let succeeded = perform_handle(|| sentinel).expect("a real handle is a success");
let failed = perform_handle(fails_with(ERROR_ACCESS_DENIED, INVALID_HANDLE_VALUE));
assert_eq!(succeeded, sentinel);
assert_eq!(
failed
.expect_err("INVALID_HANDLE_VALUE is a failure")
.code(),
ERROR_ACCESS_DENIED
);
}
#[test]
fn a_null_returning_call_uses_the_other_handle_convention() {
let failed = perform_nonnull_handle(fails_with(ERROR_INVALID_HANDLE, ptr::null_mut()));
assert_eq!(
failed.expect_err("a null handle is a failure").code(),
ERROR_INVALID_HANDLE
);
}
#[test]
fn the_two_handle_conventions_disagree_about_the_same_values() {
assert!(
perform_handle(ptr::null_mut).is_ok(),
"null is a success under the INVALID_HANDLE_VALUE convention"
);
assert!(
perform_nonnull_handle(|| INVALID_HANDLE_VALUE).is_ok(),
"INVALID_HANDLE_VALUE is a success under the null convention"
);
}
#[test]
fn a_nonzero_call_reports_its_length_or_the_raw_code() {
let succeeded = perform_nonzero(|| 42).expect("a non-zero length is a success");
let failed = perform_nonzero(fails_with(ERROR_ACCESS_DENIED, 0));
assert_eq!(succeeded, 42);
assert_eq!(
failed.expect_err("zero is a failure").code(),
ERROR_ACCESS_DENIED
);
}
#[test]
fn the_general_form_accepts_a_convention_of_its_own() {
let outcome = perform(fails_with(ERROR_FILE_NOT_FOUND, -1_i64), |result| {
*result < 0
});
assert_eq!(
outcome.expect_err("a negative result is a failure").code(),
ERROR_FILE_NOT_FOUND
);
}
#[test]
fn an_unknown_code_is_carried_rather_than_rejected() {
let unknown = 0x0BAD_F00D_u32;
let outcome = perform_bool(fails_with(unknown, FALSE));
assert_eq!(outcome.expect_err("FALSE is a failure").code(), unknown);
}
#[test]
fn an_error_renders_and_converts_without_losing_its_code() {
let error = Win32Error::from_code(ERROR_FILE_NOT_FOUND);
assert_eq!(
error.to_io_error().raw_os_error(),
Some(ERROR_FILE_NOT_FOUND as i32),
"the io::Error form is a re-presentation, not a reclassification"
);
assert!(
error
.to_string()
.contains(&ERROR_FILE_NOT_FOUND.to_string()),
"unexpected message: {error}"
);
}
#[test]
fn errors_are_comparable_and_cheap_to_carry() {
const fn assert_send<T: Send>() {}
const fn assert_sync<T: Sync>() {}
const fn assert_copy<T: Copy>() {}
assert_send::<Win32Error>();
assert_sync::<Win32Error>();
assert_copy::<Win32Error>();
assert_eq!(
Win32Error::from_code(ERROR_FILE_NOT_FOUND),
Win32Error::from_code(ERROR_FILE_NOT_FOUND)
);
assert_ne!(
Win32Error::from_code(ERROR_FILE_NOT_FOUND),
Win32Error::from_code(ERROR_ACCESS_DENIED)
);
}