//! Tests for the `cargo update` command. use cargo_test_support::registry::Package; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; #[cargo_test] fn minor_update_two_places() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] log = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.change_file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] log = "0.1.1" "#, ); p.cargo("check").run(); } #[cargo_test] fn transitive_minor_update() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1.1").publish(); // Note that `serde` isn't actually updated here! The default behavior for // `update` right now is to as conservatively as possible attempt to satisfy // an update. In this case we previously locked the dependency graph to `log // 0.1.0`, but nothing on the command line says we're allowed to update // that. As a result the update of `serde` here shouldn't update to `serde // 0.1.1` as that would also force an update to `log 0.1.1`. // // Also note that this is probably counterintuitive and weird. We may wish // to change this one day. p.cargo("update serde") .with_stderr( "\ [UPDATING] `[..]` index ", ) .run(); } #[cargo_test] fn conservative() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1").publish(); p.cargo("update serde") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] serde v0.1.0 -> v0.1.1 ", ) .run(); } #[cargo_test] fn update_via_new_dep() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] log = "0.1" # foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] log = "0.1.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").env("CARGO_LOG", "cargo=trace").run(); } #[cargo_test] fn update_via_new_member() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [workspace] # members = [ "foo" ] [dependencies] log = "0.1" "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] log = "0.1.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn add_dep_deep_new_requirement() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] log = "0.1" # bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn everything_real_deep() { Package::new("log", "0.1.0").publish(); Package::new("foo", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] foo = "0.1" # bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn change_package_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "a-foo" version = "0.2.0-alpha" authors = [] [dependencies] bar = { path = "bar", version = "0.2.0-alpha" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0-alpha")) .file("bar/src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.2.0" dependencies = ["bar 0.2.0"] [[package]] name = "bar" version = "0.2.0" "#, ) .build(); p.cargo("check").run(); } #[cargo_test] fn update_precise() { Package::new("serde", "0.1.0").publish(); Package::new("serde", "0.2.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.2" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("serde", "0.2.0").publish(); p.cargo("update serde:0.2.1 --precise 0.2.0") .with_stderr( "\ [UPDATING] `[..]` index [DOWNGRADING] serde v0.2.1 -> v0.2.0 ", ) .run(); } #[cargo_test] fn update_precise_mismatched() { Package::new("serde", "1.2.0").publish(); Package::new("serde", "1.2.1").publish(); Package::new("serde", "1.6.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "~1.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); // `1.6.0` does not match `"~1.2"` p.cargo("update serde:1.2 --precise 1.6.0") .with_stderr( "\ [UPDATING] `[..]` index [ERROR] failed to select a version for the requirement `serde = \"~1.2\"` candidate versions found which didn't match: 1.6.0 location searched: `[..]` index (which is replacing registry `crates-io`) required by package `bar v0.0.1 ([..]/foo)` perhaps a crate was updated and forgotten to be re-vendored? ", ) .with_status(101) .run(); // `1.9.0` does not exist p.cargo("update serde:1.2 --precise 1.9.0") // This terrible error message has been the same for a long time. A fix is more than welcome! .with_stderr( "\ [UPDATING] `[..]` index [ERROR] no matching package named `serde` found location searched: registry `crates-io` required by package `bar v0.0.1 ([..]/foo)` ", ) .with_status(101) .run(); } #[cargo_test] fn update_precise_build_metadata() { Package::new("serde", "0.0.1+first").publish(); Package::new("serde", "0.0.1+second").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" [dependencies] serde = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("update serde --precise 0.0.1+first").run(); p.cargo("update serde --precise 0.0.1+second") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] serde v0.0.1+first -> v0.0.1+second ", ) .run(); // This is not considered "Downgrading". Build metadata are not assumed to // be ordered. p.cargo("update serde --precise 0.0.1+first") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] serde v0.0.1+second -> v0.0.1+first ", ) .run(); } #[cargo_test] fn update_precise_do_not_force_update_deps() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --precise 0.2.2") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] serde v0.2.1 -> v0.2.2 ", ) .run(); } #[cargo_test] fn update_recursive() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --recursive") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] log v0.1.0 -> v0.1.1 [UPDATING] serde v0.2.1 -> v0.2.2 ", ) .run(); } #[cargo_test] fn update_aggressive_alias_for_recursive() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --aggressive") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] log v0.1.0 -> v0.1.1 [UPDATING] serde v0.2.1 -> v0.2.2 ", ) .run(); } #[cargo_test] fn update_recursive_conflicts_with_precise() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --precise 0.2.2 --recursive") .with_status(1) .with_stderr( "\ error: the argument '--precise ' cannot be used with '--recursive' Usage: cargo[EXE] update --precise ]> For more information, try '--help'. ", ) .run(); } // cargo update should respect its arguments even without a lockfile. // See issue "Running cargo update without a Cargo.lock ignores arguments" // at . #[cargo_test] fn update_precise_first_run() { Package::new("serde", "0.1.0").publish(); Package::new("serde", "0.2.0").publish(); Package::new("serde", "0.2.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("update serde --precise 0.2.0") .with_stderr( "\ [UPDATING] `[..]` index [DOWNGRADING] serde v0.2.1 -> v0.2.0 ", ) .run(); // Assert `cargo metadata` shows serde 0.2.0 p.cargo("metadata") .with_json( r#"{ "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "serde", "optional": false, "registry": null, "rename": null, "req": "^0.2", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+file://[..]/foo#bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[..]/foo/Cargo.toml", "metadata": null, "publish": null, "name": "bar", "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "test": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[..]/foo/src/lib.rs" } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[..]/home/.cargo/registry/src/-[..]/serde-0.2.0/Cargo.toml", "metadata": null, "publish": null, "name": "serde", "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "serde", "src_path": "[..]/home/.cargo/registry/src/-[..]/serde-0.2.0/src/lib.rs", "test": true } ], "version": "0.2.0" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "serde", "pkg": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" } ], "features": [], "id": "path+file://[..]/foo#bar@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" } ], "root": "path+file://[..]/foo#bar@0.0.1" }, "target_directory": "[..]/foo/target", "version": 1, "workspace_members": [ "path+file://[..]/foo#bar@0.0.1" ], "workspace_default_members": [ "path+file://[..]/foo#bar@0.0.1" ], "workspace_root": "[..]/foo", "metadata": null }"#, ) .run(); p.cargo("update serde --precise 0.2.0") .with_stderr( "\ [UPDATING] `[..]` index ", ) .run(); } #[cargo_test] fn preserve_top_comment() { let p = project().file("src/lib.rs", "").build(); p.cargo("update").run(); let lockfile = p.read_lockfile(); assert!(lockfile.starts_with("# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n")); let mut lines = lockfile.lines().collect::>(); lines.insert(2, "# some other comment"); let mut lockfile = lines.join("\n"); lockfile.push('\n'); // .lines/.join loses the last newline println!("saving Cargo.lock contents:\n{}", lockfile); p.change_file("Cargo.lock", &lockfile); p.cargo("update").run(); let lockfile2 = p.read_lockfile(); println!("loaded Cargo.lock contents:\n{}", lockfile2); assert_eq!(lockfile, lockfile2); } #[cargo_test] fn dry_run_update() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); let old_lockfile = p.read_lockfile(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1").publish(); p.cargo("update serde --dry-run") .with_stderr( "\ [UPDATING] `[..]` index [UPDATING] serde v0.1.0 -> v0.1.1 [WARNING] not updating lockfile due to dry run ", ) .run(); let new_lockfile = p.read_lockfile(); assert_eq!(old_lockfile, new_lockfile) } #[cargo_test] fn workspace_only() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("generate-lockfile").run(); let lock1 = p.read_lockfile(); p.change_file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.2" "#, ); p.cargo("update --workspace").run(); let lock2 = p.read_lockfile(); assert_ne!(lock1, lock2); assert!(lock1.contains("0.0.1")); assert!(lock2.contains("0.0.2")); assert!(!lock1.contains("0.0.2")); assert!(!lock2.contains("0.0.1")); } #[cargo_test] fn precise_with_build_metadata() { // +foo syntax shouldn't be necessary with --precise Package::new("bar", "0.1.0+extra-stuff.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("bar", "0.1.1+extra-stuff.1").publish(); Package::new("bar", "0.1.2+extra-stuff.2").publish(); p.cargo("update bar --precise 0.1") .with_status(101) .with_stderr( "\ error: invalid version format for precise version `0.1` Caused by: unexpected end of input while parsing minor version number ", ) .run(); p.cargo("update bar --precise 0.1.1+does-not-match") .with_status(101) .with_stderr( "\ [UPDATING] [..] index error: no matching package named `bar` found location searched: registry `crates-io` required by package `foo v0.1.0 ([ROOT]/foo)` ", ) .run(); p.cargo("update bar --precise 0.1.1") .with_stderr( "\ [UPDATING] [..] index [UPDATING] bar v0.1.0+extra-stuff.0 -> v0.1.1+extra-stuff.1 ", ) .run(); Package::new("bar", "0.1.3").publish(); p.cargo("update bar --precise 0.1.3+foo") .with_status(101) .with_stderr( "\ [UPDATING] [..] index error: no matching package named `bar` found location searched: registry `crates-io` required by package `foo v0.1.0 ([ROOT]/foo)` ", ) .run(); p.cargo("update bar --precise 0.1.3") .with_stderr( "\ [UPDATING] [..] index [UPDATING] bar v0.1.1+extra-stuff.1 -> v0.1.3 ", ) .run(); } #[cargo_test] fn update_only_members_order_one() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "rootcrate", "subcrate", ] resolver = "2" [workspace.dependencies] # Internal crates subcrate = {{ version = "*", path = "./subcrate" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "rootcrate/Cargo.toml", r#" [package] name = "rootcrate" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] subcrate.workspace = true "#, ) .file("rootcrate/src/main.rs", "fn main() {}") .file( "subcrate/Cargo.toml", r#" [package] name = "subcrate" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("subcrate/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr(&format!( "[UPDATING] git repository `{}`\n", git_project.url(), )) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update -p rootcrate") .with_stderr(&format!( "\ [UPDATING] rootcrate v2.29.8 ([CWD]/rootcrate) -> v2.29.81 [UPDATING] subcrate v2.29.8 ([CWD]/subcrate) -> v2.29.81", )) .run(); } #[cargo_test] fn update_only_members_order_two() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "crate2", "crate1", ] resolver = "2" [workspace.dependencies] # Internal crates crate1 = {{ version = "*", path = "./crate1" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] crate1.workspace = true "#, ) .file("crate2/src/main.rs", "fn main() {}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("crate1/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr(&format!( "[UPDATING] git repository `{}`\n", git_project.url(), )) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update -p crate2") .with_stderr(&format!( "\ [UPDATING] crate1 v2.29.8 ([CWD]/crate1) -> v2.29.81 [UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81", )) .run(); } #[cargo_test] fn update_only_members_with_workspace() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "crate2", "crate1", ] resolver = "2" [workspace.dependencies] # Internal crates crate1 = {{ version = "*", path = "./crate1" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] crate1.workspace = true "#, ) .file("crate2/src/main.rs", "fn main() {}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("crate1/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr(&format!( "[UPDATING] git repository `{}`\n", git_project.url(), )) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update --workspace") .with_stderr( "\ [UPDATING] crate1 v2.29.8 ([CWD]/crate1) -> v2.29.81 [UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81", ) .run(); } #[cargo_test] fn update_precise_git_revisions() { let (git_project, git_repo) = git::new_repo("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("git")) .file("src/lib.rs", "") }); let tag_name = "Nazgûl"; git::tag(&git_repo, tag_name); let tag_commit_id = git_repo.head().unwrap().target().unwrap().to_string(); git_project.change_file("src/lib.rs", "fn f() {}"); git::add(&git_repo); let head_id = git::commit(&git_repo).to_string(); let short_id = &head_id[..8]; let url = git_project.url(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] git = {{ git = '{url}' }} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr(format!("[UPDATING] git repository `{url}`")) .run(); assert!(p.read_lockfile().contains(&head_id)); p.cargo("update git --precise") .arg(tag_name) .with_stderr(format!( "\ [UPDATING] git repository `{url}` [UPDATING] git v0.5.0 ([..]) -> #{}", &tag_commit_id[..8], )) .run(); assert!(p.read_lockfile().contains(&tag_commit_id)); assert!(!p.read_lockfile().contains(&head_id)); p.cargo("update git --precise") .arg(short_id) .with_stderr(format!( "\ [UPDATING] git repository `{url}` [UPDATING] git v0.5.0 ([..]) -> #{short_id}", )) .run(); assert!(p.read_lockfile().contains(&head_id)); assert!(!p.read_lockfile().contains(&tag_commit_id)); // updating back to tag still requires a git fetch, // as the ref may change over time. p.cargo("update git --precise") .arg(tag_name) .with_stderr(format!( "\ [UPDATING] git repository `{url}` [UPDATING] git v0.5.0 ([..]) -> #{}", &tag_commit_id[..8], )) .run(); assert!(p.read_lockfile().contains(&tag_commit_id)); assert!(!p.read_lockfile().contains(&head_id)); // Now make a tag looks like an oid. // It requires a git fetch, as the oid cannot be found in preexisting git db. let arbitrary_tag: String = std::iter::repeat('a').take(head_id.len()).collect(); git::tag(&git_repo, &arbitrary_tag); p.cargo("update git --precise") .arg(&arbitrary_tag) .with_stderr(format!( "\ [UPDATING] git repository `{url}` [UPDATING] git v0.5.0 ([..]) -> #{}", &head_id[..8], )) .run(); assert!(p.read_lockfile().contains(&head_id)); assert!(!p.read_lockfile().contains(&tag_commit_id)); } #[cargo_test] fn precise_yanked() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1").yanked(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Use non-yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.0\"")); p.cargo("update --precise 0.1.1 bar") .with_status(101) .with_stderr( "\ [UPDATING] `dummy-registry` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([CWD])` Caused by: failed to query replaced source registry `crates-io` Caused by: the `--precise ` flag is unstable[..] See [..] See [..] ", ) .run(); p.cargo("update --precise 0.1.1 bar") .masquerade_as_nightly_cargo(&["--precise "]) .arg("-Zunstable-options") .with_stderr( "\ [UPDATING] `dummy-registry` index [WARNING] selected package `bar@0.1.1` was yanked by the author [NOTE] if possible, try a compatible non-yanked version [UPDATING] bar v0.1.0 -> v0.1.1 ", ) .run(); // Use yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.1\"")); } #[cargo_test] fn precise_yanked_multiple_presence() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1").yanked(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] bar = "0.1" baz = { package = "bar", version = "0.1" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Use non-yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.0\"")); p.cargo("update --precise 0.1.1 bar") .masquerade_as_nightly_cargo(&["--precise "]) .arg("-Zunstable-options") .with_stderr( "\ [UPDATING] `dummy-registry` index [WARNING] selected package `bar@0.1.1` was yanked by the author [NOTE] if possible, try a compatible non-yanked version [UPDATING] bar v0.1.0 -> v0.1.1 ", ) .run(); // Use yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.1\"")); } #[cargo_test] fn report_behind() { Package::new("breaking", "0.1.0").publish(); Package::new("breaking", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] breaking = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("breaking", "0.1.1").publish(); p.cargo("update") .with_stderr( "\ [UPDATING] `dummy-registry` index [UPDATING] breaking v0.1.0 -> v0.1.1 (latest: v0.2.0) ", ) .run(); }