|
1 | 1 | use std::any::Any;
|
2 | 2 |
|
3 | 3 | use bstr::ByteSlice;
|
| 4 | +use gix_testtools::once_cell::sync::Lazy; |
4 | 5 |
|
| 6 | +/// To see all current failures run the following command or execute cargo-nextest directly with |
| 7 | +/// the below shown arguments. |
| 8 | +/// |
| 9 | +/// ```bash |
| 10 | +/// just nt -p gix-url --test baseline --success-output immediate |
| 11 | +/// `` |
5 | 12 | #[test]
|
6 | 13 | fn run() {
|
| 14 | + // ensure the baseline is evaluated before we disable the panic hook, otherwise we swallow |
| 15 | + // errors inside the baseline generation |
| 16 | + Lazy::force(&baseline::URLS); |
| 17 | + |
7 | 18 | let panic_hook = std::panic::take_hook();
|
8 | 19 | std::panic::set_hook(Box::new(|_| {}));
|
9 | 20 |
|
10 |
| - let mut count = 0; |
| 21 | + let mut test_count = 0; |
11 | 22 | let mut failures = Vec::new();
|
12 |
| - let (mut failed_roundtrips, mut serialized_url_does_not_match_input) = (0, 0); |
| 23 | + let (mut failure_count_roundtrips, mut failure_count_reserialization) = (0, 0); |
13 | 24 | for (url, expected) in baseline::URLS.iter() {
|
14 |
| - count += 1; |
| 25 | + test_count += 1; |
15 | 26 | let actual = match gix_url::parse(url) {
|
16 | 27 | Ok(actual) => actual,
|
17 |
| - Err(err) => { |
18 |
| - failures.push(err.to_string()); |
| 28 | + Err(message) => { |
| 29 | + failures.push(format!("failure(parse): {message}")); |
19 | 30 | continue;
|
20 | 31 | }
|
21 | 32 | };
|
22 |
| - let url_as_string = actual.to_bstring(); |
23 |
| - serialized_url_does_not_match_input += usize::from(url_as_string != *url); |
24 |
| - failed_roundtrips += usize::from(gix_url::parse(url_as_string.as_ref()).ok().as_ref() != Some(&actual)); |
25 |
| - let result = std::panic::catch_unwind(|| assert_urls_equal(expected, &actual)).map_err(|panic| { |
| 33 | + if let Err(message) = std::panic::catch_unwind(|| assert_urls_equal(expected, &actual)).map_err(|panic| { |
26 | 34 | match downcast_panic_to_str(&panic) {
|
27 | 35 | Some(s) => format!("{url}: {s}\nexpected: {expected:?}\nactual: {actual:?}"),
|
28 | 36 | None => format!("{url}: expected: {expected:?}\nactual: {actual:?}"),
|
29 | 37 | }
|
30 |
| - }); |
31 |
| - if let Err(message) = result { |
32 |
| - failures.push(message); |
| 38 | + }) { |
| 39 | + failures.push(format!("failure(compare): {message}")); |
| 40 | + continue; |
33 | 41 | }
|
| 42 | + // perform additional checks only after we determined that we parsed the url correctly |
| 43 | + let url_serialized_again = actual.to_bstring(); |
| 44 | + failure_count_reserialization += usize::from(url_serialized_again != *url); |
| 45 | + failure_count_roundtrips += |
| 46 | + usize::from(gix_url::parse(url_serialized_again.as_ref()).ok().as_ref() != Some(&actual)); |
34 | 47 | }
|
35 | 48 |
|
36 | 49 | std::panic::set_hook(panic_hook);
|
37 |
| - assert_ne!(count, 0, "the baseline is never empty"); |
| 50 | + |
| 51 | + assert_ne!(test_count, 0, "the baseline is never empty"); |
38 | 52 | if failures.is_empty() {
|
39 | 53 | todo!("The baseline is currently meddling with hooks, thats not needed anymore since the failure rate is 0: move this into a module of the normal tests");
|
40 | 54 | }
|
| 55 | + |
| 56 | + let failure_count = failures.len(); |
| 57 | + let passed_count = test_count - failure_count; |
| 58 | + let expected_failure_count = baseline::Kind::new().max_num_failures(); |
| 59 | + |
| 60 | + eprintln!("failed {failure_count}/{test_count} tests ({passed_count} passed)"); |
| 61 | + |
41 | 62 | for message in &failures {
|
42 |
| - eprintln!("{message}"); |
| 63 | + // print messages to out instead of err to separate them from general test information |
| 64 | + println!("{message}"); |
| 65 | + } |
| 66 | + |
| 67 | + use core::cmp::Ordering; |
| 68 | + match Ord::cmp(&failure_count, &expected_failure_count) { |
| 69 | + Ordering::Equal => { |
| 70 | + eprintln!("the number of failing tests is as expected"); |
| 71 | + } |
| 72 | + Ordering::Less => { |
| 73 | + panic!( |
| 74 | + "{} more passing tests than expected. Great work! Please change the expected number of failures to {failure_count} to make this panic go away", |
| 75 | + expected_failure_count - failure_count, |
| 76 | + ) |
| 77 | + } |
| 78 | + Ordering::Greater => { |
| 79 | + panic!( |
| 80 | + "{} more failing tests than expected! This should get better, not worse. Please check your changes manually for any regressions", |
| 81 | + failure_count - expected_failure_count, |
| 82 | + ) |
| 83 | + } |
43 | 84 | }
|
44 |
| - eprintln!( |
45 |
| - "{} failed out of {count} tests ({} passed)", |
46 |
| - failures.len(), |
47 |
| - count - failures.len() |
48 |
| - ); |
49 |
| - assert!( |
50 |
| - serialized_url_does_not_match_input <= 126, |
51 |
| - "we shouldn't get worse when serializing to match our input URL" |
52 |
| - ); |
53 | 85 |
|
54 |
| - let kind = baseline::Kind::new(); |
55 |
| - assert_eq!(failed_roundtrips, 0); |
56 | 86 | assert!(
|
57 |
| - failures.len() <= kind.max_num_failures(), |
58 |
| - "Expected no more than {} failures, but got {} - this should get better, not worse", |
59 |
| - kind.max_num_failures(), |
60 |
| - failures.len(), |
61 |
| - ) |
| 87 | + failure_count_reserialization <= 42, |
| 88 | + "the number of reserialization errors should ideally get better, not worse - if this panic is not due to regressions but to new passing test cases, you can set this check to {failure_count_reserialization}" |
| 89 | + ); |
| 90 | + assert_eq!(failure_count_roundtrips, 0, "there should be no roundtrip errors"); |
62 | 91 | }
|
63 | 92 |
|
64 | 93 | fn downcast_panic_to_str<'a>(panic: &'a Box<dyn Any + Send + 'static>) -> Option<&'a str> {
|
|
0 commit comments