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