summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/package_features.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/package_features.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/package_features.rs704
1 files changed, 704 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/package_features.rs b/src/tools/cargo/tests/testsuite/package_features.rs
new file mode 100644
index 000000000..15f726be5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/package_features.rs
@@ -0,0 +1,704 @@
+//! Tests for feature selection on the command-line.
+
+use super::features2::switch_to_resolver_2;
+use cargo_test_support::registry::{Dependency, Package};
+use cargo_test_support::{basic_manifest, project};
+use std::fmt::Write;
+
+#[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"
+
+ [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"
+
+ [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_unordered(
+ "\
+[UPDATING] [..]
+[CHECKING] a v0.1.0 [..]
+[CHECKING] b v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+
+ p.cargo("check --features foo")
+ .with_status(101)
+ .with_stderr(
+ "[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("[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("[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("[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"
+ resolver = "2"
+
+ [features]
+ deny-warnings = []
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build()
+ .cargo("check --features a/deny-warning")
+ .with_status(101)
+ .with_stderr(
+ "[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"
+
+ [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_unordered(
+ "\
+[CHECKING] a [..]
+[CHECKING] b [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [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"
+
+ [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_unordered(
+ "\
+[CHECKING] a [..]
+[CHECKING] b [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [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"
+
+ [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("f3f4")
+ .run();
+
+ p.cargo("run -p bar --features f1,f2")
+ .with_status(101)
+ .with_stderr("[ERROR] Package `foo[..]` does not have the feature `f2`")
+ .run();
+
+ p.cargo("run -p bar --features bar/f1")
+ .with_stdout("f1f3")
+ .run();
+
+ // New behavior.
+ switch_to_resolver_2(&p);
+ p.cargo("run -p bar --features f1").with_stdout("f1").run();
+
+ p.cargo("run -p bar --features f1,f2")
+ .with_stdout("f1f2")
+ .run();
+
+ p.cargo("run -p bar --features bar/f1")
+ .with_stdout("f1")
+ .run();
+}
+
+#[cargo_test]
+fn feature_default_resolver() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+
+ [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("[ERROR] Package `a[..]` does not have the feature `testt`")
+ .run();
+
+ p.cargo("run --features test")
+ .with_status(0)
+ .with_stdout("feature set")
+ .run();
+
+ p.cargo("run --features a/test")
+ .with_status(101)
+ .with_stderr("[ERROR] package `a[..]` does not have a dependency named `a`")
+ .run();
+}
+
+#[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"
+
+ [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"
+
+ [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_contains("[..]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_contains("[..]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_contains("[..]f1 is set[..]")
+ .with_stderr_contains("[..]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_contains("[..]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_contains("[..]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"
+ 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("[ERROR] cannot specify features for packages outside of workspace")
+ .run();
+
+ p.cargo("check -p dep --all-features")
+ .with_status(101)
+ .with_stderr("[ERROR] cannot specify features for packages outside of workspace")
+ .run();
+
+ p.cargo("check -p dep --no-default-features")
+ .with_status(101)
+ .with_stderr("[ERROR] cannot specify features for packages outside of workspace")
+ .run();
+
+ p.cargo("check -p dep")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] [..]
+[DOWNLOADED] [..]
+[CHECKING] dep [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [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("m1-feature set")
+ .run();
+
+ p.cargo("check -p member1 --features member1/m2-feature")
+ .cwd("member2")
+ .with_status(101)
+ .with_stderr("[ERROR] Package `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"
+ 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(
+ "\
+[CHECKING] bar v1.0.0
+[FINISHED] [..]
+",
+ )
+ .run();
+ // TODO: This should not be allowed (future warning?)
+ p.cargo("check --features bar/jazz")
+ .with_stderr(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] jazz v1.0.0 [..]
+[CHECKING] jazz v1.0.0
+[CHECKING] bar v1.0.0
+[CHECKING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ // TODO: This should not be allowed (future warning?)
+ p.cargo("check -p bar --features bar/jazz -v")
+ .with_stderr(
+ "\
+[FRESH] jazz v1.0.0
+[FRESH] bar v1.0.0
+[FINISHED] [..]
+",
+ )
+ .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(
+ "\
+error: package ID specification `bar` did not match any packages
+
+<tab>Did you mean `foo`?
+",
+ )
+ .run();
+
+ p.cargo("check -p bar --features bar -v")
+ .with_stderr(
+ "\
+[FRESH] bar v1.0.0
+[FINISHED] [..]
+",
+ )
+ .run();
+
+ // TODO: This should not be allowed (future warning?)
+ p.cargo("check -p bar --features bar/jazz -v")
+ .with_stderr(
+ "\
+[FRESH] jazz v1.0.0
+[FRESH] bar v1.0.0
+[FINISHED] [..]
+",
+ )
+ .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(
+ "\
+[FRESH] jazz v1.0.0
+[FRESH] bar v1.0.0
+[FRESH] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("check -p bar -v")
+ .with_stderr(
+ "\
+[FRESH] bar v1.0.0
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("check -p bar --features bar/jazz")
+ .with_status(101)
+ .with_stderr("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(
+ "\
+error: package ID specification `bar` did not match any packages
+
+<tab>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("error: cannot specify features for packages outside of workspace")
+ .run();
+ p.cargo("check -p bar --features bar/jazz")
+ .with_status(101)
+ .with_stderr("error: cannot specify features for packages outside of workspace")
+ .run();
+ p.cargo("check -p bar --features foo/bar")
+ .with_status(101)
+ .with_stderr("error: cannot specify features for packages outside of workspace")
+ .run();
+}