about summary refs log tree commit diff
path: root/library/std/src/sys/process/windows/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/process/windows/tests.rs')
-rw-r--r--library/std/src/sys/process/windows/tests.rs223
1 files changed, 223 insertions, 0 deletions
diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs
new file mode 100644
index 00000000000..1377e12162f
--- /dev/null
+++ b/library/std/src/sys/process/windows/tests.rs
@@ -0,0 +1,223 @@
+use super::{Arg, make_command_line};
+use crate::env;
+use crate::ffi::{OsStr, OsString};
+use crate::process::Command;
+
+#[test]
+fn test_raw_args() {
+    let command_line = &make_command_line(
+        OsStr::new("quoted exe"),
+        &[
+            Arg::Regular(OsString::from("quote me")),
+            Arg::Raw(OsString::from("quote me *not*")),
+            Arg::Raw(OsString::from("\t\\")),
+            Arg::Raw(OsString::from("internal \\\"backslash-\"quote")),
+            Arg::Regular(OsString::from("optional-quotes")),
+        ],
+        false,
+    )
+    .unwrap();
+    assert_eq!(
+        String::from_utf16(command_line).unwrap(),
+        "\"quoted exe\" \"quote me\" quote me *not* \t\\ internal \\\"backslash-\"quote optional-quotes"
+    );
+}
+
+#[test]
+fn test_thread_handle() {
+    use crate::os::windows::io::BorrowedHandle;
+    use crate::os::windows::process::{ChildExt, CommandExt};
+    const CREATE_SUSPENDED: u32 = 0x00000004;
+
+    let p = Command::new("cmd").args(&["/C", "exit 0"]).creation_flags(CREATE_SUSPENDED).spawn();
+    assert!(p.is_ok());
+    let mut p = p.unwrap();
+
+    unsafe extern "system" {
+        fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
+    }
+    unsafe {
+        ResumeThread(p.main_thread_handle());
+    }
+
+    crate::thread::sleep(crate::time::Duration::from_millis(100));
+
+    let res = p.try_wait();
+    assert!(res.is_ok());
+    assert!(res.unwrap().is_some());
+    assert!(p.try_wait().unwrap().unwrap().success());
+}
+
+#[test]
+fn test_make_command_line() {
+    fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
+        let command_line = &make_command_line(
+            OsStr::new(prog),
+            &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
+            force_quotes,
+        )
+        .unwrap();
+        String::from_utf16(command_line).unwrap()
+    }
+
+    assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc");
+
+    assert_eq!(test_wrapper("prog", &[r"C:\"], false), r#""prog" C:\"#);
+    assert_eq!(test_wrapper("prog", &[r"2slashes\\"], false), r#""prog" 2slashes\\"#);
+    assert_eq!(test_wrapper("prog", &[r" C:\"], false), r#""prog" " C:\\""#);
+    assert_eq!(test_wrapper("prog", &[r" 2slashes\\"], false), r#""prog" " 2slashes\\\\""#);
+
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false),
+        "\"C:\\Program Files\\blah\\blah.exe\" aaa"
+    );
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], false),
+        "\"C:\\Program Files\\blah\\blah.exe\" aaa v*"
+    );
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], true),
+        "\"C:\\Program Files\\blah\\blah.exe\" \"aaa\" \"v*\""
+    );
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\test", &["aa\"bb"], false),
+        "\"C:\\Program Files\\test\" aa\\\"bb"
+    );
+    assert_eq!(test_wrapper("echo", &["a b c"], false), "\"echo\" \"a b c\"");
+    assert_eq!(
+        test_wrapper("echo", &["\" \\\" \\", "\\"], false),
+        "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
+    );
+    assert_eq!(
+        test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[], false),
+        "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
+    );
+}
+
+// On Windows, environment args are case preserving but comparisons are case-insensitive.
+// See: #85242
+#[test]
+fn windows_env_unicode_case() {
+    let test_cases = [
+        ("ä", "Ä"),
+        ("ß", "SS"),
+        ("Ä", "Ö"),
+        ("Ä", "Ö"),
+        ("I", "İ"),
+        ("I", "i"),
+        ("I", "ı"),
+        ("i", "I"),
+        ("i", "İ"),
+        ("i", "ı"),
+        ("İ", "I"),
+        ("İ", "i"),
+        ("İ", "ı"),
+        ("ı", "I"),
+        ("ı", "i"),
+        ("ı", "İ"),
+        ("ä", "Ä"),
+        ("ß", "SS"),
+        ("Ä", "Ö"),
+        ("Ä", "Ö"),
+        ("I", "İ"),
+        ("I", "i"),
+        ("I", "ı"),
+        ("i", "I"),
+        ("i", "İ"),
+        ("i", "ı"),
+        ("İ", "I"),
+        ("İ", "i"),
+        ("İ", "ı"),
+        ("ı", "I"),
+        ("ı", "i"),
+        ("ı", "İ"),
+    ];
+    // Test that `cmd.env` matches `env::set_var` when setting two strings that
+    // may (or may not) be case-folded when compared.
+    for (a, b) in test_cases.iter() {
+        let mut cmd = Command::new("cmd");
+        cmd.env(a, "1");
+        cmd.env(b, "2");
+        unsafe {
+            env::set_var(a, "1");
+            env::set_var(b, "2");
+        }
+
+        for (key, value) in cmd.get_envs() {
+            assert_eq!(
+                env::var(key).ok(),
+                value.map(|s| s.to_string_lossy().into_owned()),
+                "command environment mismatch: {a} {b}",
+            );
+        }
+    }
+}
+
+// UWP applications run in a restricted environment which means this test may not work.
+#[cfg(not(target_vendor = "uwp"))]
+#[test]
+fn windows_exe_resolver() {
+    use super::resolve_exe;
+    use crate::io;
+    use crate::sys::fs::symlink;
+    use crate::test_helpers::tmpdir;
+
+    let env_paths = || env::var_os("PATH");
+
+    // Test a full path, with and without the `exe` extension.
+    let mut current_exe = env::current_exe().unwrap();
+    assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
+    current_exe.set_extension("");
+    assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
+
+    // Test lone file names.
+    assert!(resolve_exe(OsStr::new("cmd"), env_paths, None).is_ok());
+    assert!(resolve_exe(OsStr::new("cmd.exe"), env_paths, None).is_ok());
+    assert!(resolve_exe(OsStr::new("cmd.EXE"), env_paths, None).is_ok());
+    assert!(resolve_exe(OsStr::new("fc"), env_paths, None).is_ok());
+
+    // Invalid file names should return InvalidInput.
+    assert_eq!(
+        resolve_exe(OsStr::new(""), env_paths, None).unwrap_err().kind(),
+        io::ErrorKind::InvalidInput
+    );
+    assert_eq!(
+        resolve_exe(OsStr::new("\0"), env_paths, None).unwrap_err().kind(),
+        io::ErrorKind::InvalidInput
+    );
+    // Trailing slash, therefore there's no file name component.
+    assert_eq!(
+        resolve_exe(OsStr::new(r"C:\Path\to\"), env_paths, None).unwrap_err().kind(),
+        io::ErrorKind::InvalidInput
+    );
+
+    /*
+    Some of the following tests may need to be changed if you are deliberately
+    changing the behavior of `resolve_exe`.
+    */
+
+    let empty_paths = || None;
+
+    // The resolver looks in system directories even when `PATH` is empty.
+    assert!(resolve_exe(OsStr::new("cmd.exe"), empty_paths, None).is_ok());
+
+    // The application's directory is also searched.
+    let current_exe = env::current_exe().unwrap();
+    assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok());
+
+    // Create a temporary path and add a broken symlink.
+    let temp = tmpdir();
+    let mut exe_path = temp.path().to_owned();
+    exe_path.push("exists.exe");
+
+    // A broken symlink should still be resolved.
+    // Skip this check if not in CI and creating symlinks isn't possible.
+    let is_ci = env::var("CI").is_ok();
+    let result = symlink("<DOES NOT EXIST>".as_ref(), &exe_path);
+    if is_ci || result.is_ok() {
+        result.unwrap();
+        assert!(
+            resolve_exe(OsStr::new("exists.exe"), empty_paths, Some(temp.path().as_ref())).is_ok()
+        );
+    }
+}