aboutsummaryrefslogtreecommitdiff
path: root/src/integration/test_support.rs
blob: 638beb00b90b7aeaf57dcaa8b9f611d779cc511b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//! Shared helpers for integration tests.

use std::path::Path;

/// Windows refuses file symlinks with `ERROR_PRIVILEGE_NOT_HELD` when the
/// process is neither elevated nor running with Developer Mode enabled.
#[cfg(windows)]
const ERROR_PRIVILEGE_NOT_HELD: i32 = 1314;

#[cfg(windows)]
fn symlink_privilege_denied(error: &std::io::Error) -> bool {
    error.raw_os_error() == Some(ERROR_PRIVILEGE_NOT_HELD)
}

/// Create a file symlink, returning `false` when Windows denied the symlink
/// privilege. Callers skip symlink-only assertions on `false` instead of
/// failing an ordinary non-elevated local test run.
#[cfg(unix)]
pub(super) fn symlink_file(target: &Path, link: &Path) -> bool {
    std::os::unix::fs::symlink(target, link).expect("create symlink");
    true
}

#[cfg(windows)]
pub(super) fn symlink_file(target: &Path, link: &Path) -> bool {
    match std::os::windows::fs::symlink_file(target, link) {
        Ok(()) => true,
        Err(error) if symlink_privilege_denied(&error) => {
            eprintln!("skipping symlink test: Windows denied SeCreateSymbolicLinkPrivilege");
            false
        }
        Err(error) => panic!("create symlink {}: {error}", link.display()),
    }
}

#[cfg(all(test, windows))]
mod tests {
    use super::*;

    #[test]
    fn symlink_privilege_error_is_detected() {
        assert!(symlink_privilege_denied(
            &std::io::Error::from_raw_os_error(ERROR_PRIVILEGE_NOT_HELD)
        ));
        assert!(!symlink_privilege_denied(
            &std::io::Error::from_raw_os_error(/* ERROR_ACCESS_DENIED */ 5)
        ));
    }
}