PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/library/std/src/process/tests.rs

https://gitlab.com/rust-lang/rust
Rust | 458 lines | 384 code | 56 blank | 18 comment | 35 complexity | 97487d3f42480d7bc6597de189a24b75 MD5 | raw file
  1. use crate::io::prelude::*;
  2. use super::{Command, Output, Stdio};
  3. use crate::io::ErrorKind;
  4. use crate::str;
  5. fn known_command() -> Command {
  6. if cfg!(windows) { Command::new("help") } else { Command::new("echo") }
  7. }
  8. #[cfg(target_os = "android")]
  9. fn shell_cmd() -> Command {
  10. Command::new("/system/bin/sh")
  11. }
  12. #[cfg(not(target_os = "android"))]
  13. fn shell_cmd() -> Command {
  14. Command::new("/bin/sh")
  15. }
  16. #[test]
  17. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  18. fn smoke() {
  19. let p = if cfg!(target_os = "windows") {
  20. Command::new("cmd").args(&["/C", "exit 0"]).spawn()
  21. } else {
  22. shell_cmd().arg("-c").arg("true").spawn()
  23. };
  24. assert!(p.is_ok());
  25. let mut p = p.unwrap();
  26. assert!(p.wait().unwrap().success());
  27. }
  28. #[test]
  29. #[cfg_attr(target_os = "android", ignore)]
  30. fn smoke_failure() {
  31. match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
  32. Ok(..) => panic!(),
  33. Err(..) => {}
  34. }
  35. }
  36. #[test]
  37. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  38. fn exit_reported_right() {
  39. let p = if cfg!(target_os = "windows") {
  40. Command::new("cmd").args(&["/C", "exit 1"]).spawn()
  41. } else {
  42. shell_cmd().arg("-c").arg("false").spawn()
  43. };
  44. assert!(p.is_ok());
  45. let mut p = p.unwrap();
  46. assert!(p.wait().unwrap().code() == Some(1));
  47. drop(p.wait());
  48. }
  49. #[test]
  50. #[cfg(unix)]
  51. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  52. fn signal_reported_right() {
  53. use crate::os::unix::process::ExitStatusExt;
  54. let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
  55. p.kill().unwrap();
  56. match p.wait().unwrap().signal() {
  57. Some(9) => {}
  58. result => panic!("not terminated by signal 9 (instead, {result:?})"),
  59. }
  60. }
  61. pub fn run_output(mut cmd: Command) -> String {
  62. let p = cmd.spawn();
  63. assert!(p.is_ok());
  64. let mut p = p.unwrap();
  65. assert!(p.stdout.is_some());
  66. let mut ret = String::new();
  67. p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
  68. assert!(p.wait().unwrap().success());
  69. return ret;
  70. }
  71. #[test]
  72. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  73. fn stdout_works() {
  74. if cfg!(target_os = "windows") {
  75. let mut cmd = Command::new("cmd");
  76. cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
  77. assert_eq!(run_output(cmd), "foobar\r\n");
  78. } else {
  79. let mut cmd = shell_cmd();
  80. cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped());
  81. assert_eq!(run_output(cmd), "foobar\n");
  82. }
  83. }
  84. #[test]
  85. #[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
  86. fn set_current_dir_works() {
  87. let mut cmd = shell_cmd();
  88. cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
  89. assert_eq!(run_output(cmd), "/\n");
  90. }
  91. #[test]
  92. #[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
  93. fn stdin_works() {
  94. let mut p = shell_cmd()
  95. .arg("-c")
  96. .arg("read line; echo $line")
  97. .stdin(Stdio::piped())
  98. .stdout(Stdio::piped())
  99. .spawn()
  100. .unwrap();
  101. p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
  102. drop(p.stdin.take());
  103. let mut out = String::new();
  104. p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
  105. assert!(p.wait().unwrap().success());
  106. assert_eq!(out, "foobar\n");
  107. }
  108. #[test]
  109. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  110. fn test_process_status() {
  111. let mut status = if cfg!(target_os = "windows") {
  112. Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
  113. } else {
  114. shell_cmd().arg("-c").arg("false").status().unwrap()
  115. };
  116. assert!(status.code() == Some(1));
  117. status = if cfg!(target_os = "windows") {
  118. Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
  119. } else {
  120. shell_cmd().arg("-c").arg("true").status().unwrap()
  121. };
  122. assert!(status.success());
  123. }
  124. #[test]
  125. fn test_process_output_fail_to_start() {
  126. match Command::new("/no-binary-by-this-name-should-exist").output() {
  127. Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
  128. Ok(..) => panic!(),
  129. }
  130. }
  131. #[test]
  132. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  133. fn test_process_output_output() {
  134. let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
  135. Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
  136. } else {
  137. shell_cmd().arg("-c").arg("echo hello").output().unwrap()
  138. };
  139. let output_str = str::from_utf8(&stdout).unwrap();
  140. assert!(status.success());
  141. assert_eq!(output_str.trim().to_string(), "hello");
  142. assert_eq!(stderr, Vec::new());
  143. }
  144. #[test]
  145. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  146. fn test_process_output_error() {
  147. let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
  148. Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
  149. } else {
  150. Command::new("mkdir").arg("./").output().unwrap()
  151. };
  152. assert!(status.code().is_some());
  153. assert!(status.code() != Some(0));
  154. assert_eq!(stdout, Vec::new());
  155. assert!(!stderr.is_empty());
  156. }
  157. #[test]
  158. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  159. fn test_finish_once() {
  160. let mut prog = if cfg!(target_os = "windows") {
  161. Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
  162. } else {
  163. shell_cmd().arg("-c").arg("false").spawn().unwrap()
  164. };
  165. assert!(prog.wait().unwrap().code() == Some(1));
  166. }
  167. #[test]
  168. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  169. fn test_finish_twice() {
  170. let mut prog = if cfg!(target_os = "windows") {
  171. Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
  172. } else {
  173. shell_cmd().arg("-c").arg("false").spawn().unwrap()
  174. };
  175. assert!(prog.wait().unwrap().code() == Some(1));
  176. assert!(prog.wait().unwrap().code() == Some(1));
  177. }
  178. #[test]
  179. #[cfg_attr(any(target_os = "vxworks"), ignore)]
  180. fn test_wait_with_output_once() {
  181. let prog = if cfg!(target_os = "windows") {
  182. Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
  183. } else {
  184. shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap()
  185. };
  186. let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
  187. let output_str = str::from_utf8(&stdout).unwrap();
  188. assert!(status.success());
  189. assert_eq!(output_str.trim().to_string(), "hello");
  190. assert_eq!(stderr, Vec::new());
  191. }
  192. #[cfg(all(unix, not(target_os = "android")))]
  193. pub fn env_cmd() -> Command {
  194. Command::new("env")
  195. }
  196. #[cfg(target_os = "android")]
  197. pub fn env_cmd() -> Command {
  198. let mut cmd = Command::new("/system/bin/sh");
  199. cmd.arg("-c").arg("set");
  200. cmd
  201. }
  202. #[cfg(windows)]
  203. pub fn env_cmd() -> Command {
  204. let mut cmd = Command::new("cmd");
  205. cmd.arg("/c").arg("set");
  206. cmd
  207. }
  208. #[test]
  209. #[cfg_attr(target_os = "vxworks", ignore)]
  210. fn test_override_env() {
  211. use crate::env;
  212. // In some build environments (such as chrooted Nix builds), `env` can
  213. // only be found in the explicitly-provided PATH env variable, not in
  214. // default places such as /bin or /usr/bin. So we need to pass through
  215. // PATH to our sub-process.
  216. let mut cmd = env_cmd();
  217. cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
  218. if let Some(p) = env::var_os("PATH") {
  219. cmd.env("PATH", &p);
  220. }
  221. let result = cmd.output().unwrap();
  222. let output = String::from_utf8_lossy(&result.stdout).to_string();
  223. assert!(
  224. output.contains("RUN_TEST_NEW_ENV=123"),
  225. "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}",
  226. );
  227. }
  228. #[test]
  229. #[cfg_attr(target_os = "vxworks", ignore)]
  230. fn test_add_to_env() {
  231. let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
  232. let output = String::from_utf8_lossy(&result.stdout).to_string();
  233. assert!(
  234. output.contains("RUN_TEST_NEW_ENV=123"),
  235. "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}"
  236. );
  237. }
  238. #[test]
  239. #[cfg_attr(target_os = "vxworks", ignore)]
  240. fn test_capture_env_at_spawn() {
  241. use crate::env;
  242. let mut cmd = env_cmd();
  243. cmd.env("RUN_TEST_NEW_ENV1", "123");
  244. // This variable will not be present if the environment has already
  245. // been captured above.
  246. env::set_var("RUN_TEST_NEW_ENV2", "456");
  247. let result = cmd.output().unwrap();
  248. env::remove_var("RUN_TEST_NEW_ENV2");
  249. let output = String::from_utf8_lossy(&result.stdout).to_string();
  250. assert!(
  251. output.contains("RUN_TEST_NEW_ENV1=123"),
  252. "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}"
  253. );
  254. assert!(
  255. output.contains("RUN_TEST_NEW_ENV2=456"),
  256. "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}"
  257. );
  258. }
  259. // Regression tests for #30858.
  260. #[test]
  261. fn test_interior_nul_in_progname_is_error() {
  262. match Command::new("has-some-\0\0s-inside").spawn() {
  263. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  264. Ok(_) => panic!(),
  265. }
  266. }
  267. #[test]
  268. fn test_interior_nul_in_arg_is_error() {
  269. match known_command().arg("has-some-\0\0s-inside").spawn() {
  270. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  271. Ok(_) => panic!(),
  272. }
  273. }
  274. #[test]
  275. fn test_interior_nul_in_args_is_error() {
  276. match known_command().args(&["has-some-\0\0s-inside"]).spawn() {
  277. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  278. Ok(_) => panic!(),
  279. }
  280. }
  281. #[test]
  282. fn test_interior_nul_in_current_dir_is_error() {
  283. match known_command().current_dir("has-some-\0\0s-inside").spawn() {
  284. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  285. Ok(_) => panic!(),
  286. }
  287. }
  288. // Regression tests for #30862.
  289. #[test]
  290. #[cfg_attr(target_os = "vxworks", ignore)]
  291. fn test_interior_nul_in_env_key_is_error() {
  292. match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
  293. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  294. Ok(_) => panic!(),
  295. }
  296. }
  297. #[test]
  298. #[cfg_attr(target_os = "vxworks", ignore)]
  299. fn test_interior_nul_in_env_value_is_error() {
  300. match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
  301. Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
  302. Ok(_) => panic!(),
  303. }
  304. }
  305. /// Tests that process creation flags work by debugging a process.
  306. /// Other creation flags make it hard or impossible to detect
  307. /// behavioral changes in the process.
  308. #[test]
  309. #[cfg(windows)]
  310. fn test_creation_flags() {
  311. use crate::os::windows::process::CommandExt;
  312. use crate::sys::c::{BOOL, DWORD, INFINITE};
  313. #[repr(C, packed)]
  314. struct DEBUG_EVENT {
  315. pub event_code: DWORD,
  316. pub process_id: DWORD,
  317. pub thread_id: DWORD,
  318. // This is a union in the real struct, but we don't
  319. // need this data for the purposes of this test.
  320. pub _junk: [u8; 164],
  321. }
  322. extern "system" {
  323. fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
  324. fn ContinueDebugEvent(
  325. dwProcessId: DWORD,
  326. dwThreadId: DWORD,
  327. dwContinueStatus: DWORD,
  328. ) -> BOOL;
  329. }
  330. const DEBUG_PROCESS: DWORD = 1;
  331. const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
  332. const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
  333. let mut child =
  334. Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap();
  335. child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
  336. let mut events = 0;
  337. let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
  338. loop {
  339. if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
  340. panic!("WaitForDebugEvent failed!");
  341. }
  342. events += 1;
  343. if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
  344. break;
  345. }
  346. if unsafe {
  347. ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
  348. } == 0
  349. {
  350. panic!("ContinueDebugEvent failed!");
  351. }
  352. }
  353. assert!(events > 0);
  354. }
  355. #[test]
  356. fn test_command_implements_send_sync() {
  357. fn take_send_sync_type<T: Send + Sync>(_: T) {}
  358. take_send_sync_type(Command::new(""))
  359. }
  360. // Ensure that starting a process with no environment variables works on Windows.
  361. // This will fail if the environment block is ill-formed.
  362. #[test]
  363. #[cfg(windows)]
  364. fn env_empty() {
  365. let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn();
  366. assert!(p.is_ok());
  367. }
  368. // See issue #91991
  369. #[test]
  370. #[cfg(windows)]
  371. fn run_bat_script() {
  372. let tempdir = crate::sys_common::io::test::tmpdir();
  373. let script_path = tempdir.join("hello.cmd");
  374. crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
  375. let output = Command::new(&script_path)
  376. .arg("fellow Rustaceans")
  377. .stdout(crate::process::Stdio::piped())
  378. .spawn()
  379. .unwrap()
  380. .wait_with_output()
  381. .unwrap();
  382. assert!(output.status.success());
  383. assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
  384. }
  385. // See issue #95178
  386. #[test]
  387. #[cfg(windows)]
  388. fn run_canonical_bat_script() {
  389. let tempdir = crate::sys_common::io::test::tmpdir();
  390. let script_path = tempdir.join("hello.cmd");
  391. crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
  392. // Try using a canonical path
  393. let output = Command::new(&script_path.canonicalize().unwrap())
  394. .arg("fellow Rustaceans")
  395. .stdout(crate::process::Stdio::piped())
  396. .spawn()
  397. .unwrap()
  398. .wait_with_output()
  399. .unwrap();
  400. assert!(output.status.success());
  401. assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
  402. }