Skip to content

Commit 4536bc9

Browse files
committed
Auto merge of #6683 - dwijnand:cargo-test-quicker-by-not-building-untested-examples-when-filtered, r=ehuss
Cargo test quicker by not building untested examples when filtered Alternative to #6677 Fixes #6675 r? @ehuss
2 parents b9613ec + 110c813 commit 4536bc9

File tree

9 files changed

+117
-47
lines changed

9 files changed

+117
-47
lines changed

src/bin/cargo/commands/fix.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::command_prelude::*;
22

3-
use cargo::ops::{self, CompileFilter, FilterRule};
3+
use cargo::ops::{self, CompileFilter, FilterRule, LibRule};
44

55
pub fn cli() -> App {
66
subcommand("fix")
@@ -127,7 +127,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
127127
if let CompileFilter::Default { .. } = opts.filter {
128128
opts.filter = CompileFilter::Only {
129129
all_targets: true,
130-
lib: true,
130+
lib: LibRule::Default,
131131
bins: FilterRule::All,
132132
examples: FilterRule::All,
133133
benches: FilterRule::All,

src/bin/cargo/commands/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4949
.filter_map(|pkg| pkg.manifest().default_run())
5050
.collect();
5151
if default_runs.len() == 1 {
52-
compile_opts.filter = CompileFilter::new(
52+
compile_opts.filter = CompileFilter::from_raw_arguments(
5353
false,
5454
vec![default_runs[0].to_owned()],
5555
false,

src/bin/cargo/commands/test.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cargo::ops::{self, CompileFilter};
1+
use cargo::ops::{self, CompileFilter, FilterRule, LibRule};
22

33
use crate::command_prelude::*;
44

@@ -94,6 +94,17 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
9494

9595
let mut compile_opts = args.compile_options(config, CompileMode::Test, Some(&ws))?;
9696

97+
// `TESTNAME` is actually an argument of the test binary, but it's
98+
// important, so we explicitly mention it and reconfigure.
99+
let test_name: Option<&str> = args.value_of("TESTNAME");
100+
let mut test_args = vec![];
101+
test_args.extend(test_name.into_iter().map(|s| s.to_string()));
102+
test_args.extend(
103+
args.values_of("args")
104+
.unwrap_or_default()
105+
.map(|s| s.to_string()),
106+
);
107+
97108
let no_run = args.is_present("no-run");
98109
let doc = args.is_present("doc");
99110
if doc {
@@ -111,17 +122,22 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
111122
}
112123
compile_opts.build_config.mode = CompileMode::Doctest;
113124
compile_opts.filter = ops::CompileFilter::new(
114-
true,
115-
Vec::new(),
116-
false,
117-
Vec::new(),
118-
false,
119-
Vec::new(),
120-
false,
121-
Vec::new(),
122-
false,
123-
false,
125+
LibRule::True,
126+
FilterRule::none(),
127+
FilterRule::none(),
128+
FilterRule::none(),
129+
FilterRule::none(),
124130
);
131+
} else if test_name.is_some() {
132+
if let CompileFilter::Default { .. } = compile_opts.filter {
133+
compile_opts.filter = ops::CompileFilter::new(
134+
LibRule::Default, // compile the library, so the unit tests can be run filtered
135+
FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered
136+
FilterRule::All, // compile the tests, so the integration tests can be run filtered
137+
FilterRule::none(), // specify --examples to unit test binaries filtered
138+
FilterRule::none(), // specify --benches to unit test benchmarks filtered
139+
); // also, specify --doc to run doc tests filtered
140+
}
125141
}
126142

127143
let ops = ops::TestOptions {
@@ -130,16 +146,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
130146
compile_opts,
131147
};
132148

133-
// `TESTNAME` is actually an argument of the test binary, but it's
134-
// important, so we explicitly mention it and reconfigure.
135-
let mut test_args = vec![];
136-
test_args.extend(args.value_of("TESTNAME").into_iter().map(|s| s.to_string()));
137-
test_args.extend(
138-
args.values_of("args")
139-
.unwrap_or_default()
140-
.map(|s| s.to_string()),
141-
);
142-
143149
let err = ops::run_tests(&ws, &ops, &test_args)?;
144150
match err {
145151
None => Ok(()),

src/cargo/ops/cargo_compile.rs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@ impl Packages {
184184
}
185185
}
186186

187+
#[derive(Debug, PartialEq, Eq)]
188+
pub enum LibRule {
189+
/// Include the library, fail if not present
190+
True,
191+
/// Include the library if present
192+
Default,
193+
/// Exclude the library
194+
False,
195+
}
196+
187197
#[derive(Debug)]
188198
pub enum FilterRule {
189199
All,
@@ -198,7 +208,7 @@ pub enum CompileFilter {
198208
},
199209
Only {
200210
all_targets: bool,
201-
lib: bool,
211+
lib: LibRule,
202212
bins: FilterRule,
203213
examples: FilterRule,
204214
tests: FilterRule,
@@ -361,6 +371,10 @@ impl FilterRule {
361371
}
362372
}
363373

374+
pub fn none() -> FilterRule {
375+
FilterRule::Just(Vec::new())
376+
}
377+
364378
fn matches(&self, target: &Target) -> bool {
365379
match *self {
366380
FilterRule::All => true,
@@ -384,7 +398,8 @@ impl FilterRule {
384398
}
385399

386400
impl CompileFilter {
387-
pub fn new(
401+
/// Construct a CompileFilter from raw command line arguments.
402+
pub fn from_raw_arguments(
388403
lib_only: bool,
389404
bins: Vec<String>,
390405
all_bins: bool,
@@ -396,6 +411,7 @@ impl CompileFilter {
396411
all_bens: bool,
397412
all_targets: bool,
398413
) -> CompileFilter {
414+
let rule_lib = if lib_only { LibRule::True } else { LibRule::False };
399415
let rule_bins = FilterRule::new(bins, all_bins);
400416
let rule_tsts = FilterRule::new(tsts, all_tsts);
401417
let rule_exms = FilterRule::new(exms, all_exms);
@@ -404,21 +420,34 @@ impl CompileFilter {
404420
if all_targets {
405421
CompileFilter::Only {
406422
all_targets: true,
407-
lib: true,
423+
lib: LibRule::Default,
408424
bins: FilterRule::All,
409425
examples: FilterRule::All,
410426
benches: FilterRule::All,
411427
tests: FilterRule::All,
412428
}
413-
} else if lib_only
429+
} else {
430+
CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
431+
}
432+
}
433+
434+
/// Construct a CompileFilter from underlying primitives.
435+
pub fn new(
436+
rule_lib: LibRule,
437+
rule_bins: FilterRule,
438+
rule_tsts: FilterRule,
439+
rule_exms: FilterRule,
440+
rule_bens: FilterRule,
441+
) -> CompileFilter {
442+
if rule_lib == LibRule::True
414443
|| rule_bins.is_specific()
415444
|| rule_tsts.is_specific()
416445
|| rule_exms.is_specific()
417446
|| rule_bens.is_specific()
418447
{
419448
CompileFilter::Only {
420449
all_targets: false,
421-
lib: lib_only,
450+
lib: rule_lib,
422451
bins: rule_bins,
423452
examples: rule_exms,
424453
benches: rule_bens,
@@ -454,7 +483,7 @@ impl CompileFilter {
454483
match *self {
455484
CompileFilter::Default { .. } => true,
456485
CompileFilter::Only {
457-
lib,
486+
ref lib,
458487
ref bins,
459488
ref examples,
460489
ref tests,
@@ -466,7 +495,11 @@ impl CompileFilter {
466495
TargetKind::Test => tests,
467496
TargetKind::Bench => benches,
468497
TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
469-
TargetKind::Lib(..) => return lib,
498+
TargetKind::Lib(..) => return match *lib {
499+
LibRule::True => true,
500+
LibRule::Default => true,
501+
LibRule::False => false,
502+
},
470503
TargetKind::CustomBuild => return false,
471504
};
472505
rule.matches(target)
@@ -620,13 +653,13 @@ fn generate_targets<'a>(
620653
}
621654
CompileFilter::Only {
622655
all_targets,
623-
lib,
656+
ref lib,
624657
ref bins,
625658
ref examples,
626659
ref tests,
627660
ref benches,
628661
} => {
629-
if lib {
662+
if *lib != LibRule::False {
630663
let mut libs = Vec::new();
631664
for proposal in filter_targets(packages, Target::is_lib, false, build_config.mode) {
632665
let Proposal { target, pkg, .. } = proposal;
@@ -640,7 +673,7 @@ fn generate_targets<'a>(
640673
libs.push(proposal)
641674
}
642675
}
643-
if !all_targets && libs.is_empty() {
676+
if !all_targets && libs.is_empty() && *lib == LibRule::True {
644677
let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
645678
if names.len() == 1 {
646679
failure::bail!("no library targets found in package `{}`", names[0]);

src/cargo/ops/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub use self::cargo_clean::{clean, CleanOptions};
22
pub use self::cargo_compile::{compile, compile_with_exec, compile_ws, CompileOptions};
3-
pub use self::cargo_compile::{CompileFilter, FilterRule, Packages};
3+
pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages};
44
pub use self::cargo_doc::{doc, DocOptions};
55
pub use self::cargo_fetch::{fetch, FetchOptions};
66
pub use self::cargo_generate_lockfile::generate_lockfile;

src/cargo/util/command_prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ pub trait ArgMatchesExt {
328328
all_features: self._is_present("all-features"),
329329
no_default_features: self._is_present("no-default-features"),
330330
spec,
331-
filter: CompileFilter::new(
331+
filter: CompileFilter::from_raw_arguments(
332332
self._is_present("lib"),
333333
self._values_of("bin"),
334334
self._is_present("bins"),

tests/testsuite/build.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,7 +1955,7 @@ fn explicit_examples() {
19551955
)
19561956
.build();
19571957

1958-
p.cargo("test -v").run();
1958+
p.cargo("build --examples").run();
19591959
p.process(&p.bin("examples/hello"))
19601960
.with_stdout("Hello, World!\n")
19611961
.run();
@@ -2126,7 +2126,7 @@ fn implicit_examples() {
21262126
)
21272127
.build();
21282128

2129-
p.cargo("test").run();
2129+
p.cargo("build --examples").run();
21302130
p.process(&p.bin("examples/hello"))
21312131
.with_stdout("Hello, World!\n")
21322132
.run();
@@ -2739,13 +2739,13 @@ fn example_bin_same_name() {
27392739
.file("examples/foo.rs", "fn main() {}")
27402740
.build();
27412741

2742-
p.cargo("test --no-run -v").run();
2742+
p.cargo("build --examples").run();
27432743

27442744
assert!(!p.bin("foo").is_file());
27452745
// We expect a file of the form bin/foo-{metadata_hash}
27462746
assert!(p.bin("examples/foo").is_file());
27472747

2748-
p.cargo("test --no-run -v").run();
2748+
p.cargo("build --examples").run();
27492749

27502750
assert!(!p.bin("foo").is_file());
27512751
// We expect a file of the form bin/foo-{metadata_hash}
@@ -4212,7 +4212,7 @@ fn inferred_examples() {
42124212
.file("examples/baz/main.rs", "fn main() {}")
42134213
.build();
42144214

4215-
p.cargo("test").run();
4215+
p.cargo("build --examples").run();
42164216
assert!(p.bin("examples/bar").is_file());
42174217
assert!(p.bin("examples/baz").is_file());
42184218
}

tests/testsuite/freshness.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ fn changing_profiles_caches_targets() {
247247
"\
248248
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
249249
[RUNNING] target[..]debug[..]deps[..]foo-[..][EXE]
250-
[DOCTEST] foo
251250
",
252251
)
253252
.run();

tests/testsuite/test.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ fn cargo_test_verbose() {
163163
[COMPILING] foo v0.5.0 ([CWD])
164164
[RUNNING] `rustc [..] src/main.rs [..]`
165165
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
166-
[RUNNING] `[..]target/debug/deps/foo-[..][EXE] hello`",
166+
[RUNNING] `[CWD]/target/debug/deps/foo-[..] hello`
167+
",
167168
)
168169
.with_stdout_contains("test test_hello ... ok")
169170
.run();
@@ -601,21 +602,21 @@ fn pass_through_command_line() {
601602
[COMPILING] foo v0.0.1 ([CWD])
602603
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
603604
[RUNNING] target/debug/deps/foo-[..][EXE]
604-
[DOCTEST] foo",
605+
",
605606
)
607+
.with_stdout_contains("running 1 test")
606608
.with_stdout_contains("test bar ... ok")
607-
.with_stdout_contains("running 0 tests")
608609
.run();
609610

610611
p.cargo("test foo")
611612
.with_stderr(
612613
"\
613614
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
614615
[RUNNING] target/debug/deps/foo-[..][EXE]
615-
[DOCTEST] foo",
616+
",
616617
)
618+
.with_stdout_contains("running 1 test")
617619
.with_stdout_contains("test foo ... ok")
618-
.with_stdout_contains("running 0 tests")
619620
.run();
620621
}
621622

@@ -1462,6 +1463,37 @@ fn test_run_implicit_example_target() {
14621463
.run();
14631464
}
14641465

1466+
#[test]
1467+
fn test_filtered_excludes_compiling_examples() {
1468+
let p = project()
1469+
.file(
1470+
"src/lib.rs",
1471+
"#[cfg(test)] mod tests { #[test] fn foo() { assert!(true); } }",
1472+
)
1473+
.file("examples/ex1.rs", "fn main() {}")
1474+
.build();
1475+
1476+
p.cargo("test -v foo")
1477+
.with_stdout(
1478+
"
1479+
running 1 test
1480+
test tests::foo ... ok
1481+
1482+
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
1483+
1484+
",
1485+
)
1486+
.with_stderr("\
1487+
[COMPILING] foo v0.0.1 ([CWD])
1488+
[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]`
1489+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
1490+
[RUNNING] `[CWD]/target/debug/deps/foo-[..] foo`
1491+
",
1492+
)
1493+
.with_stderr_does_not_contain("[RUNNING][..]rustc[..]ex1[..]")
1494+
.run();
1495+
}
1496+
14651497
#[test]
14661498
fn test_no_harness() {
14671499
let p = project()

0 commit comments

Comments
 (0)