]> git.lizzy.rs Git - rust.git/blob - src/libtest/lib.rs
c90c93e75acd87f930f76db4dc0fb43691a72132
[rust.git] / src / libtest / lib.rs
1 // Copyright 2012-2014 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/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 #![crate_name = "test"]
27 #![unstable(feature = "test", issue = "27812")]
28 #![crate_type = "rlib"]
29 #![crate_type = "dylib"]
30 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
31        html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
32        html_root_url = "https://doc.rust-lang.org/nightly/",
33        test(attr(deny(warnings))))]
34 #![cfg_attr(not(stage0), deny(warnings))]
35
36 #![feature(asm)]
37 #![feature(box_syntax)]
38 #![feature(fnbox)]
39 #![feature(libc)]
40 #![feature(rustc_private)]
41 #![feature(set_stdio)]
42 #![feature(staged_api)]
43 #![feature(question_mark)]
44 #![feature(panic_unwind)]
45
46 extern crate getopts;
47 extern crate term;
48 extern crate libc;
49 extern crate panic_unwind;
50
51 pub use self::TestFn::*;
52 pub use self::ColorConfig::*;
53 pub use self::TestResult::*;
54 pub use self::TestName::*;
55 use self::TestEvent::*;
56 use self::NamePadding::*;
57 use self::OutputLocation::*;
58
59 use std::boxed::FnBox;
60
61 use std::any::Any;
62 use std::cmp;
63 use std::collections::BTreeMap;
64 use std::env;
65 use std::fmt;
66 use std::fs::File;
67 use std::io::prelude::*;
68 use std::io;
69 use std::iter::repeat;
70 use std::path::PathBuf;
71 use std::sync::mpsc::{channel, Sender};
72 use std::sync::{Arc, Mutex};
73 use std::thread;
74 use std::time::{Instant, Duration};
75
76 // to be used by rustc to compile tests in libtest
77 pub mod test {
78     pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
79              TrIgnored, TrOk, Metric, MetricMap, StaticTestFn, StaticTestName, DynTestName,
80              DynTestFn, run_test, test_main, test_main_static, filter_tests, parse_opts,
81              StaticBenchFn, ShouldPanic};
82 }
83
84 pub mod stats;
85
86 // The name of a test. By convention this follows the rules for rust
87 // paths; i.e. it should be a series of identifiers separated by double
88 // colons. This way if some test runner wants to arrange the tests
89 // hierarchically it may.
90
91 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
92 pub enum TestName {
93     StaticTestName(&'static str),
94     DynTestName(String),
95 }
96 impl TestName {
97     fn as_slice(&self) -> &str {
98         match *self {
99             StaticTestName(s) => s,
100             DynTestName(ref s) => s,
101         }
102     }
103 }
104 impl fmt::Display for TestName {
105     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106         fmt::Display::fmt(self.as_slice(), f)
107     }
108 }
109
110 #[derive(Clone, Copy, PartialEq, Eq)]
111 enum NamePadding {
112     PadNone,
113     PadOnRight,
114 }
115
116 impl TestDesc {
117     fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
118         let mut name = String::from(self.name.as_slice());
119         let fill = column_count.saturating_sub(name.len());
120         let pad = repeat(" ").take(fill).collect::<String>();
121         match align {
122             PadNone => name,
123             PadOnRight => {
124                 name.push_str(&pad);
125                 name
126             }
127         }
128     }
129 }
130
131 /// Represents a benchmark function.
132 pub trait TDynBenchFn: Send {
133     fn run(&self, harness: &mut Bencher);
134 }
135
136 // A function that runs a test. If the function returns successfully,
137 // the test succeeds; if the function panics then the test fails. We
138 // may need to come up with a more clever definition of test in order
139 // to support isolation of tests into threads.
140 pub enum TestFn {
141     StaticTestFn(fn()),
142     StaticBenchFn(fn(&mut Bencher)),
143     StaticMetricFn(fn(&mut MetricMap)),
144     DynTestFn(Box<FnBox() + Send>),
145     DynMetricFn(Box<FnBox(&mut MetricMap) + Send>),
146     DynBenchFn(Box<TDynBenchFn + 'static>),
147 }
148
149 impl TestFn {
150     fn padding(&self) -> NamePadding {
151         match *self {
152             StaticTestFn(..) => PadNone,
153             StaticBenchFn(..) => PadOnRight,
154             StaticMetricFn(..) => PadOnRight,
155             DynTestFn(..) => PadNone,
156             DynMetricFn(..) => PadOnRight,
157             DynBenchFn(..) => PadOnRight,
158         }
159     }
160 }
161
162 impl fmt::Debug for TestFn {
163     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164         f.write_str(match *self {
165             StaticTestFn(..) => "StaticTestFn(..)",
166             StaticBenchFn(..) => "StaticBenchFn(..)",
167             StaticMetricFn(..) => "StaticMetricFn(..)",
168             DynTestFn(..) => "DynTestFn(..)",
169             DynMetricFn(..) => "DynMetricFn(..)",
170             DynBenchFn(..) => "DynBenchFn(..)",
171         })
172     }
173 }
174
175 /// Manager of the benchmarking runs.
176 ///
177 /// This is fed into functions marked with `#[bench]` to allow for
178 /// set-up & tear-down before running a piece of code repeatedly via a
179 /// call to `iter`.
180 #[derive(Copy, Clone)]
181 pub struct Bencher {
182     iterations: u64,
183     dur: Duration,
184     pub bytes: u64,
185 }
186
187 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
188 pub enum ShouldPanic {
189     No,
190     Yes,
191     YesWithMessage(&'static str),
192 }
193
194 // The definition of a single test. A test runner will run a list of
195 // these.
196 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
197 pub struct TestDesc {
198     pub name: TestName,
199     pub ignore: bool,
200     pub should_panic: ShouldPanic,
201 }
202
203 #[derive(Clone)]
204 pub struct TestPaths {
205     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
206     pub base: PathBuf,         // e.g., compile-test, auxiliary
207     pub relative_dir: PathBuf, // e.g., foo/bar
208 }
209
210 #[derive(Debug)]
211 pub struct TestDescAndFn {
212     pub desc: TestDesc,
213     pub testfn: TestFn,
214 }
215
216 #[derive(Clone, PartialEq, Debug, Copy)]
217 pub struct Metric {
218     value: f64,
219     noise: f64,
220 }
221
222 impl Metric {
223     pub fn new(value: f64, noise: f64) -> Metric {
224         Metric {
225             value: value,
226             noise: noise,
227         }
228     }
229 }
230
231 #[derive(PartialEq)]
232 pub struct MetricMap(BTreeMap<String, Metric>);
233
234 impl Clone for MetricMap {
235     fn clone(&self) -> MetricMap {
236         let MetricMap(ref map) = *self;
237         MetricMap(map.clone())
238     }
239 }
240
241 // The default console test runner. It accepts the command line
242 // arguments and a vector of test_descs.
243 pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>) {
244     let opts = match parse_opts(args) {
245         Some(Ok(o)) => o,
246         Some(Err(msg)) => panic!("{:?}", msg),
247         None => return,
248     };
249     match run_tests_console(&opts, tests) {
250         Ok(true) => {}
251         Ok(false) => std::process::exit(101),
252         Err(e) => panic!("io error when running tests: {:?}", e),
253     }
254 }
255
256 // A variant optimized for invocation with a static test vector.
257 // This will panic (intentionally) when fed any dynamic tests, because
258 // it is copying the static values out into a dynamic vector and cannot
259 // copy dynamic values. It is doing this because from this point on
260 // a Vec<TestDescAndFn> is used in order to effect ownership-transfer
261 // semantics into parallel test runners, which in turn requires a Vec<>
262 // rather than a &[].
263 pub fn test_main_static(tests: &[TestDescAndFn]) {
264     let args = env::args().collect::<Vec<_>>();
265     let owned_tests = tests.iter()
266                            .map(|t| {
267                                match t.testfn {
268                                    StaticTestFn(f) => {
269                                        TestDescAndFn {
270                                            testfn: StaticTestFn(f),
271                                            desc: t.desc.clone(),
272                                        }
273                                    }
274                                    StaticBenchFn(f) => {
275                                        TestDescAndFn {
276                                            testfn: StaticBenchFn(f),
277                                            desc: t.desc.clone(),
278                                        }
279                                    }
280                                    _ => panic!("non-static tests passed to test::test_main_static"),
281                                }
282                            })
283                            .collect();
284     test_main(&args, owned_tests)
285 }
286
287 #[derive(Copy, Clone)]
288 pub enum ColorConfig {
289     AutoColor,
290     AlwaysColor,
291     NeverColor,
292 }
293
294 pub struct TestOpts {
295     pub filter: Option<String>,
296     pub run_ignored: bool,
297     pub run_tests: bool,
298     pub bench_benchmarks: bool,
299     pub logfile: Option<PathBuf>,
300     pub nocapture: bool,
301     pub color: ColorConfig,
302     pub quiet: bool,
303 }
304
305 impl TestOpts {
306     #[cfg(test)]
307     fn new() -> TestOpts {
308         TestOpts {
309             filter: None,
310             run_ignored: false,
311             run_tests: false,
312             bench_benchmarks: false,
313             logfile: None,
314             nocapture: false,
315             color: AutoColor,
316             quiet: false,
317         }
318     }
319 }
320
321 /// Result of parsing the options.
322 pub type OptRes = Result<TestOpts, String>;
323
324 #[cfg_attr(rustfmt, rustfmt_skip)]
325 fn optgroups() -> Vec<getopts::OptGroup> {
326     vec!(getopts::optflag("", "ignored", "Run ignored tests"),
327       getopts::optflag("", "test", "Run tests and not benchmarks"),
328       getopts::optflag("", "bench", "Run benchmarks instead of tests"),
329       getopts::optflag("h", "help", "Display this message (longer with --help)"),
330       getopts::optopt("", "logfile", "Write logs to the specified file instead \
331                           of stdout", "PATH"),
332       getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
333                                          task, allow printing directly"),
334       getopts::optflag("q", "quiet", "Display one character per test instead of one line"),
335       getopts::optopt("", "color", "Configure coloring of output:
336             auto   = colorize if stdout is a tty and tests are run on serially (default);
337             always = always colorize output;
338             never  = never colorize output;", "auto|always|never"))
339 }
340
341 fn usage(binary: &str) {
342     let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
343     println!(r#"{usage}
344
345 The FILTER string is tested against the name of all tests, and only those
346 tests whose names contain the filter are run.
347
348 By default, all tests are run in parallel. This can be altered with the
349 RUST_TEST_THREADS environment variable when running tests (set it to 1).
350
351 All tests have their standard output and standard error captured by default.
352 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
353 environment variable to a value other than "0". Logging is not captured by default.
354
355 Test Attributes:
356
357     #[test]        - Indicates a function is a test to be run. This function
358                      takes no arguments.
359     #[bench]       - Indicates a function is a benchmark to be run. This
360                      function takes one argument (test::Bencher).
361     #[should_panic] - This function (also labeled with #[test]) will only pass if
362                      the code causes a panic (an assertion failure or panic!)
363                      A message may be provided, which the failure string must
364                      contain: #[should_panic(expected = "foo")].
365     #[ignore]      - When applied to a function which is already attributed as a
366                      test, then the test runner will ignore these tests during
367                      normal test runs. Running with --ignored will run these
368                      tests."#,
369              usage = getopts::usage(&message, &optgroups()));
370 }
371
372 // Parses command line arguments into test options
373 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
374     let args_ = &args[1..];
375     let matches = match getopts::getopts(args_, &optgroups()) {
376         Ok(m) => m,
377         Err(f) => return Some(Err(f.to_string())),
378     };
379
380     if matches.opt_present("h") {
381         usage(&args[0]);
382         return None;
383     }
384
385     let filter = if !matches.free.is_empty() {
386         Some(matches.free[0].clone())
387     } else {
388         None
389     };
390
391     let run_ignored = matches.opt_present("ignored");
392     let quiet = matches.opt_present("quiet");
393
394     let logfile = matches.opt_str("logfile");
395     let logfile = logfile.map(|s| PathBuf::from(&s));
396
397     let bench_benchmarks = matches.opt_present("bench");
398     let run_tests = !bench_benchmarks || matches.opt_present("test");
399
400     let mut nocapture = matches.opt_present("nocapture");
401     if !nocapture {
402         nocapture = match env::var("RUST_TEST_NOCAPTURE") {
403             Ok(val) => &val != "0",
404             Err(_) => false
405         };
406     }
407
408     let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
409         Some("auto") | None => AutoColor,
410         Some("always") => AlwaysColor,
411         Some("never") => NeverColor,
412
413         Some(v) => {
414             return Some(Err(format!("argument for --color must be auto, always, or never (was \
415                                      {})",
416                                     v)))
417         }
418     };
419
420     let test_opts = TestOpts {
421         filter: filter,
422         run_ignored: run_ignored,
423         run_tests: run_tests,
424         bench_benchmarks: bench_benchmarks,
425         logfile: logfile,
426         nocapture: nocapture,
427         color: color,
428         quiet: quiet,
429     };
430
431     Some(Ok(test_opts))
432 }
433
434 #[derive(Clone, PartialEq)]
435 pub struct BenchSamples {
436     ns_iter_summ: stats::Summary,
437     mb_s: usize,
438 }
439
440 #[derive(Clone, PartialEq)]
441 pub enum TestResult {
442     TrOk,
443     TrFailed,
444     TrIgnored,
445     TrMetrics(MetricMap),
446     TrBench(BenchSamples),
447 }
448
449 unsafe impl Send for TestResult {}
450
451 enum OutputLocation<T> {
452     Pretty(Box<term::StdoutTerminal>),
453     Raw(T),
454 }
455
456 struct ConsoleTestState<T> {
457     log_out: Option<File>,
458     out: OutputLocation<T>,
459     use_color: bool,
460     quiet: bool,
461     total: usize,
462     passed: usize,
463     failed: usize,
464     ignored: usize,
465     measured: usize,
466     metrics: MetricMap,
467     failures: Vec<(TestDesc, Vec<u8>)>,
468     max_name_len: usize, // number of columns to fill when aligning names
469 }
470
471 impl<T: Write> ConsoleTestState<T> {
472     pub fn new(opts: &TestOpts, _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
473         let log_out = match opts.logfile {
474             Some(ref path) => Some(File::create(path)?),
475             None => None,
476         };
477         let out = match term::stdout() {
478             None => Raw(io::stdout()),
479             Some(t) => Pretty(t),
480         };
481
482         Ok(ConsoleTestState {
483             out: out,
484             log_out: log_out,
485             use_color: use_color(opts),
486             quiet: opts.quiet,
487             total: 0,
488             passed: 0,
489             failed: 0,
490             ignored: 0,
491             measured: 0,
492             metrics: MetricMap::new(),
493             failures: Vec::new(),
494             max_name_len: 0,
495         })
496     }
497
498     pub fn write_ok(&mut self) -> io::Result<()> {
499         self.write_short_result("ok", ".", term::color::GREEN)
500     }
501
502     pub fn write_failed(&mut self) -> io::Result<()> {
503         self.write_short_result("FAILED", "F", term::color::RED)
504     }
505
506     pub fn write_ignored(&mut self) -> io::Result<()> {
507         self.write_short_result("ignored", "i", term::color::YELLOW)
508     }
509
510     pub fn write_metric(&mut self) -> io::Result<()> {
511         self.write_pretty("metric", term::color::CYAN)
512     }
513
514     pub fn write_bench(&mut self) -> io::Result<()> {
515         self.write_pretty("bench", term::color::CYAN)
516     }
517
518     pub fn write_short_result(&mut self, verbose: &str, quiet: &str, color: term::color::Color)
519                               -> io::Result<()> {
520         if self.quiet {
521             self.write_pretty(quiet, color)
522         } else {
523             self.write_pretty(verbose, color)?;
524             self.write_plain("\n")
525         }
526     }
527
528     pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
529         match self.out {
530             Pretty(ref mut term) => {
531                 if self.use_color {
532                     term.fg(color)?;
533                 }
534                 term.write_all(word.as_bytes())?;
535                 if self.use_color {
536                     term.reset()?;
537                 }
538                 term.flush()
539             }
540             Raw(ref mut stdout) => {
541                 stdout.write_all(word.as_bytes())?;
542                 stdout.flush()
543             }
544         }
545     }
546
547     pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
548         match self.out {
549             Pretty(ref mut term) => {
550                 term.write_all(s.as_bytes())?;
551                 term.flush()
552             }
553             Raw(ref mut stdout) => {
554                 stdout.write_all(s.as_bytes())?;
555                 stdout.flush()
556             }
557         }
558     }
559
560     pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
561         self.total = len;
562         let noun = if len != 1 {
563             "tests"
564         } else {
565             "test"
566         };
567         self.write_plain(&format!("\nrunning {} {}\n", len, noun))
568     }
569
570     pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
571         if self.quiet && align != PadOnRight {
572             Ok(())
573         } else {
574             let name = test.padded_name(self.max_name_len, align);
575             self.write_plain(&format!("test {} ... ", name))
576         }
577     }
578
579     pub fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
580         match *result {
581             TrOk => self.write_ok(),
582             TrFailed => self.write_failed(),
583             TrIgnored => self.write_ignored(),
584             TrMetrics(ref mm) => {
585                 self.write_metric()?;
586                 self.write_plain(&format!(": {}\n", mm.fmt_metrics()))
587             }
588             TrBench(ref bs) => {
589                 self.write_bench()?;
590                 self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
591             }
592         }
593     }
594
595     pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
596         match self.log_out {
597             None => Ok(()),
598             Some(ref mut o) => {
599                 let s = format!("{} {}\n",
600                                 match *result {
601                                     TrOk => "ok".to_owned(),
602                                     TrFailed => "failed".to_owned(),
603                                     TrIgnored => "ignored".to_owned(),
604                                     TrMetrics(ref mm) => mm.fmt_metrics(),
605                                     TrBench(ref bs) => fmt_bench_samples(bs),
606                                 },
607                                 test.name);
608                 o.write_all(s.as_bytes())
609             }
610         }
611     }
612
613     pub fn write_failures(&mut self) -> io::Result<()> {
614         self.write_plain("\nfailures:\n")?;
615         let mut failures = Vec::new();
616         let mut fail_out = String::new();
617         for &(ref f, ref stdout) in &self.failures {
618             failures.push(f.name.to_string());
619             if !stdout.is_empty() {
620                 fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
621                 let output = String::from_utf8_lossy(stdout);
622                 fail_out.push_str(&output);
623                 fail_out.push_str("\n");
624             }
625         }
626         if !fail_out.is_empty() {
627             self.write_plain("\n")?;
628             self.write_plain(&fail_out)?;
629         }
630
631         self.write_plain("\nfailures:\n")?;
632         failures.sort();
633         for name in &failures {
634             self.write_plain(&format!("    {}\n", name))?;
635         }
636         Ok(())
637     }
638
639     pub fn write_run_finish(&mut self) -> io::Result<bool> {
640         assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
641
642         let success = self.failed == 0;
643         if !success {
644             self.write_failures()?;
645         }
646
647         self.write_plain("\ntest result: ")?;
648         if success {
649             // There's no parallelism at this point so it's safe to use color
650             self.write_pretty("ok", term::color::GREEN)?;
651         } else {
652             self.write_pretty("FAILED", term::color::RED)?;
653         }
654         let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
655                         self.passed,
656                         self.failed,
657                         self.ignored,
658                         self.measured);
659         self.write_plain(&s)?;
660         return Ok(success);
661     }
662 }
663
664 // Format a number with thousands separators
665 fn fmt_thousands_sep(mut n: usize, sep: char) -> String {
666     use std::fmt::Write;
667     let mut output = String::new();
668     let mut trailing = false;
669     for &pow in &[9, 6, 3, 0] {
670         let base = 10_usize.pow(pow);
671         if pow == 0 || trailing || n / base != 0 {
672             if !trailing {
673                 output.write_fmt(format_args!("{}", n / base)).unwrap();
674             } else {
675                 output.write_fmt(format_args!("{:03}", n / base)).unwrap();
676             }
677             if pow != 0 {
678                 output.push(sep);
679             }
680             trailing = true;
681         }
682         n %= base;
683     }
684
685     output
686 }
687
688 pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
689     use std::fmt::Write;
690     let mut output = String::new();
691
692     let median = bs.ns_iter_summ.median as usize;
693     let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
694
695     output.write_fmt(format_args!("{:>11} ns/iter (+/- {})",
696                                   fmt_thousands_sep(median, ','),
697                                   fmt_thousands_sep(deviation, ',')))
698           .unwrap();
699     if bs.mb_s != 0 {
700         output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
701     }
702     output
703 }
704
705 // A simple console test runner
706 pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
707
708     fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
709         match (*event).clone() {
710             TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
711             TeWait(ref test, padding) => st.write_test_start(test, padding),
712             TeResult(test, result, stdout) => {
713                 st.write_log(&test, &result)?;
714                 st.write_result(&result)?;
715                 match result {
716                     TrOk => st.passed += 1,
717                     TrIgnored => st.ignored += 1,
718                     TrMetrics(mm) => {
719                         let tname = test.name;
720                         let MetricMap(mm) = mm;
721                         for (k, v) in &mm {
722                             st.metrics
723                               .insert_metric(&format!("{}.{}", tname, k), v.value, v.noise);
724                         }
725                         st.measured += 1
726                     }
727                     TrBench(bs) => {
728                         st.metrics.insert_metric(test.name.as_slice(),
729                                                  bs.ns_iter_summ.median,
730                                                  bs.ns_iter_summ.max - bs.ns_iter_summ.min);
731                         st.measured += 1
732                     }
733                     TrFailed => {
734                         st.failed += 1;
735                         st.failures.push((test, stdout));
736                     }
737                 }
738                 Ok(())
739             }
740         }
741     }
742
743     let mut st = ConsoleTestState::new(opts, None::<io::Stdout>)?;
744     fn len_if_padded(t: &TestDescAndFn) -> usize {
745         match t.testfn.padding() {
746             PadNone => 0,
747             PadOnRight => t.desc.name.as_slice().len(),
748         }
749     }
750     if let Some(t) = tests.iter().max_by_key(|t| len_if_padded(*t)) {
751         let n = t.desc.name.as_slice();
752         st.max_name_len = n.len();
753     }
754     run_tests(opts, tests, |x| callback(&x, &mut st))?;
755     return st.write_run_finish();
756 }
757
758 #[test]
759 fn should_sort_failures_before_printing_them() {
760     let test_a = TestDesc {
761         name: StaticTestName("a"),
762         ignore: false,
763         should_panic: ShouldPanic::No,
764     };
765
766     let test_b = TestDesc {
767         name: StaticTestName("b"),
768         ignore: false,
769         should_panic: ShouldPanic::No,
770     };
771
772     let mut st = ConsoleTestState {
773         log_out: None,
774         out: Raw(Vec::new()),
775         use_color: false,
776         quiet: false,
777         total: 0,
778         passed: 0,
779         failed: 0,
780         ignored: 0,
781         measured: 0,
782         max_name_len: 10,
783         metrics: MetricMap::new(),
784         failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
785     };
786
787     st.write_failures().unwrap();
788     let s = match st.out {
789         Raw(ref m) => String::from_utf8_lossy(&m[..]),
790         Pretty(_) => unreachable!(),
791     };
792
793     let apos = s.find("a").unwrap();
794     let bpos = s.find("b").unwrap();
795     assert!(apos < bpos);
796 }
797
798 fn use_color(opts: &TestOpts) -> bool {
799     match opts.color {
800         AutoColor => !opts.nocapture && stdout_isatty(),
801         AlwaysColor => true,
802         NeverColor => false,
803     }
804 }
805
806 #[cfg(unix)]
807 fn stdout_isatty() -> bool {
808     unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
809 }
810 #[cfg(windows)]
811 fn stdout_isatty() -> bool {
812     type DWORD = u32;
813     type BOOL = i32;
814     type HANDLE = *mut u8;
815     type LPDWORD = *mut u32;
816     const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
817     extern "system" {
818         fn GetStdHandle(which: DWORD) -> HANDLE;
819         fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
820     }
821     unsafe {
822         let handle = GetStdHandle(STD_OUTPUT_HANDLE);
823         let mut out = 0;
824         GetConsoleMode(handle, &mut out) != 0
825     }
826 }
827
828 #[derive(Clone)]
829 enum TestEvent {
830     TeFiltered(Vec<TestDesc>),
831     TeWait(TestDesc, NamePadding),
832     TeResult(TestDesc, TestResult, Vec<u8>),
833 }
834
835 pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
836
837
838 fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
839     where F: FnMut(TestEvent) -> io::Result<()>
840 {
841     let mut filtered_tests = filter_tests(opts, tests);
842     if !opts.bench_benchmarks {
843         filtered_tests = convert_benchmarks_to_tests(filtered_tests);
844     }
845
846     let filtered_descs = filtered_tests.iter()
847                                        .map(|t| t.desc.clone())
848                                        .collect();
849
850     callback(TeFiltered(filtered_descs))?;
851
852     let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
853         filtered_tests.into_iter().partition(|e| {
854             match e.testfn {
855                 StaticTestFn(_) | DynTestFn(_) => true,
856                 _ => false,
857             }
858         });
859
860     // It's tempting to just spawn all the tests at once, but since we have
861     // many tests that run in other processes we would be making a big mess.
862     let concurrency = get_concurrency();
863
864     let mut remaining = filtered_tests;
865     remaining.reverse();
866     let mut pending = 0;
867
868     let (tx, rx) = channel::<MonitorMsg>();
869
870     while pending > 0 || !remaining.is_empty() {
871         while pending < concurrency && !remaining.is_empty() {
872             let test = remaining.pop().unwrap();
873             if concurrency == 1 {
874                 // We are doing one test at a time so we can print the name
875                 // of the test before we run it. Useful for debugging tests
876                 // that hang forever.
877                 callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
878             }
879             run_test(opts, !opts.run_tests, test, tx.clone());
880             pending += 1;
881         }
882
883         let (desc, result, stdout) = rx.recv().unwrap();
884         if concurrency != 1 {
885             callback(TeWait(desc.clone(), PadNone))?;
886         }
887         callback(TeResult(desc, result, stdout))?;
888         pending -= 1;
889     }
890
891     if opts.bench_benchmarks {
892         // All benchmarks run at the end, in serial.
893         // (this includes metric fns)
894         for b in filtered_benchs_and_metrics {
895             callback(TeWait(b.desc.clone(), b.testfn.padding()))?;
896             run_test(opts, false, b, tx.clone());
897             let (test, result, stdout) = rx.recv().unwrap();
898             callback(TeResult(test, result, stdout))?;
899         }
900     }
901     Ok(())
902 }
903
904 #[allow(deprecated)]
905 fn get_concurrency() -> usize {
906     return match env::var("RUST_TEST_THREADS") {
907         Ok(s) => {
908             let opt_n: Option<usize> = s.parse().ok();
909             match opt_n {
910                 Some(n) if n > 0 => n,
911                 _ => {
912                     panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.",
913                            s)
914                 }
915             }
916         }
917         Err(..) => num_cpus(),
918     };
919
920     #[cfg(windows)]
921     #[allow(bad_style)]
922     fn num_cpus() -> usize {
923         #[repr(C)]
924         struct SYSTEM_INFO {
925             wProcessorArchitecture: u16,
926             wReserved: u16,
927             dwPageSize: u32,
928             lpMinimumApplicationAddress: *mut u8,
929             lpMaximumApplicationAddress: *mut u8,
930             dwActiveProcessorMask: *mut u8,
931             dwNumberOfProcessors: u32,
932             dwProcessorType: u32,
933             dwAllocationGranularity: u32,
934             wProcessorLevel: u16,
935             wProcessorRevision: u16,
936         }
937         extern "system" {
938             fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
939         }
940         unsafe {
941             let mut sysinfo = std::mem::zeroed();
942             GetSystemInfo(&mut sysinfo);
943             sysinfo.dwNumberOfProcessors as usize
944         }
945     }
946
947     #[cfg(any(target_os = "linux",
948               target_os = "macos",
949               target_os = "ios",
950               target_os = "android",
951               target_os = "solaris",
952               target_os = "emscripten"))]
953     fn num_cpus() -> usize {
954         unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
955     }
956
957     #[cfg(any(target_os = "freebsd",
958               target_os = "dragonfly",
959               target_os = "bitrig",
960               target_os = "netbsd"))]
961     fn num_cpus() -> usize {
962         let mut cpus: libc::c_uint = 0;
963         let mut cpus_size = std::mem::size_of_val(&cpus);
964
965         unsafe {
966             cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
967         }
968         if cpus < 1 {
969             let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
970             unsafe {
971                 libc::sysctl(mib.as_mut_ptr(),
972                              2,
973                              &mut cpus as *mut _ as *mut _,
974                              &mut cpus_size as *mut _ as *mut _,
975                              0 as *mut _,
976                              0);
977             }
978             if cpus < 1 {
979                 cpus = 1;
980             }
981         }
982         cpus as usize
983     }
984
985     #[cfg(target_os = "openbsd")]
986     fn num_cpus() -> usize {
987         let mut cpus: libc::c_uint = 0;
988         let mut cpus_size = std::mem::size_of_val(&cpus);
989         let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
990
991         unsafe {
992             libc::sysctl(mib.as_mut_ptr(),
993                          2,
994                          &mut cpus as *mut _ as *mut _,
995                          &mut cpus_size as *mut _ as *mut _,
996                          0 as *mut _,
997                          0);
998         }
999         if cpus < 1 {
1000             cpus = 1;
1001         }
1002         cpus as usize
1003     }
1004 }
1005
1006 pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
1007     let mut filtered = tests;
1008
1009     // Remove tests that don't match the test filter
1010     filtered = match opts.filter {
1011         None => filtered,
1012         Some(ref filter) => {
1013             filtered.into_iter()
1014                     .filter(|test| test.desc.name.as_slice().contains(&filter[..]))
1015                     .collect()
1016         }
1017     };
1018
1019     // Maybe pull out the ignored test and unignore them
1020     filtered = if !opts.run_ignored {
1021         filtered
1022     } else {
1023         fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> {
1024             if test.desc.ignore {
1025                 let TestDescAndFn {desc, testfn} = test;
1026                 Some(TestDescAndFn {
1027                     desc: TestDesc { ignore: false, ..desc },
1028                     testfn: testfn,
1029                 })
1030             } else {
1031                 None
1032             }
1033         }
1034         filtered.into_iter().filter_map(filter).collect()
1035     };
1036
1037     // Sort the tests alphabetically
1038     filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));
1039
1040     filtered
1041 }
1042
1043 pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
1044     // convert benchmarks to tests, if we're not benchmarking them
1045     tests.into_iter()
1046          .map(|x| {
1047              let testfn = match x.testfn {
1048                  DynBenchFn(bench) => {
1049                      DynTestFn(Box::new(move || bench::run_once(|b| bench.run(b))))
1050                  }
1051                  StaticBenchFn(benchfn) => {
1052                      DynTestFn(Box::new(move || bench::run_once(|b| benchfn(b))))
1053                  }
1054                  f => f,
1055              };
1056              TestDescAndFn {
1057                  desc: x.desc,
1058                  testfn: testfn,
1059              }
1060          })
1061          .collect()
1062 }
1063
1064 pub fn run_test(opts: &TestOpts,
1065                 force_ignore: bool,
1066                 test: TestDescAndFn,
1067                 monitor_ch: Sender<MonitorMsg>) {
1068
1069     let TestDescAndFn {desc, testfn} = test;
1070
1071     if force_ignore || desc.ignore {
1072         monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
1073         return;
1074     }
1075
1076     fn run_test_inner(desc: TestDesc,
1077                       monitor_ch: Sender<MonitorMsg>,
1078                       nocapture: bool,
1079                       testfn: Box<FnBox() + Send>) {
1080         struct Sink(Arc<Mutex<Vec<u8>>>);
1081         impl Write for Sink {
1082             fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1083                 Write::write(&mut *self.0.lock().unwrap(), data)
1084             }
1085             fn flush(&mut self) -> io::Result<()> {
1086                 Ok(())
1087             }
1088         }
1089
1090         thread::spawn(move || {
1091             let data = Arc::new(Mutex::new(Vec::new()));
1092             let data2 = data.clone();
1093             let cfg = thread::Builder::new().name(match desc.name {
1094                 DynTestName(ref name) => name.clone(),
1095                 StaticTestName(name) => name.to_owned(),
1096             });
1097
1098             let result_guard = cfg.spawn(move || {
1099                                       if !nocapture {
1100                                           io::set_print(box Sink(data2.clone()));
1101                                           io::set_panic(box Sink(data2));
1102                                       }
1103                                       testfn()
1104                                   })
1105                                   .unwrap();
1106             let test_result = calc_result(&desc, result_guard.join());
1107             let stdout = data.lock().unwrap().to_vec();
1108             monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
1109         });
1110     }
1111
1112     match testfn {
1113         DynBenchFn(bencher) => {
1114             let bs = ::bench::benchmark(|harness| bencher.run(harness));
1115             monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1116             return;
1117         }
1118         StaticBenchFn(benchfn) => {
1119             let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
1120             monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1121             return;
1122         }
1123         DynMetricFn(f) => {
1124             let mut mm = MetricMap::new();
1125             f.call_box((&mut mm,));
1126             monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1127             return;
1128         }
1129         StaticMetricFn(f) => {
1130             let mut mm = MetricMap::new();
1131             f(&mut mm);
1132             monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1133             return;
1134         }
1135         DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
1136         StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(f)),
1137     }
1138 }
1139
1140 fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any + Send>>) -> TestResult {
1141     match (&desc.should_panic, task_result) {
1142         (&ShouldPanic::No, Ok(())) |
1143         (&ShouldPanic::Yes, Err(_)) => TrOk,
1144         (&ShouldPanic::YesWithMessage(msg), Err(ref err))
1145             if err.downcast_ref::<String>()
1146                .map(|e| &**e)
1147                .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
1148                .map(|e| e.contains(msg))
1149                .unwrap_or(false) => TrOk,
1150         _ => TrFailed,
1151     }
1152 }
1153
1154 impl MetricMap {
1155     pub fn new() -> MetricMap {
1156         MetricMap(BTreeMap::new())
1157     }
1158
1159     /// Insert a named `value` (+/- `noise`) metric into the map. The value
1160     /// must be non-negative. The `noise` indicates the uncertainty of the
1161     /// metric, which doubles as the "noise range" of acceptable
1162     /// pairwise-regressions on this named value, when comparing from one
1163     /// metric to the next using `compare_to_old`.
1164     ///
1165     /// If `noise` is positive, then it means this metric is of a value
1166     /// you want to see grow smaller, so a change larger than `noise` in the
1167     /// positive direction represents a regression.
1168     ///
1169     /// If `noise` is negative, then it means this metric is of a value
1170     /// you want to see grow larger, so a change larger than `noise` in the
1171     /// negative direction represents a regression.
1172     pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
1173         let m = Metric {
1174             value: value,
1175             noise: noise,
1176         };
1177         let MetricMap(ref mut map) = *self;
1178         map.insert(name.to_owned(), m);
1179     }
1180
1181     pub fn fmt_metrics(&self) -> String {
1182         let MetricMap(ref mm) = *self;
1183         let v: Vec<String> = mm.iter()
1184                                .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise))
1185                                .collect();
1186         v.join(", ")
1187     }
1188 }
1189
1190
1191 // Benchmarking
1192
1193 /// A function that is opaque to the optimizer, to allow benchmarks to
1194 /// pretend to use outputs to assist in avoiding dead-code
1195 /// elimination.
1196 ///
1197 /// This function is a no-op, and does not even read from `dummy`.
1198 #[cfg(not(any(all(target_os = "nacl", target_arch = "le32"),
1199               target_arch = "asmjs")))]
1200 pub fn black_box<T>(dummy: T) -> T {
1201     // we need to "use" the argument in some way LLVM can't
1202     // introspect.
1203     unsafe { asm!("" : : "r"(&dummy)) }
1204     dummy
1205 }
1206 #[cfg(any(all(target_os = "nacl", target_arch = "le32"),
1207           target_arch = "asmjs"))]
1208 #[inline(never)]
1209 pub fn black_box<T>(dummy: T) -> T {
1210     dummy
1211 }
1212
1213
1214 impl Bencher {
1215     /// Callback for benchmark functions to run in their body.
1216     pub fn iter<T, F>(&mut self, mut inner: F)
1217         where F: FnMut() -> T
1218     {
1219         let start = Instant::now();
1220         let k = self.iterations;
1221         for _ in 0..k {
1222             black_box(inner());
1223         }
1224         self.dur = start.elapsed();
1225     }
1226
1227     pub fn ns_elapsed(&mut self) -> u64 {
1228         self.dur.as_secs() * 1_000_000_000 + (self.dur.subsec_nanos() as u64)
1229     }
1230
1231     pub fn ns_per_iter(&mut self) -> u64 {
1232         if self.iterations == 0 {
1233             0
1234         } else {
1235             self.ns_elapsed() / cmp::max(self.iterations, 1)
1236         }
1237     }
1238
1239     pub fn bench_n<F>(&mut self, n: u64, f: F)
1240         where F: FnOnce(&mut Bencher)
1241     {
1242         self.iterations = n;
1243         f(self);
1244     }
1245
1246     // This is a more statistics-driven benchmark algorithm
1247     pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary
1248         where F: FnMut(&mut Bencher)
1249     {
1250         // Initial bench run to get ballpark figure.
1251         let mut n = 1;
1252         self.bench_n(n, |x| f(x));
1253
1254         // Try to estimate iter count for 1ms falling back to 1m
1255         // iterations if first run took < 1ns.
1256         if self.ns_per_iter() == 0 {
1257             n = 1_000_000;
1258         } else {
1259             n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1260         }
1261         // if the first run took more than 1ms we don't want to just
1262         // be left doing 0 iterations on every loop. The unfortunate
1263         // side effect of not being able to do as many runs is
1264         // automatically handled by the statistical analysis below
1265         // (i.e. larger error bars).
1266         if n == 0 {
1267             n = 1;
1268         }
1269
1270         let mut total_run = Duration::new(0, 0);
1271         let samples: &mut [f64] = &mut [0.0_f64; 50];
1272         loop {
1273             let loop_start = Instant::now();
1274
1275             for p in &mut *samples {
1276                 self.bench_n(n, |x| f(x));
1277                 *p = self.ns_per_iter() as f64;
1278             }
1279
1280             stats::winsorize(samples, 5.0);
1281             let summ = stats::Summary::new(samples);
1282
1283             for p in &mut *samples {
1284                 self.bench_n(5 * n, |x| f(x));
1285                 *p = self.ns_per_iter() as f64;
1286             }
1287
1288             stats::winsorize(samples, 5.0);
1289             let summ5 = stats::Summary::new(samples);
1290             let loop_run = loop_start.elapsed();
1291
1292             // If we've run for 100ms and seem to have converged to a
1293             // stable median.
1294             if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 &&
1295                summ.median - summ5.median < summ5.median_abs_dev {
1296                 return summ5;
1297             }
1298
1299             total_run = total_run + loop_run;
1300             // Longest we ever run for is 3s.
1301             if total_run > Duration::from_secs(3) {
1302                 return summ5;
1303             }
1304
1305             // If we overflow here just return the results so far. We check a
1306             // multiplier of 10 because we're about to multiply by 2 and the
1307             // next iteration of the loop will also multiply by 5 (to calculate
1308             // the summ5 result)
1309             n = match n.checked_mul(10) {
1310                 Some(_) => n * 2,
1311                 None => return summ5,
1312             };
1313         }
1314     }
1315 }
1316
1317 pub mod bench {
1318     use std::cmp;
1319     use std::time::Duration;
1320     use super::{Bencher, BenchSamples};
1321
1322     pub fn benchmark<F>(f: F) -> BenchSamples
1323         where F: FnMut(&mut Bencher)
1324     {
1325         let mut bs = Bencher {
1326             iterations: 0,
1327             dur: Duration::new(0, 0),
1328             bytes: 0,
1329         };
1330
1331         let ns_iter_summ = bs.auto_bench(f);
1332
1333         let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1334         let mb_s = bs.bytes * 1000 / ns_iter;
1335
1336         BenchSamples {
1337             ns_iter_summ: ns_iter_summ,
1338             mb_s: mb_s as usize,
1339         }
1340     }
1341
1342     pub fn run_once<F>(f: F)
1343         where F: FnOnce(&mut Bencher)
1344     {
1345         let mut bs = Bencher {
1346             iterations: 0,
1347             dur: Duration::new(0, 0),
1348             bytes: 0,
1349         };
1350         bs.bench_n(1, f);
1351     }
1352 }
1353
1354 #[cfg(test)]
1355 mod tests {
1356     use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc, TestDescAndFn,
1357                TestOpts, run_test, MetricMap, StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1358     use std::sync::mpsc::channel;
1359
1360     #[test]
1361     pub fn do_not_run_ignored_tests() {
1362         fn f() {
1363             panic!();
1364         }
1365         let desc = TestDescAndFn {
1366             desc: TestDesc {
1367                 name: StaticTestName("whatever"),
1368                 ignore: true,
1369                 should_panic: ShouldPanic::No,
1370             },
1371             testfn: DynTestFn(Box::new(move || f())),
1372         };
1373         let (tx, rx) = channel();
1374         run_test(&TestOpts::new(), false, desc, tx);
1375         let (_, res, _) = rx.recv().unwrap();
1376         assert!(res != TrOk);
1377     }
1378
1379     #[test]
1380     pub fn ignored_tests_result_in_ignored() {
1381         fn f() {}
1382         let desc = TestDescAndFn {
1383             desc: TestDesc {
1384                 name: StaticTestName("whatever"),
1385                 ignore: true,
1386                 should_panic: ShouldPanic::No,
1387             },
1388             testfn: DynTestFn(Box::new(move || f())),
1389         };
1390         let (tx, rx) = channel();
1391         run_test(&TestOpts::new(), false, desc, tx);
1392         let (_, res, _) = rx.recv().unwrap();
1393         assert!(res == TrIgnored);
1394     }
1395
1396     #[test]
1397     fn test_should_panic() {
1398         fn f() {
1399             panic!();
1400         }
1401         let desc = TestDescAndFn {
1402             desc: TestDesc {
1403                 name: StaticTestName("whatever"),
1404                 ignore: false,
1405                 should_panic: ShouldPanic::Yes,
1406             },
1407             testfn: DynTestFn(Box::new(move || f())),
1408         };
1409         let (tx, rx) = channel();
1410         run_test(&TestOpts::new(), false, desc, tx);
1411         let (_, res, _) = rx.recv().unwrap();
1412         assert!(res == TrOk);
1413     }
1414
1415     #[test]
1416     fn test_should_panic_good_message() {
1417         fn f() {
1418             panic!("an error message");
1419         }
1420         let desc = TestDescAndFn {
1421             desc: TestDesc {
1422                 name: StaticTestName("whatever"),
1423                 ignore: false,
1424                 should_panic: ShouldPanic::YesWithMessage("error message"),
1425             },
1426             testfn: DynTestFn(Box::new(move || f())),
1427         };
1428         let (tx, rx) = channel();
1429         run_test(&TestOpts::new(), false, desc, tx);
1430         let (_, res, _) = rx.recv().unwrap();
1431         assert!(res == TrOk);
1432     }
1433
1434     #[test]
1435     fn test_should_panic_bad_message() {
1436         fn f() {
1437             panic!("an error message");
1438         }
1439         let desc = TestDescAndFn {
1440             desc: TestDesc {
1441                 name: StaticTestName("whatever"),
1442                 ignore: false,
1443                 should_panic: ShouldPanic::YesWithMessage("foobar"),
1444             },
1445             testfn: DynTestFn(Box::new(move || f())),
1446         };
1447         let (tx, rx) = channel();
1448         run_test(&TestOpts::new(), false, desc, tx);
1449         let (_, res, _) = rx.recv().unwrap();
1450         assert!(res == TrFailed);
1451     }
1452
1453     #[test]
1454     fn test_should_panic_but_succeeds() {
1455         fn f() {}
1456         let desc = TestDescAndFn {
1457             desc: TestDesc {
1458                 name: StaticTestName("whatever"),
1459                 ignore: false,
1460                 should_panic: ShouldPanic::Yes,
1461             },
1462             testfn: DynTestFn(Box::new(move || f())),
1463         };
1464         let (tx, rx) = channel();
1465         run_test(&TestOpts::new(), false, desc, tx);
1466         let (_, res, _) = rx.recv().unwrap();
1467         assert!(res == TrFailed);
1468     }
1469
1470     #[test]
1471     fn parse_ignored_flag() {
1472         let args = vec!["progname".to_string(), "filter".to_string(), "--ignored".to_string()];
1473         let opts = match parse_opts(&args) {
1474             Some(Ok(o)) => o,
1475             _ => panic!("Malformed arg in parse_ignored_flag"),
1476         };
1477         assert!((opts.run_ignored));
1478     }
1479
1480     #[test]
1481     pub fn filter_for_ignored_option() {
1482         // When we run ignored tests the test filter should filter out all the
1483         // unignored tests and flip the ignore flag on the rest to false
1484
1485         let mut opts = TestOpts::new();
1486         opts.run_tests = true;
1487         opts.run_ignored = true;
1488
1489         let tests = vec![TestDescAndFn {
1490                              desc: TestDesc {
1491                                  name: StaticTestName("1"),
1492                                  ignore: true,
1493                                  should_panic: ShouldPanic::No,
1494                              },
1495                              testfn: DynTestFn(Box::new(move || {})),
1496                          },
1497                          TestDescAndFn {
1498                              desc: TestDesc {
1499                                  name: StaticTestName("2"),
1500                                  ignore: false,
1501                                  should_panic: ShouldPanic::No,
1502                              },
1503                              testfn: DynTestFn(Box::new(move || {})),
1504                          }];
1505         let filtered = filter_tests(&opts, tests);
1506
1507         assert_eq!(filtered.len(), 1);
1508         assert_eq!(filtered[0].desc.name.to_string(), "1");
1509         assert!(!filtered[0].desc.ignore);
1510     }
1511
1512     #[test]
1513     pub fn sort_tests() {
1514         let mut opts = TestOpts::new();
1515         opts.run_tests = true;
1516
1517         let names = vec!["sha1::test".to_string(),
1518                          "isize::test_to_str".to_string(),
1519                          "isize::test_pow".to_string(),
1520                          "test::do_not_run_ignored_tests".to_string(),
1521                          "test::ignored_tests_result_in_ignored".to_string(),
1522                          "test::first_free_arg_should_be_a_filter".to_string(),
1523                          "test::parse_ignored_flag".to_string(),
1524                          "test::filter_for_ignored_option".to_string(),
1525                          "test::sort_tests".to_string()];
1526         let tests = {
1527             fn testfn() {}
1528             let mut tests = Vec::new();
1529             for name in &names {
1530                 let test = TestDescAndFn {
1531                     desc: TestDesc {
1532                         name: DynTestName((*name).clone()),
1533                         ignore: false,
1534                         should_panic: ShouldPanic::No,
1535                     },
1536                     testfn: DynTestFn(Box::new(testfn)),
1537                 };
1538                 tests.push(test);
1539             }
1540             tests
1541         };
1542         let filtered = filter_tests(&opts, tests);
1543
1544         let expected = vec!["isize::test_pow".to_string(),
1545                             "isize::test_to_str".to_string(),
1546                             "sha1::test".to_string(),
1547                             "test::do_not_run_ignored_tests".to_string(),
1548                             "test::filter_for_ignored_option".to_string(),
1549                             "test::first_free_arg_should_be_a_filter".to_string(),
1550                             "test::ignored_tests_result_in_ignored".to_string(),
1551                             "test::parse_ignored_flag".to_string(),
1552                             "test::sort_tests".to_string()];
1553
1554         for (a, b) in expected.iter().zip(filtered) {
1555             assert!(*a == b.desc.name.to_string());
1556         }
1557     }
1558
1559     #[test]
1560     pub fn test_metricmap_compare() {
1561         let mut m1 = MetricMap::new();
1562         let mut m2 = MetricMap::new();
1563         m1.insert_metric("in-both-noise", 1000.0, 200.0);
1564         m2.insert_metric("in-both-noise", 1100.0, 200.0);
1565
1566         m1.insert_metric("in-first-noise", 1000.0, 2.0);
1567         m2.insert_metric("in-second-noise", 1000.0, 2.0);
1568
1569         m1.insert_metric("in-both-want-downwards-but-regressed", 1000.0, 10.0);
1570         m2.insert_metric("in-both-want-downwards-but-regressed", 2000.0, 10.0);
1571
1572         m1.insert_metric("in-both-want-downwards-and-improved", 2000.0, 10.0);
1573         m2.insert_metric("in-both-want-downwards-and-improved", 1000.0, 10.0);
1574
1575         m1.insert_metric("in-both-want-upwards-but-regressed", 2000.0, -10.0);
1576         m2.insert_metric("in-both-want-upwards-but-regressed", 1000.0, -10.0);
1577
1578         m1.insert_metric("in-both-want-upwards-and-improved", 1000.0, -10.0);
1579         m2.insert_metric("in-both-want-upwards-and-improved", 2000.0, -10.0);
1580     }
1581 }