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