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
This commit is contained in:
parent
7d4b0110a9
commit
ee399e67d4
2 changed files with 89 additions and 23 deletions
111
cli/src/test.rs
111
cli/src/test.rs
|
|
@ -16,11 +16,11 @@ use walkdir::WalkDir;
|
|||
|
||||
lazy_static! {
|
||||
static ref HEADER_REGEX: ByteRegex =
|
||||
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")
|
||||
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")
|
||||
.multi_line(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
static ref DIVIDER_REGEX: ByteRegex = ByteRegexBuilder::new(r"^---+(?P<suffix>[^-\r\n][^\r\n]*)?\r?\n")
|
||||
static ref DIVIDER_REGEX: ByteRegex = ByteRegexBuilder::new(r"^(?P<hyphens>(?:-+){3,})(?P<suffix>[^-\r\n][^\r\n]*)?\r?\n")
|
||||
.multi_line(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
|
@ -40,6 +40,8 @@ pub enum TestEntry {
|
|||
name: String,
|
||||
input: Vec<u8>,
|
||||
output: String,
|
||||
header_delim_len: usize,
|
||||
divider_delim_len: usize,
|
||||
has_fields: bool,
|
||||
},
|
||||
}
|
||||
|
|
@ -179,13 +181,15 @@ fn run_tests(
|
|||
mut indent_level: i32,
|
||||
failures: &mut Vec<(String, String, String)>,
|
||||
update: bool,
|
||||
corrected_entries: &mut Vec<(String, String, String)>,
|
||||
corrected_entries: &mut Vec<(String, String, String, usize, usize)>,
|
||||
) -> Result<()> {
|
||||
match test_entry {
|
||||
TestEntry::Example {
|
||||
name,
|
||||
input,
|
||||
output,
|
||||
header_delim_len,
|
||||
divider_delim_len,
|
||||
has_fields,
|
||||
} => {
|
||||
if let Some(filter) = filter {
|
||||
|
|
@ -193,7 +197,13 @@ fn run_tests(
|
|||
if update {
|
||||
let input = String::from_utf8(input).unwrap();
|
||||
let output = format_sexp(&output);
|
||||
corrected_entries.push((name, input, output));
|
||||
corrected_entries.push((
|
||||
name,
|
||||
input,
|
||||
output,
|
||||
header_delim_len,
|
||||
divider_delim_len,
|
||||
));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -203,21 +213,31 @@ fn run_tests(
|
|||
if !has_fields {
|
||||
actual = strip_sexp_fields(actual);
|
||||
}
|
||||
for _ in 0..indent_level {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{}", " ".repeat(indent_level as usize));
|
||||
if actual == output {
|
||||
println!("✓ {}", Colour::Green.paint(&name));
|
||||
if update {
|
||||
let input = String::from_utf8(input).unwrap();
|
||||
let output = format_sexp(&output);
|
||||
corrected_entries.push((name, input, output));
|
||||
corrected_entries.push((
|
||||
name,
|
||||
input,
|
||||
output,
|
||||
header_delim_len,
|
||||
divider_delim_len,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if update {
|
||||
let input = String::from_utf8(input).unwrap();
|
||||
let output = format_sexp(&actual);
|
||||
corrected_entries.push((name.clone(), input, output));
|
||||
corrected_entries.push((
|
||||
name.clone(),
|
||||
input,
|
||||
output,
|
||||
header_delim_len,
|
||||
divider_delim_len,
|
||||
));
|
||||
println!("✓ {}", Colour::Blue.paint(&name));
|
||||
} else {
|
||||
println!("✗ {}", Colour::Red.paint(&name));
|
||||
|
|
@ -231,9 +251,7 @@ fn run_tests(
|
|||
file_path,
|
||||
} => {
|
||||
if indent_level > 0 {
|
||||
for _ in 0..indent_level {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{}", " ".repeat(indent_level as usize));
|
||||
println!("{}:", name);
|
||||
}
|
||||
|
||||
|
|
@ -314,27 +332,32 @@ fn format_sexp_indented(sexp: &String, initial_indent_level: u32) -> String {
|
|||
formatted
|
||||
}
|
||||
|
||||
fn write_tests(file_path: &Path, corrected_entries: &Vec<(String, String, String)>) -> Result<()> {
|
||||
fn write_tests(
|
||||
file_path: &Path,
|
||||
corrected_entries: &Vec<(String, String, String, usize, usize)>,
|
||||
) -> Result<()> {
|
||||
let mut buffer = fs::File::create(file_path)?;
|
||||
write_tests_to_buffer(&mut buffer, corrected_entries)
|
||||
}
|
||||
|
||||
fn write_tests_to_buffer(
|
||||
buffer: &mut impl Write,
|
||||
corrected_entries: &Vec<(String, String, String)>,
|
||||
corrected_entries: &Vec<(String, String, String, usize, usize)>,
|
||||
) -> Result<()> {
|
||||
for (i, (name, input, output)) in corrected_entries.iter().enumerate() {
|
||||
for (i, (name, input, output, header_delim_len, divider_delim_len)) in
|
||||
corrected_entries.iter().enumerate()
|
||||
{
|
||||
if i > 0 {
|
||||
write!(buffer, "\n")?;
|
||||
}
|
||||
write!(
|
||||
buffer,
|
||||
"{}\n{}\n{}\n{}\n{}\n\n{}\n",
|
||||
"=".repeat(80),
|
||||
"=".repeat(*header_delim_len),
|
||||
name,
|
||||
"=".repeat(80),
|
||||
"=".repeat(*header_delim_len),
|
||||
input,
|
||||
"-".repeat(80),
|
||||
"-".repeat(*divider_delim_len),
|
||||
output.trim()
|
||||
)?;
|
||||
}
|
||||
|
|
@ -398,6 +421,7 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
|
|||
// Ignore any matches whose suffix does not match the first header
|
||||
// suffix in the file.
|
||||
let header_matches = HEADER_REGEX.captures_iter(&bytes).filter_map(|c| {
|
||||
let header_delim_len = c.name("equals").map(|n| n.as_bytes().len()).unwrap_or(80);
|
||||
let suffix1 = c
|
||||
.name("suffix1")
|
||||
.map(|m| String::from_utf8_lossy(m.as_bytes()));
|
||||
|
|
@ -409,13 +433,17 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
|
|||
let test_name = c
|
||||
.name("test_name")
|
||||
.map(|c| String::from_utf8_lossy(c.as_bytes()).trim_end().to_string());
|
||||
Some((header_range, test_name))
|
||||
let res = Some((header_delim_len, header_range, test_name));
|
||||
res
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
for (header_range, test_name) in header_matches.chain(Some((bytes.len()..bytes.len(), None))) {
|
||||
let mut prev_header_len = 80;
|
||||
for (header_delim_len, header_range, test_name) in
|
||||
header_matches.chain(Some((80, bytes.len()..bytes.len(), None)))
|
||||
{
|
||||
// Find the longest line of dashes following each test description. That line
|
||||
// separates the input from the expected output. Ignore any matches whose suffix
|
||||
// does not match the first suffix in the file.
|
||||
|
|
@ -423,19 +451,25 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
|
|||
let divider_range = DIVIDER_REGEX
|
||||
.captures_iter(&bytes[prev_header_end..header_range.start])
|
||||
.filter_map(|m| {
|
||||
let divider_delim_len =
|
||||
m.name("hyphens").map(|m| m.as_bytes().len()).unwrap_or(80);
|
||||
let suffix = m
|
||||
.name("suffix")
|
||||
.map(|m| String::from_utf8_lossy(m.as_bytes()));
|
||||
if suffix == first_suffix {
|
||||
let range = m.get(0).unwrap().range();
|
||||
Some((prev_header_end + range.start)..(prev_header_end + range.end))
|
||||
let res = Some((
|
||||
divider_delim_len,
|
||||
(prev_header_end + range.start)..(prev_header_end + range.end),
|
||||
));
|
||||
res
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.max_by_key(|range| range.len());
|
||||
.max_by_key(|(_, range)| range.len());
|
||||
|
||||
if let Some(divider_range) = divider_range {
|
||||
if let Some((divider_delim_len, divider_range)) = divider_range {
|
||||
if let Ok(output) = str::from_utf8(&bytes[divider_range.end..header_range.start]) {
|
||||
let mut input = bytes[prev_header_end..divider_range.start].to_vec();
|
||||
|
||||
|
|
@ -460,12 +494,15 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
|
|||
name: prev_name,
|
||||
input,
|
||||
output,
|
||||
header_delim_len: prev_header_len,
|
||||
divider_delim_len,
|
||||
has_fields,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_name = test_name.unwrap_or(String::new());
|
||||
prev_header_len = header_delim_len;
|
||||
prev_header_end = header_range.end;
|
||||
}
|
||||
TestEntry::Group {
|
||||
|
|
@ -516,12 +553,16 @@ d
|
|||
name: "The first test".to_string(),
|
||||
input: "\na b c\n".as_bytes().to_vec(),
|
||||
output: "(a (b c))".to_string(),
|
||||
header_delim_len: 15,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "The second test".to_string(),
|
||||
input: "d".as_bytes().to_vec(),
|
||||
output: "(d)".to_string(),
|
||||
header_delim_len: 16,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
],
|
||||
|
|
@ -570,12 +611,16 @@ abc
|
|||
name: "Code with dashes".to_string(),
|
||||
input: "abc\n---\ndefg\n----\nhijkl".as_bytes().to_vec(),
|
||||
output: "(a (b))".to_string(),
|
||||
header_delim_len: 18,
|
||||
divider_delim_len: 7,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "Code ending with dashes".to_string(),
|
||||
input: "abc\n-----------".as_bytes().to_vec(),
|
||||
output: "(c (d))".to_string(),
|
||||
header_delim_len: 25,
|
||||
divider_delim_len: 19,
|
||||
has_fields: false,
|
||||
},
|
||||
],
|
||||
|
|
@ -619,11 +664,15 @@ abc
|
|||
"title 1".to_string(),
|
||||
"input 1".to_string(),
|
||||
"output 1".to_string(),
|
||||
80,
|
||||
80,
|
||||
),
|
||||
(
|
||||
"title 2".to_string(),
|
||||
"input 2".to_string(),
|
||||
"output 2".to_string(),
|
||||
80,
|
||||
80,
|
||||
),
|
||||
];
|
||||
write_tests_to_buffer(&mut buffer, &corrected_entries).unwrap();
|
||||
|
|
@ -700,18 +749,24 @@ code
|
|||
name: "sexp with comment".to_string(),
|
||||
input: "code".as_bytes().to_vec(),
|
||||
output: "(a (b))".to_string(),
|
||||
header_delim_len: 18,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "sexp with comment between".to_string(),
|
||||
input: "code".as_bytes().to_vec(),
|
||||
output: "(a (b))".to_string(),
|
||||
header_delim_len: 18,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "sexp with ';'".to_string(),
|
||||
input: "code".as_bytes().to_vec(),
|
||||
output: "(MISSING \";\")".to_string(),
|
||||
header_delim_len: 25,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
}
|
||||
],
|
||||
|
|
@ -784,18 +839,24 @@ NOT A TEST HEADER
|
|||
name: "First test".to_string(),
|
||||
input: expected_input.clone(),
|
||||
output: "(a)".to_string(),
|
||||
header_delim_len: 18,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "Second test".to_string(),
|
||||
input: expected_input.clone(),
|
||||
output: "(a)".to_string(),
|
||||
header_delim_len: 18,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "Test name with = symbol".to_string(),
|
||||
input: expected_input.clone(),
|
||||
output: "(a)".to_string(),
|
||||
header_delim_len: 25,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
}
|
||||
],
|
||||
|
|
@ -839,12 +900,16 @@ code with ----
|
|||
name: "name\nwith\nnewlines".to_string(),
|
||||
input: b"a".to_vec(),
|
||||
output: "(b)".to_string(),
|
||||
header_delim_len: 15,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
},
|
||||
TestEntry::Example {
|
||||
name: "name with === signs".to_string(),
|
||||
input: b"code with ----".to_vec(),
|
||||
output: "(d)".to_string(),
|
||||
header_delim_len: 20,
|
||||
divider_delim_len: 3,
|
||||
has_fields: false,
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -502,6 +502,7 @@ fn flatten_tests(test: TestEntry) -> Vec<FlattenedTest> {
|
|||
input,
|
||||
output,
|
||||
has_fields,
|
||||
..
|
||||
} => {
|
||||
if !prefix.is_empty() {
|
||||
name.insert_str(0, " - ");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue