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