PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tools/cargo/tests/testsuite/concurrent.rs

https://bitbucket.org/sailfish009/rust
Rust | 509 lines | 426 code | 66 blank | 17 comment | 13 complexity | 006dd77b4e689e79726e53654ca970dc MD5 | raw file
Possible License(s): Apache-2.0, 0BSD, JSON, MIT
  1. //! Tests for running multiple `cargo` processes at the same time.
  2. use std::fs;
  3. use std::net::TcpListener;
  4. use std::process::Stdio;
  5. use std::sync::mpsc::channel;
  6. use std::thread;
  7. use std::{env, str};
  8. use cargo_test_support::cargo_process;
  9. use cargo_test_support::git;
  10. use cargo_test_support::install::{assert_has_installed_exe, cargo_home};
  11. use cargo_test_support::registry::Package;
  12. use cargo_test_support::{basic_manifest, execs, project, slow_cpu_multiplier};
  13. fn pkg(name: &str, vers: &str) {
  14. Package::new(name, vers)
  15. .file("src/main.rs", "fn main() {{}}")
  16. .publish();
  17. }
  18. #[cargo_test]
  19. fn multiple_installs() {
  20. let p = project()
  21. .no_manifest()
  22. .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0"))
  23. .file("a/src/main.rs", "fn main() {}")
  24. .file("b/Cargo.toml", &basic_manifest("bar", "0.0.0"))
  25. .file("b/src/main.rs", "fn main() {}");
  26. let p = p.build();
  27. let mut a = p.cargo("install").cwd("a").build_command();
  28. let mut b = p.cargo("install").cwd("b").build_command();
  29. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  30. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  31. let a = a.spawn().unwrap();
  32. let b = b.spawn().unwrap();
  33. let a = thread::spawn(move || a.wait_with_output().unwrap());
  34. let b = b.wait_with_output().unwrap();
  35. let a = a.join().unwrap();
  36. execs().run_output(&a);
  37. execs().run_output(&b);
  38. assert_has_installed_exe(cargo_home(), "foo");
  39. assert_has_installed_exe(cargo_home(), "bar");
  40. }
  41. #[cargo_test]
  42. fn concurrent_installs() {
  43. const LOCKED_BUILD: &str = "waiting for file lock on build directory";
  44. pkg("foo", "0.0.1");
  45. pkg("bar", "0.0.1");
  46. let mut a = cargo_process("install foo").build_command();
  47. let mut b = cargo_process("install bar").build_command();
  48. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  49. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  50. let a = a.spawn().unwrap();
  51. let b = b.spawn().unwrap();
  52. let a = thread::spawn(move || a.wait_with_output().unwrap());
  53. let b = b.wait_with_output().unwrap();
  54. let a = a.join().unwrap();
  55. assert!(!str::from_utf8(&a.stderr).unwrap().contains(LOCKED_BUILD));
  56. assert!(!str::from_utf8(&b.stderr).unwrap().contains(LOCKED_BUILD));
  57. execs().run_output(&a);
  58. execs().run_output(&b);
  59. assert_has_installed_exe(cargo_home(), "foo");
  60. assert_has_installed_exe(cargo_home(), "bar");
  61. }
  62. #[cargo_test]
  63. fn one_install_should_be_bad() {
  64. let p = project()
  65. .no_manifest()
  66. .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0"))
  67. .file("a/src/main.rs", "fn main() {}")
  68. .file("b/Cargo.toml", &basic_manifest("foo", "0.0.0"))
  69. .file("b/src/main.rs", "fn main() {}");
  70. let p = p.build();
  71. let mut a = p.cargo("install").cwd("a").build_command();
  72. let mut b = p.cargo("install").cwd("b").build_command();
  73. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  74. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  75. let a = a.spawn().unwrap();
  76. let b = b.spawn().unwrap();
  77. let a = thread::spawn(move || a.wait_with_output().unwrap());
  78. let b = b.wait_with_output().unwrap();
  79. let a = a.join().unwrap();
  80. execs().run_output(&a);
  81. execs().run_output(&b);
  82. assert_has_installed_exe(cargo_home(), "foo");
  83. }
  84. #[cargo_test]
  85. fn multiple_registry_fetches() {
  86. let mut pkg = Package::new("bar", "1.0.2");
  87. for i in 0..10 {
  88. let name = format!("foo{}", i);
  89. Package::new(&name, "1.0.0").publish();
  90. pkg.dep(&name, "*");
  91. }
  92. pkg.publish();
  93. let p = project()
  94. .no_manifest()
  95. .file(
  96. "a/Cargo.toml",
  97. r#"
  98. [package]
  99. name = "foo"
  100. authors = []
  101. version = "0.0.0"
  102. [dependencies]
  103. bar = "*"
  104. "#,
  105. )
  106. .file("a/src/main.rs", "fn main() {}")
  107. .file(
  108. "b/Cargo.toml",
  109. r#"
  110. [package]
  111. name = "bar"
  112. authors = []
  113. version = "0.0.0"
  114. [dependencies]
  115. bar = "*"
  116. "#,
  117. )
  118. .file("b/src/main.rs", "fn main() {}");
  119. let p = p.build();
  120. let mut a = p.cargo("build").cwd("a").build_command();
  121. let mut b = p.cargo("build").cwd("b").build_command();
  122. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  123. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  124. let a = a.spawn().unwrap();
  125. let b = b.spawn().unwrap();
  126. let a = thread::spawn(move || a.wait_with_output().unwrap());
  127. let b = b.wait_with_output().unwrap();
  128. let a = a.join().unwrap();
  129. execs().run_output(&a);
  130. execs().run_output(&b);
  131. let suffix = env::consts::EXE_SUFFIX;
  132. assert!(p
  133. .root()
  134. .join("a/target/debug")
  135. .join(format!("foo{}", suffix))
  136. .is_file());
  137. assert!(p
  138. .root()
  139. .join("b/target/debug")
  140. .join(format!("bar{}", suffix))
  141. .is_file());
  142. }
  143. #[cargo_test]
  144. fn git_same_repo_different_tags() {
  145. let a = git::new("dep", |project| {
  146. project
  147. .file("Cargo.toml", &basic_manifest("dep", "0.5.0"))
  148. .file("src/lib.rs", "pub fn tag1() {}")
  149. });
  150. let repo = git2::Repository::open(&a.root()).unwrap();
  151. git::tag(&repo, "tag1");
  152. a.change_file("src/lib.rs", "pub fn tag2() {}");
  153. git::add(&repo);
  154. git::commit(&repo);
  155. git::tag(&repo, "tag2");
  156. let p = project()
  157. .no_manifest()
  158. .file(
  159. "a/Cargo.toml",
  160. &format!(
  161. r#"
  162. [package]
  163. name = "foo"
  164. authors = []
  165. version = "0.0.0"
  166. [dependencies]
  167. dep = {{ git = '{}', tag = 'tag1' }}
  168. "#,
  169. a.url()
  170. ),
  171. )
  172. .file(
  173. "a/src/main.rs",
  174. "extern crate dep; fn main() { dep::tag1(); }",
  175. )
  176. .file(
  177. "b/Cargo.toml",
  178. &format!(
  179. r#"
  180. [package]
  181. name = "bar"
  182. authors = []
  183. version = "0.0.0"
  184. [dependencies]
  185. dep = {{ git = '{}', tag = 'tag2' }}
  186. "#,
  187. a.url()
  188. ),
  189. )
  190. .file(
  191. "b/src/main.rs",
  192. "extern crate dep; fn main() { dep::tag2(); }",
  193. );
  194. let p = p.build();
  195. let mut a = p.cargo("build -v").cwd("a").build_command();
  196. let mut b = p.cargo("build -v").cwd("b").build_command();
  197. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  198. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  199. let a = a.spawn().unwrap();
  200. let b = b.spawn().unwrap();
  201. let a = thread::spawn(move || a.wait_with_output().unwrap());
  202. let b = b.wait_with_output().unwrap();
  203. let a = a.join().unwrap();
  204. execs().run_output(&a);
  205. execs().run_output(&b);
  206. }
  207. #[cargo_test]
  208. fn git_same_branch_different_revs() {
  209. let a = git::new("dep", |project| {
  210. project
  211. .file("Cargo.toml", &basic_manifest("dep", "0.5.0"))
  212. .file("src/lib.rs", "pub fn f1() {}")
  213. });
  214. let p = project()
  215. .no_manifest()
  216. .file(
  217. "a/Cargo.toml",
  218. &format!(
  219. r#"
  220. [package]
  221. name = "foo"
  222. authors = []
  223. version = "0.0.0"
  224. [dependencies]
  225. dep = {{ git = '{}' }}
  226. "#,
  227. a.url()
  228. ),
  229. )
  230. .file(
  231. "a/src/main.rs",
  232. "extern crate dep; fn main() { dep::f1(); }",
  233. )
  234. .file(
  235. "b/Cargo.toml",
  236. &format!(
  237. r#"
  238. [package]
  239. name = "bar"
  240. authors = []
  241. version = "0.0.0"
  242. [dependencies]
  243. dep = {{ git = '{}' }}
  244. "#,
  245. a.url()
  246. ),
  247. )
  248. .file(
  249. "b/src/main.rs",
  250. "extern crate dep; fn main() { dep::f2(); }",
  251. );
  252. let p = p.build();
  253. // Generate a Cargo.lock pointing at the current rev, then clear out the
  254. // target directory
  255. p.cargo("build").cwd("a").run();
  256. fs::remove_dir_all(p.root().join("a/target")).unwrap();
  257. // Make a new commit on the master branch
  258. let repo = git2::Repository::open(&a.root()).unwrap();
  259. a.change_file("src/lib.rs", "pub fn f2() {}");
  260. git::add(&repo);
  261. git::commit(&repo);
  262. // Now run both builds in parallel. The build of `b` should pick up the
  263. // newest commit while the build of `a` should use the locked old commit.
  264. let mut a = p.cargo("build").cwd("a").build_command();
  265. let mut b = p.cargo("build").cwd("b").build_command();
  266. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  267. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  268. let a = a.spawn().unwrap();
  269. let b = b.spawn().unwrap();
  270. let a = thread::spawn(move || a.wait_with_output().unwrap());
  271. let b = b.wait_with_output().unwrap();
  272. let a = a.join().unwrap();
  273. execs().run_output(&a);
  274. execs().run_output(&b);
  275. }
  276. #[cargo_test]
  277. fn same_project() {
  278. let p = project()
  279. .file("src/main.rs", "fn main() {}")
  280. .file("src/lib.rs", "");
  281. let p = p.build();
  282. let mut a = p.cargo("build").build_command();
  283. let mut b = p.cargo("build").build_command();
  284. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  285. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  286. let a = a.spawn().unwrap();
  287. let b = b.spawn().unwrap();
  288. let a = thread::spawn(move || a.wait_with_output().unwrap());
  289. let b = b.wait_with_output().unwrap();
  290. let a = a.join().unwrap();
  291. execs().run_output(&a);
  292. execs().run_output(&b);
  293. }
  294. // Make sure that if Cargo dies while holding a lock that it's released and the
  295. // next Cargo to come in will take over cleanly.
  296. // older win versions don't support job objects, so skip test there
  297. #[cargo_test]
  298. #[cfg_attr(target_os = "windows", ignore)]
  299. fn killing_cargo_releases_the_lock() {
  300. let p = project()
  301. .file(
  302. "Cargo.toml",
  303. r#"
  304. [package]
  305. name = "foo"
  306. authors = []
  307. version = "0.0.0"
  308. build = "build.rs"
  309. "#,
  310. )
  311. .file("src/main.rs", "fn main() {}")
  312. .file(
  313. "build.rs",
  314. r#"
  315. use std::net::TcpStream;
  316. fn main() {
  317. if std::env::var("A").is_ok() {
  318. TcpStream::connect(&std::env::var("ADDR").unwrap()[..])
  319. .unwrap();
  320. std::thread::sleep(std::time::Duration::new(10, 0));
  321. }
  322. }
  323. "#,
  324. );
  325. let p = p.build();
  326. // Our build script will connect to our local TCP socket to inform us that
  327. // it's started and that's how we know that `a` will have the lock
  328. // when we kill it.
  329. let l = TcpListener::bind("127.0.0.1:0").unwrap();
  330. let mut a = p.cargo("build").build_command();
  331. let mut b = p.cargo("build").build_command();
  332. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  333. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  334. a.env("ADDR", l.local_addr().unwrap().to_string())
  335. .env("A", "a");
  336. b.env("ADDR", l.local_addr().unwrap().to_string())
  337. .env_remove("A");
  338. // Spawn `a`, wait for it to get to the build script (at which point the
  339. // lock is held), then kill it.
  340. let mut a = a.spawn().unwrap();
  341. l.accept().unwrap();
  342. a.kill().unwrap();
  343. // Spawn `b`, then just finish the output of a/b the same way the above
  344. // tests does.
  345. let b = b.spawn().unwrap();
  346. let a = thread::spawn(move || a.wait_with_output().unwrap());
  347. let b = b.wait_with_output().unwrap();
  348. let a = a.join().unwrap();
  349. // We killed `a`, so it shouldn't succeed, but `b` should have succeeded.
  350. assert!(!a.status.success());
  351. execs().run_output(&b);
  352. }
  353. #[cargo_test]
  354. fn debug_release_ok() {
  355. let p = project().file("src/main.rs", "fn main() {}");
  356. let p = p.build();
  357. p.cargo("build").run();
  358. fs::remove_dir_all(p.root().join("target")).unwrap();
  359. let mut a = p.cargo("build").build_command();
  360. let mut b = p.cargo("build --release").build_command();
  361. a.stdout(Stdio::piped()).stderr(Stdio::piped());
  362. b.stdout(Stdio::piped()).stderr(Stdio::piped());
  363. let a = a.spawn().unwrap();
  364. let b = b.spawn().unwrap();
  365. let a = thread::spawn(move || a.wait_with_output().unwrap());
  366. let b = b.wait_with_output().unwrap();
  367. let a = a.join().unwrap();
  368. execs()
  369. .with_stderr_contains(
  370. "\
  371. [COMPILING] foo v0.0.1 [..]
  372. [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
  373. ",
  374. )
  375. .run_output(&a);
  376. execs()
  377. .with_stderr_contains(
  378. "\
  379. [COMPILING] foo v0.0.1 [..]
  380. [FINISHED] release [optimized] target(s) in [..]
  381. ",
  382. )
  383. .run_output(&b);
  384. }
  385. #[cargo_test]
  386. fn no_deadlock_with_git_dependencies() {
  387. let dep1 = git::new("dep1", |project| {
  388. project
  389. .file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
  390. .file("src/lib.rs", "")
  391. });
  392. let dep2 = git::new("dep2", |project| {
  393. project
  394. .file("Cargo.toml", &basic_manifest("dep2", "0.5.0"))
  395. .file("src/lib.rs", "")
  396. });
  397. let p = project()
  398. .file(
  399. "Cargo.toml",
  400. &format!(
  401. r#"
  402. [package]
  403. name = "foo"
  404. authors = []
  405. version = "0.0.0"
  406. [dependencies]
  407. dep1 = {{ git = '{}' }}
  408. dep2 = {{ git = '{}' }}
  409. "#,
  410. dep1.url(),
  411. dep2.url()
  412. ),
  413. )
  414. .file("src/main.rs", "fn main() { }");
  415. let p = p.build();
  416. let n_concurrent_builds = 5;
  417. let (tx, rx) = channel();
  418. for _ in 0..n_concurrent_builds {
  419. let cmd = p
  420. .cargo("build")
  421. .build_command()
  422. .stdout(Stdio::piped())
  423. .stderr(Stdio::piped())
  424. .spawn();
  425. let tx = tx.clone();
  426. thread::spawn(move || {
  427. let result = cmd.unwrap().wait_with_output().unwrap();
  428. tx.send(result).unwrap()
  429. });
  430. }
  431. for _ in 0..n_concurrent_builds {
  432. let result = rx.recv_timeout(slow_cpu_multiplier(30)).expect("Deadlock!");
  433. execs().run_output(&result);
  434. }
  435. }