Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --features etc #272

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
]
);
}
}
25 changes: 24 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub enum BaselineStrategy {
about,
after_help = SPONSOR_MESSAGE,
)]
struct Args {
pub struct Args {
/// show cargo output for all invocations (very verbose).
#[arg(long, help_heading = "Output")]
all_logs: bool,
Expand Down 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);
}
}
Loading