1 use super::make_command_line;
4 use crate::ffi::{OsStr, OsString};
5 use crate::process::Command;
9 let command_line = &make_command_line(
10 OsStr::new("quoted exe"),
12 Arg::Regular(OsString::from("quote me")),
13 Arg::Raw(OsString::from("quote me *not*")),
14 Arg::Raw(OsString::from("\t\\")),
15 Arg::Raw(OsString::from("internal \\\"backslash-\"quote")),
16 Arg::Regular(OsString::from("optional-quotes")),
22 String::from_utf16(command_line).unwrap(),
23 "\"quoted exe\" \"quote me\" quote me *not* \t\\ internal \\\"backslash-\"quote optional-quotes"
28 fn test_thread_handle() {
29 use crate::os::windows::io::BorrowedHandle;
30 use crate::os::windows::process::{ChildExt, CommandExt};
31 const CREATE_SUSPENDED: u32 = 0x00000004;
33 let p = Command::new("cmd").args(&["/C", "exit 0"]).creation_flags(CREATE_SUSPENDED).spawn();
35 let mut p = p.unwrap();
38 fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
41 ResumeThread(p.main_thread_handle());
44 crate::thread::sleep(crate::time::Duration::from_millis(100));
46 let res = p.try_wait();
48 assert!(res.unwrap().is_some());
49 assert!(p.try_wait().unwrap().unwrap().success());
53 fn test_make_command_line() {
54 fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
55 let command_line = &make_command_line(
57 &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
61 String::from_utf16(command_line).unwrap()
64 assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc");
66 assert_eq!(test_wrapper("prog", &[r"C:\"], false), r#""prog" C:\"#);
67 assert_eq!(test_wrapper("prog", &[r"2slashes\\"], false), r#""prog" 2slashes\\"#);
68 assert_eq!(test_wrapper("prog", &[r" C:\"], false), r#""prog" " C:\\""#);
69 assert_eq!(test_wrapper("prog", &[r" 2slashes\\"], false), r#""prog" " 2slashes\\\\""#);
72 test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false),
73 "\"C:\\Program Files\\blah\\blah.exe\" aaa"
76 test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], false),
77 "\"C:\\Program Files\\blah\\blah.exe\" aaa v*"
80 test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], true),
81 "\"C:\\Program Files\\blah\\blah.exe\" \"aaa\" \"v*\""
84 test_wrapper("C:\\Program Files\\test", &["aa\"bb"], false),
85 "\"C:\\Program Files\\test\" aa\\\"bb"
87 assert_eq!(test_wrapper("echo", &["a b c"], false), "\"echo\" \"a b c\"");
89 test_wrapper("echo", &["\" \\\" \\", "\\"], false),
90 "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
93 test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[], false),
94 "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
98 // On Windows, environment args are case preserving but comparisons are case-insensitive.
101 fn windows_env_unicode_case() {
136 // Test that `cmd.env` matches `env::set_var` when setting two strings that
137 // may (or may not) be case-folded when compared.
138 for (a, b) in test_cases.iter() {
139 let mut cmd = Command::new("cmd");
142 env::set_var(a, "1");
143 env::set_var(b, "2");
145 for (key, value) in cmd.get_envs() {
148 value.map(|s| s.to_string_lossy().into_owned()),
149 "command environment mismatch: {a} {b}",
155 // UWP applications run in a restricted environment which means this test may not work.
156 #[cfg(not(target_vendor = "uwp"))]
158 fn windows_exe_resolver() {
159 use super::resolve_exe;
161 use crate::sys::fs::symlink;
162 use crate::sys_common::io::test::tmpdir;
164 let env_paths = || env::var_os("PATH");
166 // Test a full path, with and without the `exe` extension.
167 let mut current_exe = env::current_exe().unwrap();
168 assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
169 current_exe.set_extension("");
170 assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
172 // Test lone file names.
173 assert!(resolve_exe(OsStr::new("cmd"), env_paths, None).is_ok());
174 assert!(resolve_exe(OsStr::new("cmd.exe"), env_paths, None).is_ok());
175 assert!(resolve_exe(OsStr::new("cmd.EXE"), env_paths, None).is_ok());
176 assert!(resolve_exe(OsStr::new("fc"), env_paths, None).is_ok());
178 // Invalid file names should return InvalidInput.
180 resolve_exe(OsStr::new(""), env_paths, None).unwrap_err().kind(),
181 io::ErrorKind::InvalidInput
184 resolve_exe(OsStr::new("\0"), env_paths, None).unwrap_err().kind(),
185 io::ErrorKind::InvalidInput
187 // Trailing slash, therefore there's no file name component.
189 resolve_exe(OsStr::new(r"C:\Path\to\"), env_paths, None).unwrap_err().kind(),
190 io::ErrorKind::InvalidInput
194 Some of the following tests may need to be changed if you are deliberately
195 changing the behaviour of `resolve_exe`.
198 let empty_paths = || None;
200 // The resolver looks in system directories even when `PATH` is empty.
201 assert!(resolve_exe(OsStr::new("cmd.exe"), empty_paths, None).is_ok());
203 // The application's directory is also searched.
204 let current_exe = env::current_exe().unwrap();
205 assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok());
207 // Create a temporary path and add a broken symlink.
209 let mut exe_path = temp.path().to_owned();
210 exe_path.push("exists.exe");
212 // A broken symlink should still be resolved.
213 // Skip this check if not in CI and creating symlinks isn't possible.
214 let is_ci = env::var("CI").is_ok();
215 let result = symlink("<DOES NOT EXIST>".as_ref(), &exe_path);
216 if is_ci || result.is_ok() {
219 resolve_exe(OsStr::new("exists.exe"), empty_paths, Some(temp.path().as_ref())).is_ok()