#[cfg(test)]
mod tests {
use crate::edit;
#[test]
fn test_multiple_malformed_sections() {
let input = r#"garbage before patch
--- malformed header without proper path
+++ another bad header
@@ this is not a valid hunk header @@
some random content
--- a/good_file.txt
+++ b/good_file.txt
@@ -1,2 +1,2 @@
good line
-old content
+new content
@@ malformed hunk in good file
more garbage
@@ -5,1 +5,1 @@
-another change
+that should work
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let files: Vec<_> = patch.patch_files().collect();
assert!(files.len() >= 1);
let good_file = files.iter().find(|f| {
f.old_file()
.and_then(|old| old.path())
.map(|p| p.text() == "a/good_file.txt")
.unwrap_or(false)
});
assert!(good_file.is_some());
let hunks: Vec<_> = good_file.unwrap().hunks().collect();
assert!(hunks.len() >= 1);
}
#[test]
fn test_incomplete_file_headers() {
let input = r#"--- a/file1.txt
@@ -1,1 +1,1 @@
-content
+changed
--- a/file2.txt
+++ b/file2.txt
@@ -1,1 +1,1 @@
-more
+changes
+++ b/orphan_new_file.txt
@@ -0,0 +1,1 @@
+new file content
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let files: Vec<_> = patch.patch_files().collect();
assert_eq!(files.len(), 3);
assert!(files[0].old_file().is_some());
assert!(files[0].new_file().is_none());
assert!(files[1].old_file().is_some());
assert!(files[1].new_file().is_some());
assert!(files[2].old_file().is_none());
assert!(files[2].new_file().is_some());
}
#[test]
fn test_malformed_hunk_headers() {
let input = r#"--- a/file.txt
+++ b/file.txt
@@ -1 +1 @@
-missing comma in range
+but should still parse
@@ -2,2 +2,2 @@ with context
context line
-deleted
+added
@@ invalid @@ completely
should skip this section
@@ -10,1 +10,1 @@
-valid again
+after invalid
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let file = patch.patch_files().next().unwrap();
let hunks: Vec<_> = file.hunks().collect();
assert!(hunks.len() >= 2);
for hunk in &hunks {
if let Some(header) = hunk.header() {
assert!(header.old_range().is_some() || header.new_range().is_some());
}
}
}
#[test]
fn test_mixed_line_endings() {
let input = "--- a/file.txt\r\n+++ b/file.txt\n@@ -1,2 +1,2 @@\r\n line1\n-line2\r\n+line2 modified\n";
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let file = patch.patch_files().next().unwrap();
let hunk = file.hunks().next().unwrap();
let lines: Vec<_> = hunk.lines().collect();
assert_eq!(lines.len(), 3);
assert_eq!(lines[0].text().unwrap(), "line1");
assert_eq!(lines[1].text().unwrap(), "line2");
assert_eq!(lines[2].text().unwrap(), "line2 modified");
}
#[test]
fn test_empty_hunks() {
let input = r#"--- a/file.txt
+++ b/file.txt
@@ -1,0 +1,0 @@
--- a/file2.txt
+++ b/file2.txt
@@ -1,1 +1,1 @@
-content
+changed
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let files: Vec<_> = patch.patch_files().collect();
assert_eq!(files.len(), 2);
let hunk1 = files[0].hunks().next().unwrap();
assert_eq!(hunk1.lines().count(), 0);
let hunk2 = files[1].hunks().next().unwrap();
assert_eq!(hunk2.lines().count(), 2);
}
#[test]
fn test_binary_patch_notation() {
let input = r#"--- a/image.png
+++ b/image.png
Binary files differ
--- a/text.txt
+++ b/text.txt
@@ -1,1 +1,1 @@
-old
+new
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let files: Vec<_> = patch.patch_files().collect();
assert_eq!(files.len(), 2);
assert_eq!(files[0].hunks().count(), 0);
assert_eq!(files[1].hunks().count(), 1);
}
#[test]
fn test_context_after_errors() {
let input = r#"--- a/file.txt
+++ b/file.txt
@@ COMPLETELY INVALID @@
this should be skipped
but parsing should continue
@@ -5,2 +5,2 @@ and we should find this
context line
-old line
+new line
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let file = patch.patch_files().next().unwrap();
let hunks: Vec<_> = file.hunks().collect();
assert!(hunks.len() >= 1);
let valid_hunk = hunks.into_iter().find(|h| {
h.header()
.and_then(|header| header.old_range())
.and_then(|range| range.start())
.map(|start| start == 5)
.unwrap_or(false)
});
assert!(valid_hunk.is_some());
}
#[test]
fn test_truncated_patch() {
let input = r#"--- a/file.txt
+++ b/file.txt
@@ -1,5 +1,5 @@
line 1
line 2
-line 3
+line 3 modif"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let file = patch.patch_files().next().unwrap();
let hunk = file.hunks().next().unwrap();
let lines: Vec<_> = hunk.lines().collect();
assert!(lines.len() >= 3);
let last_line = lines.last().unwrap();
assert!(last_line.as_add().is_some());
assert_eq!(last_line.text().unwrap(), "line 3 modif");
}
}