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