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