cargo/tests/features.rs

1239 lines
32 KiB
Rust
Raw Normal View History

#[macro_use]
extern crate cargotest;
extern crate hamcrest;
use std::fs::File;
use std::io::prelude::*;
use cargotest::support::paths::CargoPathExt;
use cargotest::support::{project, execs};
use hamcrest::assert_that;
#[test]
fn invalid1() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
bar = ["baz"]
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Feature `bar` includes `baz` which is neither a dependency nor another feature
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid2() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
bar = ["baz"]
[dependencies.bar]
path = "foo"
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Features and dependencies cannot have the same name: `bar`
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid3() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
bar = ["baz"]
[dependencies.baz]
path = "foo"
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Feature `bar` depends on `baz` which is not an optional dependency.
Consider adding `optional = true` to the dependency
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid4() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
features = ["bar"]
"#)
.file("src/main.rs", "")
.file("bar/Cargo.toml", r#"
[project]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] Package `bar v0.0.1 ([..])` does not have these features: `bar`
2016-05-12 17:06:36 +00:00
"));
p.change_file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
"#);
assert_that(p.cargo("build").arg("--features").arg("test"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `test`
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid5() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dev-dependencies.bar]
path = "bar"
optional = true
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Dev-dependencies are not allowed to be optional: `bar`
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid6() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = ["bar/baz"]
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build").arg("--features").arg("foo"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
2017-08-05 04:27:26 +00:00
Feature `foo` requires a feature of `bar` which is not a dependency
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid7() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = ["bar/baz"]
bar = []
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build").arg("--features").arg("foo"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] failed to parse manifest at `[..]`
Caused by:
2017-08-05 04:27:26 +00:00
Feature `foo` requires a feature of `bar` which is not a dependency
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn invalid8() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
features = ["foo/bar"]
"#)
.file("src/main.rs", "")
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "")
.build();
assert_that(p.cargo("build").arg("--features").arg("foo"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
Disallow specifying features of transitive deps Before this commit, it was possible to activate a feature in a transtive dependency, using a Cargo.toml like the following one: ... [features] # this will enable feature fast in package bar, which is a # dependency of foo default = [ foo/bar/fast ] This is a bug, and was never intended, and it is checked in other places already. The behavior was possible because `build_features::add_feature` treats the specification "foo/bar/fast" as just another feature. So when we require the feature "foo/bar/fast", add_feature for foo will generate a dependency on "foo" requiring that feature "bar/fast" is enabled. Then, when resolving foo, add_feature will find that "bar/fast" is a required feature, so it'll happily add "fast" as the required feature for the dependency "foo". The fix for this is to make sure that the `add_feature` function does not treat `a/b` specifications as just another feature. Instead, it now handles that case without recursion directly when it encounters it. We can see how this resolves the above problem: when resolving foo, add_feature for the required feature "bar/fast" will be called. Because add_feature no longer treats such specifciations differently at the top level, it will try to enable a feature with the exact name "bar/fast", and Context::resolve_features will later find that no such feature exists for package foo. To give a friendlier error message, we also check in Context::resolve_features that we never ever require a feature with a slash in a name from a dependency.
2016-07-01 20:36:05 +00:00
[ERROR] feature names may not contain slashes: `foo/bar`
"));
}
#[test]
fn invalid9() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
"#)
.file("src/main.rs", "fn main() {}")
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "")
.build();
assert_that(p.cargo("build").arg("--features").arg("bar"),
execs().with_status(0).with_stderr("\
warning: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with \
that name, but only optional dependencies can be used as features. [..]
Compiling bar v0.0.1 ([..])
Compiling foo v0.0.1 ([..])
Finished dev [unoptimized + debuginfo] target(s) in [..] secs
"));
}
#[test]
fn invalid10() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
features = ["baz"]
"#)
.file("src/main.rs", "fn main() {}")
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
[dependencies.baz]
path = "baz"
"#)
.file("bar/src/lib.rs", "")
.file("bar/baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("bar/baz/src/lib.rs", "")
.build();
assert_that(p.cargo("build"),
execs().with_status(0).with_stderr("\
warning: Package `bar v0.0.1 ([..])` does not have feature `baz`. It has a required dependency with \
that name, but only optional dependencies can be used as features. [..]
Compiling baz v0.0.1 ([..])
Compiling bar v0.0.1 ([..])
Compiling foo v0.0.1 ([..])
Finished dev [unoptimized + debuginfo] target(s) in [..] secs
"));
}
Disallow specifying features of transitive deps Before this commit, it was possible to activate a feature in a transtive dependency, using a Cargo.toml like the following one: ... [features] # this will enable feature fast in package bar, which is a # dependency of foo default = [ foo/bar/fast ] This is a bug, and was never intended, and it is checked in other places already. The behavior was possible because `build_features::add_feature` treats the specification "foo/bar/fast" as just another feature. So when we require the feature "foo/bar/fast", add_feature for foo will generate a dependency on "foo" requiring that feature "bar/fast" is enabled. Then, when resolving foo, add_feature will find that "bar/fast" is a required feature, so it'll happily add "fast" as the required feature for the dependency "foo". The fix for this is to make sure that the `add_feature` function does not treat `a/b` specifications as just another feature. Instead, it now handles that case without recursion directly when it encounters it. We can see how this resolves the above problem: when resolving foo, add_feature for the required feature "bar/fast" will be called. Because add_feature no longer treats such specifciations differently at the top level, it will try to enable a feature with the exact name "bar/fast", and Context::resolve_features will later find that no such feature exists for package foo. To give a friendlier error message, we also check in Context::resolve_features that we never ever require a feature with a slash in a name from a dependency.
2016-07-01 20:36:05 +00:00
#[test]
fn no_transitive_dep_feature_requirement() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.derived]
path = "derived"
[features]
default = ["derived/bar/qux"]
"#)
.file("src/main.rs", r#"
extern crate derived;
fn main() { derived::test(); }
"#)
.file("derived/Cargo.toml", r#"
[package]
name = "derived"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "../bar"
"#)
.file("derived/src/lib.rs", r#"
extern crate bar;
pub use bar::test;
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
[features]
qux = []
"#)
.file("bar/src/lib.rs", r#"
#[cfg(feature = "qux")]
pub fn test() { print!("test"); }
"#)
.build();
assert_that(p.cargo("build"),
Disallow specifying features of transitive deps Before this commit, it was possible to activate a feature in a transtive dependency, using a Cargo.toml like the following one: ... [features] # this will enable feature fast in package bar, which is a # dependency of foo default = [ foo/bar/fast ] This is a bug, and was never intended, and it is checked in other places already. The behavior was possible because `build_features::add_feature` treats the specification "foo/bar/fast" as just another feature. So when we require the feature "foo/bar/fast", add_feature for foo will generate a dependency on "foo" requiring that feature "bar/fast" is enabled. Then, when resolving foo, add_feature will find that "bar/fast" is a required feature, so it'll happily add "fast" as the required feature for the dependency "foo". The fix for this is to make sure that the `add_feature` function does not treat `a/b` specifications as just another feature. Instead, it now handles that case without recursion directly when it encounters it. We can see how this resolves the above problem: when resolving foo, add_feature for the required feature "bar/fast" will be called. Because add_feature no longer treats such specifciations differently at the top level, it will try to enable a feature with the exact name "bar/fast", and Context::resolve_features will later find that no such feature exists for package foo. To give a friendlier error message, we also check in Context::resolve_features that we never ever require a feature with a slash in a name from a dependency.
2016-07-01 20:36:05 +00:00
execs().with_status(101).with_stderr("\
[ERROR] feature names may not contain slashes: `bar/qux`
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn no_feature_doesnt_build() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
optional = true
"#)
.file("src/main.rs", r#"
#[cfg(feature = "bar")]
extern crate bar;
#[cfg(feature = "bar")]
fn main() { bar::bar(); println!("bar") }
#[cfg(not(feature = "bar"))]
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.build();
assert_that(p.cargo("build"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
assert_that(p.process(&p.bin("foo")),
execs().with_status(0).with_stdout(""));
assert_that(p.cargo("build").arg("--features").arg("bar"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] bar v0.0.1 ({dir}/bar)
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
assert_that(p.process(&p.bin("foo")),
execs().with_status(0).with_stdout("bar\n"));
}
#[test]
fn default_feature_pulled_in() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
default = ["bar"]
[dependencies.bar]
path = "bar"
optional = true
"#)
.file("src/main.rs", r#"
#[cfg(feature = "bar")]
extern crate bar;
#[cfg(feature = "bar")]
fn main() { bar::bar(); println!("bar") }
#[cfg(not(feature = "bar"))]
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.build();
assert_that(p.cargo("build"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] bar v0.0.1 ({dir}/bar)
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
assert_that(p.process(&p.bin("foo")),
execs().with_status(0).with_stdout("bar\n"));
assert_that(p.cargo("build").arg("--no-default-features"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
assert_that(p.process(&p.bin("foo")),
execs().with_status(0).with_stdout(""));
}
#[test]
fn cyclic_feature() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
default = ["default"]
"#)
.file("src/main.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-12 17:06:36 +00:00
execs().with_status(101).with_stderr("\
[ERROR] Cyclic feature dependency: feature `default` depends on itself
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn cyclic_feature2() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = ["bar"]
bar = ["foo"]
"#)
.file("src/main.rs", "fn main() {}")
.build();
assert_that(p.cargo("build"),
execs().with_status(0).with_stdout(""));
}
#[test]
fn groups_on_groups_on_groups() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
default = ["f1"]
f1 = ["f2", "bar"]
f2 = ["f3", "f4"]
f3 = ["f5", "f6", "baz"]
f4 = ["f5", "f7"]
f5 = ["f6"]
f6 = ["f7"]
f7 = ["bar"]
[dependencies.bar]
path = "bar"
optional = true
[dependencies.baz]
path = "baz"
optional = true
"#)
.file("src/main.rs", r#"
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bar;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate baz;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
assert_that(p.cargo("build"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
}
#[test]
fn many_cli_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
optional = true
[dependencies.baz]
path = "baz"
optional = true
"#)
.file("src/main.rs", r#"
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bar;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate baz;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
assert_that(p.cargo("build").arg("--features").arg("bar baz"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
}
#[test]
fn union_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.d1]
path = "d1"
features = ["f1"]
[dependencies.d2]
path = "d2"
features = ["f2"]
"#)
.file("src/main.rs", r#"
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate d1;
extern crate d2;
fn main() {
d2::f1();
d2::f2();
}
"#)
.file("d1/Cargo.toml", r#"
[package]
name = "d1"
version = "0.0.1"
authors = []
[features]
f1 = ["d2"]
[dependencies.d2]
path = "../d2"
features = ["f1"]
optional = true
"#)
.file("d1/src/lib.rs", "")
.file("d2/Cargo.toml", r#"
[package]
name = "d2"
version = "0.0.1"
authors = []
[features]
f1 = []
f2 = []
"#)
.file("d2/src/lib.rs", r#"
#[cfg(feature = "f1")] pub fn f1() {}
#[cfg(feature = "f2")] pub fn f2() {}
"#)
.build();
assert_that(p.cargo("build"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] d2 v0.0.1 ({dir}/d2)
[COMPILING] d1 v0.0.1 ({dir}/d1)
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
}
#[test]
fn many_features_no_rebuilds() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "b"
version = "0.1.0"
authors = []
[dependencies.a]
path = "a"
features = ["fall"]
"#)
.file("src/main.rs", "fn main() {}")
.file("a/Cargo.toml", r#"
[package]
name = "a"
version = "0.1.0"
authors = []
[features]
ftest = []
ftest2 = []
fall = ["ftest", "ftest2"]
"#)
.file("a/src/lib.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-15 21:17:56 +00:00
execs().with_status(0).with_stderr(format!("\
[COMPILING] a v0.1.0 ({dir}/a)
[COMPILING] b v0.1.0 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
p.root().move_into_the_past();
assert_that(p.cargo("build").arg("-v"),
2016-05-14 21:15:22 +00:00
execs().with_status(0).with_stderr("\
[FRESH] a v0.1.0 ([..]/a)
[FRESH] b v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
2016-05-12 17:06:36 +00:00
"));
}
// Tests that all cmd lines work with `--features ""`
#[test]
fn empty_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
"#)
.file("src/main.rs", "fn main() {}")
.build();
assert_that(p.cargo("build").arg("--features").arg(""),
execs().with_status(0));
}
// Tests that all cmd lines work with `--features ""`
#[test]
fn transitive_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = ["bar/baz"]
[dependencies.bar]
path = "bar"
"#)
.file("src/main.rs", "
extern crate bar;
fn main() { bar::baz(); }
")
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
[features]
baz = []
"#)
.file("bar/src/lib.rs", r#"
#[cfg(feature = "baz")]
pub fn baz() {}
"#)
.build();
assert_that(p.cargo("build").arg("--features").arg("foo"),
execs().with_status(0));
}
#[test]
fn everything_in_the_lockfile() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
f1 = ["d1/f1"]
f2 = ["d2"]
[dependencies.d1]
path = "d1"
[dependencies.d2]
path = "d2"
optional = true
[dependencies.d3]
path = "d3"
optional = true
"#)
.file("src/main.rs", "fn main() {}")
.file("d1/Cargo.toml", r#"
[package]
name = "d1"
version = "0.0.1"
authors = []
[features]
f1 = []
"#)
.file("d1/src/lib.rs", "")
.file("d2/Cargo.toml", r#"
[package]
name = "d2"
version = "0.0.2"
authors = []
"#)
.file("d2/src/lib.rs", "")
.file("d3/Cargo.toml", r#"
[package]
name = "d3"
version = "0.0.3"
authors = []
[features]
f3 = []
"#)
.file("d3/src/lib.rs", "")
.build();
assert_that(p.cargo("fetch"), execs().with_status(0));
let loc = p.root().join("Cargo.lock");
let mut lockfile = String::new();
t!(t!(File::open(&loc)).read_to_string(&mut lockfile));
assert!(lockfile.contains(r#"name = "d1""#), "d1 not found\n{}", lockfile);
assert!(lockfile.contains(r#"name = "d2""#), "d2 not found\n{}", lockfile);
assert!(lockfile.contains(r#"name = "d3""#), "d3 not found\n{}", lockfile);
}
#[test]
fn no_rebuild_when_frobbing_default_feature() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
[dependencies]
a = { path = "a" }
b = { path = "b" }
"#)
.file("src/lib.rs", "")
.file("b/Cargo.toml", r#"
[package]
name = "b"
version = "0.1.0"
authors = []
[dependencies]
a = { path = "../a", features = ["f1"], default-features = false }
"#)
.file("b/src/lib.rs", "")
.file("a/Cargo.toml", r#"
[package]
name = "a"
version = "0.1.0"
authors = []
[features]
default = ["f1"]
f1 = []
"#)
.file("a/src/lib.rs", "")
.build();
assert_that(p.cargo("build"), execs().with_status(0));
assert_that(p.cargo("build"), execs().with_status(0).with_stdout(""));
assert_that(p.cargo("build"), execs().with_status(0).with_stdout(""));
}
#[test]
fn unions_work_with_no_default_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
[dependencies]
a = { path = "a" }
b = { path = "b" }
"#)
.file("src/lib.rs", r#"
extern crate a;
pub fn foo() { a::a(); }
"#)
.file("b/Cargo.toml", r#"
[package]
name = "b"
version = "0.1.0"
authors = []
[dependencies]
a = { path = "../a", features = [], default-features = false }
"#)
.file("b/src/lib.rs", "")
.file("a/Cargo.toml", r#"
[package]
name = "a"
version = "0.1.0"
authors = []
[features]
default = ["f1"]
f1 = []
"#)
.file("a/src/lib.rs", r#"
#[cfg(feature = "f1")]
pub fn a() {}
"#)
.build();
assert_that(p.cargo("build"), execs().with_status(0));
assert_that(p.cargo("build"), execs().with_status(0).with_stdout(""));
assert_that(p.cargo("build"), execs().with_status(0).with_stdout(""));
}
#[test]
fn optional_and_dev_dep() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "test"
version = "0.1.0"
authors = []
[dependencies]
foo = { path = "foo", optional = true }
[dev-dependencies]
foo = { path = "foo" }
"#)
.file("src/lib.rs", "")
.file("foo/Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
"#)
.file("foo/src/lib.rs", "")
.build();
assert_that(p.cargo("build"),
2016-05-14 21:15:22 +00:00
execs().with_status(0).with_stderr("\
[COMPILING] test v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
2016-05-12 17:06:36 +00:00
"));
}
#[test]
fn activating_feature_activates_dep() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "test"
version = "0.1.0"
authors = []
[dependencies]
foo = { path = "foo", optional = true }
[features]
a = ["foo/a"]
"#)
.file("src/lib.rs", "
extern crate foo;
pub fn bar() {
foo::bar();
}
")
.file("foo/Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
[features]
a = []
"#)
.file("foo/src/lib.rs", r#"
#[cfg(feature = "a")]
pub fn bar() {}
"#)
.build();
assert_that(p.cargo("build").arg("--features").arg("a").arg("-v"),
execs().with_status(0));
}
#[test]
fn dep_feature_in_cmd_line() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.derived]
path = "derived"
"#)
.file("src/main.rs", r#"
extern crate derived;
fn main() { derived::test(); }
"#)
.file("derived/Cargo.toml", r#"
[package]
name = "derived"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "../bar"
[features]
default = []
derived-feat = ["bar/some-feat"]
"#)
.file("derived/src/lib.rs", r#"
extern crate bar;
pub use bar::test;
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
[features]
some-feat = []
"#)
.file("bar/src/lib.rs", r#"
#[cfg(feature = "some-feat")]
pub fn test() { print!("test"); }
"#)
.build();
// The foo project requires that feature "some-feat" in "bar" is enabled.
// Building without any features enabled should fail:
assert_that(p.cargo("build"),
execs().with_status(101));
// We should be able to enable the feature "derived-feat", which enables "some-feat",
// on the command line. The feature is enabled, thus building should be successful:
assert_that(p.cargo("build").arg("--features").arg("derived/derived-feat"),
execs().with_status(0));
// Trying to enable features of transitive dependencies is an error
assert_that(p.cargo("build").arg("--features").arg("bar/some-feat"),
execs().with_status(101).with_stderr("\
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `bar`
"));
// Hierarchical feature specification should still be disallowed
assert_that(p.cargo("build").arg("--features").arg("derived/bar/some-feat"),
execs().with_status(101).with_stderr("\
[ERROR] feature names may not contain slashes: `bar/some-feat`
"));
}
2016-08-31 17:03:26 +00:00
#[test]
fn all_features_flag_enables_all_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[features]
foo = []
bar = []
[dependencies.baz]
path = "baz"
optional = true
"#)
.file("src/main.rs", r#"
#[cfg(feature = "foo")]
pub fn foo() {}
#[cfg(feature = "bar")]
pub fn bar() {
extern crate baz;
baz::baz();
}
fn main() {
foo();
bar();
}
"#)
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
2016-08-31 17:03:26 +00:00
assert_that(p.cargo("build").arg("--all-features"),
2016-08-31 17:03:26 +00:00
execs().with_status(0));
}
#[test]
fn many_cli_features_comma_delimited() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
optional = true
[dependencies.baz]
path = "baz"
optional = true
"#)
.file("src/main.rs", r#"
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bar;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate baz;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
assert_that(p.cargo("build").arg("--features").arg("bar,baz"),
execs().with_status(0).with_stderr(format!("\
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
}
#[test]
fn many_cli_features_comma_and_space_delimited() {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
optional = true
[dependencies.baz]
path = "baz"
optional = true
[dependencies.bam]
path = "bam"
optional = true
[dependencies.bap]
path = "bap"
optional = true
"#)
.file("src/main.rs", r#"
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bar;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate baz;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bam;
2017-08-27 07:31:16 +00:00
#[allow(unused_extern_crates)]
extern crate bap;
fn main() {}
"#)
.file("bar/Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
"#)
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", r#"
[package]
name = "baz"
version = "0.0.1"
authors = []
"#)
.file("baz/src/lib.rs", "pub fn baz() {}")
.file("bam/Cargo.toml", r#"
[package]
name = "bam"
version = "0.0.1"
authors = []
"#)
.file("bam/src/lib.rs", "pub fn bam() {}")
.file("bap/Cargo.toml", r#"
[package]
name = "bap"
version = "0.0.1"
authors = []
"#)
.file("bap/src/lib.rs", "pub fn bap() {}")
.build();
assert_that(p.cargo("build").arg("--features").arg("bar,baz bam bap"),
execs().with_status(0).with_stderr(format!("\
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] ba[..] v0.0.1 ({dir}/ba[..])
[COMPILING] foo v0.0.1 ({dir})
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", dir = p.url())));
}