f85c746af0
"Cotejo" means nothing outside Spanish and is hard to pronounce elsewhere, so the project is now Kotej: same root, but it reads and sounds the same in English and Spanish. Renamed throughout — engine, targets, bundle ids and the URL scheme, which is now kotej://compare. Icon generated with Codex: two facing panels split by a seam, content lines on each side and one amber line marking a difference — the tool's job at a glance, still legible at 16pt. 34 tests still green; kotej:// verified end to end against the demo JARs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
82 lines
3.3 KiB
Swift
82 lines
3.3 KiB
Swift
import XCTest
|
|
@testable import KotejEngine
|
|
|
|
final class TextDiffTests: XCTestCase {
|
|
func testIdenticalTextIsAllEqual() {
|
|
let rows = TextDiff.rows(left: "uno\ndos\ntres", right: "uno\ndos\ntres")
|
|
XCTAssertEqual(rows.count, 3)
|
|
XCTAssertTrue(rows.allSatisfy { $0.kind == .equal })
|
|
XCTAssertEqual(rows[1].leftNumber, 2)
|
|
XCTAssertEqual(rows[1].rightNumber, 2)
|
|
}
|
|
|
|
func testChangedLinePairsUpInsteadOfDeletePlusInsert() {
|
|
let rows = TextDiff.rows(left: "uno\ndos\ntres", right: "uno\nDOS\ntres")
|
|
XCTAssertEqual(rows.count, 3)
|
|
XCTAssertEqual(rows[1].kind, .changed)
|
|
XCTAssertEqual(rows[1].left, "dos")
|
|
XCTAssertEqual(rows[1].right, "DOS")
|
|
// The unchanged lines keep their numbering on both sides.
|
|
XCTAssertEqual(rows[2].leftNumber, 3)
|
|
XCTAssertEqual(rows[2].rightNumber, 3)
|
|
}
|
|
|
|
func testInsertionShiftsRightNumbering() {
|
|
let rows = TextDiff.rows(left: "a\nc", right: "a\nb\nc")
|
|
XCTAssertEqual(rows.map(\.kind), [.equal, .added, .equal])
|
|
let added = rows[1]
|
|
XCTAssertNil(added.leftNumber)
|
|
XCTAssertEqual(added.rightNumber, 2)
|
|
XCTAssertEqual(added.right, "b")
|
|
XCTAssertEqual(rows[2].leftNumber, 2)
|
|
XCTAssertEqual(rows[2].rightNumber, 3)
|
|
}
|
|
|
|
func testDeletionIsReported() {
|
|
let rows = TextDiff.rows(left: "a\nb\nc", right: "a\nc")
|
|
XCTAssertEqual(rows.map(\.kind), [.equal, .removed, .equal])
|
|
XCTAssertEqual(rows[1].left, "b")
|
|
XCTAssertNil(rows[1].rightNumber)
|
|
}
|
|
|
|
func testUnrelatedTextsAreFullyChanged() {
|
|
let rows = TextDiff.rows(left: "uno\ndos", right: "tres\ncuatro")
|
|
XCTAssertTrue(rows.allSatisfy { $0.kind == .changed })
|
|
XCTAssertEqual(rows.count, 2)
|
|
}
|
|
|
|
func testHugeFileWithOneChangeStaysCheap() {
|
|
// The common prefix/suffix trim means only the changed middle is aligned,
|
|
// so this must not fall back to the block path despite the size.
|
|
let common = (0..<20_000).map { "line \($0)" }
|
|
var modified = common
|
|
modified[10_000] = "line 10000 CHANGED"
|
|
|
|
let rows = TextDiff.rows(left: common.joined(separator: "\n"),
|
|
right: modified.joined(separator: "\n"))
|
|
XCTAssertEqual(rows.count, 20_000)
|
|
let changed = rows.filter { $0.kind != .equal }
|
|
XCTAssertEqual(changed.count, 1)
|
|
XCTAssertEqual(changed.first?.leftNumber, 10_001)
|
|
}
|
|
|
|
func testVeryLargeDifferingSectionFallsBackWithoutHanging() {
|
|
let left = (0..<5_000).map { "izquierda \($0)" }.joined(separator: "\n")
|
|
let right = (0..<5_000).map { "derecha \($0)" }.joined(separator: "\n")
|
|
let rows = TextDiff.rows(left: left, right: right, limit: 1_000)
|
|
XCTAssertEqual(rows.count, 5_000)
|
|
XCTAssertTrue(rows.allSatisfy { $0.kind == .changed })
|
|
}
|
|
|
|
func testHandlesEmptySides() {
|
|
XCTAssertTrue(TextDiff.rows(left: "", right: "").allSatisfy { $0.kind == .equal })
|
|
let added = TextDiff.rows(left: "", right: "a\nb")
|
|
XCTAssertTrue(added.contains { $0.kind == .added && $0.right == "b" })
|
|
}
|
|
|
|
func testNormalisesWindowsLineEndings() {
|
|
let rows = TextDiff.rows(left: "a\r\nb", right: "a\nb")
|
|
XCTAssertTrue(rows.allSatisfy { $0.kind == .equal }, "CRLF alone isn't a change")
|
|
}
|
|
}
|