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