]> git.lizzy.rs Git - rust.git/blob - library/test/src/tests.rs
Move `os_str_bytes` to `sys::unix` and reuse it on other platforms.
[rust.git] / library / test / src / tests.rs
1 use super::*;
2
3 use crate::{
4     bench::Bencher,
5     console::OutputLocation,
6     formatters::PrettyFormatter,
7     options::OutputFormat,
8     test::{
9         filter_tests,
10         parse_opts,
11         run_test,
12         DynTestFn,
13         DynTestName,
14         MetricMap,
15         RunIgnored,
16         RunStrategy,
17         ShouldPanic,
18         StaticTestName,
19         TestDesc,
20         TestDescAndFn,
21         TestOpts,
22         TrIgnored,
23         TrOk,
24         // FIXME (introduced by #65251)
25         // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions,
26         // TestType, TrFailedMsg, TrIgnored, TrOk,
27     },
28     time::{TestTimeOptions, TimeThreshold},
29 };
30 use std::sync::mpsc::channel;
31 use std::time::Duration;
32
33 impl TestOpts {
34     fn new() -> TestOpts {
35         TestOpts {
36             list: false,
37             filters: vec![],
38             filter_exact: false,
39             force_run_in_process: false,
40             exclude_should_panic: false,
41             run_ignored: RunIgnored::No,
42             run_tests: false,
43             bench_benchmarks: false,
44             logfile: None,
45             nocapture: false,
46             color: AutoColor,
47             format: OutputFormat::Pretty,
48             test_threads: None,
49             skip: vec![],
50             time_options: None,
51             options: Options::new(),
52         }
53     }
54 }
55
56 fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
57     vec![
58         TestDescAndFn {
59             desc: TestDesc {
60                 name: StaticTestName("1"),
61                 ignore: true,
62                 should_panic: ShouldPanic::No,
63                 allow_fail: false,
64                 compile_fail: false,
65                 no_run: false,
66                 test_type: TestType::Unknown,
67             },
68             testfn: DynTestFn(Box::new(move || {})),
69         },
70         TestDescAndFn {
71             desc: TestDesc {
72                 name: StaticTestName("2"),
73                 ignore: false,
74                 should_panic: ShouldPanic::No,
75                 allow_fail: false,
76                 compile_fail: false,
77                 no_run: false,
78                 test_type: TestType::Unknown,
79             },
80             testfn: DynTestFn(Box::new(move || {})),
81         },
82     ]
83 }
84
85 #[test]
86 pub fn do_not_run_ignored_tests() {
87     fn f() {
88         panic!();
89     }
90     let desc = TestDescAndFn {
91         desc: TestDesc {
92             name: StaticTestName("whatever"),
93             ignore: true,
94             should_panic: ShouldPanic::No,
95             allow_fail: false,
96             compile_fail: false,
97             no_run: false,
98             test_type: TestType::Unknown,
99         },
100         testfn: DynTestFn(Box::new(f)),
101     };
102     let (tx, rx) = channel();
103     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
104     let result = rx.recv().unwrap().result;
105     assert_ne!(result, TrOk);
106 }
107
108 #[test]
109 pub fn ignored_tests_result_in_ignored() {
110     fn f() {}
111     let desc = TestDescAndFn {
112         desc: TestDesc {
113             name: StaticTestName("whatever"),
114             ignore: true,
115             should_panic: ShouldPanic::No,
116             allow_fail: false,
117             compile_fail: false,
118             no_run: false,
119             test_type: TestType::Unknown,
120         },
121         testfn: DynTestFn(Box::new(f)),
122     };
123     let (tx, rx) = channel();
124     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
125     let result = rx.recv().unwrap().result;
126     assert_eq!(result, TrIgnored);
127 }
128
129 // FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
130 #[test]
131 #[cfg(not(target_os = "emscripten"))]
132 fn test_should_panic() {
133     fn f() {
134         panic!();
135     }
136     let desc = TestDescAndFn {
137         desc: TestDesc {
138             name: StaticTestName("whatever"),
139             ignore: false,
140             should_panic: ShouldPanic::Yes,
141             allow_fail: false,
142             compile_fail: false,
143             no_run: false,
144             test_type: TestType::Unknown,
145         },
146         testfn: DynTestFn(Box::new(f)),
147     };
148     let (tx, rx) = channel();
149     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
150     let result = rx.recv().unwrap().result;
151     assert_eq!(result, TrOk);
152 }
153
154 // FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
155 #[test]
156 #[cfg(not(target_os = "emscripten"))]
157 fn test_should_panic_good_message() {
158     fn f() {
159         panic!("an error message");
160     }
161     let desc = TestDescAndFn {
162         desc: TestDesc {
163             name: StaticTestName("whatever"),
164             ignore: false,
165             should_panic: ShouldPanic::YesWithMessage("error message"),
166             allow_fail: false,
167             compile_fail: false,
168             no_run: false,
169             test_type: TestType::Unknown,
170         },
171         testfn: DynTestFn(Box::new(f)),
172     };
173     let (tx, rx) = channel();
174     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
175     let result = rx.recv().unwrap().result;
176     assert_eq!(result, TrOk);
177 }
178
179 // FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
180 #[test]
181 #[cfg(not(target_os = "emscripten"))]
182 fn test_should_panic_bad_message() {
183     use crate::tests::TrFailedMsg;
184     fn f() {
185         panic!("an error message");
186     }
187     let expected = "foobar";
188     let failed_msg = r#"panic did not contain expected string
189       panic message: `"an error message"`,
190  expected substring: `"foobar"`"#;
191     let desc = TestDescAndFn {
192         desc: TestDesc {
193             name: StaticTestName("whatever"),
194             ignore: false,
195             should_panic: ShouldPanic::YesWithMessage(expected),
196             allow_fail: false,
197             compile_fail: false,
198             no_run: false,
199             test_type: TestType::Unknown,
200         },
201         testfn: DynTestFn(Box::new(f)),
202     };
203     let (tx, rx) = channel();
204     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
205     let result = rx.recv().unwrap().result;
206     assert_eq!(result, TrFailedMsg(failed_msg.to_string()));
207 }
208
209 // FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
210 #[test]
211 #[cfg(not(target_os = "emscripten"))]
212 fn test_should_panic_non_string_message_type() {
213     use crate::tests::TrFailedMsg;
214     use std::any::TypeId;
215     fn f() {
216         std::panic::panic_any(1i32);
217     }
218     let expected = "foobar";
219     let failed_msg = format!(
220         r#"expected panic with string value,
221  found non-string value: `{:?}`
222      expected substring: `"foobar"`"#,
223         TypeId::of::<i32>()
224     );
225     let desc = TestDescAndFn {
226         desc: TestDesc {
227             name: StaticTestName("whatever"),
228             ignore: false,
229             should_panic: ShouldPanic::YesWithMessage(expected),
230             allow_fail: false,
231             compile_fail: false,
232             no_run: false,
233             test_type: TestType::Unknown,
234         },
235         testfn: DynTestFn(Box::new(f)),
236     };
237     let (tx, rx) = channel();
238     run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
239     let result = rx.recv().unwrap().result;
240     assert_eq!(result, TrFailedMsg(failed_msg));
241 }
242
243 // FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
244 #[test]
245 #[cfg(not(target_os = "emscripten"))]
246 fn test_should_panic_but_succeeds() {
247     let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")];
248
249     for &should_panic in should_panic_variants.iter() {
250         fn f() {}
251         let desc = TestDescAndFn {
252             desc: TestDesc {
253                 name: StaticTestName("whatever"),
254                 ignore: false,
255                 should_panic,
256                 allow_fail: false,
257                 compile_fail: false,
258                 no_run: false,
259                 test_type: TestType::Unknown,
260             },
261             testfn: DynTestFn(Box::new(f)),
262         };
263         let (tx, rx) = channel();
264         run_test(
265             &TestOpts::new(),
266             false,
267             TestId(0),
268             desc,
269             RunStrategy::InProcess,
270             tx,
271             Concurrent::No,
272         );
273         let result = rx.recv().unwrap().result;
274         assert_eq!(
275             result,
276             TrFailedMsg("test did not panic as expected".to_string()),
277             "should_panic == {:?}",
278             should_panic
279         );
280     }
281 }
282
283 fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
284     fn f() {}
285     let desc = TestDescAndFn {
286         desc: TestDesc {
287             name: StaticTestName("whatever"),
288             ignore: false,
289             should_panic: ShouldPanic::No,
290             allow_fail: false,
291             compile_fail: false,
292             no_run: false,
293             test_type: TestType::Unknown,
294         },
295         testfn: DynTestFn(Box::new(f)),
296     };
297     let time_options = if report_time { Some(TestTimeOptions::default()) } else { None };
298
299     let test_opts = TestOpts { time_options, ..TestOpts::new() };
300     let (tx, rx) = channel();
301     run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
302     let exec_time = rx.recv().unwrap().exec_time;
303     exec_time
304 }
305
306 #[test]
307 fn test_should_not_report_time() {
308     let exec_time = report_time_test_template(false);
309     assert!(exec_time.is_none());
310 }
311
312 #[test]
313 fn test_should_report_time() {
314     let exec_time = report_time_test_template(true);
315     assert!(exec_time.is_some());
316 }
317
318 fn time_test_failure_template(test_type: TestType) -> TestResult {
319     fn f() {}
320     let desc = TestDescAndFn {
321         desc: TestDesc {
322             name: StaticTestName("whatever"),
323             ignore: false,
324             should_panic: ShouldPanic::No,
325             allow_fail: false,
326             compile_fail: false,
327             no_run: false,
328             test_type,
329         },
330         testfn: DynTestFn(Box::new(f)),
331     };
332     // `Default` will initialize all the thresholds to 0 milliseconds.
333     let mut time_options = TestTimeOptions::default();
334     time_options.error_on_excess = true;
335
336     let test_opts = TestOpts { time_options: Some(time_options), ..TestOpts::new() };
337     let (tx, rx) = channel();
338     run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
339     let result = rx.recv().unwrap().result;
340
341     result
342 }
343
344 #[test]
345 fn test_error_on_exceed() {
346     let types = [TestType::UnitTest, TestType::IntegrationTest, TestType::DocTest];
347
348     for test_type in types.iter() {
349         let result = time_test_failure_template(*test_type);
350
351         assert_eq!(result, TestResult::TrTimedFail);
352     }
353
354     // Check that for unknown tests thresholds aren't applied.
355     let result = time_test_failure_template(TestType::Unknown);
356     assert_eq!(result, TestResult::TrOk);
357 }
358
359 fn typed_test_desc(test_type: TestType) -> TestDesc {
360     TestDesc {
361         name: StaticTestName("whatever"),
362         ignore: false,
363         should_panic: ShouldPanic::No,
364         allow_fail: false,
365         compile_fail: false,
366         no_run: false,
367         test_type,
368     }
369 }
370
371 fn test_exec_time(millis: u64) -> TestExecTime {
372     TestExecTime(Duration::from_millis(millis))
373 }
374
375 #[test]
376 fn test_time_options_threshold() {
377     let unit = TimeThreshold::new(Duration::from_millis(50), Duration::from_millis(100));
378     let integration = TimeThreshold::new(Duration::from_millis(500), Duration::from_millis(1000));
379     let doc = TimeThreshold::new(Duration::from_millis(5000), Duration::from_millis(10000));
380
381     let options = TestTimeOptions {
382         error_on_excess: false,
383         colored: false,
384         unit_threshold: unit.clone(),
385         integration_threshold: integration.clone(),
386         doctest_threshold: doc.clone(),
387     };
388
389     let test_vector = [
390         (TestType::UnitTest, unit.warn.as_millis() - 1, false, false),
391         (TestType::UnitTest, unit.warn.as_millis(), true, false),
392         (TestType::UnitTest, unit.critical.as_millis(), true, true),
393         (TestType::IntegrationTest, integration.warn.as_millis() - 1, false, false),
394         (TestType::IntegrationTest, integration.warn.as_millis(), true, false),
395         (TestType::IntegrationTest, integration.critical.as_millis(), true, true),
396         (TestType::DocTest, doc.warn.as_millis() - 1, false, false),
397         (TestType::DocTest, doc.warn.as_millis(), true, false),
398         (TestType::DocTest, doc.critical.as_millis(), true, true),
399     ];
400
401     for (test_type, time, expected_warn, expected_critical) in test_vector.iter() {
402         let test_desc = typed_test_desc(*test_type);
403         let exec_time = test_exec_time(*time as u64);
404
405         assert_eq!(options.is_warn(&test_desc, &exec_time), *expected_warn);
406         assert_eq!(options.is_critical(&test_desc, &exec_time), *expected_critical);
407     }
408 }
409
410 #[test]
411 fn parse_ignored_flag() {
412     let args = vec!["progname".to_string(), "filter".to_string(), "--ignored".to_string()];
413     let opts = parse_opts(&args).unwrap().unwrap();
414     assert_eq!(opts.run_ignored, RunIgnored::Only);
415 }
416
417 #[test]
418 fn parse_show_output_flag() {
419     let args = vec!["progname".to_string(), "filter".to_string(), "--show-output".to_string()];
420     let opts = parse_opts(&args).unwrap().unwrap();
421     assert!(opts.options.display_output);
422 }
423
424 #[test]
425 fn parse_include_ignored_flag() {
426     let args = vec!["progname".to_string(), "filter".to_string(), "--include-ignored".to_string()];
427     let opts = parse_opts(&args).unwrap().unwrap();
428     assert_eq!(opts.run_ignored, RunIgnored::Yes);
429 }
430
431 #[test]
432 pub fn filter_for_ignored_option() {
433     // When we run ignored tests the test filter should filter out all the
434     // unignored tests and flip the ignore flag on the rest to false
435
436     let mut opts = TestOpts::new();
437     opts.run_tests = true;
438     opts.run_ignored = RunIgnored::Only;
439
440     let tests = one_ignored_one_unignored_test();
441     let filtered = filter_tests(&opts, tests);
442
443     assert_eq!(filtered.len(), 1);
444     assert_eq!(filtered[0].desc.name.to_string(), "1");
445     assert!(!filtered[0].desc.ignore);
446 }
447
448 #[test]
449 pub fn run_include_ignored_option() {
450     // When we "--include-ignored" tests, the ignore flag should be set to false on
451     // all tests and no test filtered out
452
453     let mut opts = TestOpts::new();
454     opts.run_tests = true;
455     opts.run_ignored = RunIgnored::Yes;
456
457     let tests = one_ignored_one_unignored_test();
458     let filtered = filter_tests(&opts, tests);
459
460     assert_eq!(filtered.len(), 2);
461     assert!(!filtered[0].desc.ignore);
462     assert!(!filtered[1].desc.ignore);
463 }
464
465 #[test]
466 pub fn exclude_should_panic_option() {
467     let mut opts = TestOpts::new();
468     opts.run_tests = true;
469     opts.exclude_should_panic = true;
470
471     let mut tests = one_ignored_one_unignored_test();
472     tests.push(TestDescAndFn {
473         desc: TestDesc {
474             name: StaticTestName("3"),
475             ignore: false,
476             should_panic: ShouldPanic::Yes,
477             allow_fail: false,
478             compile_fail: false,
479             no_run: false,
480             test_type: TestType::Unknown,
481         },
482         testfn: DynTestFn(Box::new(move || {})),
483     });
484
485     let filtered = filter_tests(&opts, tests);
486
487     assert_eq!(filtered.len(), 2);
488     assert!(filtered.iter().all(|test| test.desc.should_panic == ShouldPanic::No));
489 }
490
491 #[test]
492 pub fn exact_filter_match() {
493     fn tests() -> Vec<TestDescAndFn> {
494         vec!["base", "base::test", "base::test1", "base::test2"]
495             .into_iter()
496             .map(|name| TestDescAndFn {
497                 desc: TestDesc {
498                     name: StaticTestName(name),
499                     ignore: false,
500                     should_panic: ShouldPanic::No,
501                     allow_fail: false,
502                     compile_fail: false,
503                     no_run: false,
504                     test_type: TestType::Unknown,
505                 },
506                 testfn: DynTestFn(Box::new(move || {})),
507             })
508             .collect()
509     }
510
511     let substr =
512         filter_tests(&TestOpts { filters: vec!["base".into()], ..TestOpts::new() }, tests());
513     assert_eq!(substr.len(), 4);
514
515     let substr =
516         filter_tests(&TestOpts { filters: vec!["bas".into()], ..TestOpts::new() }, tests());
517     assert_eq!(substr.len(), 4);
518
519     let substr =
520         filter_tests(&TestOpts { filters: vec!["::test".into()], ..TestOpts::new() }, tests());
521     assert_eq!(substr.len(), 3);
522
523     let substr =
524         filter_tests(&TestOpts { filters: vec!["base::test".into()], ..TestOpts::new() }, tests());
525     assert_eq!(substr.len(), 3);
526
527     let substr = filter_tests(
528         &TestOpts { filters: vec!["test1".into(), "test2".into()], ..TestOpts::new() },
529         tests(),
530     );
531     assert_eq!(substr.len(), 2);
532
533     let exact = filter_tests(
534         &TestOpts { filters: vec!["base".into()], filter_exact: true, ..TestOpts::new() },
535         tests(),
536     );
537     assert_eq!(exact.len(), 1);
538
539     let exact = filter_tests(
540         &TestOpts { filters: vec!["bas".into()], filter_exact: true, ..TestOpts::new() },
541         tests(),
542     );
543     assert_eq!(exact.len(), 0);
544
545     let exact = filter_tests(
546         &TestOpts { filters: vec!["::test".into()], filter_exact: true, ..TestOpts::new() },
547         tests(),
548     );
549     assert_eq!(exact.len(), 0);
550
551     let exact = filter_tests(
552         &TestOpts { filters: vec!["base::test".into()], filter_exact: true, ..TestOpts::new() },
553         tests(),
554     );
555     assert_eq!(exact.len(), 1);
556
557     let exact = filter_tests(
558         &TestOpts {
559             filters: vec!["base".into(), "base::test".into()],
560             filter_exact: true,
561             ..TestOpts::new()
562         },
563         tests(),
564     );
565     assert_eq!(exact.len(), 2);
566 }
567
568 #[test]
569 pub fn sort_tests() {
570     let mut opts = TestOpts::new();
571     opts.run_tests = true;
572
573     let names = vec![
574         "sha1::test".to_string(),
575         "isize::test_to_str".to_string(),
576         "isize::test_pow".to_string(),
577         "test::do_not_run_ignored_tests".to_string(),
578         "test::ignored_tests_result_in_ignored".to_string(),
579         "test::first_free_arg_should_be_a_filter".to_string(),
580         "test::parse_ignored_flag".to_string(),
581         "test::parse_include_ignored_flag".to_string(),
582         "test::filter_for_ignored_option".to_string(),
583         "test::run_include_ignored_option".to_string(),
584         "test::sort_tests".to_string(),
585     ];
586     let tests = {
587         fn testfn() {}
588         let mut tests = Vec::new();
589         for name in &names {
590             let test = TestDescAndFn {
591                 desc: TestDesc {
592                     name: DynTestName((*name).clone()),
593                     ignore: false,
594                     should_panic: ShouldPanic::No,
595                     allow_fail: false,
596                     compile_fail: false,
597                     no_run: false,
598                     test_type: TestType::Unknown,
599                 },
600                 testfn: DynTestFn(Box::new(testfn)),
601             };
602             tests.push(test);
603         }
604         tests
605     };
606     let filtered = filter_tests(&opts, tests);
607
608     let expected = vec![
609         "isize::test_pow".to_string(),
610         "isize::test_to_str".to_string(),
611         "sha1::test".to_string(),
612         "test::do_not_run_ignored_tests".to_string(),
613         "test::filter_for_ignored_option".to_string(),
614         "test::first_free_arg_should_be_a_filter".to_string(),
615         "test::ignored_tests_result_in_ignored".to_string(),
616         "test::parse_ignored_flag".to_string(),
617         "test::parse_include_ignored_flag".to_string(),
618         "test::run_include_ignored_option".to_string(),
619         "test::sort_tests".to_string(),
620     ];
621
622     for (a, b) in expected.iter().zip(filtered) {
623         assert_eq!(*a, b.desc.name.to_string());
624     }
625 }
626
627 #[test]
628 pub fn test_metricmap_compare() {
629     let mut m1 = MetricMap::new();
630     let mut m2 = MetricMap::new();
631     m1.insert_metric("in-both-noise", 1000.0, 200.0);
632     m2.insert_metric("in-both-noise", 1100.0, 200.0);
633
634     m1.insert_metric("in-first-noise", 1000.0, 2.0);
635     m2.insert_metric("in-second-noise", 1000.0, 2.0);
636
637     m1.insert_metric("in-both-want-downwards-but-regressed", 1000.0, 10.0);
638     m2.insert_metric("in-both-want-downwards-but-regressed", 2000.0, 10.0);
639
640     m1.insert_metric("in-both-want-downwards-and-improved", 2000.0, 10.0);
641     m2.insert_metric("in-both-want-downwards-and-improved", 1000.0, 10.0);
642
643     m1.insert_metric("in-both-want-upwards-but-regressed", 2000.0, -10.0);
644     m2.insert_metric("in-both-want-upwards-but-regressed", 1000.0, -10.0);
645
646     m1.insert_metric("in-both-want-upwards-and-improved", 1000.0, -10.0);
647     m2.insert_metric("in-both-want-upwards-and-improved", 2000.0, -10.0);
648 }
649
650 #[test]
651 pub fn test_bench_once_no_iter() {
652     fn f(_: &mut Bencher) {}
653     bench::run_once(f);
654 }
655
656 #[test]
657 pub fn test_bench_once_iter() {
658     fn f(b: &mut Bencher) {
659         b.iter(|| {})
660     }
661     bench::run_once(f);
662 }
663
664 #[test]
665 pub fn test_bench_no_iter() {
666     fn f(_: &mut Bencher) {}
667
668     let (tx, rx) = channel();
669
670     let desc = TestDesc {
671         name: StaticTestName("f"),
672         ignore: false,
673         should_panic: ShouldPanic::No,
674         allow_fail: false,
675         compile_fail: false,
676         no_run: false,
677         test_type: TestType::Unknown,
678     };
679
680     crate::bench::benchmark(TestId(0), desc, tx, true, f);
681     rx.recv().unwrap();
682 }
683
684 #[test]
685 pub fn test_bench_iter() {
686     fn f(b: &mut Bencher) {
687         b.iter(|| {})
688     }
689
690     let (tx, rx) = channel();
691
692     let desc = TestDesc {
693         name: StaticTestName("f"),
694         ignore: false,
695         should_panic: ShouldPanic::No,
696         allow_fail: false,
697         compile_fail: false,
698         no_run: false,
699         test_type: TestType::Unknown,
700     };
701
702     crate::bench::benchmark(TestId(0), desc, tx, true, f);
703     rx.recv().unwrap();
704 }
705
706 #[test]
707 fn should_sort_failures_before_printing_them() {
708     let test_a = TestDesc {
709         name: StaticTestName("a"),
710         ignore: false,
711         should_panic: ShouldPanic::No,
712         allow_fail: false,
713         compile_fail: false,
714         no_run: false,
715         test_type: TestType::Unknown,
716     };
717
718     let test_b = TestDesc {
719         name: StaticTestName("b"),
720         ignore: false,
721         should_panic: ShouldPanic::No,
722         allow_fail: false,
723         compile_fail: false,
724         no_run: false,
725         test_type: TestType::Unknown,
726     };
727
728     let mut out = PrettyFormatter::new(OutputLocation::Raw(Vec::new()), false, 10, false, None);
729
730     let st = console::ConsoleTestState {
731         log_out: None,
732         total: 0,
733         passed: 0,
734         failed: 0,
735         ignored: 0,
736         allowed_fail: 0,
737         filtered_out: 0,
738         measured: 0,
739         exec_time: None,
740         metrics: MetricMap::new(),
741         failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
742         options: Options::new(),
743         not_failures: Vec::new(),
744         time_failures: Vec::new(),
745     };
746
747     out.write_failures(&st).unwrap();
748     let s = match out.output_location() {
749         &OutputLocation::Raw(ref m) => String::from_utf8_lossy(&m[..]),
750         &OutputLocation::Pretty(_) => unreachable!(),
751     };
752
753     let apos = s.find("a").unwrap();
754     let bpos = s.find("b").unwrap();
755     assert!(apos < bpos);
756 }