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