compare.spec.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. /** Line comparison with its timeout degradation. */
  2. import { describe, expect, it } from 'vitest'
  3. import { compareText } from '../src/compare.ts'
  4. describe('compareText', () => {
  5. it('yields unified hunks with context and counts only changed lines', () => {
  6. const before = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].join('\n')
  7. const after = ['a', 'b', 'c', 'D', 'e', 'f', 'g', 'h', 'i', 'j', 'k'].join('\n')
  8. const result = compareText(before, after, 100)
  9. expect(result.coarse).toBe(false)
  10. expect(result).toMatchObject({ added: 2, deleted: 1 })
  11. // Changes whose context lines touch share one hunk.
  12. expect(result.hunks).toEqual([
  13. { oldStart: 1, oldLines: 10, newStart: 1, newLines: 11, lines: [' a', ' b', ' c', '-d', '+D', ' e', ' f', ' g', ' h', ' i', ' j', '+k'] },
  14. ])
  15. const far = compareText(`${before}\n${'z\n'.repeat(10)}end`, `${after}\n${'z\n'.repeat(10)}END`, 100)
  16. expect(far.hunks.map(hunk => [hunk.oldStart, hunk.oldLines, hunk.newStart, hunk.newLines])).toEqual([[1, 13, 1, 14], [18, 4, 19, 4]])
  17. })
  18. it('treats a missing side as no lines and an unterminated last line by content alone', () => {
  19. expect(compareText(null, 'x\ny\n', 100)).toMatchObject({ added: 2, deleted: 0, hunks: [{ oldStart: 1, oldLines: 0, newStart: 1, newLines: 2 }] })
  20. expect(compareText('x\n', null, 100)).toMatchObject({ added: 0, deleted: 1 })
  21. expect(compareText('last', 'last\nadded', 100)).toMatchObject({ added: 1, deleted: 0 })
  22. expect(compareText('same\n', 'same\n', 100)).toEqual({ hunks: [], coarse: false, added: 0, deleted: 0 })
  23. expect(compareText(null, null, 100)).toEqual({ hunks: [], coarse: false, added: 0, deleted: 0 })
  24. })
  25. it('degrades to whole-file replacement once the timeout passes', () => {
  26. const before = Array.from({ length: 4000 }, (_, i) => `old ${i}`).join('\n')
  27. const after = Array.from({ length: 4000 }, (_, i) => `new ${i}`).join('\n')
  28. const result = compareText(before, after, 1)
  29. expect(result.coarse).toBe(true)
  30. expect(result).toMatchObject({ added: 4000, deleted: 4000 })
  31. expect(result.hunks).toHaveLength(1)
  32. expect(result.hunks[0]).toMatchObject({ oldStart: 1, oldLines: 4000, newStart: 1, newLines: 4000 })
  33. expect(result.hunks[0]!.lines[0]).toBe('-old 0')
  34. expect(result.hunks[0]!.lines.at(-1)).toBe('+new 3999')
  35. const created = compareText(null, `${after}\n${before}`, 1)
  36. expect(created.coarse).toBe(true)
  37. expect(created).toMatchObject({ added: 8000, deleted: 0, hunks: [{ oldStart: 1, oldLines: 0, newStart: 1, newLines: 8000 }] })
  38. })
  39. })