PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/testsuite/death.rs

https://gitlab.com/frewsxcv/cargo
Rust | 101 lines | 76 code | 11 blank | 14 comment | 2 complexity | 2f7559028702434a28f043bbbb2cf303 MD5 | raw file
  1. //! Tests for ctrl-C handling.
  2. use std::fs;
  3. use std::io::{self, Read};
  4. use std::net::TcpListener;
  5. use std::process::{Child, Stdio};
  6. use std::thread;
  7. use cargo_test_support::{project, slow_cpu_multiplier};
  8. #[cargo_test]
  9. fn ctrl_c_kills_everyone() {
  10. let listener = TcpListener::bind("127.0.0.1:0").unwrap();
  11. let addr = listener.local_addr().unwrap();
  12. let p = project()
  13. .file(
  14. "Cargo.toml",
  15. r#"
  16. [package]
  17. name = "foo"
  18. version = "0.0.1"
  19. authors = []
  20. build = "build.rs"
  21. "#,
  22. )
  23. .file("src/lib.rs", "")
  24. .file(
  25. "build.rs",
  26. &format!(
  27. r#"
  28. use std::net::TcpStream;
  29. use std::io::Read;
  30. fn main() {{
  31. let mut socket = TcpStream::connect("{}").unwrap();
  32. let _ = socket.read(&mut [0; 10]);
  33. panic!("that read should never return");
  34. }}
  35. "#,
  36. addr
  37. ),
  38. )
  39. .build();
  40. let mut cargo = p.cargo("build").build_command();
  41. cargo
  42. .stdin(Stdio::piped())
  43. .stdout(Stdio::piped())
  44. .stderr(Stdio::piped())
  45. .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
  46. let mut child = cargo.spawn().unwrap();
  47. let mut sock = listener.accept().unwrap().0;
  48. ctrl_c(&mut child);
  49. assert!(!child.wait().unwrap().success());
  50. match sock.read(&mut [0; 10]) {
  51. Ok(n) => assert_eq!(n, 0),
  52. Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
  53. }
  54. // Ok so what we just did was spawn cargo that spawned a build script, then
  55. // we killed cargo in hopes of it killing the build script as well. If all
  56. // went well the build script is now dead. On Windows, however, this is
  57. // enforced with job objects which means that it may actually be in the
  58. // *process* of being torn down at this point.
  59. //
  60. // Now on Windows we can't completely remove a file until all handles to it
  61. // have been closed. Including those that represent running processes. So if
  62. // we were to return here then there may still be an open reference to some
  63. // file in the build directory. What we want to actually do is wait for the
  64. // build script to *complete* exit. Take care of that by blowing away the
  65. // build directory here, and panicking if we eventually spin too long
  66. // without being able to.
  67. for i in 0..10 {
  68. match fs::remove_dir_all(&p.root().join("target")) {
  69. Ok(()) => return,
  70. Err(e) => println!("attempt {}: {}", i, e),
  71. }
  72. thread::sleep(slow_cpu_multiplier(100));
  73. }
  74. panic!(
  75. "couldn't remove build directory after a few tries, seems like \
  76. we won't be able to!"
  77. );
  78. }
  79. #[cfg(unix)]
  80. pub fn ctrl_c(child: &mut Child) {
  81. let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
  82. if r < 0 {
  83. panic!("failed to kill: {}", io::Error::last_os_error());
  84. }
  85. }
  86. #[cfg(windows)]
  87. pub fn ctrl_c(child: &mut Child) {
  88. child.kill().unwrap();
  89. }