//! Tests for `lockfile-path` flag use std::fs; use snapbox::str; use cargo_test_support::compare::assert_e2e; use cargo_test_support::install::assert_has_installed_exe; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::{ basic_bin_manifest, cargo_process, cargo_test, paths, project, symlink_supported, ProjectBuilder, }; /////////////////////////////// //// Unstable feature tests start /////////////////////////////// #[cargo_test] fn must_have_unstable_options() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("--lockfile-path") .arg(lockfile_path) .with_stderr_data(str![[ r#"[ERROR] the `--lockfile-path` flag is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/14421 for more information about the `--lockfile-path` flag. "#]]) .with_status(101) .run(); } #[cargo_test] fn must_be_nightly() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_stderr_data(str![[ r#"[ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. "#]]) .with_status(101) .run(); } /////////////////////////////// //// Unstable feature tests end /////////////////////////////// #[cargo_test] fn basic_lockfile_created() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn basic_lockfile_read() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project().file(lockfile_path, VALID_LOCKFILE).build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn basic_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(p.root().join(lockfile_path).is_file()); } ////////////////////// ///// Symlink tests ////////////////////// #[cargo_test] fn symlink_in_path() { if !symlink_supported() { return; } let dst = "dst"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project().symlink_dir(dst, src).build(); fs::create_dir(p.root().join("dst")).unwrap(); assert!(p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path.as_str()) .run(); assert!(p.root().join(lockfile_path).is_file()); assert!(p.root().join(dst).join("Cargo.lock").is_file()); } #[cargo_test] fn symlink_lockfile() { if !symlink_supported() { return; } let lockfile_path = "dst/Cargo.lock"; let src = "somedir/link"; let lock_body = VALID_LOCKFILE; let p = make_project() .file(lockfile_path, lock_body) .symlink(lockfile_path, src) .build(); assert!(p.root().join(src).is_file()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); } #[cargo_test] fn broken_symlink() { if !symlink_supported() { return; } let invalid_dst = "invalid_path"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project().symlink_dir(invalid_dst, src).build(); assert!(!p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_status(101) .with_stderr_data(str![[ r#"[ERROR] failed to create directory `[ROOT]/foo/somedir/link` ... "# ]]) .run(); } #[cargo_test] fn loop_symlink() { if !symlink_supported() { return; } let loop_link = "loop"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project() .symlink_dir(loop_link, src) .symlink_dir(src, loop_link) .build(); assert!(!p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_status(101) .with_stderr_data(str![[ r#"[ERROR] failed to create directory `[ROOT]/foo/somedir/link` ... "# ]]) .run(); } ///////////////////////// //// Commands tests ///////////////////////// #[cargo_test] fn add_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; project() .at("bar") .file("Cargo.toml", LIB_TOML) .file("src/main.rs", "fn main() {}") .build(); let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("add") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--path") .arg("../bar") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn clean_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("clean") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--package") .arg("test_foo") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn fix_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("fix") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--package") .arg("test_foo") .arg("--allow-no-vcs") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn publish_lockfile_read() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project().file(lockfile_path, VALID_LOCKFILE).build(); let registry = RegistryBuilder::new().http_api().http_index().build(); p.cargo("publish") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .replace_crates_io(registry.index_url()) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn remove_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let manifest = r#" [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2015" [[bin]] name = "foo" [dependencies] test_bar = { version = "0.1.0", path = "../bar" } "#; project() .at("bar") .file("Cargo.toml", LIB_TOML) .file("src/main.rs", "fn main() {}") .build(); let p = project() .file("Cargo.toml", &manifest) .file("src/main.rs", "fn main() {}") .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("remove") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("test_bar") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn assert_respect_pinned_version_from_lockfile_path() { let lockfile_path = "mylockfile/Cargo.lock"; let p = project() .file( "Cargo.toml", r#"# [package] name = "test_foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2015" [[bin]] name = "test_foo" [dependencies] bar = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); let lockfile_original = fs::read_to_string(p.root().join(lockfile_path)).unwrap(); Package::new("bar", "0.1.1").publish(); p.cargo("package") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(p .root() .join("target/package/test_foo-0.5.0/Cargo.lock") .is_file()); let path = p.root().join("target/package/test_foo-0.5.0/Cargo.lock"); let contents = fs::read_to_string(path).unwrap(); assert_e2e().eq(contents, lockfile_original); } #[cargo_test] fn install_respects_lock_file_path() { // `cargo install` will imply --locked when lockfile path is provided Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1") .file("src/lib.rs", "not rust") .publish(); // Publish with lockfile containing bad version of `bar` (0.1.1) Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .publish(); cargo_process("install foo --locked") .with_stderr_data(str![[r#" ... [..]not rust[..] ... "#]]) .with_status(101) .run(); // Create lockfile with the good `bar` version (0.1.0) and use it for install project() .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .build(); cargo_process("install foo -Zunstable-options --lockfile-path foo/Cargo.lock") .masquerade_as_nightly_cargo(&["lockfile-path"]) .run(); assert!(paths::root().join("foo/Cargo.lock").is_file()); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_lock_file_path_must_present() { // `cargo install` will imply --locked when lockfile path is provided Package::new("bar", "0.1.0").publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .publish(); cargo_process("install foo -Zunstable-options --lockfile-path lockfile_dir/Cargo.lock") .masquerade_as_nightly_cargo(&["lockfile-path"]) .with_stderr_data(str![[r#" ... [ERROR] no Cargo.lock file found in the requested path [ROOT]/lockfile_dir/Cargo.lock ... "#]]) .with_status(101) .run(); } #[cargo_test] fn run_embed() { let lockfile_path = "mylockfile/Cargo.lock"; let invalid_lockfile = "Cargo.lock"; let p = project() .file("src/main.rs", "fn main() {}") .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("run") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("-Zscript") .arg("--lockfile-path") .arg(lockfile_path) .arg("--manifest-path") .arg("src/main.rs") .run(); assert!(p.root().join(lockfile_path).is_file()); p.cargo("run") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("-Zscript") .arg("--lockfile-path") .arg(invalid_lockfile) .arg("--manifest-path") .arg("src/main.rs") .with_status(101) .with_stderr_data(str![[ r#"[WARNING] `package.edition` is unspecified, defaulting to `2021` [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock ... "# ]]) .run(); } const VALID_LOCKFILE: &str = r#"# Test lockfile version = 4 [[package]] name = "test_foo" version = "0.5.0" "#; const LIB_TOML: &str = r#" [package] name = "test_bar" version = "0.1.0" edition = "2021" "#; fn make_project() -> ProjectBuilder { project() .file("Cargo.toml", &basic_bin_manifest("test_foo")) .file("src/main.rs", "fn main() {}") }