Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: deleted trailing newline causes panic #97

Merged
merged 1 commit into from
Apr 2, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 123 additions & 6 deletions pretty_assertions/src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub(crate) fn write_header(f: &mut fmt::Formatter) -> fmt::Result {
/// Delay formatting this deleted chunk until later.
///
/// It can be formatted as a whole chunk by calling `flush`, or the inner value
/// obtained with `take` for further processing.
/// obtained with `take` for further processing (such as an inline diff).
#[derive(Default)]
struct LatentDeletion<'a> {
// The most recent deleted line we've seen
Expand All @@ -47,7 +47,7 @@ impl<'a> LatentDeletion<'a> {

/// Take the underlying chunk value, if it's suitable for inline diffing.
///
/// If there is no value of we've seen more than one line, return `None`.
/// If there is no value or we've seen more than one line, return `None`.
fn take(&mut self) -> Option<&'a str> {
if self.count == 1 {
self.value.take()
Expand Down Expand Up @@ -100,10 +100,6 @@ pub(crate) fn write_lines<TWrite: fmt::Write>(
previous_deletion.flush(f)?;
previous_deletion.set(deleted);
}
// Underlying diff library should never return this sequence
(::diff::Result::Right(_), Some(::diff::Result::Left(_))) => {
panic!("insertion followed by deletion");
}
// If we're being followed by more insertions, don't inline diff
(::diff::Result::Right(inserted), Some(::diff::Result::Right(_))) => {
previous_deletion.flush(f)?;
Expand Down Expand Up @@ -478,4 +474,125 @@ Cabbage"#;

check_printer(write_lines, left, right, &expected);
}

mod write_lines_edge_newlines {
use super::*;

#[test]
fn both_trailing() {
let left = "fan\n";
let right = "mug\n";
// Note the additional space at the bottom is caused by a trailing newline
// adding an additional line with zero content to both sides of the diff
let expected = format!(
r#"{red_light}<{reset}{red_heavy}fan{reset}
{green_light}>{reset}{green_heavy}mug{reset}

"#,
red_light = RED_LIGHT,
red_heavy = RED_HEAVY,
green_light = GREEN_LIGHT,
green_heavy = GREEN_HEAVY,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}

#[test]
fn both_leading() {
let left = "\nfan";
let right = "\nmug";
// Note the additional space at the top is caused by a leading newline
// adding an additional line with zero content to both sides of the diff
let expected = format!(
r#"
{red_light}<{reset}{red_heavy}fan{reset}
{green_light}>{reset}{green_heavy}mug{reset}
"#,
red_light = RED_LIGHT,
red_heavy = RED_HEAVY,
green_light = GREEN_LIGHT,
green_heavy = GREEN_HEAVY,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}

#[test]
fn leading_added() {
let left = "fan";
let right = "\nmug";
let expected = format!(
r#"{red_light}<fan{reset}
{green_light}>{reset}
{green_light}>mug{reset}
"#,
red_light = RED_LIGHT,
green_light = GREEN_LIGHT,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}

#[test]
fn leading_deleted() {
let left = "\nfan";
let right = "mug";
let expected = format!(
r#"{red_light}<{reset}
{red_light}<fan{reset}
{green_light}>mug{reset}
"#,
red_light = RED_LIGHT,
green_light = GREEN_LIGHT,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}

#[test]
fn trailing_added() {
let left = "fan";
let right = "mug\n";
let expected = format!(
r#"{red_light}<fan{reset}
{green_light}>mug{reset}
{green_light}>{reset}
"#,
red_light = RED_LIGHT,
green_light = GREEN_LIGHT,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}

/// Regression test for double abort
///
/// See: https://github.com/colin-kiegel/rust-pretty-assertions/issues/96
#[test]
fn trailing_deleted() {
// The below inputs caused an abort via double panic
// we panicked at 'insertion followed by deletion'
let left = "fan\n";
let right = "mug";
let expected = format!(
r#"{red_light}<{reset}{red_heavy}fan{reset}
{green_light}>{reset}{green_heavy}mug{reset}
{red_light}<{reset}
"#,
red_light = RED_LIGHT,
red_heavy = RED_HEAVY,
green_light = GREEN_LIGHT,
green_heavy = GREEN_HEAVY,
reset = RESET,
);

check_printer(write_lines, left, right, &expected);
}
}
}