Skip to content

Commit ee399e6

Browse files
committed
fix: retain header and divider delimiter lengths when updating tests
This helps to prevent visual bloat in diffs when updating tests where the delimiter is not 80 chars long
1 parent 7d4b011 commit ee399e6

File tree

2 files changed

+89
-23
lines changed

2 files changed

+89
-23
lines changed

cli/src/test.rs

Lines changed: 88 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ use walkdir::WalkDir;
1616

1717
lazy_static! {
1818
static ref HEADER_REGEX: ByteRegex =
19-
ByteRegexBuilder::new(r"^===+(?P<suffix1>[^=\r\n][^\r\n]*)?\r?\n(?P<test_name>([^=\r\n][^\r\n]*\r?\n)+)===+(?P<suffix2>[^=\r\n][^\r\n]*)?\r?\n")
19+
ByteRegexBuilder::new(r"^(?P<equals>(?:=+){3,})(?P<suffix1>[^=\r\n][^\r\n]*)?\r?\n(?P<test_name>([^=\r\n][^\r\n]*\r?\n)+)===+(?P<suffix2>[^=\r\n][^\r\n]*)?\r?\n")
2020
.multi_line(true)
2121
.build()
2222
.unwrap();
23-
static ref DIVIDER_REGEX: ByteRegex = ByteRegexBuilder::new(r"^---+(?P<suffix>[^-\r\n][^\r\n]*)?\r?\n")
23+
static ref DIVIDER_REGEX: ByteRegex = ByteRegexBuilder::new(r"^(?P<hyphens>(?:-+){3,})(?P<suffix>[^-\r\n][^\r\n]*)?\r?\n")
2424
.multi_line(true)
2525
.build()
2626
.unwrap();
@@ -40,6 +40,8 @@ pub enum TestEntry {
4040
name: String,
4141
input: Vec<u8>,
4242
output: String,
43+
header_delim_len: usize,
44+
divider_delim_len: usize,
4345
has_fields: bool,
4446
},
4547
}
@@ -179,21 +181,29 @@ fn run_tests(
179181
mut indent_level: i32,
180182
failures: &mut Vec<(String, String, String)>,
181183
update: bool,
182-
corrected_entries: &mut Vec<(String, String, String)>,
184+
corrected_entries: &mut Vec<(String, String, String, usize, usize)>,
183185
) -> Result<()> {
184186
match test_entry {
185187
TestEntry::Example {
186188
name,
187189
input,
188190
output,
191+
header_delim_len,
192+
divider_delim_len,
189193
has_fields,
190194
} => {
191195
if let Some(filter) = filter {
192196
if !name.contains(filter) {
193197
if update {
194198
let input = String::from_utf8(input).unwrap();
195199
let output = format_sexp(&output);
196-
corrected_entries.push((name, input, output));
200+
corrected_entries.push((
201+
name,
202+
input,
203+
output,
204+
header_delim_len,
205+
divider_delim_len,
206+
));
197207
}
198208
return Ok(());
199209
}
@@ -203,21 +213,31 @@ fn run_tests(
203213
if !has_fields {
204214
actual = strip_sexp_fields(actual);
205215
}
206-
for _ in 0..indent_level {
207-
print!(" ");
208-
}
216+
print!("{}", " ".repeat(indent_level as usize));
209217
if actual == output {
210218
println!("✓ {}", Colour::Green.paint(&name));
211219
if update {
212220
let input = String::from_utf8(input).unwrap();
213221
let output = format_sexp(&output);
214-
corrected_entries.push((name, input, output));
222+
corrected_entries.push((
223+
name,
224+
input,
225+
output,
226+
header_delim_len,
227+
divider_delim_len,
228+
));
215229
}
216230
} else {
217231
if update {
218232
let input = String::from_utf8(input).unwrap();
219233
let output = format_sexp(&actual);
220-
corrected_entries.push((name.clone(), input, output));
234+
corrected_entries.push((
235+
name.clone(),
236+
input,
237+
output,
238+
header_delim_len,
239+
divider_delim_len,
240+
));
221241
println!("✓ {}", Colour::Blue.paint(&name));
222242
} else {
223243
println!("✗ {}", Colour::Red.paint(&name));
@@ -231,9 +251,7 @@ fn run_tests(
231251
file_path,
232252
} => {
233253
if indent_level > 0 {
234-
for _ in 0..indent_level {
235-
print!(" ");
236-
}
254+
print!("{}", " ".repeat(indent_level as usize));
237255
println!("{}:", name);
238256
}
239257

@@ -314,27 +332,32 @@ fn format_sexp_indented(sexp: &String, initial_indent_level: u32) -> String {
314332
formatted
315333
}
316334

317-
fn write_tests(file_path: &Path, corrected_entries: &Vec<(String, String, String)>) -> Result<()> {
335+
fn write_tests(
336+
file_path: &Path,
337+
corrected_entries: &Vec<(String, String, String, usize, usize)>,
338+
) -> Result<()> {
318339
let mut buffer = fs::File::create(file_path)?;
319340
write_tests_to_buffer(&mut buffer, corrected_entries)
320341
}
321342

322343
fn write_tests_to_buffer(
323344
buffer: &mut impl Write,
324-
corrected_entries: &Vec<(String, String, String)>,
345+
corrected_entries: &Vec<(String, String, String, usize, usize)>,
325346
) -> Result<()> {
326-
for (i, (name, input, output)) in corrected_entries.iter().enumerate() {
347+
for (i, (name, input, output, header_delim_len, divider_delim_len)) in
348+
corrected_entries.iter().enumerate()
349+
{
327350
if i > 0 {
328351
write!(buffer, "\n")?;
329352
}
330353
write!(
331354
buffer,
332355
"{}\n{}\n{}\n{}\n{}\n\n{}\n",
333-
"=".repeat(80),
356+
"=".repeat(*header_delim_len),
334357
name,
335-
"=".repeat(80),
358+
"=".repeat(*header_delim_len),
336359
input,
337-
"-".repeat(80),
360+
"-".repeat(*divider_delim_len),
338361
output.trim()
339362
)?;
340363
}
@@ -398,6 +421,7 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
398421
// Ignore any matches whose suffix does not match the first header
399422
// suffix in the file.
400423
let header_matches = HEADER_REGEX.captures_iter(&bytes).filter_map(|c| {
424+
let header_delim_len = c.name("equals").map(|n| n.as_bytes().len()).unwrap_or(80);
401425
let suffix1 = c
402426
.name("suffix1")
403427
.map(|m| String::from_utf8_lossy(m.as_bytes()));
@@ -409,33 +433,43 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
409433
let test_name = c
410434
.name("test_name")
411435
.map(|c| String::from_utf8_lossy(c.as_bytes()).trim_end().to_string());
412-
Some((header_range, test_name))
436+
let res = Some((header_delim_len, header_range, test_name));
437+
res
413438
} else {
414439
None
415440
}
416441
});
417442

418-
for (header_range, test_name) in header_matches.chain(Some((bytes.len()..bytes.len(), None))) {
443+
let mut prev_header_len = 80;
444+
for (header_delim_len, header_range, test_name) in
445+
header_matches.chain(Some((80, bytes.len()..bytes.len(), None)))
446+
{
419447
// Find the longest line of dashes following each test description. That line
420448
// separates the input from the expected output. Ignore any matches whose suffix
421449
// does not match the first suffix in the file.
422450
if prev_header_end > 0 {
423451
let divider_range = DIVIDER_REGEX
424452
.captures_iter(&bytes[prev_header_end..header_range.start])
425453
.filter_map(|m| {
454+
let divider_delim_len =
455+
m.name("hyphens").map(|m| m.as_bytes().len()).unwrap_or(80);
426456
let suffix = m
427457
.name("suffix")
428458
.map(|m| String::from_utf8_lossy(m.as_bytes()));
429459
if suffix == first_suffix {
430460
let range = m.get(0).unwrap().range();
431-
Some((prev_header_end + range.start)..(prev_header_end + range.end))
461+
let res = Some((
462+
divider_delim_len,
463+
(prev_header_end + range.start)..(prev_header_end + range.end),
464+
));
465+
res
432466
} else {
433467
None
434468
}
435469
})
436-
.max_by_key(|range| range.len());
470+
.max_by_key(|(_, range)| range.len());
437471

438-
if let Some(divider_range) = divider_range {
472+
if let Some((divider_delim_len, divider_range)) = divider_range {
439473
if let Ok(output) = str::from_utf8(&bytes[divider_range.end..header_range.start]) {
440474
let mut input = bytes[prev_header_end..divider_range.start].to_vec();
441475

@@ -460,12 +494,15 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
460494
name: prev_name,
461495
input,
462496
output,
497+
header_delim_len: prev_header_len,
498+
divider_delim_len,
463499
has_fields,
464500
});
465501
}
466502
}
467503
}
468504
prev_name = test_name.unwrap_or(String::new());
505+
prev_header_len = header_delim_len;
469506
prev_header_end = header_range.end;
470507
}
471508
TestEntry::Group {
@@ -516,12 +553,16 @@ d
516553
name: "The first test".to_string(),
517554
input: "\na b c\n".as_bytes().to_vec(),
518555
output: "(a (b c))".to_string(),
556+
header_delim_len: 15,
557+
divider_delim_len: 3,
519558
has_fields: false,
520559
},
521560
TestEntry::Example {
522561
name: "The second test".to_string(),
523562
input: "d".as_bytes().to_vec(),
524563
output: "(d)".to_string(),
564+
header_delim_len: 16,
565+
divider_delim_len: 3,
525566
has_fields: false,
526567
},
527568
],
@@ -570,12 +611,16 @@ abc
570611
name: "Code with dashes".to_string(),
571612
input: "abc\n---\ndefg\n----\nhijkl".as_bytes().to_vec(),
572613
output: "(a (b))".to_string(),
614+
header_delim_len: 18,
615+
divider_delim_len: 7,
573616
has_fields: false,
574617
},
575618
TestEntry::Example {
576619
name: "Code ending with dashes".to_string(),
577620
input: "abc\n-----------".as_bytes().to_vec(),
578621
output: "(c (d))".to_string(),
622+
header_delim_len: 25,
623+
divider_delim_len: 19,
579624
has_fields: false,
580625
},
581626
],
@@ -619,11 +664,15 @@ abc
619664
"title 1".to_string(),
620665
"input 1".to_string(),
621666
"output 1".to_string(),
667+
80,
668+
80,
622669
),
623670
(
624671
"title 2".to_string(),
625672
"input 2".to_string(),
626673
"output 2".to_string(),
674+
80,
675+
80,
627676
),
628677
];
629678
write_tests_to_buffer(&mut buffer, &corrected_entries).unwrap();
@@ -700,18 +749,24 @@ code
700749
name: "sexp with comment".to_string(),
701750
input: "code".as_bytes().to_vec(),
702751
output: "(a (b))".to_string(),
752+
header_delim_len: 18,
753+
divider_delim_len: 3,
703754
has_fields: false,
704755
},
705756
TestEntry::Example {
706757
name: "sexp with comment between".to_string(),
707758
input: "code".as_bytes().to_vec(),
708759
output: "(a (b))".to_string(),
760+
header_delim_len: 18,
761+
divider_delim_len: 3,
709762
has_fields: false,
710763
},
711764
TestEntry::Example {
712765
name: "sexp with ';'".to_string(),
713766
input: "code".as_bytes().to_vec(),
714767
output: "(MISSING \";\")".to_string(),
768+
header_delim_len: 25,
769+
divider_delim_len: 3,
715770
has_fields: false,
716771
}
717772
],
@@ -784,18 +839,24 @@ NOT A TEST HEADER
784839
name: "First test".to_string(),
785840
input: expected_input.clone(),
786841
output: "(a)".to_string(),
842+
header_delim_len: 18,
843+
divider_delim_len: 3,
787844
has_fields: false,
788845
},
789846
TestEntry::Example {
790847
name: "Second test".to_string(),
791848
input: expected_input.clone(),
792849
output: "(a)".to_string(),
850+
header_delim_len: 18,
851+
divider_delim_len: 3,
793852
has_fields: false,
794853
},
795854
TestEntry::Example {
796855
name: "Test name with = symbol".to_string(),
797856
input: expected_input.clone(),
798857
output: "(a)".to_string(),
858+
header_delim_len: 25,
859+
divider_delim_len: 3,
799860
has_fields: false,
800861
}
801862
],
@@ -839,12 +900,16 @@ code with ----
839900
name: "name\nwith\nnewlines".to_string(),
840901
input: b"a".to_vec(),
841902
output: "(b)".to_string(),
903+
header_delim_len: 15,
904+
divider_delim_len: 3,
842905
has_fields: false,
843906
},
844907
TestEntry::Example {
845908
name: "name with === signs".to_string(),
846909
input: b"code with ----".to_vec(),
847910
output: "(d)".to_string(),
911+
header_delim_len: 20,
912+
divider_delim_len: 3,
848913
has_fields: false,
849914
}
850915
]

cli/src/tests/corpus_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ fn flatten_tests(test: TestEntry) -> Vec<FlattenedTest> {
502502
input,
503503
output,
504504
has_fields,
505+
..
505506
} => {
506507
if !prefix.is_empty() {
507508
name.insert_str(0, " - ");

0 commit comments

Comments
 (0)