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