//! Tests for supporting older versions of the Cargo.lock file format. use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, project}; #[cargo_test] fn oldest_lockfile_still_works() { let cargo_commands = vec!["build", "update"]; for cargo_command in cargo_commands { oldest_lockfile_still_works_with_command(cargo_command); } } fn oldest_lockfile_still_works_with_command(cargo_command: &str) { Package::new("bar", "0.1.0").publish(); let expected_lockfile = str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]]; let old_lockfile = r#" [root] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" "#; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", old_lockfile) .build(); p.cargo(cargo_command).run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, expected_lockfile); } #[cargo_test] fn frozen_flag_preserves_old_lockfile() { let cksum = Package::new("bar", "0.1.0").publish(); let old_lockfile = format!( r#"[root] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, cksum, ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &old_lockfile) .build(); p.cargo("check --locked").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &old_lockfile); } #[cargo_test] fn totally_wild_checksums_works() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum baz 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "checksum bar 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "#, ); let p = p.build(); p.cargo("check").run(); let lock = p.read_lockfile(); assert_e2e().eq( &lock, str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]], ); } #[cargo_test] fn wrong_checksum_is_an_error() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "#, ); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] checksum for `bar v0.1.0` changed between lock files this could be indicative of a few possible errors: * the lock file is corrupt * a replacement source in use (e.g., a mirror) returned a different checksum * the source itself may be corrupt in one way or another unable to verify that `bar v0.1.0` is the same as when the lockfile was generated "#]]) .run(); } // If the checksum is unlisted in the lock file (e.g., ) yet we can // calculate it (e.g., it's a registry dep), then we should in theory just fill // it in. #[cargo_test] fn unlisted_checksum_is_bad_if_we_calculate() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "" "#, ); let p = p.build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] checksum for `bar v0.1.0` was not previously calculated, but a checksum could now be calculated this could be indicative of a few possible situations: * the source `registry `crates-io`` did not previously support checksums, but was replaced with one that does * newer Cargo implementations know how to checksum this source, but this older implementation does not * the lock file is corrupt "#]]).run(); } // If the checksum is listed in the lock file yet we cannot calculate it (e.g., // Git dependencies as of today), then make sure we choke. #[cargo_test] fn listed_checksum_bad_if_we_cannot_compute() { let git = git::new("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .file( "Cargo.lock", &format!( r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (git+{0})" ] [[package]] name = "bar" version = "0.1.0" source = "git+{0}" [metadata] "checksum bar 0.1.0 (git+{0})" = "checksum" "#, git.url() ), ); let p = p.build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [ERROR] checksum for `bar v0.1.0 ([ROOTURL]/bar)` could not be calculated, but a checksum is listed in the existing lock file this could be indicative of a few possible situations: * the source `[ROOTURL]/bar` supports checksums, but was replaced with one that doesn't * the lock file is corrupt unable to verify that `bar v0.1.0 ([ROOTURL]/bar)` is the same as when the lockfile was generated "#]]).run(); } #[cargo_test] fn current_lockfile_format() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); let actual = p.read_lockfile(); let expected = str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]]; assert_e2e().eq(&actual, expected); } #[cargo_test] fn lockfile_without_root() { Package::new("bar", "0.1.0").publish(); let lockfile = r#" # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "#; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", lockfile); let p = p.build(); p.cargo("check").run(); let lock = p.read_lockfile(); assert_e2e().eq( &lock, str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]], ); } #[cargo_test] fn locked_correct_error() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("check --locked") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] the lock file [ROOT]/foo/Cargo.lock needs to be updated but --locked was passed to prevent this If you want to try to generate the lock file without accessing the network, remove the --locked flag and use --offline instead. "#]]) .run(); } #[cargo_test] fn v2_format_preserved() { let cksum = Package::new("bar", "0.1.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "#, cksum ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn v2_path_and_crates_io() { let cksum010 = Package::new("a", "0.1.0").publish(); let cksum020 = Package::new("a", "0.2.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "a" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "a" version = "0.2.0" [[package]] name = "a" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "a 0.1.0", "a 0.2.0", "a 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, cksum010, cksum020, ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = 'a' } b = { version = "0.1", package = 'a' } c = { version = "0.2", package = 'a' } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.2.0" edition = "2015" "#, ) .file("a/src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("fetch").run(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn v3_and_git() { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?branch=master#{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, head_id, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep1 = {{ git = '{}', branch = 'master' }} "#, git_project.url(), ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 3") .build(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn lock_from_the_future() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 10000000") .build(); p.cargo("fetch").with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `10000000` was found, but this version of Cargo does not understand this lock file, perhaps Cargo needs to be updated? "#]]).with_status(101).run(); } #[cargo_test] fn preserve_old_format_if_no_update_needed() { let cksum = Package::new("bar", "0.1.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, cksum ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("check --locked").run(); } #[cargo_test] fn same_name_version_different_sources() { let cksum = Package::new("foo", "0.1.0").publish(); let (git_project, repo) = git::new_repo("dep1", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); // Lockfile was generated with Rust 1.51 let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "foo" version = "0.1.0" dependencies = [ "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "foo 0.1.0 (git+{url})", ] [[package]] name = "foo" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{cksum}" [[package]] name = "foo" version = "0.1.0" source = "git+{url}#{sha}" "#, sha = head_id, url = git_project.url(), cksum = cksum ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] foo = "0.1.0" foo2 = {{ git = '{}', package = 'foo' }} "#, git_project.url(), ), ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("check").run(); assert_eq!(p.read_file("Cargo.lock"), lockfile); } #[cargo_test] fn bad_data_in_lockfile_error_meg() { Package::new("bar", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .file( "Cargo.lock", r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1b9346248cf3391ead604c4407258d327c28e37209f6d56127598165165dda" [[package]] name = "test" version = "0.0.0" dependencies = [ "bar", ]"#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` (locked to 0.1.0) candidate versions found which didn't match: 0.0.1 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `test v0.0.0 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); } #[cargo_test] fn next_version_is_always_unstable() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 5") .build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `5` was found, but this version of Cargo does not understand this lock file, perhaps Cargo needs to be updated? "#]]).run(); // On nightly, let the user know about the `-Z` flag. p.cargo("fetch") .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `5` requires `-Znext-lockfile-bump` "#]]) .run(); } #[cargo_test] fn v4_cannot_be_created_from_scratch() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); let lockfile = r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "foo" version = "0.0.1" "#; let lock = p.read_lockfile(); assert_e2e().eq(&lock, lockfile); } fn create_branch(repo: &git2::Repository, branch: &str, head_id: git2::Oid) { repo.branch(branch, &repo.find_commit(head_id).unwrap(), true) .unwrap(); } fn create_tag(repo: &git2::Repository, tag: &str, head_id: git2::Oid) { repo.tag( tag, &repo.find_object(head_id, None).unwrap(), &repo.signature().unwrap(), "make a new tag", false, ) .unwrap(); } fn v3_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let url = git_project.url(); let head_id = repo.head().unwrap().target().unwrap(); // Ref name with special characters let git_ref = "a-_+#$)"; f(&repo, git_ref, head_id); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?{ref_kind}={git_ref}#{head_id}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }} "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 3") .build(); p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 2 packages to latest compatible versions [ADDING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={git_ref}#[..]) [ADDING] foo v0.0.1 ([ROOT]/foo) [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={git_ref}#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); // v3 doesn't URL-encode URL parameters, but `url` crate does decode as it // was URL-encoded. Therefore Cargo thinks they are from different source // and clones the repository again. p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); } #[cargo_test] fn v3_and_git_url_encoded_branch() { v3_and_git_url_encoded("branch", create_branch); } #[cargo_test] fn v3_and_git_url_encoded_tag() { v3_and_git_url_encoded("tag", create_tag); } #[cargo_test] fn v3_and_git_url_encoded_rev() { v3_and_git_url_encoded("rev", create_tag); } fn v4_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let url = git_project.url(); let head_id = repo.head().unwrap().target().unwrap(); // Ref name with special characters let git_ref = "a-_+#$)"; let encoded_ref = "a-_%2B%23%24%29"; f(&repo, git_ref, head_id); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?{ref_kind}={encoded_ref}#{head_id}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }} "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 4") .build(); p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 2 packages to latest compatible versions [ADDING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={git_ref}#[..]) [ADDING] foo v0.0.1 ([ROOT]/foo) [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={git_ref}#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); // Unlike v3_and_git_url_encoded, v4 encodes URL parameters so no git // repository re-clone happen. p.cargo("check") .with_stderr_data( "\ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", ) .run(); } #[cargo_test] fn v4_and_git_url_encoded_branch() { v4_and_git_url_encoded("branch", create_branch); } #[cargo_test] fn v4_and_git_url_encoded_tag() { v4_and_git_url_encoded("tag", create_tag); } #[cargo_test] fn v4_and_git_url_encoded_rev() { v4_and_git_url_encoded("rev", create_tag) } #[cargo_test] fn with_msrv() { let cksum = Package::new("bar", "0.1.0").publish(); let v2_lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{cksum}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "# ); let v1_lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{cksum}" "# ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); let cases = [ // v1 is the default ("1.37", None, 1), ("1.37", Some(1), 1), ("1.37", Some(2), 2), ("1.37", Some(3), 3), ("1.37", Some(4), 4), // v2 introduced ("1.38", None, 1), // last version of v1 as the default ("1.40", None, 1), // v2 is the default ("1.41", None, 2), ("1.41", Some(1), 1), ("1.41", Some(2), 2), ("1.41", Some(3), 3), ("1.41", Some(4), 4), // v3 introduced ("1.47", None, 2), // last version of v2 as the default ("1.48", None, 2), // v3 is the default ("1.53", None, 3), ("1.53", Some(1), 1), ("1.53", Some(2), 2), ("1.53", Some(3), 3), ("1.53", Some(4), 4), // v4 introduced ("1.78", None, 3), ]; for (msrv, existing_lockfile, expected_version) in cases { // Clean previous lockfile. _ = std::fs::remove_file(p.root().join("Cargo.lock")); p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" rust-version = "{msrv}" [dependencies] bar = "0.1.0" "#, ), ); if let Some(existing_lockfile) = existing_lockfile { let existing_lockfile = match existing_lockfile { 1 => v1_lockfile.as_str().into(), 2 => v2_lockfile.as_str().into(), v => std::borrow::Cow::from(format!("version = {v}")), }; p.change_file("Cargo.lock", &existing_lockfile); } p.cargo("fetch").run(); let lock = p.read_lockfile(); let toml = lock.parse::().unwrap(); // get `version = ` from Cargo.lock let version_field = toml.get("version").and_then(|v| v.as_integer()); let actual_version = if let Some(ver) = version_field { ver } else if lock.find("\nchecksum = ").is_some() { 2 } else { 1 }; assert_eq!( expected_version, actual_version, "msrv: {msrv}, existing lockfile: {existing_lockfile:?}" ); } }