fork of go-gitdiff with jj support
1package gitdiff
2
3import (
4 "encoding/binary"
5 "io"
6 "reflect"
7 "strings"
8 "testing"
9)
10
11func TestParseBinaryMarker(t *testing.T) {
12 tests := map[string]struct {
13 Input string
14 IsBinary bool
15 HasData bool
16 Err bool
17 }{
18 "binaryPatch": {
19 Input: "GIT binary patch\n",
20 IsBinary: true,
21 HasData: true,
22 },
23 "binaryFileNoPatch": {
24 Input: "Binary files differ\n",
25 IsBinary: true,
26 HasData: false,
27 },
28 "binaryFileNoPatchPaths": {
29 Input: "Binary files a/foo.bin and b/foo.bin differ\n",
30 IsBinary: true,
31 HasData: false,
32 },
33 "fileNoPatch": {
34 Input: "Files differ\n",
35 IsBinary: true,
36 HasData: false,
37 },
38 "textFile": {
39 Input: "@@ -10,14 +22,31 @@\n",
40 IsBinary: false,
41 HasData: false,
42 },
43 }
44
45 for name, test := range tests {
46 t.Run(name, func(t *testing.T) {
47 p := newTestParser(test.Input, true)
48
49 isBinary, hasData, err := p.ParseBinaryMarker()
50 if test.Err {
51 if err == nil || err == io.EOF {
52 t.Fatalf("expected error parsing binary marker, but got %v", err)
53 }
54 return
55 }
56 if err != nil {
57 t.Fatalf("unexpected error parsing binary marker: %v", err)
58 }
59 if test.IsBinary != isBinary {
60 t.Errorf("incorrect isBinary value: expected %t, actual %t", test.IsBinary, isBinary)
61 }
62 if test.HasData != hasData {
63 t.Errorf("incorrect hasData value: expected %t, actual %t", test.HasData, hasData)
64 }
65 })
66 }
67}
68
69func TestParseBinaryFragmentHeader(t *testing.T) {
70 tests := map[string]struct {
71 Input string
72 Output *BinaryFragment
73 Err bool
74 }{
75 "delta": {
76 Input: "delta 1234\n",
77 Output: &BinaryFragment{
78 Method: BinaryPatchDelta,
79 Size: 1234,
80 },
81 },
82 "literal": {
83 Input: "literal 1234\n",
84 Output: &BinaryFragment{
85 Method: BinaryPatchLiteral,
86 Size: 1234,
87 },
88 },
89 "unknownMethod": {
90 Input: "compressed 1234\n",
91 Output: nil,
92 },
93 "notAHeader": {
94 Input: "Binary files differ\n",
95 Output: nil,
96 },
97 "invalidSize": {
98 Input: "delta 123abc\n",
99 Err: true,
100 },
101 }
102
103 for name, test := range tests {
104 t.Run(name, func(t *testing.T) {
105 p := newTestParser(test.Input, true)
106
107 frag, err := p.ParseBinaryFragmentHeader()
108 if test.Err {
109 if err == nil || err == io.EOF {
110 t.Fatalf("expected error parsing binary header, but got %v", err)
111 }
112 return
113 }
114 if err != nil {
115 t.Fatalf("unexpected error parsing binary header: %v", err)
116 }
117 if !reflect.DeepEqual(test.Output, frag) {
118 t.Errorf("incorrect binary fragment\nexpected: %+v\n actual: %+v", test.Output, frag)
119 }
120 })
121 }
122}
123
124func TestParseBinaryChunk(t *testing.T) {
125 tests := map[string]struct {
126 Input string
127 Fragment BinaryFragment
128 Output []byte
129 Err string
130 }{
131 "singleline": {
132 Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n\n",
133 Fragment: BinaryFragment{
134 Size: 20,
135 },
136 Output: fib(5, binary.BigEndian),
137 },
138 "multiline": {
139 Input: "zcmZQzU|?i`U?w2V48*KJ%mKu_Kr9NxN<eH5#F0Qe0f=7$l~*z_FeL$%-)3N7vt?l5\n" +
140 "zl3-vE2xVZ9%4J~CI>f->s?WfX|B-=Vs{#X~svra7Ekg#T|4s}nH;WnAZ)|1Y*`&cB\n" +
141 "s(sh?X(Uz6L^!Ou&aF*u`J!eibJifSrv0z>$Q%Hd(^HIJ<Y?5`S0gT5UE&u=k\n\n",
142 Fragment: BinaryFragment{
143 Size: 160,
144 },
145 Output: fib(40, binary.BigEndian),
146 },
147 "shortLine": {
148 Input: "A00\n\n",
149 Err: "corrupt data line",
150 },
151 "underpaddedLine": {
152 Input: "H00000000\n\n",
153 Err: "corrupt data line",
154 },
155 "invalidLengthByte": {
156 Input: "!00000\n\n",
157 Err: "invalid length byte",
158 },
159 "miscountedLine": {
160 Input: "H00000\n\n",
161 Err: "incorrect byte count",
162 },
163 "invalidEncoding": {
164 Input: "TcmZQzU|?i'U?w2V48*Je09XJG\n",
165 Err: "invalid base85 byte",
166 },
167 "noTrailingEmptyLine": {
168 Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n",
169 Err: "unexpected EOF",
170 },
171 "invalidCompression": {
172 Input: "F007GV%KiWV\n\n",
173 Err: "zlib",
174 },
175 "incorrectSize": {
176 Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n\n",
177 Fragment: BinaryFragment{
178 Size: 16,
179 },
180 Err: "16 byte fragment inflated to 20",
181 },
182 }
183
184 for name, test := range tests {
185 t.Run(name, func(t *testing.T) {
186 p := newTestParser(test.Input, true)
187
188 frag := test.Fragment
189 err := p.ParseBinaryChunk(&frag)
190 if test.Err != "" {
191 if err == nil || !strings.Contains(err.Error(), test.Err) {
192 t.Fatalf("expected error containing %q parsing binary chunk, but got %v", test.Err, err)
193 }
194 return
195 }
196 if err != nil {
197 t.Fatalf("unexpected error parsing binary chunk: %v", err)
198 }
199 if !reflect.DeepEqual(test.Output, frag.Data) {
200 t.Errorf("incorrect binary chunk\nexpected: %+v\n actual: %+v", test.Output, frag.Data)
201 }
202 })
203 }
204}
205
206func TestParseBinaryFragments(t *testing.T) {
207 tests := map[string]struct {
208 Input string
209 File File
210
211 Binary bool
212 Fragment *BinaryFragment
213 ReverseFragment *BinaryFragment
214 Err bool
215 }{
216 "dataWithReverse": {
217 Input: `GIT binary patch
218literal 40
219gcmZQzU|?i` + "`" + `U?w2V48*KJ%mKu_Kr9NxN<eH500b)lkN^Mx
220
221literal 0
222HcmV?d00001
223
224`,
225 Binary: true,
226 Fragment: &BinaryFragment{
227 Method: BinaryPatchLiteral,
228 Size: 40,
229 Data: fib(10, binary.BigEndian),
230 },
231 ReverseFragment: &BinaryFragment{
232 Method: BinaryPatchLiteral,
233 Size: 0,
234 Data: []byte{},
235 },
236 },
237 "dataWithoutReverse": {
238 Input: `GIT binary patch
239literal 40
240gcmZQzU|?i` + "`" + `U?w2V48*KJ%mKu_Kr9NxN<eH500b)lkN^Mx
241
242`,
243 Binary: true,
244 Fragment: &BinaryFragment{
245 Method: BinaryPatchLiteral,
246 Size: 40,
247 Data: fib(10, binary.BigEndian),
248 },
249 },
250 "noData": {
251 Input: "Binary files differ\n",
252 Binary: true,
253 },
254 "text": {
255 Input: `@@ -1 +1 @@
256-old line
257+new line
258`,
259 Binary: false,
260 },
261 "missingData": {
262 Input: "GIT binary patch\n",
263 Err: true,
264 },
265 "invalidData": {
266 Input: `GIT binary patch
267literal 20
268TcmZQzU|?i'U?w2V48*Je09XJG
269
270`,
271 Err: true,
272 },
273 "invalidReverseData": {
274 Input: `GIT binary patch
275literal 20
276TcmZQzU|?i` + "`" + `U?w2V48*Je09XJG
277
278literal 0
279zcmV?d00001
280
281`,
282 Err: true,
283 },
284 }
285
286 for name, test := range tests {
287 t.Run(name, func(t *testing.T) {
288 p := newTestParser(test.Input, true)
289
290 file := test.File
291 _, err := p.ParseBinaryFragments(&file)
292 if test.Err {
293 if err == nil || err == io.EOF {
294 t.Fatalf("expected error parsing binary fragments, but got %v", err)
295 }
296 return
297 }
298 if err != nil {
299 t.Fatalf("unexpected error parsing binary fragments: %v", err)
300 }
301 if test.Binary != file.IsBinary {
302 t.Errorf("incorrect binary state: expected %t, actual %t", test.Binary, file.IsBinary)
303 }
304 if !reflect.DeepEqual(test.Fragment, file.BinaryFragment) {
305 t.Errorf("incorrect binary fragment\nexpected: %+v\n actual: %+v", test.Fragment, file.BinaryFragment)
306 }
307 if !reflect.DeepEqual(test.ReverseFragment, file.ReverseBinaryFragment) {
308 t.Errorf("incorrect reverse binary fragment\nexpected: %+v\n actual: %+v", test.ReverseFragment, file.ReverseBinaryFragment)
309 }
310 })
311 }
312}
313
314func fib(n int, ord binary.ByteOrder) []byte {
315 buf := make([]byte, 4*n)
316 for i := 0; i < len(buf); i += 4 {
317 if i < 8 {
318 ord.PutUint32(buf[i:], 1)
319 } else {
320 ord.PutUint32(buf[i:], ord.Uint32(buf[i-4:])+ord.Uint32(buf[i-8:]))
321 }
322 }
323 return buf
324}