From 403f1e12f6469fd7ba5f2c776461a47963ea856d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Thu, 19 Jun 2025 22:40:54 +0200 Subject: [PATCH] tests: add future `package` feature unification tests --- tests/testsuite/feature_unification.rs | 1167 +++++++++++++++++++++++- 1 file changed, 1166 insertions(+), 1 deletion(-) diff --git a/tests/testsuite/feature_unification.rs b/tests/testsuite/feature_unification.rs index f5337336a..0b9c07bee 100644 --- a/tests/testsuite/feature_unification.rs +++ b/tests/testsuite/feature_unification.rs @@ -2,7 +2,13 @@ use crate::prelude::*; use crate::utils::cargo_process; -use cargo_test_support::{basic_manifest, project, str}; +use cargo_test_support::{ + basic_manifest, + compare::assert_e2e, + project, + registry::{Dependency, Package}, + str, +}; #[cargo_test] fn workspace_feature_unification() { @@ -107,6 +113,792 @@ fn workspace_feature_unification() { .run(); } +#[cargo_test] +fn package_feature_unification() { + Package::new("outside", "0.1.0") + .feature("a", &[]) + .feature("b", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .publish(); + + let p = project() + .file( + ".cargo/config.toml", + r#" + [resolver] + feature-unification = "selected" + "#, + ) + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["common", "a", "b"] + "#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.1.0" + edition = "2021" + + [features] + a = [] + b = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["a"] } + outside = { version = "0.1.0", features = ["a"] } + "#, + ) + .file("a/src/lib.rs", "pub use common::a;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["b"] } + outside = { version = "0.1.0", features = ["b"] } + "#, + ) + .file("b/src/lib.rs", "pub use common::b;") + .build(); + + p.cargo("check -p common") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[LOCKING] 1 package to latest compatible version +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + p.cargo("check -p a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data( + str![[r#" +[DOWNLOADING] crates ... +[DOWNLOADED] outside v0.1.0 (registry `dummy-registry`) +[CHECKING] outside v0.1.0 +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[CHECKING] a v0.1.0 ([ROOT]/foo/a) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) + .run(); + p.cargo("check -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data( + str![[r#" +[CHECKING] outside v0.1.0 +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[CHECKING] b v0.1.0 ([ROOT]/foo/b) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) + .run(); + p.cargo("check -p a -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); + p.cargo("check") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); + // Sanity check that compilation without package feature unification does not work + p.cargo("check -p a -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); +} + +#[cargo_test] +fn package_feature_unification_default_features() { + let p = project() + .file( + ".cargo/config.toml", + r#" + [resolver] + feature-unification = "selected" + "#, + ) + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["common", "a", "b"] + "#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.1.0" + edition = "2021" + + [features] + default = ["a"] + a = [] + b = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common" } + "#, + ) + .file("a/src/lib.rs", "pub use common::a;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["b"], default-features = false } + "#, + ) + .file("b/src/lib.rs", "pub use common::b;") + .build(); + + p.cargo("check -p common") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data(str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + p.cargo("check -p a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data(str![[r#" +[CHECKING] a v0.1.0 ([ROOT]/foo/a) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + p.cargo("check -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data(str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[CHECKING] b v0.1.0 ([ROOT]/foo/b) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + p.cargo("check") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data( + str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `common` (lib) due to 1 previous error + +"#]] + .unordered(), + ) + .run(); +} + +#[cargo_test] +fn package_feature_unification_cli_features() { + Package::new("outside", "0.1.0") + .feature("a", &[]) + .feature("b", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .publish(); + + let p = project() + .file( + ".cargo/config.toml", + r#" + [resolver] + feature-unification = "selected" + "#, + ) + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["common", "a", "b"] + "#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.1.0" + edition = "2021" + + [features] + a = [] + b = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common" } + outside = "0.1.0" + + [features] + a = ["common/a", "outside/a"] + "#, + ) + .file("a/src/lib.rs", "pub use common::a;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["b"] } + outside = "0.1.0" + + [features] + b = ["common/b", "outside/b"] + "#, + ) + .file("b/src/lib.rs", "pub use common::b;") + .build(); + + p.cargo("check -p a -p b -F a,b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data( + str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `common` (lib) due to 1 previous error +[UPDATING] `dummy-registry` index +[LOCKING] 1 package to latest compatible version +[DOWNLOADING] crates ... +[DOWNLOADED] outside v0.1.0 (registry `dummy-registry`) +[CHECKING] outside v0.1.0 +[ERROR] features were unified + --> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `outside` (lib) due to 1 previous error +[WARNING] build failed, waiting for other jobs to finish... + +"#]] + .unordered(), + ) + .run(); + p.cargo("check --workspace --exclude common -F a,b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data( + str![[r#" +[CHECKING] outside v0.1.0 +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] features were unified + --> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `outside` (lib) due to 1 previous error +[WARNING] build failed, waiting for other jobs to finish... +[ERROR] could not compile `common` (lib) due to 1 previous error + +"#]] + .unordered(), + ) + .run(); + + p.cargo("check -p a -p b -F a/a,b/b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data( + str![[r#" +[CHECKING] outside v0.1.0 +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] features were unified + --> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `common` (lib) due to 1 previous error +[WARNING] build failed, waiting for other jobs to finish... +[ERROR] could not compile `outside` (lib) due to 1 previous error + +"#]] + .unordered(), + ) + .run(); + p.cargo("check -p a -p b -F a,b,c") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] none of the selected packages contains this feature: c +selected packages: a, b + +"#]]) + .run(); + p.cargo("check -p a -F b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'a' does not contain this feature: b +[HELP] packages with the missing feature: common, b + +"#]]) + .run(); + p.cargo("check -p a -F a/a,common/b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); + + p.cargo("check -p a -F a/a,outside/b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); + + // Sanity check that compilation without package feature unification does not work + p.cargo("check -p a -p b -F a,b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); +} + +#[cargo_test] +fn package_feature_unification_weak_dependencies() { + let p = project() + .file( + ".cargo/config.toml", + r#" + [resolver] + feature-unification = "selected" + "#, + ) + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["common", "a", "b"] + "#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.1.0" + edition = "2021" + + [features] + a = [] + b = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", optional = true } + + [features] + default = ["dep:common", "common?/a"] + "#, + ) + .file("a/src/lib.rs", "pub use common::a;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", optional = true } + + [features] + default = ["dep:common", "common?/b"] + "#, + ) + .file("b/src/lib.rs", "pub use common::b;") + .build(); + + p.cargo("check -p a -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data( + str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `common` (lib) due to 1 previous error + +"#]] + .unordered(), + ) + .run(); + p.cargo("check") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(101) + .with_stderr_data(str![[r#" +[CHECKING] common v0.1.0 ([ROOT]/foo/common) +[ERROR] features were unified + --> common/src/lib.rs:3:17 + | +3 | compile_error!("features were unified"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +[ERROR] could not compile `common` (lib) due to 1 previous error + +"#]]) + .run(); + + // Sanity check that compilation without package feature unification does not work + p.cargo("check -p a -p b") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_status(101) + .with_stderr_contains("[ERROR] features were unified") + .run(); +} + +#[cargo_test] +fn feature_unification_cargo_tree() { + Package::new("outside", "0.1.0") + .feature("a", &[]) + .feature("b", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["common", "a", "b"] + "#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.1.0" + edition = "2021" + + [features] + a = [] + b = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + compile_error!("features were unified"); + #[cfg(feature = "a")] + pub fn a() {} + #[cfg(feature = "b")] + pub fn b() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["a"] } + outside = { version = "0.1.0", features = ["a"] } + "#, + ) + .file("a/src/lib.rs", "pub use common::a;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = { path = "../common", features = ["b"] } + outside = { version = "0.1.0", features = ["b"] } + "#, + ) + .file("b/src/lib.rs", "pub use common::b;") + .build(); + + p.cargo("tree -e features") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_stdout_data(str![[r#" +a v0.1.0 ([ROOT]/foo/a) +├── common feature "a" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) +│ └── common v0.1.0 ([ROOT]/foo/common) +├── outside feature "a" +│ └── outside v0.1.0 +└── outside feature "default" + └── outside v0.1.0 + +b v0.1.0 ([ROOT]/foo/b) +├── common feature "b" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) (*) +├── outside feature "b" +│ └── outside v0.1.0 +└── outside feature "default" (*) + +common v0.1.0 ([ROOT]/foo/common) + +"#]]) + .run(); + + p.cargo("tree -e features") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_stdout_data(str![[r#" +a v0.1.0 ([ROOT]/foo/a) +├── common feature "a" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) +│ └── common v0.1.0 ([ROOT]/foo/common) +├── outside feature "a" +│ └── outside v0.1.0 +└── outside feature "default" + └── outside v0.1.0 + +b v0.1.0 ([ROOT]/foo/b) +├── common feature "b" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) (*) +├── outside feature "b" +│ └── outside v0.1.0 +└── outside feature "default" (*) + +common v0.1.0 ([ROOT]/foo/common) + +"#]]) + .run(); + + p.cargo("tree -e features") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") // To be changed to package later + .with_stdout_data(str![[r#" +a v0.1.0 ([ROOT]/foo/a) +├── common feature "a" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) +│ └── common v0.1.0 ([ROOT]/foo/common) +├── outside feature "a" +│ └── outside v0.1.0 +└── outside feature "default" + └── outside v0.1.0 + +b v0.1.0 ([ROOT]/foo/b) +├── common feature "b" +│ └── common v0.1.0 ([ROOT]/foo/common) +├── common feature "default" (command-line) (*) +├── outside feature "b" +│ └── outside v0.1.0 +└── outside feature "default" (*) + +common v0.1.0 ([ROOT]/foo/common) + +"#]]) + .run(); +} + #[cargo_test] fn cargo_install_ignores_config() { let p = project() @@ -175,6 +967,20 @@ fn cargo_install_ignores_config() { [INSTALLED] package `a v0.1.0 ([ROOT]/foo)` (executable `a[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries +"#]]) + .run(); + cargo_process("install --path") + .arg(p.root()) + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_stderr_data(str![[r#" +[INSTALLING] a v0.1.0 ([ROOT]/foo) +[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s +[REPLACING] [ROOT]/home/.cargo/bin/a[EXE] +[REPLACED] package `a v0.1.0 ([ROOT]/foo)` with `a v0.1.0 ([ROOT]/foo)` (executable `a[EXE]`) +[WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries + "#]]) .run(); } @@ -204,3 +1010,362 @@ fn unstable_config_on_stable() { "#]]) .run(); } + +#[cargo_test] +fn cargo_fix_works() { + let p = project() + .file( + "Cargo.toml", + r#" +# Before project +[ project ] # After project header +# After project header line +name = "foo" +edition = "2021" +# After project table +"#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_stderr_data(str![[r#" +[MIGRATING] Cargo.toml from 2021 edition to 2024 +[FIXED] Cargo.toml (1 fix) +[CHECKING] foo v0.0.0 ([ROOT]/foo) +[MIGRATING] src/lib.rs from 2021 edition to 2024 +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + assert_e2e().eq( + p.read_file("Cargo.toml"), + str![[r#" + +# Before project +[ package ] # After project header +# After project header line +name = "foo" +edition = "2021" +# After project table + +"#]], + ); +} + +#[cargo_test] +fn edition_v2_resolver_report() { + // Show a report if the V2 resolver shows differences. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .feature("dev-feat", &[]) + .add_dep(Dependency::new("opt_dep", "1.0").optional(true)) + .publish(); + Package::new("opt_dep", "1.0.0").publish(); + + Package::new("bar", "1.0.0") + .add_dep( + Dependency::new("common", "1.0") + .target("cfg(whatever)") + .enable_features(&["f1"]), + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = "1.0" + bar = "1.0" + + [build-dependencies] + common = { version = "1.0", features = ["opt_dep"] } + + [dev-dependencies] + common = { version="1.0", features=["dev-feat"] } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + edition = "2018" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs --workspace") + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .with_status(0) + .with_stderr_data( + str![[r#" +[MIGRATING] Cargo.toml from 2018 edition to 2021 +[UPDATING] `dummy-registry` index +[LOCKING] 3 packages to latest compatible versions +[DOWNLOADING] crates ... +[DOWNLOADED] common v1.0.0 (registry `dummy-registry`) +[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) +[DOWNLOADED] opt_dep v1.0.0 (registry `dummy-registry`) +[MIGRATING] bar/Cargo.toml from 2018 edition to 2021 +[NOTE] Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo. +This may cause some dependencies to be built with fewer features enabled than previously. +More information about the resolver changes may be found at https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html +When building the following dependencies, the given features will no longer be used: + + common v1.0.0 removed features: dev-feat, f1, opt_dep + common v1.0.0 (as host dependency) removed features: dev-feat, f1 + +The following differences only apply when building with dev-dependencies: + + common v1.0.0 removed features: f1, opt_dep + +[CHECKING] opt_dep v1.0.0 +[CHECKING] bar v1.0.0 +[CHECKING] bar v0.1.0 ([ROOT]/foo/bar) +[MIGRATING] bar/src/lib.rs from 2018 edition to 2021 +[CHECKING] common v1.0.0 +[CHECKING] foo v0.1.0 ([ROOT]/foo) +[MIGRATING] src/lib.rs from 2018 edition to 2021 +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) + .run(); +} + +#[cargo_test] +fn feature_unification_of_cli_features_within_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["parent", "child", "grandchild"] + "#, + ) + .file( + "grandchild/Cargo.toml", + r#" + [package] + name = "grandchild" + version = "0.1.0" + edition = "2021" + + [features] + a = [] + "#, + ) + .file( + "grandchild/src/lib.rs", + r#" + #[cfg(feature = "a")] + pub fn a() {} + "#, + ) + .file( + "child/Cargo.toml", + r#" + [package] + name = "child" + version = "0.1.0" + edition = "2021" + + [dependencies] + grandchild = { path = "../grandchild" } + "#, + ) + .file("child/src/lib.rs", "pub use grandchild::*;") + .file( + "parent/Cargo.toml", + r#" + [package] + name = "parent" + version = "0.1.0" + edition = "2021" + + [dependencies] + child = { path = "../child" } + "#, + ) + .file("parent/src/lib.rs", "pub use child::a;") + .build(); + + p.cargo("check -p parent -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + // .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); + + p.cargo("check -p parent -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); + + p.cargo("check -p parent -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); + + p.cargo("check -p child -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + // .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") + .with_stderr_data(str![[r#" +[CHECKING] grandchild v0.1.0 ([ROOT]/foo/grandchild) +[CHECKING] child v0.1.0 ([ROOT]/foo/child) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -p child -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -p child -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + // .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") + .with_stderr_data(str![[r#" +[CHECKING] parent v0.1.0 ([ROOT]/foo/parent) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + // .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + // .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); + + p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") + .arg("-Zfeature-unification") + .masquerade_as_nightly_cargo(&["feature-unification"]) + .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] the package 'parent' does not contain this feature: grandchild/a + +"#]]) + .run(); +}