mirror of
https://github.com/rust-lang/cargo
synced 2024-11-05 18:50:39 +00:00
320c279f43
Benefits: - A TOML 1.0 compliant parser - Unblock future work - Have `cargo init` add the current crate to the workspace, rather than error - #5586: Upstream `cargo-add`
863 lines
28 KiB
Rust
863 lines
28 KiB
Rust
//! 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 toml_edit::easy as toml;
|
|
|
|
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<String> = 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<String> = 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<String> = 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 `--vers` 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();
|
|
}
|