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