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