use super::super::parse::{Error, FileOp, Hunk, parse};
use super::{envelope, parsed};
#[test]
fn add_delete_update_rename_in_one_envelope() {
let patch = parsed(
"*** Add File: new.txt\n+hello\n+world\n\
*** Delete File: gone.txt\n\
*** Update File: a.txt\n*** Move to: b.txt\n@@\n ctx\n-old\n+new",
);
assert_eq!(patch.ops.len(), 3);
assert_eq!(
patch.ops[0],
FileOp::Add {
path: "new.txt".into(),
lines: vec!["hello".into(), "world".into()],
}
);
assert_eq!(
patch.ops[1],
FileOp::Delete {
path: "gone.txt".into()
}
);
let FileOp::Update {
path,
move_to,
hunks,
} = &patch.ops[2]
else {
panic!("third op is an update");
};
assert_eq!(path, "a.txt");
assert_eq!(move_to.as_deref(), Some("b.txt"));
assert_eq!(
hunks[..],
[Hunk {
anchors: vec![],
old: vec!["ctx".into(), "old".into()],
new: vec!["ctx".into(), "new".into()],
eof: false,
}]
);
}
#[test]
fn blank_lines_around_the_envelope_are_tolerated() {
let text = format!("\n\n{}\n\n", envelope("*** Delete File: x"));
assert_eq!(parse(&text).unwrap().ops.len(), 1);
}
#[test]
fn blank_line_between_sections_is_declined() {
let err = parse(&envelope("*** Delete File: x\n\n*** Add File: y\n+hi")).unwrap_err();
assert_eq!(err, Error::BlankLine { line: 3 });
assert!(err.to_string().contains("bare blank line"), "{err}");
}
#[test]
fn blank_line_in_add_body_is_declined() {
let err = parse(&envelope("*** Add File: a\n+one\n\n+three")).unwrap_err();
assert_eq!(err, Error::BlankLine { line: 4 });
let err = parse(&envelope("*** Add File: a\n+hello\n")).unwrap_err();
assert_eq!(err, Error::BlankLine { line: 4 });
}
#[test]
fn blank_line_in_update_body_is_an_empty_context_line() {
let patch = parsed("*** Update File: a\n-x\n\n+y");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks[0].old, ["x", ""]);
assert_eq!(hunks[0].new, ["", "y"]);
let patch = parsed("*** Update File: a\n-x\n+y\n");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks[0].old, ["x", ""]);
assert_eq!(hunks[0].new, ["y", ""]);
}
#[test]
fn blank_lines_after_end_of_file_are_ignored() {
let patch = parsed("*** Update File: a\n+appended\n*** End of File\n");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks.len(), 1);
assert!(hunks[0].eof);
assert_eq!(hunks[0].new, ["appended"]);
}
#[test]
fn bare_at_at_separates_hunks_and_anchor_after_body_opens_a_new_one() {
let patch = parsed("*** Update File: a\n-x\n+y\n@@\n-p\n+q\n@@ fn two\n-r\n+s");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks.len(), 3);
assert!(hunks[1].anchors.is_empty());
assert_eq!(hunks[2].anchors, ["fn two"]);
}
#[test]
fn anchors_stack_onto_one_hunk() {
let patch = parsed("*** Update File: a\n@@ class C\n@@ fn m\n-x\n+y");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks.len(), 1);
assert_eq!(hunks[0].anchors, ["class C", "fn m"]);
}
#[test]
fn end_of_file_pins_the_hunk_and_a_trailing_bare_separator_is_ignored() {
let patch = parsed("*** Update File: a\n+appended\n*** End of File\n@@");
let FileOp::Update { hunks, .. } = &patch.ops[0] else {
panic!("update");
};
assert_eq!(hunks.len(), 1);
assert!(hunks[0].eof);
assert!(hunks[0].old.is_empty());
}