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