1package patchutil
2
3import (
4 "errors"
5 "reflect"
6 "testing"
7
8 "tangled.org/core/types"
9)
10
11func TestIsPatchValid(t *testing.T) {
12 tests := []struct {
13 name string
14 patch string
15 expected error
16 }{
17 {
18 name: `empty patch`,
19 patch: ``,
20 expected: EmptyPatchError,
21 },
22 {
23 name: `single line patch`,
24 patch: `single line`,
25 expected: EmptyPatchError,
26 },
27 {
28 name: `valid diff patch`,
29 patch: `diff --git a/file.txt b/file.txt
30index abc..def 100644
31--- a/file.txt
32+++ b/file.txt
33@@ -1,3 +1,3 @@
34-old line
35+new line
36 context`,
37 expected: nil,
38 },
39 {
40 name: `valid patch starting with ---`,
41 patch: `--- a/file.txt
42+++ b/file.txt
43@@ -1,3 +1,3 @@
44-old line
45+new line
46 context`,
47 expected: nil,
48 },
49 {
50 name: `valid patch starting with Index`,
51 patch: `Index: file.txt
52==========
53--- a/file.txt
54+++ b/file.txt
55@@ -1,3 +1,3 @@
56-old line
57+new line
58 context`,
59 expected: nil,
60 },
61 {
62 name: `valid patch starting with +++`,
63 patch: `+++ b/file.txt
64--- a/file.txt
65@@ -1,3 +1,3 @@
66-old line
67+new line
68 context`,
69 expected: nil,
70 },
71 {
72 name: `valid patch starting with @@`,
73 patch: `@@ -1,3 +1,3 @@
74-old line
75+new line
76 context
77`,
78 expected: nil,
79 },
80 {
81 name: `valid format patch`,
82 patch: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
83From: Author <author@example.com>
84Date: Wed, 16 Apr 2025 11:01:00 +0300
85Subject: [PATCH] Example patch
86
87diff --git a/file.txt b/file.txt
88index 123456..789012 100644
89--- a/file.txt
90+++ b/file.txt
91@@ -1 +1 @@
92-old content
93+new content
94--
952.48.1`,
96 expected: nil,
97 },
98 {
99 name: `invalid format patch`,
100 patch: `From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
101From: Author <author@example.com>
102This is not a valid patch format`,
103 expected: FormatPatchError,
104 },
105 {
106 name: `not a patch at all`,
107 patch: `This is
108just some
109random text
110that isn't a patch`,
111 expected: GenericPatchError,
112 },
113 }
114
115 for _, tt := range tests {
116 t.Run(tt.name, func(t *testing.T) {
117 result := IsPatchValid(tt.patch)
118 if !errors.Is(result, tt.expected) {
119 t.Errorf("IsPatchValid() = %v, want %v", result, tt.expected)
120 }
121 })
122 }
123}
124
125func TestSplitPatches(t *testing.T) {
126 tests := []struct {
127 name string
128 input string
129 expected []string
130 }{
131 {
132 name: "Empty input",
133 input: "",
134 expected: []string{},
135 },
136 {
137 name: "No valid patches",
138 input: "This is not a \nJust some random text",
139 expected: []string{},
140 },
141 {
142 name: "Single patch",
143 input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
144From: Author <author@example.com>
145Date: Wed, 16 Apr 2025 11:01:00 +0300
146Subject: [PATCH] Example patch
147
148diff --git a/file.txt b/file.txt
149index 123456..789012 100644
150--- a/file.txt
151+++ b/file.txt
152@@ -1 +1 @@
153-old content
154+new content
155--
1562.48.1`,
157 expected: []string{
158 `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
159From: Author <author@example.com>
160Date: Wed, 16 Apr 2025 11:01:00 +0300
161Subject: [PATCH] Example patch
162
163diff --git a/file.txt b/file.txt
164index 123456..789012 100644
165--- a/file.txt
166+++ b/file.txt
167@@ -1 +1 @@
168-old content
169+new content
170--
1712.48.1`,
172 },
173 },
174 {
175 name: "Two patches",
176 input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
177From: Author <author@example.com>
178Date: Wed, 16 Apr 2025 11:01:00 +0300
179Subject: [PATCH 1/2] First patch
180
181diff --git a/file1.txt b/file1.txt
182index 123456..789012 100644
183--- a/file1.txt
184+++ b/file1.txt
185@@ -1 +1 @@
186-old content
187+new content
188--
1892.48.1
190From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
191From: Author <author@example.com>
192Date: Wed, 16 Apr 2025 11:03:11 +0300
193Subject: [PATCH 2/2] Second patch
194
195diff --git a/file2.txt b/file2.txt
196index abcdef..ghijkl 100644
197--- a/file2.txt
198+++ b/file2.txt
199@@ -1 +1 @@
200-foo bar
201+baz qux
202--
2032.48.1`,
204 expected: []string{
205 `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
206From: Author <author@example.com>
207Date: Wed, 16 Apr 2025 11:01:00 +0300
208Subject: [PATCH 1/2] First patch
209
210diff --git a/file1.txt b/file1.txt
211index 123456..789012 100644
212--- a/file1.txt
213+++ b/file1.txt
214@@ -1 +1 @@
215-old content
216+new content
217--
2182.48.1`,
219 `From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
220From: Author <author@example.com>
221Date: Wed, 16 Apr 2025 11:03:11 +0300
222Subject: [PATCH 2/2] Second patch
223
224diff --git a/file2.txt b/file2.txt
225index abcdef..ghijkl 100644
226--- a/file2.txt
227+++ b/file2.txt
228@@ -1 +1 @@
229-foo bar
230+baz qux
231--
2322.48.1`,
233 },
234 },
235 {
236 name: "Patches with additional text between them",
237 input: `Some text before the patches
238
239From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
240From: Author <author@example.com>
241Subject: [PATCH] First patch
242
243diff content here
244--
2452.48.1
246
247Some text between patches
248
249From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
250From: Author <author@example.com>
251Subject: [PATCH] Second patch
252
253more diff content
254--
2552.48.1
256
257Text after patches`,
258 expected: []string{
259 `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
260From: Author <author@example.com>
261Subject: [PATCH] First patch
262
263diff content here
264--
2652.48.1
266
267Some text between patches`,
268 `From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
269From: Author <author@example.com>
270Subject: [PATCH] Second patch
271
272more diff content
273--
2742.48.1
275
276Text after patches`,
277 },
278 },
279 {
280 name: "Patches with whitespace padding",
281 input: `
282
283From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
284From: Author <author@example.com>
285Subject: Patch
286
287content
288--
2892.48.1
290
291
292From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
293From: Author <author@example.com>
294Subject: Another patch
295
296content
297--
2982.48.1
299 `,
300 expected: []string{
301 `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
302From: Author <author@example.com>
303Subject: Patch
304
305content
306--
3072.48.1`,
308 `From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001
309From: Author <author@example.com>
310Subject: Another patch
311
312content
313--
3142.48.1`,
315 },
316 },
317 }
318
319 for _, tt := range tests {
320 t.Run(tt.name, func(t *testing.T) {
321 result := splitFormatPatch(tt.input)
322 if !reflect.DeepEqual(result, tt.expected) {
323 t.Errorf("splitPatches() = %v, want %v", result, tt.expected)
324 }
325 })
326 }
327}
328
329func TestImplsInterfaces(t *testing.T) {
330 id := &InterdiffResult{}
331 _ = isDiffsRenderer(id)
332}
333
334func isDiffsRenderer[S types.DiffRenderer](S) bool { return true }