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