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