diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index bc654297439..c25b5657c83 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -1,6 +1,6 @@ use crate::command_prelude::*; -use cargo::ops::{self, CompileFilter, FilterRule}; +use cargo::ops::{self, CompileFilter, FilterRule, LibRule}; pub fn cli() -> App { subcommand("fix") @@ -127,7 +127,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { if let CompileFilter::Default { .. } = opts.filter { opts.filter = CompileFilter::Only { all_targets: true, - lib: true, + lib: LibRule::Default, bins: FilterRule::All, examples: FilterRule::All, benches: FilterRule::All, diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index 3f37fc0910b..ea65f998ff7 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -49,7 +49,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { .filter_map(|pkg| pkg.manifest().default_run()) .collect(); if default_runs.len() == 1 { - compile_opts.filter = CompileFilter::new( + compile_opts.filter = CompileFilter::from_raw_arguments( false, vec![default_runs[0].to_owned()], false, diff --git a/src/bin/cargo/commands/test.rs b/src/bin/cargo/commands/test.rs index dca409845de..e77aaa547bb 100644 --- a/src/bin/cargo/commands/test.rs +++ b/src/bin/cargo/commands/test.rs @@ -1,4 +1,4 @@ -use cargo::ops::{self, CompileFilter}; +use cargo::ops::{self, CompileFilter, FilterRule, LibRule}; use crate::command_prelude::*; @@ -94,6 +94,17 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let mut compile_opts = args.compile_options(config, CompileMode::Test, Some(&ws))?; + // `TESTNAME` is actually an argument of the test binary, but it's + // important, so we explicitly mention it and reconfigure. + let test_name: Option<&str> = args.value_of("TESTNAME"); + let mut test_args = vec![]; + test_args.extend(test_name.into_iter().map(|s| s.to_string())); + test_args.extend( + args.values_of("args") + .unwrap_or_default() + .map(|s| s.to_string()), + ); + let no_run = args.is_present("no-run"); let doc = args.is_present("doc"); if doc { @@ -111,17 +122,22 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { } compile_opts.build_config.mode = CompileMode::Doctest; compile_opts.filter = ops::CompileFilter::new( - true, - Vec::new(), - false, - Vec::new(), - false, - Vec::new(), - false, - Vec::new(), - false, - false, + LibRule::True, + FilterRule::none(), + FilterRule::none(), + FilterRule::none(), + FilterRule::none(), ); + } else if test_name.is_some() { + if let CompileFilter::Default { .. } = compile_opts.filter { + compile_opts.filter = ops::CompileFilter::new( + LibRule::Default, // compile the library, so the unit tests can be run filtered + FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered + FilterRule::All, // compile the tests, so the integration tests can be run filtered + FilterRule::none(), // specify --examples to unit test binaries filtered + FilterRule::none(), // specify --benches to unit test benchmarks filtered + ); // also, specify --doc to run doc tests filtered + } } let ops = ops::TestOptions { @@ -130,16 +146,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { compile_opts, }; - // `TESTNAME` is actually an argument of the test binary, but it's - // important, so we explicitly mention it and reconfigure. - let mut test_args = vec![]; - test_args.extend(args.value_of("TESTNAME").into_iter().map(|s| s.to_string())); - test_args.extend( - args.values_of("args") - .unwrap_or_default() - .map(|s| s.to_string()), - ); - let err = ops::run_tests(&ws, &ops, &test_args)?; match err { None => Ok(()), diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 3e222f59336..0a8c38b21ea 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -172,6 +172,16 @@ impl Packages { } } +#[derive(Debug, PartialEq, Eq)] +pub enum LibRule { + /// Include the library, fail if not present + True, + /// Include the library if present + Default, + /// Exclude the library + False, +} + #[derive(Debug)] pub enum FilterRule { All, @@ -186,7 +196,7 @@ pub enum CompileFilter { }, Only { all_targets: bool, - lib: bool, + lib: LibRule, bins: FilterRule, examples: FilterRule, tests: FilterRule, @@ -349,6 +359,10 @@ impl FilterRule { } } + pub fn none() -> FilterRule { + FilterRule::Just(Vec::new()) + } + fn matches(&self, target: &Target) -> bool { match *self { FilterRule::All => true, @@ -372,7 +386,8 @@ impl FilterRule { } impl CompileFilter { - pub fn new( + /// Construct a CompileFilter from raw command line arguments. + pub fn from_raw_arguments( lib_only: bool, bins: Vec, all_bins: bool, @@ -384,6 +399,7 @@ impl CompileFilter { all_bens: bool, all_targets: bool, ) -> CompileFilter { + let rule_lib = if lib_only { LibRule::True } else { LibRule::False }; let rule_bins = FilterRule::new(bins, all_bins); let rule_tsts = FilterRule::new(tsts, all_tsts); let rule_exms = FilterRule::new(exms, all_exms); @@ -392,13 +408,26 @@ impl CompileFilter { if all_targets { CompileFilter::Only { all_targets: true, - lib: true, + lib: LibRule::Default, bins: FilterRule::All, examples: FilterRule::All, benches: FilterRule::All, tests: FilterRule::All, } - } else if lib_only + } else { + CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens) + } + } + + /// Construct a CompileFilter from underlying primitives. + pub fn new( + rule_lib: LibRule, + rule_bins: FilterRule, + rule_tsts: FilterRule, + rule_exms: FilterRule, + rule_bens: FilterRule, + ) -> CompileFilter { + if rule_lib == LibRule::True || rule_bins.is_specific() || rule_tsts.is_specific() || rule_exms.is_specific() @@ -406,7 +435,7 @@ impl CompileFilter { { CompileFilter::Only { all_targets: false, - lib: lib_only, + lib: rule_lib, bins: rule_bins, examples: rule_exms, benches: rule_bens, @@ -442,7 +471,7 @@ impl CompileFilter { match *self { CompileFilter::Default { .. } => true, CompileFilter::Only { - lib, + ref lib, ref bins, ref examples, ref tests, @@ -454,7 +483,11 @@ impl CompileFilter { TargetKind::Test => tests, TargetKind::Bench => benches, TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples, - TargetKind::Lib(..) => return lib, + TargetKind::Lib(..) => return match *lib { + LibRule::True => true, + LibRule::Default => true, + LibRule::False => false, + }, TargetKind::CustomBuild => return false, }; rule.matches(target) @@ -608,13 +641,13 @@ fn generate_targets<'a>( } CompileFilter::Only { all_targets, - lib, + ref lib, ref bins, ref examples, ref tests, ref benches, } => { - if lib { + if *lib != LibRule::False { let mut libs = Vec::new(); for proposal in filter_targets(packages, Target::is_lib, false, build_config.mode) { let Proposal { target, pkg, .. } = proposal; @@ -628,7 +661,7 @@ fn generate_targets<'a>( libs.push(proposal) } } - if !all_targets && libs.is_empty() { + if !all_targets && libs.is_empty() && *lib == LibRule::True { let names = packages.iter().map(|pkg| pkg.name()).collect::>(); if names.len() == 1 { failure::bail!("no library targets found in package `{}`", names[0]); diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index f12f66bef9c..e60dcd7b85a 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -1,6 +1,6 @@ pub use self::cargo_clean::{clean, CleanOptions}; pub use self::cargo_compile::{compile, compile_with_exec, compile_ws, CompileOptions}; -pub use self::cargo_compile::{CompileFilter, FilterRule, Packages}; +pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages}; pub use self::cargo_doc::{doc, DocOptions}; pub use self::cargo_fetch::{fetch, FetchOptions}; pub use self::cargo_generate_lockfile::generate_lockfile; diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 770a2fd2a2a..d004f5561e3 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -328,7 +328,7 @@ pub trait ArgMatchesExt { all_features: self._is_present("all-features"), no_default_features: self._is_present("no-default-features"), spec, - filter: CompileFilter::new( + filter: CompileFilter::from_raw_arguments( self._is_present("lib"), self._values_of("bin"), self._is_present("bins"), diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index beae1562d21..a12a12caf60 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -1955,7 +1955,7 @@ fn explicit_examples() { ) .build(); - p.cargo("test -v").run(); + p.cargo("build --examples").run(); p.process(&p.bin("examples/hello")) .with_stdout("Hello, World!\n") .run(); @@ -2126,7 +2126,7 @@ fn implicit_examples() { ) .build(); - p.cargo("test").run(); + p.cargo("build --examples").run(); p.process(&p.bin("examples/hello")) .with_stdout("Hello, World!\n") .run(); @@ -2739,13 +2739,13 @@ fn example_bin_same_name() { .file("examples/foo.rs", "fn main() {}") .build(); - p.cargo("test --no-run -v").run(); + p.cargo("build --examples").run(); assert!(!p.bin("foo").is_file()); // We expect a file of the form bin/foo-{metadata_hash} assert!(p.bin("examples/foo").is_file()); - p.cargo("test --no-run -v").run(); + p.cargo("build --examples").run(); assert!(!p.bin("foo").is_file()); // We expect a file of the form bin/foo-{metadata_hash} @@ -4212,7 +4212,7 @@ fn inferred_examples() { .file("examples/baz/main.rs", "fn main() {}") .build(); - p.cargo("test").run(); + p.cargo("build --examples").run(); assert!(p.bin("examples/bar").is_file()); assert!(p.bin("examples/baz").is_file()); } diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 9d14ca533c2..5083b63b332 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -247,7 +247,6 @@ fn changing_profiles_caches_targets() { "\ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] target[..]debug[..]deps[..]foo-[..][EXE] -[DOCTEST] foo ", ) .run(); diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index df5b15df7d8..f322a506e0d 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -163,7 +163,8 @@ fn cargo_test_verbose() { [COMPILING] foo v0.5.0 ([CWD]) [RUNNING] `rustc [..] src/main.rs [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] -[RUNNING] `[..]target/debug/deps/foo-[..][EXE] hello`", +[RUNNING] `[CWD]/target/debug/deps/foo-[..] hello` +", ) .with_stdout_contains("test test_hello ... ok") .run(); @@ -601,10 +602,10 @@ fn pass_through_command_line() { [COMPILING] foo v0.0.1 ([CWD]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] target/debug/deps/foo-[..][EXE] -[DOCTEST] foo", +", ) + .with_stdout_contains("running 1 test") .with_stdout_contains("test bar ... ok") - .with_stdout_contains("running 0 tests") .run(); p.cargo("test foo") @@ -612,10 +613,10 @@ fn pass_through_command_line() { "\ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] target/debug/deps/foo-[..][EXE] -[DOCTEST] foo", +", ) + .with_stdout_contains("running 1 test") .with_stdout_contains("test foo ... ok") - .with_stdout_contains("running 0 tests") .run(); } @@ -1462,6 +1463,37 @@ fn test_run_implicit_example_target() { .run(); } +#[test] +fn test_filtered_excludes_compiling_examples() { + let p = project() + .file( + "src/lib.rs", + "#[cfg(test)] mod tests { #[test] fn foo() { assert!(true); } }", + ) + .file("examples/ex1.rs", "fn main() {}") + .build(); + + p.cargo("test -v foo") + .with_stdout( + " +running 1 test +test tests::foo ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out + +", + ) + .with_stderr("\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/debug/deps/foo-[..] foo` +", + ) + .with_stderr_does_not_contain("[RUNNING][..]rustc[..]ex1[..]") + .run(); +} + #[test] fn test_no_harness() { let p = project()