diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index 46078c5a3b0..9d47091b29a 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -300,6 +300,12 @@ impl<'cfg> RegistryIndex<'cfg> { let summaries = summaries .iter() .filter(|&(summary, yanked)| { + // Note: This particular logic can cause problems with + // optional dependencies when offline. If at least 1 version + // of an optional dependency is downloaded, but that version + // does not satisfy the requirements, then resolution will + // fail. Unfortunately, whether or not something is optional + // is not known here. (online || load.is_crate_downloaded(summary.package_id())) && (!yanked || { log::debug!("{:?}", yanked_whitelist); diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs index 9a1933c13bd..7a0dcbf9b59 100644 --- a/src/cargo/sources/registry/remote.rs +++ b/src/cargo/sources/registry/remote.rs @@ -176,6 +176,18 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { fn update_index(&mut self) -> CargoResult<()> { if self.config.cli_unstable().offline { + if self.repo()?.is_empty()? { + // An empty repository is guaranteed to fail, since hitting + // this path means we need at least one crate. This is an + // attempt to provide a better error message other than "no + // matching package named …". + failure::bail!( + "unable to fetch {} in offline mode\n\ + Try running without the offline flag, or try running \ + `cargo fetch` within your project directory before going offline.", + self.source_id + ); + } return Ok(()); } if self.config.cli_unstable().no_index_update { diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index cb7d021254a..f6aabe544e2 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -956,261 +956,6 @@ Did you mean `a`?", .run(); } -#[test] -fn cargo_compile_path_with_offline() { - let p = project() - .file( - "Cargo.toml", - r#" - [package] - name = "foo" - version = "0.0.1" - authors = [] - - [dependencies.bar] - path = "bar" - "#, - ) - .file("src/lib.rs", "") - .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) - .file("bar/src/lib.rs", "") - .build(); - - p.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .run(); -} - -#[test] -fn cargo_compile_with_downloaded_dependency_with_offline() { - Package::new("present_dep", "1.2.3") - .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) - .file("src/lib.rs", "") - .publish(); - - { - // make package downloaded - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.1.0" - - [dependencies] - present_dep = "1.2.3" - "#, - ) - .file("src/lib.rs", "") - .build(); - p.cargo("build").run(); - } - - let p2 = project() - .at("bar") - .file( - "Cargo.toml", - r#" - [project] - name = "bar" - version = "0.1.0" - - [dependencies] - present_dep = "1.2.3" - "#, - ) - .file("src/lib.rs", "") - .build(); - - p2.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .with_stderr( - "\ -[COMPILING] present_dep v1.2.3 -[COMPILING] bar v0.1.0 ([..]) -[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", - ) - .run(); -} - -#[test] -fn cargo_compile_offline_not_try_update() { - let p = project() - .at("bar") - .file( - "Cargo.toml", - r#" - [project] - name = "bar" - version = "0.1.0" - - [dependencies] - not_cached_dep = "1.2.5" - "#, - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .with_status(101) - .with_stderr( - "\ -error: no matching package named `not_cached_dep` found -location searched: registry `[..]` -required by package `bar v0.1.0 ([..])` -As a reminder, you're using offline mode (-Z offline) \ -which can sometimes cause surprising resolution failures, \ -if this error is too confusing you may wish to retry \ -without the offline flag.", - ) - .run(); -} - -#[test] -fn compile_offline_without_maxvers_cached() { - Package::new("present_dep", "1.2.1").publish(); - Package::new("present_dep", "1.2.2").publish(); - - Package::new("present_dep", "1.2.3") - .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) - .file( - "src/lib.rs", - r#"pub fn get_version()->&'static str {"1.2.3"}"#, - ) - .publish(); - - Package::new("present_dep", "1.2.5") - .file("Cargo.toml", &basic_manifest("present_dep", "1.2.5")) - .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#) - .publish(); - - { - // make package cached - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.1.0" - - [dependencies] - present_dep = "=1.2.3" - "#, - ) - .file("src/lib.rs", "") - .build(); - p.cargo("build").run(); - } - - let p2 = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.1.0" - - [dependencies] - present_dep = "1.2" - "#, - ) - .file( - "src/main.rs", - "\ -extern crate present_dep; -fn main(){ - println!(\"{}\", present_dep::get_version()); -}", - ) - .build(); - - p2.cargo("run -Zoffline") - .masquerade_as_nightly_cargo() - .with_stderr( - "\ -[COMPILING] present_dep v1.2.3 -[COMPILING] foo v0.1.0 ([CWD]) -[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] - Running `[..]`", - ) - .with_stdout("1.2.3") - .run(); -} - -#[test] -fn offline_unused_target_dep() { - // -Z offline with a target dependency that is not used and not downloaded. - Package::new("unused_dep", "1.0.0").publish(); - Package::new("used_dep", "1.0.0").publish(); - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.1.0" - [dependencies] - used_dep = "1.0" - [target.'cfg(unused)'.dependencies] - unused_dep = "1.0" - "#, - ) - .file("src/lib.rs", "") - .build(); - // Do a build that downloads only what is necessary. - p.cargo("build") - .with_stderr_contains("[DOWNLOADED] used_dep [..]") - .with_stderr_does_not_contain("[DOWNLOADED] unused_dep [..]") - .run(); - p.cargo("clean").run(); - // Build offline, make sure it works. - p.cargo("build -Z offline") - .masquerade_as_nightly_cargo() - .run(); -} - -#[test] -fn offline_missing_optional() { - Package::new("opt_dep", "1.0.0").publish(); - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.1.0" - [dependencies] - opt_dep = { version = "1.0", optional = true } - "#, - ) - .file("src/lib.rs", "") - .build(); - // Do a build that downloads only what is necessary. - p.cargo("build") - .with_stderr_does_not_contain("[DOWNLOADED] opt_dep [..]") - .run(); - p.cargo("clean").run(); - // Build offline, make sure it works. - p.cargo("build -Z offline") - .masquerade_as_nightly_cargo() - .run(); - p.cargo("build -Z offline --features=opt_dep") - .masquerade_as_nightly_cargo() - .with_stderr( - "\ -[ERROR] failed to download `opt_dep v1.0.0` - -Caused by: - can't make HTTP request in the offline mode -", - ) - .with_status(101) - .run(); -} - #[test] fn incompatible_dependencies() { Package::new("bad", "0.1.0").publish(); @@ -1310,58 +1055,6 @@ failed to select a version for `bad` which could resolve this conflict", .run(); } -#[test] -fn compile_offline_while_transitive_dep_not_cached() { - let baz = Package::new("baz", "1.0.0"); - let baz_path = baz.archive_dst(); - baz.publish(); - - let mut content = Vec::new(); - - let mut file = File::open(baz_path.clone()).ok().unwrap(); - let _ok = file.read_to_end(&mut content).ok().unwrap(); - drop(file); - drop(File::create(baz_path.clone()).ok().unwrap()); - - Package::new("bar", "0.1.0").dep("baz", "1.0.0").publish(); - - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.0.1" - - [dependencies] - bar = "0.1.0" - "#, - ) - .file("src/main.rs", "fn main(){}") - .build(); - - // simulate download bar, but fail to download baz - p.cargo("build") - .with_status(101) - .with_stderr_contains("[..]failed to verify the checksum of `baz[..]") - .run(); - - drop(File::create(baz_path).ok().unwrap().write_all(&content)); - - p.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .with_status(101) - .with_stderr( - "\ -[ERROR] failed to download `baz v1.0.0` - -Caused by: - can't make HTTP request in the offline mode -", - ) - .run(); -} - #[test] fn compile_path_dep_then_change_version() { let p = project() diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 934af0b722e..62ed8cfbd0d 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -83,175 +83,6 @@ fn cargo_compile_simple_git_dep() { .run(); } -#[test] -fn cargo_compile_forbird_git_httpsrepo_offline() { - let p = project() - .file( - "Cargo.toml", - r#" - - [project] - name = "foo" - version = "0.5.0" - authors = ["chabapok@example.com"] - - [dependencies.dep1] - git = 'https://github.com/some_user/dep1.git' - "#, - ) - .file("src/main.rs", "") - .build(); - - p.cargo("build -Zoffline").masquerade_as_nightly_cargo().with_status(101). - with_stderr("\ -error: failed to load source for a dependency on `dep1` - -Caused by: - Unable to update https://github.com/some_user/dep1.git - -Caused by: - can't checkout from 'https://github.com/some_user/dep1.git': you are in the offline mode (-Z offline)").run(); -} - -#[test] -fn cargo_compile_offline_with_cached_git_dep() { - let git_project = git::new("dep1", |project| { - project - .file("Cargo.toml", &basic_lib_manifest("dep1")) - .file( - "src/lib.rs", - r#" - pub static COOL_STR:&str = "cached git repo rev1"; - "#, - ) - }) - .unwrap(); - - let repo = git2::Repository::open(&git_project.root()).unwrap(); - let rev1 = repo.revparse_single("HEAD").unwrap().id(); - - // Commit the changes and make sure we trigger a recompile - File::create(&git_project.root().join("src/lib.rs")) - .unwrap() - .write_all(br#"pub static COOL_STR:&str = "cached git repo rev2";"#) - .unwrap(); - git::add(&repo); - let rev2 = git::commit(&repo); - - { - // cache to registry rev1 and rev2 - let prj = project() - .at("cache_git_dep") - .file( - "Cargo.toml", - &format!( - r#" - [project] - name = "cache_git_dep" - version = "0.5.0" - - [dependencies.dep1] - git = '{}' - rev = "{}" - "#, - git_project.url(), - rev1 - ), - ) - .file("src/main.rs", "fn main(){}") - .build(); - prj.cargo("build").run(); - - File::create(&prj.root().join("Cargo.toml")) - .unwrap() - .write_all( - format!( - r#" - [project] - name = "cache_git_dep" - version = "0.5.0" - - [dependencies.dep1] - git = '{}' - rev = "{}" - "#, - git_project.url(), - rev2 - ) - .as_bytes(), - ) - .unwrap(); - prj.cargo("build").run(); - } - - let p = project() - .file( - "Cargo.toml", - &format!( - r#" - [project] - name = "foo" - version = "0.5.0" - - [dependencies.dep1] - git = '{}' - "#, - git_project.url() - ), - ) - .file( - "src/main.rs", - &main_file(r#""hello from {}", dep1::COOL_STR"#, &["dep1"]), - ) - .build(); - - let git_root = git_project.root(); - - p.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .with_stderr(format!( - "\ -[COMPILING] dep1 v0.5.0 ({}#[..]) -[COMPILING] foo v0.5.0 ([CWD]) -[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", - path2url(git_root), - )) - .run(); - - assert!(p.bin("foo").is_file()); - - p.process(&p.bin("foo")) - .with_stdout("hello from cached git repo rev2\n") - .run(); - - File::create(&p.root().join("Cargo.toml")) - .unwrap() - .write_all( - format!( - r#" - [project] - name = "foo" - version = "0.5.0" - - [dependencies.dep1] - git = '{}' - rev = "{}" - "#, - git_project.url(), - rev1 - ) - .as_bytes(), - ) - .unwrap(); - - p.cargo("build -Zoffline") - .masquerade_as_nightly_cargo() - .run(); - p.process(&p.bin("foo")) - .with_stdout("hello from cached git repo rev1\n") - .run(); -} - #[test] fn cargo_compile_git_dep_branch() { let project = project(); diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 74c767a150c..5dcf126ec82 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -56,6 +56,7 @@ mod metabuild; mod metadata; mod net_config; mod new; +mod offline; mod out_dir; mod overrides; mod package; diff --git a/tests/testsuite/offline.rs b/tests/testsuite/offline.rs new file mode 100644 index 00000000000..91653135d66 --- /dev/null +++ b/tests/testsuite/offline.rs @@ -0,0 +1,548 @@ +use crate::support::{basic_manifest, git, main_file, path2url, project, registry::Package}; +use std::fs; + +#[test] +fn offline_unused_target_dep() { + // -Z offline with a target dependency that is not used and not downloaded. + Package::new("unused_dep", "1.0.0").publish(); + Package::new("used_dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.1.0" + [dependencies] + used_dep = "1.0" + [target.'cfg(unused)'.dependencies] + unused_dep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + // Do a build that downloads only what is necessary. + p.cargo("build") + .with_stderr_contains("[DOWNLOADED] used_dep [..]") + .with_stderr_does_not_contain("[DOWNLOADED] unused_dep [..]") + .run(); + p.cargo("clean").run(); + // Build offline, make sure it works. + p.cargo("build -Z offline") + .masquerade_as_nightly_cargo() + .run(); +} + +#[test] +fn offline_missing_optional() { + Package::new("opt_dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.1.0" + [dependencies] + opt_dep = { version = "1.0", optional = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + // Do a build that downloads only what is necessary. + p.cargo("build") + .with_stderr_does_not_contain("[DOWNLOADED] opt_dep [..]") + .run(); + p.cargo("clean").run(); + // Build offline, make sure it works. + p.cargo("build -Z offline") + .masquerade_as_nightly_cargo() + .run(); + p.cargo("build -Z offline --features=opt_dep") + .masquerade_as_nightly_cargo() + .with_stderr( + "\ +[ERROR] failed to download `opt_dep v1.0.0` + +Caused by: + can't make HTTP request in the offline mode +", + ) + .with_status(101) + .run(); +} + +#[test] +fn cargo_compile_path_with_offline() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .run(); +} + +#[test] +fn cargo_compile_with_downloaded_dependency_with_offline() { + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) + .file("src/lib.rs", "") + .publish(); + + // make package downloaded + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + + let p2 = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [project] + name = "bar" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p2.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .with_stderr( + "\ +[COMPILING] present_dep v1.2.3 +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[test] +fn cargo_compile_offline_not_try_update() { + let p = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [project] + name = "bar" + version = "0.1.0" + + [dependencies] + not_cached_dep = "1.2.5" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to load source for a dependency on `not_cached_dep` + +Caused by: + Unable to update registry `https://github.com/rust-lang/crates.io-index` + +Caused by: + unable to fetch registry `https://github.com/rust-lang/crates.io-index` in offline mode +Try running without the offline flag, or try running `cargo fetch` within your \ +project directory before going offline. +", + ) + .run(); +} + +#[test] +fn compile_offline_without_maxvers_cached() { + Package::new("present_dep", "1.2.1").publish(); + Package::new("present_dep", "1.2.2").publish(); + + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) + .file( + "src/lib.rs", + r#"pub fn get_version()->&'static str {"1.2.3"}"#, + ) + .publish(); + + Package::new("present_dep", "1.2.5") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.5")) + .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#) + .publish(); + + // make package cached + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "=1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + + let p2 = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2" + "#, + ) + .file( + "src/main.rs", + "\ +extern crate present_dep; +fn main(){ + println!(\"{}\", present_dep::get_version()); +}", + ) + .build(); + + p2.cargo("run -Zoffline") + .masquerade_as_nightly_cargo() + .with_stderr( + "\ +[COMPILING] present_dep v1.2.3 +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + Running `[..]`", + ) + .with_stdout("1.2.3") + .run(); +} + +#[test] +fn cargo_compile_forbird_git_httpsrepo_offline() { + let p = project() + .file( + "Cargo.toml", + r#" + + [project] + name = "foo" + version = "0.5.0" + authors = ["chabapok@example.com"] + + [dependencies.dep1] + git = 'https://github.com/some_user/dep1.git' + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("build -Zoffline").masquerade_as_nightly_cargo().with_status(101). + with_stderr("\ +error: failed to load source for a dependency on `dep1` + +Caused by: + Unable to update https://github.com/some_user/dep1.git + +Caused by: + can't checkout from 'https://github.com/some_user/dep1.git': you are in the offline mode (-Z offline)").run(); +} + +#[test] +fn compile_offline_while_transitive_dep_not_cached() { + let baz = Package::new("baz", "1.0.0"); + let baz_path = baz.archive_dst(); + baz.publish(); + + let baz_content = fs::read(&baz_path).unwrap(); + // Truncate the file to simulate a download failure. + fs::write(&baz_path, &[]).unwrap(); + + Package::new("bar", "0.1.0").dep("baz", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + // simulate download bar, but fail to download baz + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..]failed to verify the checksum of `baz[..]") + .run(); + + // Restore the file contents. + fs::write(&baz_path, &baz_content).unwrap(); + + p.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to download `baz v1.0.0` + +Caused by: + can't make HTTP request in the offline mode +", + ) + .run(); +} + +#[test] +fn update_offline() { + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("update -Zoffline") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr("error: you can't update in the offline mode[..]") + .run(); +} + +#[test] +fn cargo_compile_offline_with_cached_git_dep() { + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub static COOL_STR:&str = "cached git repo rev1"; + "#, + ) + }) + .unwrap(); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let rev1 = repo.revparse_single("HEAD").unwrap().id(); + + // Commit the changes and make sure we trigger a recompile + git_project.change_file( + "src/lib.rs", + r#"pub static COOL_STR:&str = "cached git repo rev2";"#, + ); + git::add(&repo); + let rev2 = git::commit(&repo); + + // cache to registry rev1 and rev2 + let prj = project() + .at("cache_git_dep") + .file( + "Cargo.toml", + &format!( + r#" + [project] + name = "cache_git_dep" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev1 + ), + ) + .file("src/main.rs", "fn main(){}") + .build(); + prj.cargo("build").run(); + + prj.change_file( + "Cargo.toml", + &format!( + r#" + [project] + name = "cache_git_dep" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev2 + ), + ); + prj.cargo("build").run(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [project] + name = "foo" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""hello from {}", dep1::COOL_STR"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + p.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .with_stderr(format!( + "\ +[COMPILING] dep1 v0.5.0 ({}#[..]) +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + path2url(git_root), + )) + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")) + .with_stdout("hello from cached git repo rev2\n") + .run(); + + p.change_file( + "Cargo.toml", + &format!( + r#" + [project] + name = "foo" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev1 + ), + ); + + p.cargo("build -Zoffline") + .masquerade_as_nightly_cargo() + .run(); + p.process(&p.bin("foo")) + .with_stdout("hello from cached git repo rev1\n") + .run(); +} + +#[test] +fn offline_resolve_optional_fail() { + // Example where resolve fails offline. + // + // This happens if at least 1 version of an optional dependency is + // available, but none of them satisfy the requirements. The current logic + // that handles this is `RegistryIndex::query_inner`, and it doesn't know + // if the package being queried is an optional one. This is not ideal, it + // would be best if it just ignored optional (unselected) dependencies. + Package::new("dep", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = { version = "1.0", optional = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch").run(); + + // Change dep to 2.0. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = { version = "2.0", optional = true } + "#, + ); + + p.cargo("build -Z offline") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr("\ +[ERROR] failed to select a version for the requirement `dep = \"^2.0\"` + candidate versions found which didn't match: 1.0.0 + location searched: `[..]` index (which is replacing registry `https://github.com/rust-lang/crates.io-index`) +required by package `foo v0.1.0 ([..]/foo)` +perhaps a crate was updated and forgotten to be re-vendored? +As a reminder, you're using offline mode (-Z offline) which can sometimes cause \ +surprising resolution failures, if this error is too confusing you may wish to \ +retry without the offline flag. +") + .run(); +} diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 4330e1ddec0..c99f081092d 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -819,30 +819,6 @@ fn update_lockfile() { .run(); } -#[test] -fn update_offline() { - let p = project() - .file( - "Cargo.toml", - r#" - [project] - name = "foo" - version = "0.0.1" - authors = [] - - [dependencies] - bar = "*" - "#, - ) - .file("src/main.rs", "fn main() {}") - .build(); - p.cargo("update -Zoffline") - .masquerade_as_nightly_cargo() - .with_status(101) - .with_stderr("error: you can't update in the offline mode[..]") - .run(); -} - #[test] fn dev_dependency_not_used() { let p = project()