From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- src/tools/cargo/tests/testsuite/install_upgrade.rs | 862 +++++++++++++++++++++ 1 file changed, 862 insertions(+) create mode 100644 src/tools/cargo/tests/testsuite/install_upgrade.rs (limited to 'src/tools/cargo/tests/testsuite/install_upgrade.rs') diff --git a/src/tools/cargo/tests/testsuite/install_upgrade.rs b/src/tools/cargo/tests/testsuite/install_upgrade.rs new file mode 100644 index 000000000..ae641ba98 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/install_upgrade.rs @@ -0,0 +1,862 @@ +//! Tests for `cargo install` where it upgrades a package if it is out-of-date. + +use cargo::core::PackageId; +use std::collections::BTreeSet; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use cargo_test_support::install::{cargo_home, exe}; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{ + basic_manifest, cargo_process, cross_compile, execs, git, process, project, Execs, +}; + +fn pkg_maybe_yanked(name: &str, vers: &str, yanked: bool) { + Package::new(name, vers) + .yanked(yanked) + .file( + "src/main.rs", + r#"fn main() { println!("{}", env!("CARGO_PKG_VERSION")) }"#, + ) + .publish(); +} + +// Helper for publishing a package. +fn pkg(name: &str, vers: &str) { + pkg_maybe_yanked(name, vers, false) +} + +fn v1_path() -> PathBuf { + cargo_home().join(".crates.toml") +} + +fn v2_path() -> PathBuf { + cargo_home().join(".crates2.json") +} + +fn load_crates1() -> toml::Value { + toml::from_str(&fs::read_to_string(v1_path()).unwrap()).unwrap() +} + +fn load_crates2() -> serde_json::Value { + serde_json::from_str(&fs::read_to_string(v2_path()).unwrap()).unwrap() +} + +fn installed_exe(name: &str) -> PathBuf { + cargo_home().join("bin").join(exe(name)) +} + +/// Helper for executing binaries installed by cargo. +fn installed_process(name: &str) -> Execs { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + thread_local!(static UNIQUE_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst)); + + // This copies the executable to a unique name so that it may be safely + // replaced on Windows. See Project::rename_run for details. + let src = installed_exe(name); + let dst = installed_exe(&UNIQUE_ID.with(|my_id| format!("{}-{}", name, my_id))); + // Note: Cannot use copy. On Linux, file descriptors may be left open to + // the executable as other tests in other threads are constantly spawning + // new processes (see https://github.com/rust-lang/cargo/pull/5557 for + // more). + fs::rename(&src, &dst) + .unwrap_or_else(|e| panic!("Failed to rename `{:?}` to `{:?}`: {}", src, dst, e)); + // Leave behind a fake file so that reinstall duplicate check works. + fs::write(src, "").unwrap(); + let p = process(dst); + execs().with_process_builder(p) +} + +/// Check that the given package name/version has the following bins listed in +/// the trackers. Also verifies that both trackers are in sync and valid. +/// Pass in an empty `bins` list to assert that the package is *not* installed. +fn validate_trackers(name: &str, version: &str, bins: &[&str]) { + let v1 = load_crates1(); + let v1_table = v1.get("v1").unwrap().as_table().unwrap(); + let v2 = load_crates2(); + let v2_table = v2["installs"].as_object().unwrap(); + assert_eq!(v1_table.len(), v2_table.len()); + // Convert `bins` to a BTreeSet. + let bins: BTreeSet = bins + .iter() + .map(|b| format!("{}{}", b, env::consts::EXE_SUFFIX)) + .collect(); + // Check every entry matches between v1 and v2. + for (pkg_id_str, v1_bins) in v1_table { + let pkg_id: PackageId = toml::Value::from(pkg_id_str.to_string()) + .try_into() + .unwrap(); + let v1_bins: BTreeSet = v1_bins + .as_array() + .unwrap() + .iter() + .map(|b| b.as_str().unwrap().to_string()) + .collect(); + if pkg_id.name().as_str() == name && pkg_id.version().to_string() == version { + if bins.is_empty() { + panic!( + "Expected {} to not be installed, but found: {:?}", + name, v1_bins + ); + } else { + assert_eq!(bins, v1_bins); + } + } + let pkg_id_value = serde_json::to_value(&pkg_id).unwrap(); + let pkg_id_str = pkg_id_value.as_str().unwrap(); + let v2_info = v2_table + .get(pkg_id_str) + .expect("v2 missing v1 pkg") + .as_object() + .unwrap(); + let v2_bins = v2_info["bins"].as_array().unwrap(); + let v2_bins: BTreeSet = v2_bins + .iter() + .map(|b| b.as_str().unwrap().to_string()) + .collect(); + assert_eq!(v1_bins, v2_bins); + } +} + +#[cargo_test] +fn registry_upgrade() { + // Installing and upgrading from a registry. + pkg("foo", "1.0.0"); + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.0 (registry [..]) +[INSTALLING] foo v1.0.0 +[COMPILING] foo v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + installed_process("foo").with_stdout("1.0.0").run(); + validate_trackers("foo", "1.0.0", &["foo"]); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + + pkg("foo", "1.0.1"); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + + installed_process("foo").with_stdout("1.0.1").run(); + validate_trackers("foo", "1.0.1", &["foo"]); + + cargo_process("install foo --version=1.0.0") + .with_stderr_contains("[COMPILING] foo v1.0.0") + .run(); + installed_process("foo").with_stdout("1.0.0").run(); + validate_trackers("foo", "1.0.0", &["foo"]); + + cargo_process("install foo --version=^1.0") + .with_stderr_contains("[COMPILING] foo v1.0.1") + .run(); + installed_process("foo").with_stdout("1.0.1").run(); + validate_trackers("foo", "1.0.1", &["foo"]); + + cargo_process("install foo --version=^1.0") + .with_stderr_contains("[IGNORED] package `foo v1.0.1` is already installed[..]") + .run(); +} + +#[cargo_test] +fn uninstall() { + // Basic uninstall test. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("uninstall foo").run(); + let data = load_crates2(); + assert_eq!(data["installs"].as_object().unwrap().len(), 0); + let v1_table = load_crates1(); + assert_eq!(v1_table.get("v1").unwrap().as_table().unwrap().len(), 0); +} + +#[cargo_test] +fn upgrade_force() { + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo --force") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] foo v1.0.0 +[COMPILING] foo v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + validate_trackers("foo", "1.0.0", &["foo"]); +} + +#[cargo_test] +fn ambiguous_version_no_longer_allowed() { + // Non-semver-requirement is not allowed for `--version`. + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0") + .with_stderr( + "\ +[ERROR] the `--version` provided, `1.0`, is not a valid semver version: cannot parse '1.0' as a semver + +if you want to specify semver range, add an explicit qualifier, like ^1.0 +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn path_is_always_dirty() { + // --path should always reinstall. + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("install --path .").run(); + p.cargo("install --path .") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); +} + +#[cargo_test] +fn fails_for_conflicts_unknown() { + // If an untracked file is in the way, it should fail. + pkg("foo", "1.0.0"); + let exe = installed_exe("foo"); + exe.parent().unwrap().mkdir_p(); + fs::write(exe, "").unwrap(); + cargo_process("install foo") + .with_stderr_contains("[ERROR] binary `foo[EXE]` already exists in destination") + .with_status(101) + .run(); +} + +#[cargo_test] +fn fails_for_conflicts_known() { + // If the same binary exists in another package, it should fail. + pkg("foo", "1.0.0"); + Package::new("bar", "1.0.0") + .file("src/bin/foo.rs", "fn main() {}") + .publish(); + cargo_process("install foo").run(); + cargo_process("install bar") + .with_stderr_contains( + "[ERROR] binary `foo[EXE]` already exists in destination as part of `foo v1.0.0`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn supports_multiple_binary_names() { + // Can individually install with --bin or --example + Package::new("foo", "1.0.0") + .file("src/main.rs", r#"fn main() { println!("foo"); }"#) + .file("src/bin/a.rs", r#"fn main() { println!("a"); }"#) + .file("examples/ex1.rs", r#"fn main() { println!("ex1"); }"#) + .publish(); + cargo_process("install foo --bin foo").run(); + installed_process("foo").with_stdout("foo").run(); + assert!(!installed_exe("a").exists()); + assert!(!installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["foo"]); + cargo_process("install foo --bin a").run(); + installed_process("a").with_stdout("a").run(); + assert!(!installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["a", "foo"]); + cargo_process("install foo --example ex1").run(); + installed_process("ex1").with_stdout("ex1").run(); + validate_trackers("foo", "1.0.0", &["a", "ex1", "foo"]); + cargo_process("uninstall foo --bin foo").run(); + assert!(!installed_exe("foo").exists()); + assert!(installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["a", "ex1"]); + cargo_process("uninstall foo").run(); + assert!(!installed_exe("ex1").exists()); + assert!(!installed_exe("a").exists()); +} + +#[cargo_test] +fn v1_already_installed_fresh() { + // Install with v1, then try to install again with v2. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo") + .with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]") + .run(); +} + +#[cargo_test] +fn v1_already_installed_dirty() { + // Install with v1, then install a new version with v2. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + pkg("foo", "1.0.1"); + cargo_process("install foo") + .with_stderr_contains("[COMPILING] foo v1.0.1") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); + validate_trackers("foo", "1.0.1", &["foo"]); +} + +#[cargo_test] +fn change_features_rebuilds() { + Package::new("foo", "1.0.0") + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature = "f1") { + println!("f1"); + } + if cfg!(feature = "f2") { + println!("f2"); + } + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [features] + f1 = [] + f2 = [] + default = ["f1"] + "#, + ) + .publish(); + cargo_process("install foo").run(); + installed_process("foo").with_stdout("f1").run(); + cargo_process("install foo --no-default-features").run(); + installed_process("foo").with_stdout("").run(); + cargo_process("install foo --all-features").run(); + installed_process("foo").with_stdout("f1\nf2").run(); + cargo_process("install foo --no-default-features --features=f1").run(); + installed_process("foo").with_stdout("f1").run(); +} + +#[cargo_test] +fn change_profile_rebuilds() { + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo --debug") + .with_stderr_contains("[COMPILING] foo v1.0.0") + .with_stderr_contains("[REPLACING] [..]foo[EXE]") + .run(); + cargo_process("install foo --debug") + .with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]") + .run(); +} + +#[cargo_test] +fn change_target_rebuilds() { + if cross_compile::disabled() { + return; + } + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + let target = cross_compile::alternate(); + cargo_process("install foo -v --target") + .arg(&target) + .with_stderr_contains("[COMPILING] foo v1.0.0") + .with_stderr_contains("[REPLACING] [..]foo[EXE]") + .with_stderr_contains(&format!("[..]--target {}[..]", target)) + .run(); +} + +#[cargo_test] +fn change_bin_sets_rebuilds() { + // Changing which bins in a multi-bin project should reinstall. + Package::new("foo", "1.0.0") + .file("src/main.rs", "fn main() { }") + .file("src/bin/x.rs", "fn main() { }") + .file("src/bin/y.rs", "fn main() { }") + .publish(); + cargo_process("install foo --bin x").run(); + assert!(installed_exe("x").exists()); + assert!(!installed_exe("y").exists()); + assert!(!installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["x"]); + cargo_process("install foo --bin y") + .with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `y[EXE]`)") + .run(); + assert!(installed_exe("x").exists()); + assert!(installed_exe("y").exists()); + assert!(!installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["x", "y"]); + cargo_process("install foo") + .with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`)") + .with_stderr_contains( + "[REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executables `x[EXE]`, `y[EXE]`)", + ) + .run(); + assert!(installed_exe("x").exists()); + assert!(installed_exe("y").exists()); + assert!(installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["foo", "x", "y"]); +} + +#[cargo_test] +fn forwards_compatible() { + // Unknown fields should be preserved. + pkg("foo", "1.0.0"); + pkg("bar", "1.0.0"); + cargo_process("install foo").run(); + let key = "foo 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"; + let v2 = cargo_home().join(".crates2.json"); + let mut data = load_crates2(); + data["newfield"] = serde_json::Value::Bool(true); + data["installs"][key]["moreinfo"] = serde_json::Value::String("shazam".to_string()); + fs::write(&v2, serde_json::to_string(&data).unwrap()).unwrap(); + cargo_process("install bar").run(); + let data: serde_json::Value = serde_json::from_str(&fs::read_to_string(&v2).unwrap()).unwrap(); + assert_eq!(data["newfield"].as_bool().unwrap(), true); + assert_eq!( + data["installs"][key]["moreinfo"].as_str().unwrap(), + "shazam" + ); +} + +#[cargo_test] +fn v2_syncs() { + // V2 inherits the installs from V1. + pkg("one", "1.0.0"); + pkg("two", "1.0.0"); + pkg("three", "1.0.0"); + let p = project() + .file("src/bin/x.rs", "fn main() {}") + .file("src/bin/y.rs", "fn main() {}") + .build(); + cargo_process("install one").run(); + validate_trackers("one", "1.0.0", &["one"]); + p.cargo("install --path .").run(); + validate_trackers("foo", "1.0.0", &["x", "y"]); + // v1 add/remove + cargo_process("install two").run(); + cargo_process("uninstall one").run(); + // This should pick up that `two` was added, `one` was removed. + cargo_process("install three").run(); + validate_trackers("three", "1.0.0", &["three"]); + cargo_process("install --list") + .with_stdout( + "\ +foo v0.0.1 ([..]/foo): + x[EXE] + y[EXE] +three v1.0.0: + three[EXE] +two v1.0.0: + two[EXE] +", + ) + .run(); + cargo_process("install one").run(); + installed_process("one").with_stdout("1.0.0").run(); + validate_trackers("one", "1.0.0", &["one"]); + cargo_process("install two") + .with_stderr_contains("[IGNORED] package `two v1.0.0` is already installed[..]") + .run(); + // v1 remove + p.cargo("uninstall --bin x").run(); + pkg("x", "1.0.0"); + pkg("y", "1.0.0"); + // This should succeed because `x` was removed in V1. + cargo_process("install x").run(); + validate_trackers("x", "1.0.0", &["x"]); + // This should fail because `y` still exists in a different package. + cargo_process("install y") + .with_stderr_contains( + "[ERROR] binary `y[EXE]` already exists in destination \ + as part of `foo v0.0.1 ([..])`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn upgrade_git() { + let git_project = git::new("foo", |project| project.file("src/main.rs", "fn main() {}")); + // install + cargo_process("install --git") + .arg(git_project.url().to_string()) + .run(); + // Check install stays fresh. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains( + "[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \ + already installed,[..]", + ) + .run(); + // Modify a file. + let repo = git2::Repository::open(git_project.root()).unwrap(); + git_project.change_file("src/main.rs", r#"fn main() {println!("onomatopoeia");}"#); + git::add(&repo); + git::commit(&repo); + // Install should reinstall. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains("[COMPILING] foo v0.0.1 ([..])") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); + installed_process("foo").with_stdout("onomatopoeia").run(); + // Check install stays fresh. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains( + "[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \ + already installed,[..]", + ) + .run(); +} + +#[cargo_test] +fn switch_sources() { + // Installing what appears to be the same thing, but from different + // sources should reinstall. + registry::alt_init(); + pkg("foo", "1.0.0"); + Package::new("foo", "1.0.0") + .file("src/main.rs", r#"fn main() { println!("alt"); }"#) + .alternative(true) + .publish(); + let p = project() + .at("foo-local") // so it doesn't use the same directory as the git project + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", r#"fn main() { println!("local"); }"#) + .build(); + let git_project = git::new("foo", |project| { + project.file("src/main.rs", r#"fn main() { println!("git"); }"#) + }); + + cargo_process("install foo").run(); + installed_process("foo").with_stdout("1.0.0").run(); + cargo_process("install foo --registry alternative").run(); + installed_process("foo").with_stdout("alt").run(); + p.cargo("install --path .").run(); + installed_process("foo").with_stdout("local").run(); + cargo_process("install --git") + .arg(git_project.url().to_string()) + .run(); + installed_process("foo").with_stdout("git").run(); +} + +#[cargo_test] +fn multiple_report() { + // Testing the full output that indicates installed/ignored/replaced/summary. + pkg("one", "1.0.0"); + pkg("two", "1.0.0"); + fn three(vers: &str) { + Package::new("three", vers) + .file("src/main.rs", "fn main() { }") + .file("src/bin/x.rs", "fn main() { }") + .file("src/bin/y.rs", "fn main() { }") + .publish(); + } + three("1.0.0"); + cargo_process("install one two three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] one v1.0.0 (registry `[..]`) +[DOWNLOADING] crates ... +[DOWNLOADED] two v1.0.0 (registry `[..]`) +[DOWNLOADING] crates ... +[DOWNLOADED] three v1.0.0 (registry `[..]`) +[INSTALLING] one v1.0.0 +[COMPILING] one v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/one[EXE] +[INSTALLED] package `one v1.0.0` (executable `one[EXE]`) +[INSTALLING] two v1.0.0 +[COMPILING] two v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/two[EXE] +[INSTALLED] package `two v1.0.0` (executable `two[EXE]`) +[INSTALLING] three v1.0.0 +[COMPILING] three v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/three[EXE] +[INSTALLING] [..]/.cargo/bin/x[EXE] +[INSTALLING] [..]/.cargo/bin/y[EXE] +[INSTALLED] package `three v1.0.0` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) +[SUMMARY] Successfully installed one, two, three! +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + pkg("foo", "1.0.1"); + pkg("bar", "1.0.1"); + three("1.0.1"); + cargo_process("install one two three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `one v1.0.0` is already installed, use --force to override +[IGNORED] package `two v1.0.0` is already installed, use --force to override +[DOWNLOADING] crates ... +[DOWNLOADED] three v1.0.1 (registry `[..]`) +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [..]/.cargo/bin/three[EXE] +[REPLACING] [..]/.cargo/bin/x[EXE] +[REPLACING] [..]/.cargo/bin/y[EXE] +[REPLACED] package `three v1.0.0` with `three v1.0.1` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) +[SUMMARY] Successfully installed one, two, three! +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + cargo_process("uninstall three") + .with_stderr( + "\ +[REMOVING] [..]/.cargo/bin/three[EXE] +[REMOVING] [..]/.cargo/bin/x[EXE] +[REMOVING] [..]/.cargo/bin/y[EXE] +", + ) + .run(); + cargo_process("install three --bin x") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/x[EXE] +[INSTALLED] package `three v1.0.1` (executable `x[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + cargo_process("install three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/three[EXE] +[INSTALLING] [..]/.cargo/bin/y[EXE] +[REPLACING] [..]/.cargo/bin/x[EXE] +[INSTALLED] package `three v1.0.1` (executables `three[EXE]`, `y[EXE]`) +[REPLACED] package `three v1.0.1` with `three v1.0.1` (executable `x[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_track() { + pkg("foo", "1.0.0"); + cargo_process("install --no-track foo").run(); + assert!(!v1_path().exists()); + assert!(!v2_path().exists()); + cargo_process("install --no-track foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[ERROR] binary `foo[EXE]` already exists in destination `[..]/.cargo/bin/foo[EXE]` +Add --force to overwrite +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn deletes_orphaned() { + // When an executable is removed from a project, upgrading should remove it. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/bin/other.rs", "fn main() {}") + .file("examples/ex1.rs", "fn main() {}") + .build(); + p.cargo("install --path . --bins --examples").run(); + assert!(installed_exe("other").exists()); + + // Remove a binary, add a new one, and bump the version. + fs::remove_file(p.root().join("src/bin/other.rs")).unwrap(); + p.change_file("examples/ex2.rs", "fn main() {}"); + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.2.0" + "#, + ); + p.cargo("install --path . --bins --examples") + .with_stderr( + "\ +[INSTALLING] foo v0.2.0 [..] +[COMPILING] foo v0.2.0 [..] +[FINISHED] release [..] +[INSTALLING] [..]/.cargo/bin/ex2[EXE] +[REPLACING] [..]/.cargo/bin/ex1[EXE] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REMOVING] executable `[..]/.cargo/bin/other[EXE]` from previous version foo v0.1.0 [..] +[INSTALLED] package `foo v0.2.0 [..]` (executable `ex2[EXE]`) +[REPLACED] package `foo v0.1.0 [..]` with `foo v0.2.0 [..]` (executables `ex1[EXE]`, `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + assert!(!installed_exe("other").exists()); + validate_trackers("foo", "0.2.0", &["foo", "ex1", "ex2"]); + // 0.1.0 should not have any entries. + validate_trackers("foo", "0.1.0", &[]); +} + +#[cargo_test] +fn already_installed_exact_does_not_update() { + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0.0").run(); + cargo_process("install foo --version=1.0.0") + .with_stderr( + "\ +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + + cargo_process("install foo --version=>=1.0.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + pkg("foo", "1.0.1"); + cargo_process("install foo --version=>=1.0.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn already_installed_updates_yank_status_on_upgrade() { + pkg("foo", "1.0.0"); + pkg_maybe_yanked("foo", "1.0.1", true); + cargo_process("install foo --version=1.0.0").run(); + + cargo_process("install foo --version=1.0.1") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] cannot install package `foo`, it has been yanked from registry `crates-io` +", + ) + .run(); + + pkg_maybe_yanked("foo", "1.0.1", false); + + pkg("foo", "1.0.1"); + cargo_process("install foo --version=1.0.1") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn partially_already_installed_does_one_update() { + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0.0").run(); + pkg("bar", "1.0.0"); + pkg("baz", "1.0.0"); + cargo_process("install foo bar baz --version=1.0.0") + .with_stderr( + "\ +[IGNORED] package `foo v1.0.0` is already installed[..] +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 (registry [..]) +[DOWNLOADING] crates ... +[DOWNLOADED] baz v1.0.0 (registry [..]) +[INSTALLING] bar v1.0.0 +[COMPILING] bar v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE] +[INSTALLED] package `bar v1.0.0` (executable `bar[EXE]`) +[INSTALLING] baz v1.0.0 +[COMPILING] baz v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/baz[EXE] +[INSTALLED] package `baz v1.0.0` (executable `baz[EXE]`) +[SUMMARY] Successfully installed foo, bar, baz! +[WARNING] be sure to add [..] +", + ) + .run(); +} -- cgit v1.2.3