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