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