]> git.lizzy.rs Git - rust.git/blob - src/libtest/lib.rs
Rollup merge of #60278 - ehuss:doc-html_root_url, r=GuillaumeGomez
[rust.git] / src / libtest / lib.rs
1 //! Support code for rustc's built in unit-test and micro-benchmarking
2 //! framework.
3 //!
4 //! Almost all user code will only be interested in `Bencher` and
5 //! `black_box`. All other interactions (such as writing tests and
6 //! benchmarks themselves) should be done via the `#[test]` and
7 //! `#[bench]` attributes.
8 //!
9 //! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details.
10
11 // Currently, not much of this is meant for users. It is intended to
12 // support the simplest interface possible for representing and
13 // running tests while providing a base that other test frameworks may
14 // build off of.
15
16 // N.B., this is also specified in this crate's Cargo.toml, but libsyntax contains logic specific to
17 // this crate, which relies on this attribute (rather than the value of `--crate-name` passed by
18 // cargo) to detect this crate.
19
20 #![deny(rust_2018_idioms)]
21 #![crate_name = "test"]
22 #![unstable(feature = "test", issue = "27812")]
23 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))]
24 #![feature(asm)]
25 #![feature(fnbox)]
26 #![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc, rustc_private))]
27 #![feature(nll)]
28 #![feature(set_stdio)]
29 #![feature(panic_unwind)]
30 #![feature(staged_api)]
31 #![feature(termination_trait_lib)]
32 #![feature(test)]
33
34 use getopts;
35 #[cfg(any(unix, target_os = "cloudabi"))]
36 extern crate libc;
37 use term;
38
39 // FIXME(#54291): rustc and/or LLVM don't yet support building with panic-unwind
40 //                on aarch64-pc-windows-msvc, so we don't link libtest against
41 //                libunwind (for the time being), even though it means that
42 //                libtest won't be fully functional on this platform.
43 //
44 // See also: https://github.com/rust-lang/rust/issues/54190#issuecomment-422904437
45 #[cfg(not(all(windows, target_arch = "aarch64")))]
46 extern crate panic_unwind;
47
48 pub use self::ColorConfig::*;
49 use self::NamePadding::*;
50 use self::OutputLocation::*;
51 use self::TestEvent::*;
52 pub use self::TestFn::*;
53 pub use self::TestName::*;
54 pub use self::TestResult::*;
55
56 use std::any::Any;
57 use std::borrow::Cow;
58 use std::boxed::FnBox;
59 use std::cmp;
60 use std::collections::BTreeMap;
61 use std::env;
62 use std::fmt;
63 use std::fs::File;
64 use std::io;
65 use std::io::prelude::*;
66 use std::panic::{catch_unwind, AssertUnwindSafe};
67 use std::path::PathBuf;
68 use std::process;
69 use std::process::Termination;
70 use std::sync::mpsc::{channel, Sender};
71 use std::sync::{Arc, Mutex};
72 use std::thread;
73 use std::time::{Duration, Instant};
74
75 const TEST_WARN_TIMEOUT_S: u64 = 60;
76 const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode
77
78 // to be used by rustc to compile tests in libtest
79 pub mod test {
80     pub use crate::{
81         assert_test_result, filter_tests, parse_opts, run_test, test_main, test_main_static,
82         Bencher, DynTestFn, DynTestName, Metric, MetricMap, Options, RunIgnored, ShouldPanic,
83         StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestName, TestOpts,
84         TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk,
85     };
86 }
87
88 mod formatters;
89 pub mod stats;
90
91 use crate::formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter};
92
93 /// Whether to execute tests concurrently or not
94 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
95 pub enum Concurrent {
96     Yes,
97     No,
98 }
99
100 // The name of a test. By convention this follows the rules for rust
101 // paths; i.e., it should be a series of identifiers separated by double
102 // colons. This way if some test runner wants to arrange the tests
103 // hierarchically it may.
104
105 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
106 pub enum TestName {
107     StaticTestName(&'static str),
108     DynTestName(String),
109     AlignedTestName(Cow<'static, str>, NamePadding),
110 }
111 impl TestName {
112     fn as_slice(&self) -> &str {
113         match *self {
114             StaticTestName(s) => s,
115             DynTestName(ref s) => s,
116             AlignedTestName(ref s, _) => &*s,
117         }
118     }
119
120     fn padding(&self) -> NamePadding {
121         match self {
122             &AlignedTestName(_, p) => p,
123             _ => PadNone,
124         }
125     }
126
127     fn with_padding(&self, padding: NamePadding) -> TestName {
128         let name = match self {
129             &TestName::StaticTestName(name) => Cow::Borrowed(name),
130             &TestName::DynTestName(ref name) => Cow::Owned(name.clone()),
131             &TestName::AlignedTestName(ref name, _) => name.clone(),
132         };
133
134         TestName::AlignedTestName(name, padding)
135     }
136 }
137 impl fmt::Display for TestName {
138     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139         fmt::Display::fmt(self.as_slice(), f)
140     }
141 }
142
143 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
144 pub enum NamePadding {
145     PadNone,
146     PadOnRight,
147 }
148
149 impl TestDesc {
150     fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
151         let mut name = String::from(self.name.as_slice());
152         let fill = column_count.saturating_sub(name.len());
153         let pad = " ".repeat(fill);
154         match align {
155             PadNone => name,
156             PadOnRight => {
157                 name.push_str(&pad);
158                 name
159             }
160         }
161     }
162 }
163
164 /// Represents a benchmark function.
165 pub trait TDynBenchFn: Send {
166     fn run(&self, harness: &mut Bencher);
167 }
168
169 // A function that runs a test. If the function returns successfully,
170 // the test succeeds; if the function panics then the test fails. We
171 // may need to come up with a more clever definition of test in order
172 // to support isolation of tests into threads.
173 pub enum TestFn {
174     StaticTestFn(fn()),
175     StaticBenchFn(fn(&mut Bencher)),
176     DynTestFn(Box<dyn FnBox() + Send>),
177     DynBenchFn(Box<dyn TDynBenchFn + 'static>),
178 }
179
180 impl TestFn {
181     fn padding(&self) -> NamePadding {
182         match *self {
183             StaticTestFn(..) => PadNone,
184             StaticBenchFn(..) => PadOnRight,
185             DynTestFn(..) => PadNone,
186             DynBenchFn(..) => PadOnRight,
187         }
188     }
189 }
190
191 impl fmt::Debug for TestFn {
192     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193         f.write_str(match *self {
194             StaticTestFn(..) => "StaticTestFn(..)",
195             StaticBenchFn(..) => "StaticBenchFn(..)",
196             DynTestFn(..) => "DynTestFn(..)",
197             DynBenchFn(..) => "DynBenchFn(..)",
198         })
199     }
200 }
201
202 /// Manager of the benchmarking runs.
203 ///
204 /// This is fed into functions marked with `#[bench]` to allow for
205 /// set-up & tear-down before running a piece of code repeatedly via a
206 /// call to `iter`.
207 #[derive(Clone)]
208 pub struct Bencher {
209     mode: BenchMode,
210     summary: Option<stats::Summary>,
211     pub bytes: u64,
212 }
213
214 #[derive(Clone, PartialEq, Eq)]
215 pub enum BenchMode {
216     Auto,
217     Single,
218 }
219
220 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
221 pub enum ShouldPanic {
222     No,
223     Yes,
224     YesWithMessage(&'static str),
225 }
226
227 // The definition of a single test. A test runner will run a list of
228 // these.
229 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
230 pub struct TestDesc {
231     pub name: TestName,
232     pub ignore: bool,
233     pub should_panic: ShouldPanic,
234     pub allow_fail: bool,
235 }
236
237 #[derive(Debug)]
238 pub struct TestDescAndFn {
239     pub desc: TestDesc,
240     pub testfn: TestFn,
241 }
242
243 #[derive(Clone, PartialEq, Debug, Copy)]
244 pub struct Metric {
245     value: f64,
246     noise: f64,
247 }
248
249 impl Metric {
250     pub fn new(value: f64, noise: f64) -> Metric {
251         Metric { value, noise }
252     }
253 }
254
255 /// In case we want to add other options as well, just add them in this struct.
256 #[derive(Copy, Clone, Debug)]
257 pub struct Options {
258     display_output: bool,
259 }
260
261 impl Options {
262     pub fn new() -> Options {
263         Options {
264             display_output: false,
265         }
266     }
267
268     pub fn display_output(mut self, display_output: bool) -> Options {
269         self.display_output = display_output;
270         self
271     }
272 }
273
274 // The default console test runner. It accepts the command line
275 // arguments and a vector of test_descs.
276 pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Options) {
277     let mut opts = match parse_opts(args) {
278         Some(Ok(o)) => o,
279         Some(Err(msg)) => {
280             eprintln!("error: {}", msg);
281             process::exit(101);
282         }
283         None => return,
284     };
285
286     opts.options = options;
287     if opts.list {
288         if let Err(e) = list_tests_console(&opts, tests) {
289             eprintln!("error: io error when listing tests: {:?}", e);
290             process::exit(101);
291         }
292     } else {
293         match run_tests_console(&opts, tests) {
294             Ok(true) => {}
295             Ok(false) => process::exit(101),
296             Err(e) => {
297                 eprintln!("error: io error when listing tests: {:?}", e);
298                 process::exit(101);
299             }
300         }
301     }
302 }
303
304 // A variant optimized for invocation with a static test vector.
305 // This will panic (intentionally) when fed any dynamic tests, because
306 // it is copying the static values out into a dynamic vector and cannot
307 // copy dynamic values. It is doing this because from this point on
308 // a Vec<TestDescAndFn> is used in order to effect ownership-transfer
309 // semantics into parallel test runners, which in turn requires a Vec<>
310 // rather than a &[].
311 pub fn test_main_static(tests: &[&TestDescAndFn]) {
312     let args = env::args().collect::<Vec<_>>();
313     let owned_tests = tests
314         .iter()
315         .map(|t| match t.testfn {
316             StaticTestFn(f) => TestDescAndFn {
317                 testfn: StaticTestFn(f),
318                 desc: t.desc.clone(),
319             },
320             StaticBenchFn(f) => TestDescAndFn {
321                 testfn: StaticBenchFn(f),
322                 desc: t.desc.clone(),
323             },
324             _ => panic!("non-static tests passed to test::test_main_static"),
325         })
326         .collect();
327     test_main(&args, owned_tests, Options::new())
328 }
329
330 /// Invoked when unit tests terminate. Should panic if the unit
331 /// Tests is considered a failure. By default, invokes `report()`
332 /// and checks for a `0` result.
333 pub fn assert_test_result<T: Termination>(result: T) {
334     let code = result.report();
335     assert_eq!(
336         code, 0,
337         "the test returned a termination value with a non-zero status code ({}) \
338          which indicates a failure",
339         code
340     );
341 }
342
343 #[derive(Copy, Clone, Debug)]
344 pub enum ColorConfig {
345     AutoColor,
346     AlwaysColor,
347     NeverColor,
348 }
349
350 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
351 pub enum OutputFormat {
352     Pretty,
353     Terse,
354     Json,
355 }
356
357 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
358 pub enum RunIgnored {
359     Yes,
360     No,
361     Only,
362 }
363
364 #[derive(Debug)]
365 pub struct TestOpts {
366     pub list: bool,
367     pub filter: Option<String>,
368     pub filter_exact: bool,
369     pub exclude_should_panic: bool,
370     pub run_ignored: RunIgnored,
371     pub run_tests: bool,
372     pub bench_benchmarks: bool,
373     pub logfile: Option<PathBuf>,
374     pub nocapture: bool,
375     pub color: ColorConfig,
376     pub format: OutputFormat,
377     pub test_threads: Option<usize>,
378     pub skip: Vec<String>,
379     pub options: Options,
380 }
381
382 impl TestOpts {
383     #[cfg(test)]
384     fn new() -> TestOpts {
385         TestOpts {
386             list: false,
387             filter: None,
388             filter_exact: false,
389             exclude_should_panic: false,
390             run_ignored: RunIgnored::No,
391             run_tests: false,
392             bench_benchmarks: false,
393             logfile: None,
394             nocapture: false,
395             color: AutoColor,
396             format: OutputFormat::Pretty,
397             test_threads: None,
398             skip: vec![],
399             options: Options::new(),
400         }
401     }
402 }
403
404 /// Result of parsing the options.
405 pub type OptRes = Result<TestOpts, String>;
406
407 fn optgroups() -> getopts::Options {
408     let mut opts = getopts::Options::new();
409     opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
410         .optflag("", "ignored", "Run only ignored tests")
411         .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
412         .optflag("", "test", "Run tests and not benchmarks")
413         .optflag("", "bench", "Run benchmarks instead of tests")
414         .optflag("", "list", "List all tests and benchmarks")
415         .optflag("h", "help", "Display this message (longer with --help)")
416         .optopt(
417             "",
418             "logfile",
419             "Write logs to the specified file instead \
420              of stdout",
421             "PATH",
422         )
423         .optflag(
424             "",
425             "nocapture",
426             "don't capture stdout/stderr of each \
427              task, allow printing directly",
428         )
429         .optopt(
430             "",
431             "test-threads",
432             "Number of threads used for running tests \
433              in parallel",
434             "n_threads",
435         )
436         .optmulti(
437             "",
438             "skip",
439             "Skip tests whose names contain FILTER (this flag can \
440              be used multiple times)",
441             "FILTER",
442         )
443         .optflag(
444             "q",
445             "quiet",
446             "Display one character per test instead of one line. \
447              Alias to --format=terse",
448         )
449         .optflag(
450             "",
451             "exact",
452             "Exactly match filters rather than by substring",
453         )
454         .optopt(
455             "",
456             "color",
457             "Configure coloring of output:
458             auto   = colorize if stdout is a tty and tests are run on serially (default);
459             always = always colorize output;
460             never  = never colorize output;",
461             "auto|always|never",
462         )
463         .optopt(
464             "",
465             "format",
466             "Configure formatting of output:
467             pretty = Print verbose output;
468             terse  = Display one character per test;
469             json   = Output a json document",
470             "pretty|terse|json",
471         )
472         .optopt(
473             "Z",
474             "",
475             "Enable nightly-only flags:
476             unstable-options = Allow use of experimental features",
477             "unstable-options",
478         );
479     return opts;
480 }
481
482 fn usage(binary: &str, options: &getopts::Options) {
483     let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
484     println!(
485         r#"{usage}
486
487 The FILTER string is tested against the name of all tests, and only those
488 tests whose names contain the filter are run.
489
490 By default, all tests are run in parallel. This can be altered with the
491 --test-threads flag or the RUST_TEST_THREADS environment variable when running
492 tests (set it to 1).
493
494 All tests have their standard output and standard error captured by default.
495 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
496 environment variable to a value other than "0". Logging is not captured by default.
497
498 Test Attributes:
499
500     #[test]        - Indicates a function is a test to be run. This function
501                      takes no arguments.
502     #[bench]       - Indicates a function is a benchmark to be run. This
503                      function takes one argument (test::Bencher).
504     #[should_panic] - This function (also labeled with #[test]) will only pass if
505                      the code causes a panic (an assertion failure or panic!)
506                      A message may be provided, which the failure string must
507                      contain: #[should_panic(expected = "foo")].
508     #[ignore]      - When applied to a function which is already attributed as a
509                      test, then the test runner will ignore these tests during
510                      normal test runs. Running with --ignored or --include-ignored will run
511                      these tests."#,
512         usage = options.usage(&message)
513     );
514 }
515
516 // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
517 fn is_nightly() -> bool {
518     // Whether this is a feature-staged build, i.e., on the beta or stable channel
519     let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
520     // Whether we should enable unstable features for bootstrapping
521     let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
522
523     bootstrap || !disable_unstable_features
524 }
525
526 // Parses command line arguments into test options
527 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
528     let mut allow_unstable = false;
529     let opts = optgroups();
530     let args = args.get(1..).unwrap_or(args);
531     let matches = match opts.parse(args) {
532         Ok(m) => m,
533         Err(f) => return Some(Err(f.to_string())),
534     };
535
536     if let Some(opt) = matches.opt_str("Z") {
537         if !is_nightly() {
538             return Some(Err(
539                 "the option `Z` is only accepted on the nightly compiler".into(),
540             ));
541         }
542
543         match &*opt {
544             "unstable-options" => {
545                 allow_unstable = true;
546             }
547             _ => {
548                 return Some(Err("Unrecognized option to `Z`".into()));
549             }
550         }
551     };
552
553     if matches.opt_present("h") {
554         usage(&args[0], &opts);
555         return None;
556     }
557
558     let filter = if !matches.free.is_empty() {
559         Some(matches.free[0].clone())
560     } else {
561         None
562     };
563
564     let exclude_should_panic = matches.opt_present("exclude-should-panic");
565     if !allow_unstable && exclude_should_panic {
566         return Some(Err(
567             "The \"exclude-should-panic\" flag is only accepted on the nightly compiler".into(),
568         ));
569     }
570
571     let include_ignored = matches.opt_present("include-ignored");
572     if !allow_unstable && include_ignored {
573         return Some(Err(
574             "The \"include-ignored\" flag is only accepted on the nightly compiler".into(),
575         ));
576     }
577
578     let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
579         (true, true) => {
580             return Some(Err(
581                 "the options --include-ignored and --ignored are mutually exclusive".into(),
582             ));
583         }
584         (true, false) => RunIgnored::Yes,
585         (false, true) => RunIgnored::Only,
586         (false, false) => RunIgnored::No,
587     };
588     let quiet = matches.opt_present("quiet");
589     let exact = matches.opt_present("exact");
590     let list = matches.opt_present("list");
591
592     let logfile = matches.opt_str("logfile");
593     let logfile = logfile.map(|s| PathBuf::from(&s));
594
595     let bench_benchmarks = matches.opt_present("bench");
596     let run_tests = !bench_benchmarks || matches.opt_present("test");
597
598     let mut nocapture = matches.opt_present("nocapture");
599     if !nocapture {
600         nocapture = match env::var("RUST_TEST_NOCAPTURE") {
601             Ok(val) => &val != "0",
602             Err(_) => false,
603         };
604     }
605
606     let test_threads = match matches.opt_str("test-threads") {
607         Some(n_str) => match n_str.parse::<usize>() {
608             Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())),
609             Ok(n) => Some(n),
610             Err(e) => {
611                 return Some(Err(format!(
612                     "argument for --test-threads must be a number > 0 \
613                      (error: {})",
614                     e
615                 )));
616             }
617         },
618         None => None,
619     };
620
621     let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
622         Some("auto") | None => AutoColor,
623         Some("always") => AlwaysColor,
624         Some("never") => NeverColor,
625
626         Some(v) => {
627             return Some(Err(format!(
628                 "argument for --color must be auto, always, or never (was \
629                  {})",
630                 v
631             )));
632         }
633     };
634
635     let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
636         None if quiet => OutputFormat::Terse,
637         Some("pretty") | None => OutputFormat::Pretty,
638         Some("terse") => OutputFormat::Terse,
639         Some("json") => {
640             if !allow_unstable {
641                 return Some(Err(
642                     "The \"json\" format is only accepted on the nightly compiler".into(),
643                 ));
644             }
645             OutputFormat::Json
646         }
647
648         Some(v) => {
649             return Some(Err(format!(
650                 "argument for --format must be pretty, terse, or json (was \
651                  {})",
652                 v
653             )));
654         }
655     };
656
657     let test_opts = TestOpts {
658         list,
659         filter,
660         filter_exact: exact,
661         exclude_should_panic,
662         run_ignored,
663         run_tests,
664         bench_benchmarks,
665         logfile,
666         nocapture,
667         color,
668         format,
669         test_threads,
670         skip: matches.opt_strs("skip"),
671         options: Options::new(),
672     };
673
674     Some(Ok(test_opts))
675 }
676
677 #[derive(Clone, PartialEq)]
678 pub struct BenchSamples {
679     ns_iter_summ: stats::Summary,
680     mb_s: usize,
681 }
682
683 #[derive(Clone, PartialEq)]
684 pub enum TestResult {
685     TrOk,
686     TrFailed,
687     TrFailedMsg(String),
688     TrIgnored,
689     TrAllowedFail,
690     TrBench(BenchSamples),
691 }
692
693 unsafe impl Send for TestResult {}
694
695 enum OutputLocation<T> {
696     Pretty(Box<term::StdoutTerminal>),
697     Raw(T),
698 }
699
700 impl<T: Write> Write for OutputLocation<T> {
701     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
702         match *self {
703             Pretty(ref mut term) => term.write(buf),
704             Raw(ref mut stdout) => stdout.write(buf),
705         }
706     }
707
708     fn flush(&mut self) -> io::Result<()> {
709         match *self {
710             Pretty(ref mut term) => term.flush(),
711             Raw(ref mut stdout) => stdout.flush(),
712         }
713     }
714 }
715
716 struct ConsoleTestState {
717     log_out: Option<File>,
718     total: usize,
719     passed: usize,
720     failed: usize,
721     ignored: usize,
722     allowed_fail: usize,
723     filtered_out: usize,
724     measured: usize,
725     metrics: MetricMap,
726     failures: Vec<(TestDesc, Vec<u8>)>,
727     not_failures: Vec<(TestDesc, Vec<u8>)>,
728     options: Options,
729 }
730
731 impl ConsoleTestState {
732     pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestState> {
733         let log_out = match opts.logfile {
734             Some(ref path) => Some(File::create(path)?),
735             None => None,
736         };
737
738         Ok(ConsoleTestState {
739             log_out,
740             total: 0,
741             passed: 0,
742             failed: 0,
743             ignored: 0,
744             allowed_fail: 0,
745             filtered_out: 0,
746             measured: 0,
747             metrics: MetricMap::new(),
748             failures: Vec::new(),
749             not_failures: Vec::new(),
750             options: opts.options,
751         })
752     }
753
754     pub fn write_log<S: AsRef<str>>(&mut self, msg: S) -> io::Result<()> {
755         let msg = msg.as_ref();
756         match self.log_out {
757             None => Ok(()),
758             Some(ref mut o) => o.write_all(msg.as_bytes()),
759         }
760     }
761
762     pub fn write_log_result(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
763         self.write_log(format!(
764             "{} {}\n",
765             match *result {
766                 TrOk => "ok".to_owned(),
767                 TrFailed => "failed".to_owned(),
768                 TrFailedMsg(ref msg) => format!("failed: {}", msg),
769                 TrIgnored => "ignored".to_owned(),
770                 TrAllowedFail => "failed (allowed)".to_owned(),
771                 TrBench(ref bs) => fmt_bench_samples(bs),
772             },
773             test.name
774         ))
775     }
776
777     fn current_test_count(&self) -> usize {
778         self.passed + self.failed + self.ignored + self.measured + self.allowed_fail
779     }
780 }
781
782 // Format a number with thousands separators
783 fn fmt_thousands_sep(mut n: usize, sep: char) -> String {
784     use std::fmt::Write;
785     let mut output = String::new();
786     let mut trailing = false;
787     for &pow in &[9, 6, 3, 0] {
788         let base = 10_usize.pow(pow);
789         if pow == 0 || trailing || n / base != 0 {
790             if !trailing {
791                 output.write_fmt(format_args!("{}", n / base)).unwrap();
792             } else {
793                 output.write_fmt(format_args!("{:03}", n / base)).unwrap();
794             }
795             if pow != 0 {
796                 output.push(sep);
797             }
798             trailing = true;
799         }
800         n %= base;
801     }
802
803     output
804 }
805
806 pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
807     use std::fmt::Write;
808     let mut output = String::new();
809
810     let median = bs.ns_iter_summ.median as usize;
811     let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
812
813     output
814         .write_fmt(format_args!(
815             "{:>11} ns/iter (+/- {})",
816             fmt_thousands_sep(median, ','),
817             fmt_thousands_sep(deviation, ',')
818         ))
819         .unwrap();
820     if bs.mb_s != 0 {
821         output
822             .write_fmt(format_args!(" = {} MB/s", bs.mb_s))
823             .unwrap();
824     }
825     output
826 }
827
828 // List the tests to console, and optionally to logfile. Filters are honored.
829 pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> {
830     let mut output = match term::stdout() {
831         None => Raw(io::stdout()),
832         Some(t) => Pretty(t),
833     };
834
835     let quiet = opts.format == OutputFormat::Terse;
836     let mut st = ConsoleTestState::new(opts)?;
837
838     let mut ntest = 0;
839     let mut nbench = 0;
840
841     for test in filter_tests(&opts, tests) {
842         use crate::TestFn::*;
843
844         let TestDescAndFn {
845             desc: TestDesc { name, .. },
846             testfn,
847         } = test;
848
849         let fntype = match testfn {
850             StaticTestFn(..) | DynTestFn(..) => {
851                 ntest += 1;
852                 "test"
853             }
854             StaticBenchFn(..) | DynBenchFn(..) => {
855                 nbench += 1;
856                 "benchmark"
857             }
858         };
859
860         writeln!(output, "{}: {}", name, fntype)?;
861         st.write_log(format!("{} {}\n", fntype, name))?;
862     }
863
864     fn plural(count: u32, s: &str) -> String {
865         match count {
866             1 => format!("{} {}", 1, s),
867             n => format!("{} {}s", n, s),
868         }
869     }
870
871     if !quiet {
872         if ntest != 0 || nbench != 0 {
873             writeln!(output, "")?;
874         }
875
876         writeln!(
877             output,
878             "{}, {}",
879             plural(ntest, "test"),
880             plural(nbench, "benchmark")
881         )?;
882     }
883
884     Ok(())
885 }
886
887 // A simple console test runner
888 pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
889     fn callback(
890         event: &TestEvent,
891         st: &mut ConsoleTestState,
892         out: &mut dyn OutputFormatter,
893     ) -> io::Result<()> {
894         match (*event).clone() {
895             TeFiltered(ref filtered_tests) => {
896                 st.total = filtered_tests.len();
897                 out.write_run_start(filtered_tests.len())
898             }
899             TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out),
900             TeWait(ref test) => out.write_test_start(test),
901             TeTimeout(ref test) => out.write_timeout(test),
902             TeResult(test, result, stdout) => {
903                 st.write_log_result(&test, &result)?;
904                 out.write_result(&test, &result, &*stdout)?;
905                 match result {
906                     TrOk => {
907                         st.passed += 1;
908                         st.not_failures.push((test, stdout));
909                     }
910                     TrIgnored => st.ignored += 1,
911                     TrAllowedFail => st.allowed_fail += 1,
912                     TrBench(bs) => {
913                         st.metrics.insert_metric(
914                             test.name.as_slice(),
915                             bs.ns_iter_summ.median,
916                             bs.ns_iter_summ.max - bs.ns_iter_summ.min,
917                         );
918                         st.measured += 1
919                     }
920                     TrFailed => {
921                         st.failed += 1;
922                         st.failures.push((test, stdout));
923                     }
924                     TrFailedMsg(msg) => {
925                         st.failed += 1;
926                         let mut stdout = stdout;
927                         stdout.extend_from_slice(format!("note: {}", msg).as_bytes());
928                         st.failures.push((test, stdout));
929                     }
930                 }
931                 Ok(())
932             }
933         }
934     }
935
936     let output = match term::stdout() {
937         None => Raw(io::stdout()),
938         Some(t) => Pretty(t),
939     };
940
941     let max_name_len = tests
942         .iter()
943         .max_by_key(|t| len_if_padded(*t))
944         .map(|t| t.desc.name.as_slice().len())
945         .unwrap_or(0);
946
947     let is_multithreaded = opts.test_threads.unwrap_or_else(get_concurrency) > 1;
948
949     let mut out: Box<dyn OutputFormatter> = match opts.format {
950         OutputFormat::Pretty => Box::new(PrettyFormatter::new(
951             output,
952             use_color(opts),
953             max_name_len,
954             is_multithreaded,
955         )),
956         OutputFormat::Terse => Box::new(TerseFormatter::new(
957             output,
958             use_color(opts),
959             max_name_len,
960             is_multithreaded,
961         )),
962         OutputFormat::Json => Box::new(JsonFormatter::new(output)),
963     };
964     let mut st = ConsoleTestState::new(opts)?;
965     fn len_if_padded(t: &TestDescAndFn) -> usize {
966         match t.testfn.padding() {
967             PadNone => 0,
968             PadOnRight => t.desc.name.as_slice().len(),
969         }
970     }
971
972     run_tests(opts, tests, |x| callback(&x, &mut st, &mut *out))?;
973
974     assert!(st.current_test_count() == st.total);
975
976     return out.write_run_finish(&st);
977 }
978
979 #[test]
980 fn should_sort_failures_before_printing_them() {
981     let test_a = TestDesc {
982         name: StaticTestName("a"),
983         ignore: false,
984         should_panic: ShouldPanic::No,
985         allow_fail: false,
986     };
987
988     let test_b = TestDesc {
989         name: StaticTestName("b"),
990         ignore: false,
991         should_panic: ShouldPanic::No,
992         allow_fail: false,
993     };
994
995     let mut out = PrettyFormatter::new(Raw(Vec::new()), false, 10, false);
996
997     let st = ConsoleTestState {
998         log_out: None,
999         total: 0,
1000         passed: 0,
1001         failed: 0,
1002         ignored: 0,
1003         allowed_fail: 0,
1004         filtered_out: 0,
1005         measured: 0,
1006         metrics: MetricMap::new(),
1007         failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
1008         options: Options::new(),
1009         not_failures: Vec::new(),
1010     };
1011
1012     out.write_failures(&st).unwrap();
1013     let s = match out.output_location() {
1014         &Raw(ref m) => String::from_utf8_lossy(&m[..]),
1015         &Pretty(_) => unreachable!(),
1016     };
1017
1018     let apos = s.find("a").unwrap();
1019     let bpos = s.find("b").unwrap();
1020     assert!(apos < bpos);
1021 }
1022
1023 fn use_color(opts: &TestOpts) -> bool {
1024     match opts.color {
1025         AutoColor => !opts.nocapture && stdout_isatty(),
1026         AlwaysColor => true,
1027         NeverColor => false,
1028     }
1029 }
1030
1031 #[cfg(any(
1032     target_os = "cloudabi",
1033     target_os = "redox",
1034     all(target_arch = "wasm32", not(target_os = "emscripten")),
1035     all(target_vendor = "fortanix", target_env = "sgx")
1036 ))]
1037 fn stdout_isatty() -> bool {
1038     // FIXME: Implement isatty on Redox and SGX
1039     false
1040 }
1041 #[cfg(unix)]
1042 fn stdout_isatty() -> bool {
1043     unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
1044 }
1045 #[cfg(windows)]
1046 fn stdout_isatty() -> bool {
1047     type DWORD = u32;
1048     type BOOL = i32;
1049     type HANDLE = *mut u8;
1050     type LPDWORD = *mut u32;
1051     const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
1052     extern "system" {
1053         fn GetStdHandle(which: DWORD) -> HANDLE;
1054         fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
1055     }
1056     unsafe {
1057         let handle = GetStdHandle(STD_OUTPUT_HANDLE);
1058         let mut out = 0;
1059         GetConsoleMode(handle, &mut out) != 0
1060     }
1061 }
1062
1063 #[derive(Clone)]
1064 pub enum TestEvent {
1065     TeFiltered(Vec<TestDesc>),
1066     TeWait(TestDesc),
1067     TeResult(TestDesc, TestResult, Vec<u8>),
1068     TeTimeout(TestDesc),
1069     TeFilteredOut(usize),
1070 }
1071
1072 pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
1073
1074 struct Sink(Arc<Mutex<Vec<u8>>>);
1075 impl Write for Sink {
1076     fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1077         Write::write(&mut *self.0.lock().unwrap(), data)
1078     }
1079     fn flush(&mut self) -> io::Result<()> {
1080         Ok(())
1081     }
1082 }
1083
1084 pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
1085 where
1086     F: FnMut(TestEvent) -> io::Result<()>,
1087 {
1088     use std::collections::{self, HashMap};
1089     use std::hash::BuildHasherDefault;
1090     use std::sync::mpsc::RecvTimeoutError;
1091     // Use a deterministic hasher
1092     type TestMap =
1093         HashMap<TestDesc, Instant, BuildHasherDefault<collections::hash_map::DefaultHasher>>;
1094
1095     let tests_len = tests.len();
1096
1097     let mut filtered_tests = filter_tests(opts, tests);
1098     if !opts.bench_benchmarks {
1099         filtered_tests = convert_benchmarks_to_tests(filtered_tests);
1100     }
1101
1102     let filtered_tests = {
1103         let mut filtered_tests = filtered_tests;
1104         for test in filtered_tests.iter_mut() {
1105             test.desc.name = test.desc.name.with_padding(test.testfn.padding());
1106         }
1107
1108         filtered_tests
1109     };
1110
1111     let filtered_out = tests_len - filtered_tests.len();
1112     callback(TeFilteredOut(filtered_out))?;
1113
1114     let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect();
1115
1116     callback(TeFiltered(filtered_descs))?;
1117
1118     let (filtered_tests, filtered_benchs): (Vec<_>, _) =
1119         filtered_tests.into_iter().partition(|e| match e.testfn {
1120             StaticTestFn(_) | DynTestFn(_) => true,
1121             _ => false,
1122         });
1123
1124     let concurrency = opts.test_threads.unwrap_or_else(get_concurrency);
1125
1126     let mut remaining = filtered_tests;
1127     remaining.reverse();
1128     let mut pending = 0;
1129
1130     let (tx, rx) = channel::<MonitorMsg>();
1131
1132     let mut running_tests: TestMap = HashMap::default();
1133
1134     fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec<TestDesc> {
1135         let now = Instant::now();
1136         let timed_out = running_tests
1137             .iter()
1138             .filter_map(|(desc, timeout)| {
1139                 if &now >= timeout {
1140                     Some(desc.clone())
1141                 } else {
1142                     None
1143                 }
1144             })
1145             .collect();
1146         for test in &timed_out {
1147             running_tests.remove(test);
1148         }
1149         timed_out
1150     };
1151
1152     fn calc_timeout(running_tests: &TestMap) -> Option<Duration> {
1153         running_tests.values().min().map(|next_timeout| {
1154             let now = Instant::now();
1155             if *next_timeout >= now {
1156                 *next_timeout - now
1157             } else {
1158                 Duration::new(0, 0)
1159             }
1160         })
1161     };
1162
1163     if concurrency == 1 {
1164         while !remaining.is_empty() {
1165             let test = remaining.pop().unwrap();
1166             callback(TeWait(test.desc.clone()))?;
1167             run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::No);
1168             let (test, result, stdout) = rx.recv().unwrap();
1169             callback(TeResult(test, result, stdout))?;
1170         }
1171     } else {
1172         while pending > 0 || !remaining.is_empty() {
1173             while pending < concurrency && !remaining.is_empty() {
1174                 let test = remaining.pop().unwrap();
1175                 let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
1176                 running_tests.insert(test.desc.clone(), timeout);
1177                 callback(TeWait(test.desc.clone()))?; //here no pad
1178                 run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::Yes);
1179                 pending += 1;
1180             }
1181
1182             let mut res;
1183             loop {
1184                 if let Some(timeout) = calc_timeout(&running_tests) {
1185                     res = rx.recv_timeout(timeout);
1186                     for test in get_timed_out_tests(&mut running_tests) {
1187                         callback(TeTimeout(test))?;
1188                     }
1189                     if res != Err(RecvTimeoutError::Timeout) {
1190                         break;
1191                     }
1192                 } else {
1193                     res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
1194                     break;
1195                 }
1196             }
1197
1198             let (desc, result, stdout) = res.unwrap();
1199             running_tests.remove(&desc);
1200
1201             callback(TeResult(desc, result, stdout))?;
1202             pending -= 1;
1203         }
1204     }
1205
1206     if opts.bench_benchmarks {
1207         // All benchmarks run at the end, in serial.
1208         for b in filtered_benchs {
1209             callback(TeWait(b.desc.clone()))?;
1210             run_test(opts, false, b, tx.clone(), Concurrent::No);
1211             let (test, result, stdout) = rx.recv().unwrap();
1212             callback(TeResult(test, result, stdout))?;
1213         }
1214     }
1215     Ok(())
1216 }
1217
1218 #[allow(deprecated)]
1219 fn get_concurrency() -> usize {
1220     return match env::var("RUST_TEST_THREADS") {
1221         Ok(s) => {
1222             let opt_n: Option<usize> = s.parse().ok();
1223             match opt_n {
1224                 Some(n) if n > 0 => n,
1225                 _ => panic!(
1226                     "RUST_TEST_THREADS is `{}`, should be a positive integer.",
1227                     s
1228                 ),
1229             }
1230         }
1231         Err(..) => num_cpus(),
1232     };
1233
1234     #[cfg(windows)]
1235     #[allow(nonstandard_style)]
1236     fn num_cpus() -> usize {
1237         #[repr(C)]
1238         struct SYSTEM_INFO {
1239             wProcessorArchitecture: u16,
1240             wReserved: u16,
1241             dwPageSize: u32,
1242             lpMinimumApplicationAddress: *mut u8,
1243             lpMaximumApplicationAddress: *mut u8,
1244             dwActiveProcessorMask: *mut u8,
1245             dwNumberOfProcessors: u32,
1246             dwProcessorType: u32,
1247             dwAllocationGranularity: u32,
1248             wProcessorLevel: u16,
1249             wProcessorRevision: u16,
1250         }
1251         extern "system" {
1252             fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
1253         }
1254         unsafe {
1255             let mut sysinfo = std::mem::zeroed();
1256             GetSystemInfo(&mut sysinfo);
1257             sysinfo.dwNumberOfProcessors as usize
1258         }
1259     }
1260
1261     #[cfg(target_os = "redox")]
1262     fn num_cpus() -> usize {
1263         // FIXME: Implement num_cpus on Redox
1264         1
1265     }
1266
1267     #[cfg(any(
1268         all(target_arch = "wasm32", not(target_os = "emscripten")),
1269         all(target_vendor = "fortanix", target_env = "sgx")
1270     ))]
1271     fn num_cpus() -> usize {
1272         1
1273     }
1274
1275     #[cfg(any(
1276         target_os = "android",
1277         target_os = "cloudabi",
1278         target_os = "emscripten",
1279         target_os = "fuchsia",
1280         target_os = "ios",
1281         target_os = "linux",
1282         target_os = "macos",
1283         target_os = "solaris"
1284     ))]
1285     fn num_cpus() -> usize {
1286         unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
1287     }
1288
1289     #[cfg(any(
1290         target_os = "freebsd",
1291         target_os = "dragonfly",
1292         target_os = "netbsd"
1293     ))]
1294     fn num_cpus() -> usize {
1295         use std::ptr;
1296
1297         let mut cpus: libc::c_uint = 0;
1298         let mut cpus_size = std::mem::size_of_val(&cpus);
1299
1300         unsafe {
1301             cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
1302         }
1303         if cpus < 1 {
1304             let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
1305             unsafe {
1306                 libc::sysctl(
1307                     mib.as_mut_ptr(),
1308                     2,
1309                     &mut cpus as *mut _ as *mut _,
1310                     &mut cpus_size as *mut _ as *mut _,
1311                     ptr::null_mut(),
1312                     0,
1313                 );
1314             }
1315             if cpus < 1 {
1316                 cpus = 1;
1317             }
1318         }
1319         cpus as usize
1320     }
1321
1322     #[cfg(target_os = "openbsd")]
1323     fn num_cpus() -> usize {
1324         use std::ptr;
1325
1326         let mut cpus: libc::c_uint = 0;
1327         let mut cpus_size = std::mem::size_of_val(&cpus);
1328         let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
1329
1330         unsafe {
1331             libc::sysctl(
1332                 mib.as_mut_ptr(),
1333                 2,
1334                 &mut cpus as *mut _ as *mut _,
1335                 &mut cpus_size as *mut _ as *mut _,
1336                 ptr::null_mut(),
1337                 0,
1338             );
1339         }
1340         if cpus < 1 {
1341             cpus = 1;
1342         }
1343         cpus as usize
1344     }
1345
1346     #[cfg(target_os = "haiku")]
1347     fn num_cpus() -> usize {
1348         // FIXME: implement
1349         1
1350     }
1351
1352     #[cfg(target_os = "l4re")]
1353     fn num_cpus() -> usize {
1354         // FIXME: implement
1355         1
1356     }
1357 }
1358
1359 pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
1360     let mut filtered = tests;
1361     let matches_filter = |test: &TestDescAndFn, filter: &str| {
1362         let test_name = test.desc.name.as_slice();
1363
1364         match opts.filter_exact {
1365             true => test_name == filter,
1366             false => test_name.contains(filter),
1367         }
1368     };
1369
1370     // Remove tests that don't match the test filter
1371     if let Some(ref filter) = opts.filter {
1372         filtered.retain(|test| matches_filter(test, filter));
1373     }
1374
1375     // Skip tests that match any of the skip filters
1376     filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf)));
1377
1378     // Excludes #[should_panic] tests
1379     if opts.exclude_should_panic {
1380         filtered.retain(|test| test.desc.should_panic == ShouldPanic::No);
1381     }
1382
1383     // maybe unignore tests
1384     match opts.run_ignored {
1385         RunIgnored::Yes => {
1386             filtered
1387                 .iter_mut()
1388                 .for_each(|test| test.desc.ignore = false);
1389         }
1390         RunIgnored::Only => {
1391             filtered.retain(|test| test.desc.ignore);
1392             filtered
1393                 .iter_mut()
1394                 .for_each(|test| test.desc.ignore = false);
1395         }
1396         RunIgnored::No => {}
1397     }
1398
1399     // Sort the tests alphabetically
1400     filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));
1401
1402     filtered
1403 }
1404
1405 pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
1406     // convert benchmarks to tests, if we're not benchmarking them
1407     tests
1408         .into_iter()
1409         .map(|x| {
1410             let testfn = match x.testfn {
1411                 DynBenchFn(bench) => DynTestFn(Box::new(move || {
1412                     bench::run_once(|b| __rust_begin_short_backtrace(|| bench.run(b)))
1413                 })),
1414                 StaticBenchFn(benchfn) => DynTestFn(Box::new(move || {
1415                     bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b)))
1416                 })),
1417                 f => f,
1418             };
1419             TestDescAndFn {
1420                 desc: x.desc,
1421                 testfn,
1422             }
1423         })
1424         .collect()
1425 }
1426
1427 pub fn run_test(
1428     opts: &TestOpts,
1429     force_ignore: bool,
1430     test: TestDescAndFn,
1431     monitor_ch: Sender<MonitorMsg>,
1432     concurrency: Concurrent,
1433 ) {
1434     let TestDescAndFn { desc, testfn } = test;
1435
1436     let ignore_because_panic_abort = cfg!(target_arch = "wasm32")
1437         && !cfg!(target_os = "emscripten")
1438         && desc.should_panic != ShouldPanic::No;
1439
1440     if force_ignore || desc.ignore || ignore_because_panic_abort {
1441         monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
1442         return;
1443     }
1444
1445     fn run_test_inner(
1446         desc: TestDesc,
1447         monitor_ch: Sender<MonitorMsg>,
1448         nocapture: bool,
1449         testfn: Box<dyn FnBox() + Send>,
1450         concurrency: Concurrent,
1451     ) {
1452         // Buffer for capturing standard I/O
1453         let data = Arc::new(Mutex::new(Vec::new()));
1454         let data2 = data.clone();
1455
1456         let name = desc.name.clone();
1457         let runtest = move || {
1458             let oldio = if !nocapture {
1459                 Some((
1460                     io::set_print(Some(Box::new(Sink(data2.clone())))),
1461                     io::set_panic(Some(Box::new(Sink(data2)))),
1462                 ))
1463             } else {
1464                 None
1465             };
1466
1467             let result = catch_unwind(AssertUnwindSafe(testfn));
1468
1469             if let Some((printio, panicio)) = oldio {
1470                 io::set_print(printio);
1471                 io::set_panic(panicio);
1472             };
1473
1474             let test_result = calc_result(&desc, result);
1475             let stdout = data.lock().unwrap().to_vec();
1476             monitor_ch
1477                 .send((desc.clone(), test_result, stdout))
1478                 .unwrap();
1479         };
1480
1481         // If the platform is single-threaded we're just going to run
1482         // the test synchronously, regardless of the concurrency
1483         // level.
1484         let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32");
1485         if concurrency == Concurrent::Yes && supports_threads {
1486             let cfg = thread::Builder::new().name(name.as_slice().to_owned());
1487             cfg.spawn(runtest).unwrap();
1488         } else {
1489             runtest();
1490         }
1491     }
1492
1493     match testfn {
1494         DynBenchFn(bencher) => {
1495             crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
1496                 bencher.run(harness)
1497             });
1498         }
1499         StaticBenchFn(benchfn) => {
1500             crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
1501                 (benchfn.clone())(harness)
1502             });
1503         }
1504         DynTestFn(f) => {
1505             let cb = move || __rust_begin_short_backtrace(f);
1506             run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb), concurrency)
1507         }
1508         StaticTestFn(f) => run_test_inner(
1509             desc,
1510             monitor_ch,
1511             opts.nocapture,
1512             Box::new(move || __rust_begin_short_backtrace(f)),
1513             concurrency,
1514         ),
1515     }
1516 }
1517
1518 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
1519 #[inline(never)]
1520 fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
1521     f()
1522 }
1523
1524 fn calc_result(desc: &TestDesc, task_result: Result<(), Box<dyn Any + Send>>) -> TestResult {
1525     match (&desc.should_panic, task_result) {
1526         (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TrOk,
1527         (&ShouldPanic::YesWithMessage(msg), Err(ref err)) => {
1528             if err
1529                 .downcast_ref::<String>()
1530                 .map(|e| &**e)
1531                 .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
1532                 .map(|e| e.contains(msg))
1533                 .unwrap_or(false)
1534             {
1535                 TrOk
1536             } else {
1537                 if desc.allow_fail {
1538                     TrAllowedFail
1539                 } else {
1540                     TrFailedMsg(format!("Panic did not include expected string '{}'", msg))
1541                 }
1542             }
1543         }
1544         _ if desc.allow_fail => TrAllowedFail,
1545         _ => TrFailed,
1546     }
1547 }
1548
1549 #[derive(Clone, PartialEq)]
1550 pub struct MetricMap(BTreeMap<String, Metric>);
1551
1552 impl MetricMap {
1553     pub fn new() -> MetricMap {
1554         MetricMap(BTreeMap::new())
1555     }
1556
1557     /// Insert a named `value` (+/- `noise`) metric into the map. The value
1558     /// must be non-negative. The `noise` indicates the uncertainty of the
1559     /// metric, which doubles as the "noise range" of acceptable
1560     /// pairwise-regressions on this named value, when comparing from one
1561     /// metric to the next using `compare_to_old`.
1562     ///
1563     /// If `noise` is positive, then it means this metric is of a value
1564     /// you want to see grow smaller, so a change larger than `noise` in the
1565     /// positive direction represents a regression.
1566     ///
1567     /// If `noise` is negative, then it means this metric is of a value
1568     /// you want to see grow larger, so a change larger than `noise` in the
1569     /// negative direction represents a regression.
1570     pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
1571         let m = Metric { value, noise };
1572         self.0.insert(name.to_owned(), m);
1573     }
1574
1575     pub fn fmt_metrics(&self) -> String {
1576         let v = self
1577             .0
1578             .iter()
1579             .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise))
1580             .collect::<Vec<_>>();
1581         v.join(", ")
1582     }
1583 }
1584
1585 // Benchmarking
1586
1587 pub use std::hint::black_box;
1588
1589 impl Bencher {
1590     /// Callback for benchmark functions to run in their body.
1591     pub fn iter<T, F>(&mut self, mut inner: F)
1592     where
1593         F: FnMut() -> T,
1594     {
1595         if self.mode == BenchMode::Single {
1596             ns_iter_inner(&mut inner, 1);
1597             return;
1598         }
1599
1600         self.summary = Some(iter(&mut inner));
1601     }
1602
1603     pub fn bench<F>(&mut self, mut f: F) -> Option<stats::Summary>
1604     where
1605         F: FnMut(&mut Bencher),
1606     {
1607         f(self);
1608         return self.summary;
1609     }
1610 }
1611
1612 fn ns_from_dur(dur: Duration) -> u64 {
1613     dur.as_secs() * 1_000_000_000 + (dur.subsec_nanos() as u64)
1614 }
1615
1616 fn ns_iter_inner<T, F>(inner: &mut F, k: u64) -> u64
1617 where
1618     F: FnMut() -> T,
1619 {
1620     let start = Instant::now();
1621     for _ in 0..k {
1622         black_box(inner());
1623     }
1624     return ns_from_dur(start.elapsed());
1625 }
1626
1627 pub fn iter<T, F>(inner: &mut F) -> stats::Summary
1628 where
1629     F: FnMut() -> T,
1630 {
1631     // Initial bench run to get ballpark figure.
1632     let ns_single = ns_iter_inner(inner, 1);
1633
1634     // Try to estimate iter count for 1ms falling back to 1m
1635     // iterations if first run took < 1ns.
1636     let ns_target_total = 1_000_000; // 1ms
1637     let mut n = ns_target_total / cmp::max(1, ns_single);
1638
1639     // if the first run took more than 1ms we don't want to just
1640     // be left doing 0 iterations on every loop. The unfortunate
1641     // side effect of not being able to do as many runs is
1642     // automatically handled by the statistical analysis below
1643     // (i.e., larger error bars).
1644     n = cmp::max(1, n);
1645
1646     let mut total_run = Duration::new(0, 0);
1647     let samples: &mut [f64] = &mut [0.0_f64; 50];
1648     loop {
1649         let loop_start = Instant::now();
1650
1651         for p in &mut *samples {
1652             *p = ns_iter_inner(inner, n) as f64 / n as f64;
1653         }
1654
1655         stats::winsorize(samples, 5.0);
1656         let summ = stats::Summary::new(samples);
1657
1658         for p in &mut *samples {
1659             let ns = ns_iter_inner(inner, 5 * n);
1660             *p = ns as f64 / (5 * n) as f64;
1661         }
1662
1663         stats::winsorize(samples, 5.0);
1664         let summ5 = stats::Summary::new(samples);
1665
1666         let loop_run = loop_start.elapsed();
1667
1668         // If we've run for 100ms and seem to have converged to a
1669         // stable median.
1670         if loop_run > Duration::from_millis(100)
1671             && summ.median_abs_dev_pct < 1.0
1672             && summ.median - summ5.median < summ5.median_abs_dev
1673         {
1674             return summ5;
1675         }
1676
1677         total_run = total_run + loop_run;
1678         // Longest we ever run for is 3s.
1679         if total_run > Duration::from_secs(3) {
1680             return summ5;
1681         }
1682
1683         // If we overflow here just return the results so far. We check a
1684         // multiplier of 10 because we're about to multiply by 2 and the
1685         // next iteration of the loop will also multiply by 5 (to calculate
1686         // the summ5 result)
1687         n = match n.checked_mul(10) {
1688             Some(_) => n * 2,
1689             None => {
1690                 return summ5;
1691             }
1692         };
1693     }
1694 }
1695
1696 pub mod bench {
1697     use super::{BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestResult};
1698     use crate::stats;
1699     use std::cmp;
1700     use std::io;
1701     use std::panic::{catch_unwind, AssertUnwindSafe};
1702     use std::sync::{Arc, Mutex};
1703
1704     pub fn benchmark<F>(desc: TestDesc, monitor_ch: Sender<MonitorMsg>, nocapture: bool, f: F)
1705     where
1706         F: FnMut(&mut Bencher),
1707     {
1708         let mut bs = Bencher {
1709             mode: BenchMode::Auto,
1710             summary: None,
1711             bytes: 0,
1712         };
1713
1714         let data = Arc::new(Mutex::new(Vec::new()));
1715         let data2 = data.clone();
1716
1717         let oldio = if !nocapture {
1718             Some((
1719                 io::set_print(Some(Box::new(Sink(data2.clone())))),
1720                 io::set_panic(Some(Box::new(Sink(data2)))),
1721             ))
1722         } else {
1723             None
1724         };
1725
1726         let result = catch_unwind(AssertUnwindSafe(|| bs.bench(f)));
1727
1728         if let Some((printio, panicio)) = oldio {
1729             io::set_print(printio);
1730             io::set_panic(panicio);
1731         };
1732
1733         let test_result = match result {
1734             //bs.bench(f) {
1735             Ok(Some(ns_iter_summ)) => {
1736                 let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1737                 let mb_s = bs.bytes * 1000 / ns_iter;
1738
1739                 let bs = BenchSamples {
1740                     ns_iter_summ,
1741                     mb_s: mb_s as usize,
1742                 };
1743                 TestResult::TrBench(bs)
1744             }
1745             Ok(None) => {
1746                 // iter not called, so no data.
1747                 // FIXME: error in this case?
1748                 let samples: &mut [f64] = &mut [0.0_f64; 1];
1749                 let bs = BenchSamples {
1750                     ns_iter_summ: stats::Summary::new(samples),
1751                     mb_s: 0,
1752                 };
1753                 TestResult::TrBench(bs)
1754             }
1755             Err(_) => TestResult::TrFailed,
1756         };
1757
1758         let stdout = data.lock().unwrap().to_vec();
1759         monitor_ch.send((desc, test_result, stdout)).unwrap();
1760     }
1761
1762     pub fn run_once<F>(f: F)
1763     where
1764         F: FnMut(&mut Bencher),
1765     {
1766         let mut bs = Bencher {
1767             mode: BenchMode::Single,
1768             summary: None,
1769             bytes: 0,
1770         };
1771         bs.bench(f);
1772     }
1773 }
1774
1775 #[cfg(test)]
1776 mod tests {
1777     use crate::bench;
1778     use crate::test::{
1779         filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored,
1780         ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TrFailed, TrFailedMsg,
1781         TrIgnored, TrOk,
1782     };
1783     use crate::Bencher;
1784     use crate::Concurrent;
1785     use std::sync::mpsc::channel;
1786
1787     fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
1788         vec![
1789             TestDescAndFn {
1790                 desc: TestDesc {
1791                     name: StaticTestName("1"),
1792                     ignore: true,
1793                     should_panic: ShouldPanic::No,
1794                     allow_fail: false,
1795                 },
1796                 testfn: DynTestFn(Box::new(move || {})),
1797             },
1798             TestDescAndFn {
1799                 desc: TestDesc {
1800                     name: StaticTestName("2"),
1801                     ignore: false,
1802                     should_panic: ShouldPanic::No,
1803                     allow_fail: false,
1804                 },
1805                 testfn: DynTestFn(Box::new(move || {})),
1806             },
1807         ]
1808     }
1809
1810     #[test]
1811     pub fn do_not_run_ignored_tests() {
1812         fn f() {
1813             panic!();
1814         }
1815         let desc = TestDescAndFn {
1816             desc: TestDesc {
1817                 name: StaticTestName("whatever"),
1818                 ignore: true,
1819                 should_panic: ShouldPanic::No,
1820                 allow_fail: false,
1821             },
1822             testfn: DynTestFn(Box::new(f)),
1823         };
1824         let (tx, rx) = channel();
1825         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1826         let (_, res, _) = rx.recv().unwrap();
1827         assert!(res != TrOk);
1828     }
1829
1830     #[test]
1831     pub fn ignored_tests_result_in_ignored() {
1832         fn f() {}
1833         let desc = TestDescAndFn {
1834             desc: TestDesc {
1835                 name: StaticTestName("whatever"),
1836                 ignore: true,
1837                 should_panic: ShouldPanic::No,
1838                 allow_fail: false,
1839             },
1840             testfn: DynTestFn(Box::new(f)),
1841         };
1842         let (tx, rx) = channel();
1843         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1844         let (_, res, _) = rx.recv().unwrap();
1845         assert!(res == TrIgnored);
1846     }
1847
1848     #[test]
1849     fn test_should_panic() {
1850         fn f() {
1851             panic!();
1852         }
1853         let desc = TestDescAndFn {
1854             desc: TestDesc {
1855                 name: StaticTestName("whatever"),
1856                 ignore: false,
1857                 should_panic: ShouldPanic::Yes,
1858                 allow_fail: false,
1859             },
1860             testfn: DynTestFn(Box::new(f)),
1861         };
1862         let (tx, rx) = channel();
1863         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1864         let (_, res, _) = rx.recv().unwrap();
1865         assert!(res == TrOk);
1866     }
1867
1868     #[test]
1869     fn test_should_panic_good_message() {
1870         fn f() {
1871             panic!("an error message");
1872         }
1873         let desc = TestDescAndFn {
1874             desc: TestDesc {
1875                 name: StaticTestName("whatever"),
1876                 ignore: false,
1877                 should_panic: ShouldPanic::YesWithMessage("error message"),
1878                 allow_fail: false,
1879             },
1880             testfn: DynTestFn(Box::new(f)),
1881         };
1882         let (tx, rx) = channel();
1883         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1884         let (_, res, _) = rx.recv().unwrap();
1885         assert!(res == TrOk);
1886     }
1887
1888     #[test]
1889     fn test_should_panic_bad_message() {
1890         fn f() {
1891             panic!("an error message");
1892         }
1893         let expected = "foobar";
1894         let failed_msg = "Panic did not include expected string";
1895         let desc = TestDescAndFn {
1896             desc: TestDesc {
1897                 name: StaticTestName("whatever"),
1898                 ignore: false,
1899                 should_panic: ShouldPanic::YesWithMessage(expected),
1900                 allow_fail: false,
1901             },
1902             testfn: DynTestFn(Box::new(f)),
1903         };
1904         let (tx, rx) = channel();
1905         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1906         let (_, res, _) = rx.recv().unwrap();
1907         assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
1908     }
1909
1910     #[test]
1911     fn test_should_panic_but_succeeds() {
1912         fn f() {}
1913         let desc = TestDescAndFn {
1914             desc: TestDesc {
1915                 name: StaticTestName("whatever"),
1916                 ignore: false,
1917                 should_panic: ShouldPanic::Yes,
1918                 allow_fail: false,
1919             },
1920             testfn: DynTestFn(Box::new(f)),
1921         };
1922         let (tx, rx) = channel();
1923         run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
1924         let (_, res, _) = rx.recv().unwrap();
1925         assert!(res == TrFailed);
1926     }
1927
1928     #[test]
1929     fn parse_ignored_flag() {
1930         let args = vec![
1931             "progname".to_string(),
1932             "filter".to_string(),
1933             "--ignored".to_string(),
1934         ];
1935         let opts = parse_opts(&args).unwrap().unwrap();
1936         assert_eq!(opts.run_ignored, RunIgnored::Only);
1937     }
1938
1939     #[test]
1940     fn parse_include_ignored_flag() {
1941         let args = vec![
1942             "progname".to_string(),
1943             "filter".to_string(),
1944             "-Zunstable-options".to_string(),
1945             "--include-ignored".to_string(),
1946         ];
1947         let opts = parse_opts(&args).unwrap().unwrap();
1948         assert_eq!(opts.run_ignored, RunIgnored::Yes);
1949     }
1950
1951     #[test]
1952     pub fn filter_for_ignored_option() {
1953         // When we run ignored tests the test filter should filter out all the
1954         // unignored tests and flip the ignore flag on the rest to false
1955
1956         let mut opts = TestOpts::new();
1957         opts.run_tests = true;
1958         opts.run_ignored = RunIgnored::Only;
1959
1960         let tests = one_ignored_one_unignored_test();
1961         let filtered = filter_tests(&opts, tests);
1962
1963         assert_eq!(filtered.len(), 1);
1964         assert_eq!(filtered[0].desc.name.to_string(), "1");
1965         assert!(!filtered[0].desc.ignore);
1966     }
1967
1968     #[test]
1969     pub fn run_include_ignored_option() {
1970         // When we "--include-ignored" tests, the ignore flag should be set to false on
1971         // all tests and no test filtered out
1972
1973         let mut opts = TestOpts::new();
1974         opts.run_tests = true;
1975         opts.run_ignored = RunIgnored::Yes;
1976
1977         let tests = one_ignored_one_unignored_test();
1978         let filtered = filter_tests(&opts, tests);
1979
1980         assert_eq!(filtered.len(), 2);
1981         assert!(!filtered[0].desc.ignore);
1982         assert!(!filtered[1].desc.ignore);
1983     }
1984
1985     #[test]
1986     pub fn exclude_should_panic_option() {
1987         let mut opts = TestOpts::new();
1988         opts.run_tests = true;
1989         opts.exclude_should_panic = true;
1990
1991         let mut tests = one_ignored_one_unignored_test();
1992         tests.push(TestDescAndFn {
1993             desc: TestDesc {
1994                 name: StaticTestName("3"),
1995                 ignore: false,
1996                 should_panic: ShouldPanic::Yes,
1997                 allow_fail: false,
1998             },
1999             testfn: DynTestFn(Box::new(move || {})),
2000         });
2001
2002         let filtered = filter_tests(&opts, tests);
2003
2004         assert_eq!(filtered.len(), 2);
2005         assert!(filtered.iter().all(|test| test.desc.should_panic == ShouldPanic::No));
2006     }
2007
2008     #[test]
2009     pub fn exact_filter_match() {
2010         fn tests() -> Vec<TestDescAndFn> {
2011             vec!["base", "base::test", "base::test1", "base::test2"]
2012                 .into_iter()
2013                 .map(|name| TestDescAndFn {
2014                     desc: TestDesc {
2015                         name: StaticTestName(name),
2016                         ignore: false,
2017                         should_panic: ShouldPanic::No,
2018                         allow_fail: false,
2019                     },
2020                     testfn: DynTestFn(Box::new(move || {})),
2021                 })
2022                 .collect()
2023         }
2024
2025         let substr = filter_tests(
2026             &TestOpts {
2027                 filter: Some("base".into()),
2028                 ..TestOpts::new()
2029             },
2030             tests(),
2031         );
2032         assert_eq!(substr.len(), 4);
2033
2034         let substr = filter_tests(
2035             &TestOpts {
2036                 filter: Some("bas".into()),
2037                 ..TestOpts::new()
2038             },
2039             tests(),
2040         );
2041         assert_eq!(substr.len(), 4);
2042
2043         let substr = filter_tests(
2044             &TestOpts {
2045                 filter: Some("::test".into()),
2046                 ..TestOpts::new()
2047             },
2048             tests(),
2049         );
2050         assert_eq!(substr.len(), 3);
2051
2052         let substr = filter_tests(
2053             &TestOpts {
2054                 filter: Some("base::test".into()),
2055                 ..TestOpts::new()
2056             },
2057             tests(),
2058         );
2059         assert_eq!(substr.len(), 3);
2060
2061         let exact = filter_tests(
2062             &TestOpts {
2063                 filter: Some("base".into()),
2064                 filter_exact: true,
2065                 ..TestOpts::new()
2066             },
2067             tests(),
2068         );
2069         assert_eq!(exact.len(), 1);
2070
2071         let exact = filter_tests(
2072             &TestOpts {
2073                 filter: Some("bas".into()),
2074                 filter_exact: true,
2075                 ..TestOpts::new()
2076             },
2077             tests(),
2078         );
2079         assert_eq!(exact.len(), 0);
2080
2081         let exact = filter_tests(
2082             &TestOpts {
2083                 filter: Some("::test".into()),
2084                 filter_exact: true,
2085                 ..TestOpts::new()
2086             },
2087             tests(),
2088         );
2089         assert_eq!(exact.len(), 0);
2090
2091         let exact = filter_tests(
2092             &TestOpts {
2093                 filter: Some("base::test".into()),
2094                 filter_exact: true,
2095                 ..TestOpts::new()
2096             },
2097             tests(),
2098         );
2099         assert_eq!(exact.len(), 1);
2100     }
2101
2102     #[test]
2103     pub fn sort_tests() {
2104         let mut opts = TestOpts::new();
2105         opts.run_tests = true;
2106
2107         let names = vec![
2108             "sha1::test".to_string(),
2109             "isize::test_to_str".to_string(),
2110             "isize::test_pow".to_string(),
2111             "test::do_not_run_ignored_tests".to_string(),
2112             "test::ignored_tests_result_in_ignored".to_string(),
2113             "test::first_free_arg_should_be_a_filter".to_string(),
2114             "test::parse_ignored_flag".to_string(),
2115             "test::parse_include_ignored_flag".to_string(),
2116             "test::filter_for_ignored_option".to_string(),
2117             "test::run_include_ignored_option".to_string(),
2118             "test::sort_tests".to_string(),
2119         ];
2120         let tests = {
2121             fn testfn() {}
2122             let mut tests = Vec::new();
2123             for name in &names {
2124                 let test = TestDescAndFn {
2125                     desc: TestDesc {
2126                         name: DynTestName((*name).clone()),
2127                         ignore: false,
2128                         should_panic: ShouldPanic::No,
2129                         allow_fail: false,
2130                     },
2131                     testfn: DynTestFn(Box::new(testfn)),
2132                 };
2133                 tests.push(test);
2134             }
2135             tests
2136         };
2137         let filtered = filter_tests(&opts, tests);
2138
2139         let expected = vec![
2140             "isize::test_pow".to_string(),
2141             "isize::test_to_str".to_string(),
2142             "sha1::test".to_string(),
2143             "test::do_not_run_ignored_tests".to_string(),
2144             "test::filter_for_ignored_option".to_string(),
2145             "test::first_free_arg_should_be_a_filter".to_string(),
2146             "test::ignored_tests_result_in_ignored".to_string(),
2147             "test::parse_ignored_flag".to_string(),
2148             "test::parse_include_ignored_flag".to_string(),
2149             "test::run_include_ignored_option".to_string(),
2150             "test::sort_tests".to_string(),
2151         ];
2152
2153         for (a, b) in expected.iter().zip(filtered) {
2154             assert!(*a == b.desc.name.to_string());
2155         }
2156     }
2157
2158     #[test]
2159     pub fn test_metricmap_compare() {
2160         let mut m1 = MetricMap::new();
2161         let mut m2 = MetricMap::new();
2162         m1.insert_metric("in-both-noise", 1000.0, 200.0);
2163         m2.insert_metric("in-both-noise", 1100.0, 200.0);
2164
2165         m1.insert_metric("in-first-noise", 1000.0, 2.0);
2166         m2.insert_metric("in-second-noise", 1000.0, 2.0);
2167
2168         m1.insert_metric("in-both-want-downwards-but-regressed", 1000.0, 10.0);
2169         m2.insert_metric("in-both-want-downwards-but-regressed", 2000.0, 10.0);
2170
2171         m1.insert_metric("in-both-want-downwards-and-improved", 2000.0, 10.0);
2172         m2.insert_metric("in-both-want-downwards-and-improved", 1000.0, 10.0);
2173
2174         m1.insert_metric("in-both-want-upwards-but-regressed", 2000.0, -10.0);
2175         m2.insert_metric("in-both-want-upwards-but-regressed", 1000.0, -10.0);
2176
2177         m1.insert_metric("in-both-want-upwards-and-improved", 1000.0, -10.0);
2178         m2.insert_metric("in-both-want-upwards-and-improved", 2000.0, -10.0);
2179     }
2180
2181     #[test]
2182     pub fn test_bench_once_no_iter() {
2183         fn f(_: &mut Bencher) {}
2184         bench::run_once(f);
2185     }
2186
2187     #[test]
2188     pub fn test_bench_once_iter() {
2189         fn f(b: &mut Bencher) {
2190             b.iter(|| {})
2191         }
2192         bench::run_once(f);
2193     }
2194
2195     #[test]
2196     pub fn test_bench_no_iter() {
2197         fn f(_: &mut Bencher) {}
2198
2199         let (tx, rx) = channel();
2200
2201         let desc = TestDesc {
2202             name: StaticTestName("f"),
2203             ignore: false,
2204             should_panic: ShouldPanic::No,
2205             allow_fail: false,
2206         };
2207
2208         crate::bench::benchmark(desc, tx, true, f);
2209         rx.recv().unwrap();
2210     }
2211
2212     #[test]
2213     pub fn test_bench_iter() {
2214         fn f(b: &mut Bencher) {
2215             b.iter(|| {})
2216         }
2217
2218         let (tx, rx) = channel();
2219
2220         let desc = TestDesc {
2221             name: StaticTestName("f"),
2222             ignore: false,
2223             should_panic: ShouldPanic::No,
2224             allow_fail: false,
2225         };
2226
2227         crate::bench::benchmark(desc, tx, true, f);
2228         rx.recv().unwrap();
2229     }
2230 }