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