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