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