//! Tests for the `cargo update` command. use cargo_test_support::registry::Package; use cargo_test_support::{basic_manifest, 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 -p 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 -p 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 -p 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_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 -p 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_aggressive() { 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 -p 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 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 -p 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": "bar 0.0.1 (path+file://[..]/foo)", "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": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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": [ "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "serde", "pkg": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" } ], "features": [], "id": "bar 0.0.1 (path+file://[..]/foo)" }, { "dependencies": [], "deps": [], "features": [], "id": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" } ], "root": "bar 0.0.1 (path+file://[..]/foo)" }, "target_directory": "[..]/foo/target", "version": 1, "workspace_members": [ "bar 0.0.1 (path+file://[..]/foo)" ], "workspace_default_members": [ "bar 0.0.1 (path+file://[..]/foo)" ], "workspace_root": "[..]/foo", "metadata": null }"#, ) .run(); p.cargo("update -p 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 -p 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 -p 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 -p 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 -p 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 -p 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 -p bar --precise 0.1.3") .with_stderr( "\ [UPDATING] [..] index [UPDATING] bar v0.1.1+extra-stuff.1 -> v0.1.3 ", ) .run(); }