1 //! Tests for checking behavior of old cargos.
3 //! These tests are ignored because it is intended to be run on a developer
4 //! system with a bunch of toolchains installed. This requires `rustup` to be
5 //! installed. It will iterate over installed toolchains, and run some tests
6 //! over each one, producing a report at the end. As of this writing, I have
7 //! tested 1.0 to 1.51. Run this with:
10 //! cargo test --test testsuite -- old_cargos --nocapture --ignored
13 use cargo
::CargoResult
;
14 use cargo_test_support
::paths
::CargoPathExt
;
15 use cargo_test_support
::registry
::{self, Dependency, Package}
;
16 use cargo_test_support
::{cargo_exe, execs, paths, process, project, rustc_host}
;
17 use cargo_util
::{ProcessBuilder, ProcessError}
;
21 fn tc_process(cmd
: &str, toolchain
: &str) -> ProcessBuilder
{
22 let mut p
= if toolchain
== "this" {
29 let mut cmd
= process(cmd
);
30 cmd
.arg(format
!("+{}", toolchain
));
33 // Reset PATH since `process` modifies it to remove rustup.
34 p
.env("PATH", std
::env
::var_os("PATH").unwrap());
38 /// Returns a sorted list of all toolchains.
40 /// The returned value includes the parsed version, and the rustup toolchain
42 fn collect_all_toolchains() -> Vec
<(Version
, String
)> {
43 let rustc_version
= |tc
| {
44 let mut cmd
= tc_process("rustc", tc
);
46 let output
= cmd
.exec_with_output().expect("rustc installed");
47 let version
= std
::str::from_utf8(&output
.stdout
).unwrap();
48 let parts
: Vec
<_
> = version
.split_whitespace().collect();
49 assert_eq
!(parts
[0], "rustc");
50 assert
!(parts
[1].starts_with("1."));
51 Version
::parse(parts
[1]).expect("valid version")
54 // Provide a way to override the list.
55 if let Ok(tcs
) = std
::env
::var("OLD_CARGO") {
58 .map(|tc
| (rustc_version(tc
), tc
.to_string()))
62 let host
= rustc_host();
63 // I tend to have lots of toolchains installed, but I don't want to test
64 // all of them (like dated nightlies, or toolchains for non-host targets).
66 format
!("stable-{}", host
),
67 format
!("beta-{}", host
),
68 format
!("nightly-{}", host
),
71 let output
= ProcessBuilder
::new("rustup")
72 .args(&["toolchain", "list"])
74 .expect("rustup should be installed");
75 let stdout
= std
::str::from_utf8(&output
.stdout
).unwrap();
76 let mut toolchains
: Vec
<_
> = stdout
79 // Some lines say things like (default), just get the version.
80 line
.split_whitespace().next().expect("non-empty line")
84 && (line
.starts_with("1.") || valid_names
.iter().any(|name
| name
== line
))
86 .map(|line
| (rustc_version(line
), line
.to_string()))
89 // Also include *this* cargo.
90 toolchains
.push((rustc_version("this"), "this".to_string()));
91 toolchains
.sort_by(|a
, b
| a
.0.cmp(&b
.0));
95 // This is a test for exercising the behavior of older versions of cargo with
96 // the new feature syntax.
98 // The test involves a few dependencies with different feature requirements:
100 // * `bar` 1.0.0 is the base version that does not use the new syntax.
101 // * `bar` 1.0.1 has a feature with the new syntax, but the feature is unused.
102 // The optional dependency `new-baz-dep` should not be activated.
103 // * `bar` 1.0.2 has a dependency on `baz` that *requires* the new feature
108 if std
::process
::Command
::new("rustup").output().is_err() {
109 panic
!("old_cargos requires rustup to be installed");
111 Package
::new("new-baz-dep", "1.0.0").publish();
113 Package
::new("baz", "1.0.0").publish();
114 let baz101_cksum
= Package
::new("baz", "1.0.1")
115 .add_dep(Dependency
::new("new-baz-dep", "1.0").optional(true))
116 .feature("new-feat", &["dep:new-baz-dep"])
119 let bar100_cksum
= Package
::new("bar", "1.0.0")
120 .add_dep(Dependency
::new("baz", "1.0").optional(true))
121 .feature("feat", &["baz"])
123 let bar101_cksum
= Package
::new("bar", "1.0.1")
124 .add_dep(Dependency
::new("baz", "1.0").optional(true))
125 .feature("feat", &["dep:baz"])
127 let bar102_cksum
= Package
::new("bar", "1.0.2")
128 .add_dep(Dependency
::new("baz", "1.0").enable_features(&["new-feat"]))
143 .file("src/lib.rs", "")
146 let lock_bar_to
= |toolchain_version
: &Version
, bar_version
| {
147 let lock
= if toolchain_version
< &Version
::new(1, 12, 0) {
148 let url
= registry
::registry_url();
156 "bar 1.0.0 (registry+{url})",
162 source = "registry+{url}"
172 "bar 1.0.1 (registry+{url})",
178 source = "registry+{url}"
188 "bar 1.0.2 (registry+{url})",
194 source = "registry+{url}"
196 "baz 1.0.1 (registry+{url})",
202 source = "registry+{url}"
206 _
=> panic
!("unexpected version"),
216 "bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
222 source
= "registry+https://github.com/rust-lang/crates.io-index"
225 "checksum bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}"
235 "bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
241 source
= "registry+https://github.com/rust-lang/crates.io-index"
244 "checksum bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{}"
254 "bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
260 source
= "registry+https://github.com/rust-lang/crates.io-index"
262 "baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
268 source
= "registry+https://github.com/rust-lang/crates.io-index"
271 "checksum bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "{bar102_cksum}"
272 "checksum baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{baz101_cksum}"
274 bar102_cksum = bar102_cksum,
275 baz101_cksum = baz101_cksum
277 _ => panic!("unexpected version
"),
280 p.change_file("Cargo
.lock
", &lock);
283 let toolchains = collect_all_toolchains();
285 let config_path = paths::home().join(".cargo
/config
");
286 let lock_path = p.root().join("Cargo
.lock
");
288 struct ToolchainBehavior {
289 bar: Option<Version>,
290 baz: Option<Version>,
291 new_baz_dep: Option<Version>,
294 // Collect errors to print at the end. One entry per toolchain, a list of
296 let mut unexpected_results: Vec<Vec<String>> = Vec::new();
298 for (version, toolchain) in &toolchains {
299 let mut tc_result = Vec::new();
300 // Write a config appropriate for this version.
301 if version < &Version::new(1, 12, 0) {
309 registry::registry_url()
319 registry
= 'https
://wut' # only needed by 1.12
320 replace
-with
= 'dummy
-registry'
322 [source
.dummy
-registry
]
325 registry::registry_url()
331 // Fetches the version of a package in the lock file.
332 let pkg_version = |pkg| -> Option<Version> {
333 let output = tc_process("cargo
", toolchain)
334 .args(&["pkgid
", pkg])
338 let stdout = std::str::from_utf8(&output.stdout).unwrap();
343 .expect("version after colon
");
344 Some(Version::parse(version).expect("parseable version
"))
347 // Runs `cargo build` and returns the versions selected in the lock.
348 let run_cargo = || -> CargoResult<ToolchainBehavior> {
349 match tc_process("cargo
", toolchain)
350 .args(&["build
", "--verbose
"])
355 eprintln!("{} ok
", toolchain);
356 let bar = pkg_version("bar
");
357 let baz = pkg_version("baz
");
358 let new_baz_dep = pkg_version("new
-baz
-dep
");
359 Ok(ToolchainBehavior {
366 eprintln!("{} err {}
", toolchain, e);
372 macro_rules! check_lock {
373 ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, None) => {
374 check_lock!(= $tc_result, $pkg, $which, $actual, None);
376 ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => {
377 check_lock!(= $tc_result, $pkg, $which, $actual, Some(Version::parse($expected).unwrap()));
379 (= $tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => {
380 let exp: Option<Version> = $expected;
381 if $actual != $expected {
382 $tc_result.push(format!(
383 "{}
for {} saw {:?} but expected {:?}
",
384 $which, $pkg, $actual, exp
390 let check_err_contains = |tc_result: &mut Vec<_>, err: anyhow::Error, contents| {
391 if let Some(ProcessError {
392 stderr: Some(stderr),
394 }) = err.downcast_ref::<ProcessError>()
396 let stderr = std::str::from_utf8(stderr).unwrap();
397 if !stderr.contains(contents) {
398 tc_result.push(format!(
399 "{} expected to see error contents
:\n{}
\nbut saw
:\n{}
",
400 toolchain, contents, stderr
404 panic!("{} unexpected error {}
", toolchain, err);
408 // Unlocked behavior.
409 let which = "unlocked
";
411 p.build_dir().rm_rf();
414 // TODO: Switch to 51 after backport.
415 if version < &Version::new(1, 52, 0) && toolchain != "this
" {
416 check_lock!(tc_result, "bar
", which, behavior.bar, "1.0.2");
417 check_lock!(tc_result, "baz
", which, behavior.baz, "1.0.1");
418 check_lock!(tc_result, "new
-baz
-dep
", which, behavior.new_baz_dep, None);
420 check_lock!(tc_result, "bar
", which, behavior.bar, "1.0.0");
421 check_lock!(tc_result, "baz
", which, behavior.baz, None);
422 check_lock!(tc_result, "new
-baz
-dep
", which, behavior.new_baz_dep, None);
426 tc_result.push(format!("unlocked build failed
: {}
", e));
430 let which = "locked bar
1.0.0";
431 lock_bar_to(version, 100);
434 check_lock!(tc_result, "bar
", which, behavior.bar, "1.0.0");
435 check_lock!(tc_result, "baz
", which, behavior.baz, None);
436 check_lock!(tc_result, "new
-baz
-dep
", which, behavior.new_baz_dep, None);
439 tc_result.push(format!("bar
1.0.0 locked build failed
: {}
", e));
443 let which = "locked bar
1.0.1";
444 lock_bar_to(version, 101);
447 check_lock!(tc_result, "bar
", which, behavior.bar, "1.0.1");
448 check_lock!(tc_result, "baz
", which, behavior.baz, None);
449 check_lock!(tc_result, "new
-baz
-dep
", which, behavior.new_baz_dep, None);
452 if toolchain == "this
" {
453 // 1.0.1 can't be used without -Znamespaced-features
454 // It gets filtered out of the index.
455 check_err_contains(&mut tc_result, e,
456 "error
: failed to select a version
for the requirement `bar
= \"=1.0.1\"`
\n\
457 candidate versions found which didn't
match: 1.0.2, 1.0.0"
460 tc_result.push(format!("bar
1.0.1 locked build failed
: {}
", e));
465 let which = "locked bar
1.0.2";
466 lock_bar_to(version, 102);
469 check_lock!(tc_result, "bar
", which, behavior.bar, "1.0.2");
470 check_lock!(tc_result, "baz
", which, behavior.baz, "1.0.1");
471 check_lock!(tc_result, "new
-baz
-dep
", which, behavior.new_baz_dep, None);
474 if toolchain == "this
" {
475 // baz can't lock to 1.0.1, it requires -Znamespaced-features
476 check_err_contains(&mut tc_result, e,
477 "error
: failed to select a version
for the requirement `baz
= \"=1.0.1\"`
\n\
478 candidate versions found which didn't
match: 1.0.0"
481 tc_result.push(format!("bar
1.0.2 locked build failed
: {}
", e));
486 unexpected_results.push(tc_result);
489 // Generate a report.
490 let mut has_err = false;
491 for ((tc_vers, tc_name), errs) in toolchains.iter().zip(unexpected_results) {
495 eprintln!("error
: toolchain {}
(version {}
):", tc_name, tc_vers);
497 eprintln!(" {}
", err);
502 panic!("at least one toolchain did not run
as expected
");
508 fn index_cache_rebuild() {
509 // Checks that the index cache gets rebuilt.
511 // 1.48 will not cache entries with features with the same name as a
512 // dependency. If the cache does not get rebuilt, then running with
513 // `-Znamespaced-features` would prevent the new cargo from seeing those
514 // entries. The index cache version was changed to prevent this from
515 // happening, and switching between versions should work correctly
516 // (although it will thrash the cash, that's better than not working
518 Package::new("baz
", "1.0.0").publish();
519 Package::new("bar
", "1.0.0").publish();
520 Package::new("bar
", "1.0.1")
521 .add_dep(Dependency::new("baz
", "1.0").optional(true))
522 .feature("baz
", &["dep
:baz
"])
537 .file("src
/lib
.rs
", "")
540 // This version of Cargo errors on index entries that have overlapping
541 // feature names, so 1.0.1 will be missing.
543 .with_process_builder(tc_process("cargo
", "1.48.0"))
549 [DOWNLOADING
] crates
...
550 [DOWNLOADED
] bar v1
.0
.0 [..]
551 [CHECKING
] bar v1
.0
.0
552 [CHECKING
] foo v0
.1
.0 [..]
558 fs::remove_file(p.root().join("Cargo
.lock
")).unwrap();
560 // This should rebuild the cache and use 1.0.1.
565 [DOWNLOADING
] crates
...
566 [DOWNLOADED
] bar v1
.0
.1 [..]
567 [CHECKING
] bar v1
.0
.1
568 [CHECKING
] foo v0
.1
.0 [..]
574 fs::remove_file(p.root().join("Cargo
.lock
")).unwrap();
576 // Verify 1.48 can still resolve, and is at 1.0.0.
578 .with_process_builder(tc_process("cargo
", "1.48.0"))
592 fn avoids_split_debuginfo_collision() {
593 // Checks for a bug where .o files were being incorrectly shared between
594 // different toolchains using incremental and split-debuginfo on macOS.
604 split
-debuginfo
= "unpacked"
607 .file("src
/main
.rs
", "fn main() {}
")
611 .with_process_builder(tc_process("cargo
", "stable
"))
613 .env("CARGO_INCREMENTAL
", "1")
617 [COMPILING
] foo v0
.1
.0 [..]
624 .env("CARGO_INCREMENTAL
", "1")
627 [COMPILING
] foo v0
.1
.0 [..]
634 .with_process_builder(tc_process("cargo
", "stable
"))
636 .env("CARGO_INCREMENTAL
", "1")
640 [COMPILING
] foo v0
.1
.0 [..]