Skip to content

Commit

Permalink
Add --features etc
Browse files Browse the repository at this point in the history
Fixes `--features`, `--no-default-features` options #218
  • Loading branch information
sourcefrog committed Feb 5, 2024
1 parent 9f47fee commit 7432744
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 4 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- New: `--features`, `--no-default-features` and `--all-features` options are passed through to Cargo.

## 24.2.0

- New: Colored output can be enabled in CI or other noninteractive situations by passing `--colors=always`, or setting `CARGO_TERM_COLOR=always`, or `CLICOLOR_FORCE=1`. Colors can similarly be forced off with `--colors=never`, `CARGO_TERM_COLOR=never`, or `NO_COLOR=1`.
Expand Down
10 changes: 10 additions & 0 deletions book/src/cargo-args.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ test`.

There is not yet a way to pass options only to `cargo build` but not to `cargo test`.

## Feature flags

The `--features`, `--all-features`, and `--no-default-features` flags can be given to cargo-mutants and they will be passed down to `cargo build` and `cargo test`.

For example, this can be useful if you have tests that are only enabled with a feature flag:

```shell
cargo mutants -- --features=fail/failpoints
```

## Arguments to all `cargo` commands

To pass more arguments to every Cargo invocation, use `--cargo-arg`, or the `additional_cargo_args` configuration key.
Expand Down
55 changes: 55 additions & 0 deletions src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ fn cargo_argv(
} else {
cargo_args.push("--workspace".to_string());
}
let features = &options.features;
if features.no_default_features {
cargo_args.push("--no-default-features".to_owned());
}
if features.all_features {
cargo_args.push("--all-features".to_owned());
}
cargo_args.extend(
features
.features
.iter()
.map(|f| format!("--features={}", f)),
);
cargo_args.extend(options.additional_cargo_args.iter().cloned());
if phase == Phase::Test {
cargo_args.extend(options.additional_cargo_test_args.iter().cloned());
Expand Down Expand Up @@ -232,4 +245,46 @@ mod test {
]
);
}

#[test]
fn no_default_features_args_passed_to_cargo() {
let args = Args::try_parse_from(["mutants", "--no-default-features"].as_slice()).unwrap();
let options = Options::from_args(&args).unwrap();
let build_dir = Utf8Path::new("/tmp/buildXYZ");
assert_eq!(
cargo_argv(build_dir, None, Phase::Check, &options)[1..],
["check", "--tests", "--workspace", "--no-default-features"]
);
}

#[test]
fn all_features_args_passed_to_cargo() {
let args = Args::try_parse_from(["mutants", "--all-features"].as_slice()).unwrap();
let options = Options::from_args(&args).unwrap();
let build_dir = Utf8Path::new("/tmp/buildXYZ");
assert_eq!(
cargo_argv(build_dir, None, Phase::Check, &options)[1..],
["check", "--tests", "--workspace", "--all-features"]
);
}

#[test]
fn feature_args_passed_to_cargo() {
let args = Args::try_parse_from(
["mutants", "--features", "foo", "--features", "bar,baz"].as_slice(),
)
.unwrap();
let options = Options::from_args(&args).unwrap();
let build_dir = Utf8Path::new("/tmp/buildXYZ");
assert_eq!(
cargo_argv(build_dir, None, Phase::Check, &options)[1..],
[
"check",
"--tests",
"--workspace",
"--features=foo",
"--features=bar,baz"
]
);
}
}
23 changes: 23 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,28 @@ struct Args {
/// pass remaining arguments to cargo test after all options and after `--`.
#[arg(last = true, help_heading = "Execution")]
cargo_test_args: Vec<String>,

#[command(flatten)]
features: Features,
}

#[derive(clap::Args, PartialEq, Eq, Debug, Default, Clone)]
pub struct Features {
//--- features
/// Space or comma separated list of features to activate.
// (The features are not split or parsed, just passed through to Cargo.)
#[arg(long, help_heading = "Feature Selection")]
pub features: Vec<String>,

/// Do not activate the `default` feature.
#[arg(long, help_heading = "Feature Selection")]
pub no_default_features: bool,

/// Activate all features.
// (This does not conflict because this only turns on features in the top level package,
// and you might use --features to turn on features in dependencies.)
#[arg(long, help_heading = "Feature Selection")]
pub all_features: bool,
}

fn main() -> Result<()> {
Expand Down Expand Up @@ -358,6 +380,7 @@ fn main() -> Result<()> {
config::Config::read_tree_config(&workspace.dir)?
};
debug!(?config);
debug!(?args.features);
let options = Options::new(&args, &config)?;
debug!(?options);
let package_filter = if !args.mutate_packages.is_empty() {
Expand Down
66 changes: 66 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ pub struct Options {
/// Additional arguments to `cargo test`.
pub additional_cargo_test_args: Vec<String>,

/// Selection of features for cargo.
pub features: super::Features,

/// Files to examine.
pub examine_globset: Option<GlobSet>,

Expand Down Expand Up @@ -194,6 +197,7 @@ impl Options {
.context("Failed to compile exclude_re regex")?,
examine_globset: build_glob_set(or_slices(&args.file, &config.examine_globs))?,
exclude_globset: build_glob_set(or_slices(&args.exclude, &config.exclude_globs))?,
features: args.features.clone(),
gitignore: args.gitignore,
in_place: args.in_place,
jobs: args.jobs,
Expand All @@ -219,6 +223,11 @@ impl Options {
});
Ok(options)
}

#[cfg(test)]
pub fn from_args(args: &Args) -> Result<Options> {
Options::new(args, &Config::default())
}
}

/// If the first slices is non-empty, return that, otherwise the second.
Expand Down Expand Up @@ -301,4 +310,61 @@ mod test {
let options = Options::new(&args, &config).unwrap();
assert_eq!(options.test_tool, TestTool::Nextest);
}

#[test]
fn features_arg() {
let args = Args::try_parse_from(["mutants", "--features", "nice,shiny features"]).unwrap();
assert_eq!(
args.features.features.iter().as_ref(),
["nice,shiny features"]
);
assert!(!args.features.no_default_features);
assert!(!args.features.all_features);

let options = Options::new(&args, &Config::default()).unwrap();
assert_eq!(
options.features.features.iter().as_ref(),
["nice,shiny features"]
);
assert!(!options.features.no_default_features);
assert!(!options.features.all_features);
}

#[test]
fn no_default_features_arg() {
let args = Args::try_parse_from([
"mutants",
"--no-default-features",
"--features",
"nice,shiny features",
])
.unwrap();

let options = Options::new(&args, &Config::default()).unwrap();
assert_eq!(
options.features.features.iter().as_ref(),
["nice,shiny features"]
);
assert!(options.features.no_default_features);
assert!(!options.features.all_features);
}

#[test]
fn all_features_arg() {
let args = Args::try_parse_from([
"mutants",
"--all-features",
"--features",
"nice,shiny features",
])
.unwrap();

let options = Options::new(&args, &Config::default()).unwrap();
assert_eq!(
options.features.features.iter().as_ref(),
["nice,shiny features"]
);
assert!(!options.features.no_default_features);
assert!(options.features.all_features);
}
}

0 comments on commit 7432744

Please sign in to comment.