mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
312 lines
9.5 KiB
Swift
312 lines
9.5 KiB
Swift
import Foundation
|
|
import HapiUI
|
|
import Testing
|
|
|
|
/// Unified-diff parser fixtures: modify / add / delete / rename / binary /
|
|
/// bare hunks. Line semantics follow the web's `CodexDiffView.parseUnifiedDiff`
|
|
/// (empty in-hunk line = context, `\` marker is not content); rename/binary
|
|
/// semantics follow `gitParsers.ts` (R/C → renamed, binary → 0/0 counts).
|
|
@Suite("UnifiedDiffParser")
|
|
struct DiffModelTests {
|
|
// MARK: - Modify
|
|
|
|
private let modifyDiff = """
|
|
diff --git a/src/app.ts b/src/app.ts
|
|
index 1234567..89abcde 100644
|
|
--- a/src/app.ts
|
|
+++ b/src/app.ts
|
|
@@ -1,3 +1,4 @@ import section
|
|
import a
|
|
-import b
|
|
+import b2
|
|
+import c
|
|
import d
|
|
@@ -10,2 +11,2 @@
|
|
x
|
|
y
|
|
"""
|
|
|
|
@Test func parsesModifiedFile() throws {
|
|
let files = UnifiedDiffParser.parse(modifyDiff)
|
|
#expect(files.count == 1)
|
|
let file = try #require(files.first)
|
|
#expect(file.kind == .modified)
|
|
#expect(file.oldPath == "src/app.ts")
|
|
#expect(file.newPath == "src/app.ts")
|
|
#expect(file.isBinary == false)
|
|
#expect(file.hunks.count == 2)
|
|
#expect(file.additions == 2)
|
|
#expect(file.deletions == 1)
|
|
#expect(file.displayPath == "src/app.ts")
|
|
}
|
|
|
|
@Test func parsesHunkHeaderNumbersAndHeading() throws {
|
|
let file = try #require(UnifiedDiffParser.parse(modifyDiff).first)
|
|
let first = try #require(file.hunks.first)
|
|
#expect(first.oldStart == 1)
|
|
#expect(first.oldCount == 3)
|
|
#expect(first.newStart == 1)
|
|
#expect(first.newCount == 4)
|
|
#expect(first.sectionHeading == "import section")
|
|
#expect(first.header == "@@ -1,3 +1,4 @@ import section")
|
|
let second = try #require(file.hunks.last)
|
|
#expect(second.sectionHeading == nil)
|
|
}
|
|
|
|
@Test func computesLineNumbers() throws {
|
|
let file = try #require(UnifiedDiffParser.parse(modifyDiff).first)
|
|
let lines = try #require(file.hunks.first).lines
|
|
#expect(lines.count == 5)
|
|
#expect(lines[0] == DiffLine(kind: .context, text: "import a", oldNumber: 1, newNumber: 1))
|
|
#expect(lines[1] == DiffLine(kind: .deletion, text: "import b", oldNumber: 2))
|
|
#expect(lines[2] == DiffLine(kind: .addition, text: "import b2", newNumber: 2))
|
|
#expect(lines[3] == DiffLine(kind: .addition, text: "import c", newNumber: 3))
|
|
#expect(lines[4] == DiffLine(kind: .context, text: "import d", oldNumber: 3, newNumber: 4))
|
|
|
|
let secondHunk = try #require(file.hunks.last).lines
|
|
#expect(secondHunk[0].oldNumber == 10)
|
|
#expect(secondHunk[0].newNumber == 11)
|
|
#expect(secondHunk[1].oldNumber == 11)
|
|
#expect(secondHunk[1].newNumber == 12)
|
|
}
|
|
|
|
// MARK: - Add / delete
|
|
|
|
@Test func parsesAddedFile() throws {
|
|
let diff = """
|
|
diff --git a/new.txt b/new.txt
|
|
new file mode 100644
|
|
index 0000000..e69de29
|
|
--- /dev/null
|
|
+++ b/new.txt
|
|
@@ -0,0 +1,2 @@
|
|
+hello
|
|
+world
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.kind == .added)
|
|
#expect(file.oldPath == nil)
|
|
#expect(file.newPath == "new.txt")
|
|
#expect(file.additions == 2)
|
|
#expect(file.deletions == 0)
|
|
#expect(file.hunks.first?.lines.map(\.newNumber) == [1, 2])
|
|
#expect(file.displayPath == "new.txt")
|
|
}
|
|
|
|
@Test func parsesDeletedFile() throws {
|
|
let diff = """
|
|
diff --git a/old.txt b/old.txt
|
|
deleted file mode 100644
|
|
index e69de29..0000000
|
|
--- a/old.txt
|
|
+++ /dev/null
|
|
@@ -1,2 +0,0 @@
|
|
-hello
|
|
-world
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.kind == .deleted)
|
|
#expect(file.oldPath == "old.txt")
|
|
#expect(file.newPath == nil)
|
|
#expect(file.additions == 0)
|
|
#expect(file.deletions == 2)
|
|
#expect(file.hunks.first?.lines.map(\.oldNumber) == [1, 2])
|
|
#expect(file.displayPath == "old.txt")
|
|
}
|
|
|
|
// MARK: - Rename
|
|
|
|
@Test func parsesPureRename() throws {
|
|
let diff = """
|
|
diff --git a/lib/a.ts b/lib/b.ts
|
|
similarity index 100%
|
|
rename from lib/a.ts
|
|
rename to lib/b.ts
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.kind == .renamed)
|
|
#expect(file.oldPath == "lib/a.ts")
|
|
#expect(file.newPath == "lib/b.ts")
|
|
#expect(file.hunks.isEmpty)
|
|
#expect(file.additions == 0)
|
|
#expect(file.displayPath == "lib/a.ts → lib/b.ts")
|
|
}
|
|
|
|
@Test func parsesRenameWithEdits() throws {
|
|
let diff = """
|
|
diff --git a/lib/a.ts b/lib/b.ts
|
|
similarity index 90%
|
|
rename from lib/a.ts
|
|
rename to lib/b.ts
|
|
index 111..222 100644
|
|
--- a/lib/a.ts
|
|
+++ b/lib/b.ts
|
|
@@ -1,2 +1,2 @@
|
|
keep
|
|
-old
|
|
+new
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.kind == .renamed)
|
|
#expect(file.hunks.count == 1)
|
|
#expect(file.additions == 1)
|
|
#expect(file.deletions == 1)
|
|
}
|
|
|
|
@Test func copyCountsAsRenamed() throws {
|
|
let diff = """
|
|
diff --git a/x.ts b/y.ts
|
|
copy from x.ts
|
|
copy to y.ts
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.kind == .renamed)
|
|
#expect(file.oldPath == "x.ts")
|
|
#expect(file.newPath == "y.ts")
|
|
}
|
|
|
|
// MARK: - Binary
|
|
|
|
@Test func parsesBinaryFile() throws {
|
|
let diff = """
|
|
diff --git a/img.png b/img.png
|
|
index 1234567..89abcde 100644
|
|
Binary files a/img.png and b/img.png differ
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
#expect(file.isBinary)
|
|
#expect(file.hunks.isEmpty)
|
|
// gitParsers numstat parity: binary reports 0/0.
|
|
#expect(file.additions == 0)
|
|
#expect(file.deletions == 0)
|
|
}
|
|
|
|
// MARK: - Bare hunks (agent unified_diff inputs)
|
|
|
|
@Test func parsesHeaderlessHunk() throws {
|
|
let diff = """
|
|
@@ -1,2 +1,2 @@
|
|
a
|
|
-b
|
|
+c
|
|
"""
|
|
let files = UnifiedDiffParser.parse(diff)
|
|
#expect(files.count == 1)
|
|
let file = try #require(files.first)
|
|
#expect(file.oldPath == nil)
|
|
#expect(file.newPath == nil)
|
|
#expect(file.kind == .modified)
|
|
#expect(file.hunks.count == 1)
|
|
#expect(file.hunks.first?.lines.count == 3)
|
|
#expect(file.additions == 1)
|
|
#expect(file.deletions == 1)
|
|
#expect(file.displayPath == "diff")
|
|
}
|
|
|
|
@Test func parsesOmittedCountsAsOne() throws {
|
|
let diff = """
|
|
@@ -3 +7 @@
|
|
-x
|
|
+y
|
|
"""
|
|
let hunk = try #require(UnifiedDiffParser.parse(diff).first?.hunks.first)
|
|
#expect(hunk.oldStart == 3)
|
|
#expect(hunk.oldCount == 1)
|
|
#expect(hunk.newStart == 7)
|
|
#expect(hunk.newCount == 1)
|
|
#expect(hunk.lines[0].oldNumber == 3)
|
|
#expect(hunk.lines[1].newNumber == 7)
|
|
}
|
|
|
|
@Test func emptyInHunkLineIsContext() throws {
|
|
let diff = """
|
|
--- a/f.txt
|
|
+++ b/f.txt
|
|
@@ -1,3 +1,3 @@
|
|
a
|
|
|
|
b
|
|
"""
|
|
let lines = try #require(UnifiedDiffParser.parse(diff).first?.hunks.first).lines
|
|
#expect(lines.count == 3)
|
|
#expect(lines[1] == DiffLine(kind: .context, text: "", oldNumber: 2, newNumber: 2))
|
|
}
|
|
|
|
@Test func noNewlineMarkerIsNotContent() throws {
|
|
let diff = """
|
|
--- a/f.txt
|
|
+++ b/f.txt
|
|
@@ -1 +1 @@
|
|
-old
|
|
\\ No newline at end of file
|
|
+new
|
|
\\ No newline at end of file
|
|
"""
|
|
let file = try #require(UnifiedDiffParser.parse(diff).first)
|
|
let lines = try #require(file.hunks.first).lines
|
|
#expect(lines.count == 4)
|
|
#expect(lines[1].kind == .noNewlineMarker)
|
|
#expect(lines[3].kind == .noNewlineMarker)
|
|
#expect(lines[1].oldNumber == nil)
|
|
#expect(lines[1].newNumber == nil)
|
|
#expect(file.additions == 1)
|
|
#expect(file.deletions == 1)
|
|
}
|
|
|
|
// MARK: - Multi-file and junk
|
|
|
|
@Test func parsesMultiFileDiff() {
|
|
let diff = """
|
|
diff --git a/a.txt b/a.txt
|
|
--- a/a.txt
|
|
+++ b/a.txt
|
|
@@ -1 +1 @@
|
|
-x
|
|
+y
|
|
diff --git a/b.txt b/b.txt
|
|
--- a/b.txt
|
|
+++ b/b.txt
|
|
@@ -1 +1 @@
|
|
-p
|
|
+q
|
|
"""
|
|
let files = UnifiedDiffParser.parse(diff)
|
|
#expect(files.count == 2)
|
|
#expect(files.map(\.displayPath) == ["a.txt", "b.txt"])
|
|
#expect(files.allSatisfy { $0.hunks.count == 1 })
|
|
}
|
|
|
|
@Test func ignoresProseAndStrayMarkers() {
|
|
let text = """
|
|
Here is a summary of changes.
|
|
--- notes below
|
|
nothing else
|
|
"""
|
|
#expect(UnifiedDiffParser.parse(text).isEmpty)
|
|
#expect(UnifiedDiffParser.parse("").isEmpty)
|
|
}
|
|
|
|
@Test func closesHunkWhenCountsLie() throws {
|
|
// Declared counts promise more lines than delivered; the next file
|
|
// header must still start a fresh file.
|
|
let diff = """
|
|
diff --git a/a.txt b/a.txt
|
|
--- a/a.txt
|
|
+++ b/a.txt
|
|
@@ -1,5 +1,5 @@
|
|
-x
|
|
+y
|
|
diff --git a/b.txt b/b.txt
|
|
--- a/b.txt
|
|
+++ b/b.txt
|
|
@@ -1 +1 @@
|
|
-p
|
|
+q
|
|
"""
|
|
let files = UnifiedDiffParser.parse(diff)
|
|
#expect(files.count == 2)
|
|
#expect(files.last?.newPath == "b.txt")
|
|
#expect(files.first?.hunks.count == 1)
|
|
}
|
|
}
|