#[cfg(test)]
mod tests {
use crate::edit;
use crate::edit::DiffFormat;
use rowan::ast::AstNode;
#[test]
fn test_parse_context_diff() {
let input = r#"*** a/file.txt 2023-01-01 00:00:00
--- b/file.txt 2023-01-02 00:00:00
***************
*** 1,3 ****
line 1
! line 2
line 3
--- 1,3 ----
line 1
! line 2 modified
line 3
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
assert_eq!(patch.detect_format(), Some(DiffFormat::Context));
let mut files = patch.context_diff_files();
let file = files.next().unwrap();
assert!(files.next().is_none());
let old_file = file.old_file().unwrap();
assert!(old_file.path().unwrap().text().contains("a/file.txt"));
let new_file = file.new_file().unwrap();
assert!(new_file.path().unwrap().text().contains("b/file.txt"));
let mut hunks = file.hunks();
let hunk = hunks.next().unwrap();
assert!(hunks.next().is_none());
let old_section = hunk.old_section().unwrap();
let _new_section = hunk.new_section().unwrap();
assert!(old_section
.syntax()
.children()
.any(|n| n.kind() == crate::edit::lex::SyntaxKind::CONTEXT_LINE));
assert!(old_section
.syntax()
.children()
.any(|n| n.kind() == crate::edit::lex::SyntaxKind::CONTEXT_CHANGE_LINE));
}
#[test]
fn test_parse_ed_diff() {
let input = r#"2c
line 2 modified
.
5a
new line after 5
.
3d
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
assert_eq!(patch.detect_format(), Some(DiffFormat::Ed));
let commands: Vec<_> = patch.ed_commands().collect();
assert_eq!(commands.len(), 3);
let change = commands[0].as_change().unwrap();
let (start, end) = change.line_numbers();
assert_eq!(start, Some(2));
assert_eq!(end, None); let content: Vec<_> = change.content_lines().collect();
assert_eq!(content.len(), 1);
let add = commands[1].as_add().unwrap();
let (start, _) = add.line_numbers();
assert_eq!(start, Some(5));
let delete = commands[2].as_delete().unwrap();
let (start, _) = delete.line_numbers();
assert_eq!(start, Some(3));
}
#[test]
fn test_parse_normal_diff() {
let input = r#"2c2
< line 2
---
> line 2 modified
5a6,7
> new line 6
> new line 7
8,9d10
< old line 8
< old line 9
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
assert_eq!(patch.detect_format(), Some(DiffFormat::Normal));
let hunks: Vec<_> = patch.normal_hunks().collect();
assert_eq!(hunks.len(), 3);
let hunk1 = &hunks[0];
assert!(hunk1.old_lines().is_some());
assert!(hunk1.new_lines().is_some());
let hunk2 = &hunks[1];
assert!(hunk2.old_lines().is_none()); assert!(hunk2.new_lines().is_some());
let hunk3 = &hunks[2];
assert!(hunk3.old_lines().is_some());
assert!(hunk3.new_lines().is_none()); }
#[test]
fn test_mixed_formats_in_one_file() {
let input = r#"--- a/unified.txt
+++ b/unified.txt
@@ -1,1 +1,1 @@
-old
+new
*** a/context.txt
--- b/context.txt
***************
*** 1 ****
! old
--- 1 ----
! new
2c
new content
.
5c5
< old line
---
> new line
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let patch_files = patch.patch_files().count();
let context_diff_files = patch.context_diff_files().count();
let ed_commands = patch.ed_commands().count();
let normal_hunks = patch.normal_hunks().count();
assert!(parsed.ok());
let total_sections = patch_files + context_diff_files + ed_commands + normal_hunks;
assert!(total_sections >= 3); }
#[test]
fn test_context_diff_with_additions_deletions() {
let input = r#"*** a/file.txt
--- b/file.txt
***************
*** 1,5 ****
line 1
- line 2
line 3
line 4
line 5
--- 1,6 ----
line 1
line 3
+ line 3.5
line 4
line 5
+ line 6
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let file = patch.context_diff_files().next().unwrap();
let hunk = file.hunks().next().unwrap();
let old_section = hunk.old_section().unwrap();
assert!(old_section
.syntax()
.children()
.any(|n| n.kind() == crate::edit::lex::SyntaxKind::DELETE_LINE));
let new_section = hunk.new_section().unwrap();
assert!(new_section
.syntax()
.children()
.any(|n| n.kind() == crate::edit::lex::SyntaxKind::ADD_LINE));
}
#[test]
fn test_ed_diff_with_ranges() {
let input = r#"3,5c
line 3 new
line 4 new
line 5 new
.
10,12d
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let commands: Vec<_> = patch.ed_commands().collect();
assert_eq!(commands.len(), 2);
let change = commands[0].as_change().unwrap();
let (start, end) = change.line_numbers();
assert_eq!(start, Some(3));
assert_eq!(end, Some(5));
let delete = commands[1].as_delete().unwrap();
let (start, end) = delete.line_numbers();
assert_eq!(start, Some(10));
assert_eq!(end, Some(12));
}
#[test]
fn test_normal_diff_all_operations() {
let input = r#"0a1,2
> new line 1
> new line 2
3,4c5,6
< old line 3
< old line 4
---
> new line 5
> new line 6
7,8d6
< deleted line 7
< deleted line 8
"#;
let parsed = edit::parse(input);
assert!(parsed.ok());
let patch = parsed.tree();
let hunks: Vec<_> = patch.normal_hunks().collect();
assert_eq!(hunks.len(), 3);
for hunk in &hunks {
assert!(hunk.command().is_some());
}
}
#[test]
fn test_lossless_roundtrip_all_formats() {
let inputs = vec![
r#"--- a/file.txt 2023-01-01
+++ b/file.txt 2023-01-02
@@ -1,3 +1,3 @@
line 1
-line 2
+line 2 modified
line 3
"#,
r#"*** a/file.txt 2023-01-01
--- b/file.txt 2023-01-02
***************
*** 1,3 ****
line 1
! line 2
line 3
--- 1,3 ----
line 1
! line 2 modified
line 3
"#,
r#"2c
line 2 modified
.
"#,
r#"2c2
< line 2
---
> line 2 modified
"#,
];
for input in inputs {
let parsed = edit::parse(input);
assert!(parsed.ok());
let syntax = parsed.syntax_node();
let output = syntax.text().to_string();
assert_eq!(input, output);
}
}
#[test]
fn test_format_detection_accuracy() {
assert_eq!(
edit::parse("--- a/file\n+++ b/file\n@@ -1 +1 @@\n-old\n+new\n")
.tree()
.detect_format(),
Some(DiffFormat::Unified)
);
assert_eq!(
edit::parse(
"*** a/file\n--- b/file\n***************\n*** 1 ****\n! old\n--- 1 ----\n! new\n"
)
.tree()
.detect_format(),
Some(DiffFormat::Context)
);
assert_eq!(
edit::parse("1c\nnew line\n.\n").tree().detect_format(),
Some(DiffFormat::Ed)
);
assert_eq!(
edit::parse("1c1\n< old\n---\n> new\n")
.tree()
.detect_format(),
Some(DiffFormat::Normal)
);
}
}