//! Tests for feature selection on the command-line. use std::fmt::Write; use cargo_test_support::prelude::*; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::{basic_manifest, project, str}; use super::features2::switch_to_resolver_2; #[cargo_test] fn virtual_no_default_features() { // --no-default-features in root of virtual workspace. Package::new("dep1", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] dep1 = {version = "1.0", optional = true} [features] default = ["dep1"] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] default = ["f1"] f1 = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!{"expected f1 off"} "#, ) .build(); p.cargo("check --no-default-features") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check --features foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: foo, did you mean: f1? "#]]) .run(); p.cargo("check --features a/dep1,b/f1,b/f2,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: b/f2, f2, did you mean: f1? "#]]) .run(); p.cargo("check --features a/dep,b/f1,b/f2,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: a/dep, b/f2, f2, did you mean: a/dep1, f1? "#]]) .run(); p.cargo("check --features a/dep,a/dep1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: a/dep, did you mean: b/f1? "#]]) .run(); } #[cargo_test] fn virtual_typo_member_feature() { project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" resolver = "2" [features] deny-warnings = [] "#, ) .file("src/lib.rs", "") .build() .cargo("check --features a/deny-warning") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: a/deny-warning, did you mean: a/deny-warnings? "#]]) .run(); } #[cargo_test] fn virtual_features() { // --features in root of virtual workspace. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(not(feature = "f1"))] compile_error!{"f1 is missing"} "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "") .build(); p.cargo("check --features f1") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn virtual_with_specific() { // -p flags with --features in root of virtual. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = [] f2 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(not(feature = "f1"))] compile_error!{"f1 is missing"} #[cfg(not(feature = "f2"))] compile_error!{"f2 is missing"} "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] f2 = [] f3 = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(not(feature = "f2"))] compile_error!{"f2 is missing"} #[cfg(not(feature = "f3"))] compile_error!{"f3 is missing"} "#, ) .build(); p.cargo("check -p a -p b --features f1,f2,f3") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn other_member_from_current() { // -p for another member while in the current directory. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path="bar", features=["f3"] } [features] f1 = ["bar/f4"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [features] f1 = [] f2 = [] f3 = [] f4 = [] "#, ) .file("bar/src/lib.rs", "") .file( "bar/src/main.rs", r#" fn main() { if cfg!(feature = "f1") { print!("f1"); } if cfg!(feature = "f2") { print!("f2"); } if cfg!(feature = "f3") { print!("f3"); } if cfg!(feature = "f4") { print!("f4"); } println!(); } "#, ) .build(); // Old behavior. p.cargo("run -p bar --features f1") .with_stdout_data(str![[r#" f3f4 "#]]) .run(); p.cargo("run -p bar --features f1,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Package `foo v0.1.0 ([ROOT]/foo)` does not have the feature `f2` "#]]) .run(); p.cargo("run -p bar --features bar/f1") .with_stdout_data(str![[r#" f1f3 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("run -p bar --features f1") .with_stdout_data(str![[r#" f1 "#]]) .run(); p.cargo("run -p bar --features f1,f2") .with_stdout_data(str![[r#" f1f2 "#]]) .run(); p.cargo("run -p bar --features bar/f1") .with_stdout_data(str![[r#" f1 "#]]) .run(); } #[cargo_test] fn feature_default_resolver() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] test = [] "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(feature = "test") { println!("feature set"); } } "#, ) .build(); p.cargo("check --features testt") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Package `a v0.1.0 ([ROOT]/foo)` does not have the feature `testt` "#]]) .run(); p.cargo("run --features test") .with_status(0) .with_stdout_data(str![[r#" feature set "#]]) .run(); p.cargo("run --features a/test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have a dependency named `a` "#]]) .run(); } #[allow(deprecated)] #[cargo_test] fn virtual_member_slash() { // member slash feature syntax let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] b = {path="../b", optional=true} [features] default = ["f1"] f1 = [] f2 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!{"f1 is set"} #[cfg(feature = "f2")] compile_error!{"f2 is set"} #[cfg(feature = "b")] compile_error!{"b is set"} "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] bfeat = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(feature = "bfeat")] compile_error!{"bfeat is set"} "#, ) .build(); p.cargo("check -p a") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... "#]]) .with_stderr_does_not_contain("[..]f2 is set[..]") .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features a/f1") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... "#]]) .with_stderr_does_not_contain("[..]f2 is set[..]") .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features a/f2") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... [ERROR] f2 is set ... "#]]) .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features b/bfeat") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] bfeat is set ... "#]]) .run(); p.cargo("check -p a --no-default-features").run(); p.cargo("check -p a --no-default-features --features b") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] b is set ... "#]]) .run(); } #[cargo_test] fn non_member() { // -p for a non-member Package::new("dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] dep = "1.0" [features] f1 = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -p dep --features f1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep --all-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep v1.0.0 (registry `dummy-registry`) [CHECKING] dep v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn resolver1_member_features() { // --features member-name/feature-name with resolver="1" let p = project() .file( "Cargo.toml", r#" [workspace] members = ["member1", "member2"] "#, ) .file( "member1/Cargo.toml", r#" [package] name = "member1" version = "0.1.0" edition = "2015" [features] m1-feature = [] "#, ) .file( "member1/src/main.rs", r#" fn main() { if cfg!(feature = "m1-feature") { println!("m1-feature set"); } } "#, ) .file("member2/Cargo.toml", &basic_manifest("member2", "0.1.0")) .file("member2/src/lib.rs", "") .build(); p.cargo("run -p member1 --features member1/m1-feature") .cwd("member2") .with_stdout_data(str![[r#" m1-feature set "#]]) .run(); p.cargo("check -p member1 --features member1/m2-feature") .cwd("member2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Package `member1 v0.1.0 ([ROOT]/foo/member1)` does not have the feature `m2-feature` "#]]) .run(); } #[cargo_test] fn non_member_feature() { // --features for a non-member Package::new("jazz", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("jazz", "1.0").optional(true)) .publish(); let make_toml = |resolver, optional| { let mut s = String::new(); write!( s, r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "{}" [dependencies] "#, resolver ) .unwrap(); if optional { s.push_str(r#"bar = { version = "1.0", optional = true } "#); } else { s.push_str(r#"bar = "1.0""#) } s.push('\n'); s }; let p = project() .file("Cargo.toml", &make_toml("1", false)) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); ///////////////////////// V1 non-optional eprintln!("V1 non-optional"); p.cargo("check -p bar") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check --features bar/jazz") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] jazz v1.0.0 (registry `dummy-registry`) [CHECKING] jazz v1.0.0 [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check -p bar --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); ///////////////////////// V1 optional eprintln!("V1 optional"); p.change_file("Cargo.toml", &make_toml("1", true)); // This error isn't great, but is probably unlikely to be common in // practice, so I'm not going to put much effort into improving it. p.cargo("check -p bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `bar` did not match any packages Did you mean `foo`? "#]]) .run(); p.cargo("check -p bar --features bar -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check -p bar --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); ///////////////////////// V2 non-optional eprintln!("V2 non-optional"); p.change_file("Cargo.toml", &make_toml("2", false)); // TODO: This should not be allowed (future warning?) p.cargo("check --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p bar -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p bar --features bar/jazz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); ///////////////////////// V2 optional eprintln!("V2 optional"); p.change_file("Cargo.toml", &make_toml("2", true)); p.cargo("check -p bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `bar` did not match any packages Did you mean `foo`? "#]]) .run(); // New --features behavior does not look at cwd. p.cargo("check -p bar --features bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p bar --features bar/jazz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p bar --features foo/bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); }