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