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