//! 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(); }