diff options
| author | Anton Medvedev <anton@medv.io> | 2025-11-30 12:46:34 +0100 |
|---|---|---|
| committer | Anton Medvedev <anton@medv.io> | 2025-11-30 12:46:34 +0100 |
| commit | f6b0f38af648d028422a7494378b5dabdc90573f (patch) | |
| tree | 3c26cfc269c021300a2d1e4e02623dd440c20226 /pkg/gitdiff/binary_test.go | |
First commit
Diffstat (limited to 'pkg/gitdiff/binary_test.go')
| -rw-r--r-- | pkg/gitdiff/binary_test.go | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/pkg/gitdiff/binary_test.go b/pkg/gitdiff/binary_test.go new file mode 100644 index 0000000..64db243 --- /dev/null +++ b/pkg/gitdiff/binary_test.go @@ -0,0 +1,324 @@ +package gitdiff + +import ( + "encoding/binary" + "io" + "reflect" + "strings" + "testing" +) + +func TestParseBinaryMarker(t *testing.T) { + tests := map[string]struct { + Input string + IsBinary bool + HasData bool + Err bool + }{ + "binaryPatch": { + Input: "GIT binary patch\n", + IsBinary: true, + HasData: true, + }, + "binaryFileNoPatch": { + Input: "Binary files differ\n", + IsBinary: true, + HasData: false, + }, + "binaryFileNoPatchPaths": { + Input: "Binary files a/foo.bin and b/foo.bin differ\n", + IsBinary: true, + HasData: false, + }, + "fileNoPatch": { + Input: "Files differ\n", + IsBinary: true, + HasData: false, + }, + "textFile": { + Input: "@@ -10,14 +22,31 @@\n", + IsBinary: false, + HasData: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + isBinary, hasData, err := p.ParseBinaryMarker() + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing binary marker, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("unexpected error parsing binary marker: %v", err) + } + if test.IsBinary != isBinary { + t.Errorf("incorrect isBinary value: expected %t, actual %t", test.IsBinary, isBinary) + } + if test.HasData != hasData { + t.Errorf("incorrect hasData value: expected %t, actual %t", test.HasData, hasData) + } + }) + } +} + +func TestParseBinaryFragmentHeader(t *testing.T) { + tests := map[string]struct { + Input string + Output *BinaryFragment + Err bool + }{ + "delta": { + Input: "delta 1234\n", + Output: &BinaryFragment{ + Method: BinaryPatchDelta, + Size: 1234, + }, + }, + "literal": { + Input: "literal 1234\n", + Output: &BinaryFragment{ + Method: BinaryPatchLiteral, + Size: 1234, + }, + }, + "unknownMethod": { + Input: "compressed 1234\n", + Output: nil, + }, + "notAHeader": { + Input: "Binary files differ\n", + Output: nil, + }, + "invalidSize": { + Input: "delta 123abc\n", + Err: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + frag, err := p.ParseBinaryFragmentHeader() + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing binary header, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("unexpected error parsing binary header: %v", err) + } + if !reflect.DeepEqual(test.Output, frag) { + t.Errorf("incorrect binary fragment\nexpected: %+v\n actual: %+v", test.Output, frag) + } + }) + } +} + +func TestParseBinaryChunk(t *testing.T) { + tests := map[string]struct { + Input string + Fragment BinaryFragment + Output []byte + Err string + }{ + "singleline": { + Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n\n", + Fragment: BinaryFragment{ + Size: 20, + }, + Output: fib(5, binary.BigEndian), + }, + "multiline": { + Input: "zcmZQzU|?i`U?w2V48*KJ%mKu_Kr9NxN<eH5#F0Qe0f=7$l~*z_FeL$%-)3N7vt?l5\n" + + "zl3-vE2xVZ9%4J~CI>f->s?WfX|B-=Vs{#X~svra7Ekg#T|4s}nH;WnAZ)|1Y*`&cB\n" + + "s(sh?X(Uz6L^!Ou&aF*u`J!eibJifSrv0z>$Q%Hd(^HIJ<Y?5`S0gT5UE&u=k\n\n", + Fragment: BinaryFragment{ + Size: 160, + }, + Output: fib(40, binary.BigEndian), + }, + "shortLine": { + Input: "A00\n\n", + Err: "corrupt data line", + }, + "underpaddedLine": { + Input: "H00000000\n\n", + Err: "corrupt data line", + }, + "invalidLengthByte": { + Input: "!00000\n\n", + Err: "invalid length byte", + }, + "miscountedLine": { + Input: "H00000\n\n", + Err: "incorrect byte count", + }, + "invalidEncoding": { + Input: "TcmZQzU|?i'U?w2V48*Je09XJG\n", + Err: "invalid base85 byte", + }, + "noTrailingEmptyLine": { + Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n", + Err: "unexpected EOF", + }, + "invalidCompression": { + Input: "F007GV%KiWV\n\n", + Err: "zlib", + }, + "incorrectSize": { + Input: "TcmZQzU|?i`U?w2V48*Je09XJG\n\n", + Fragment: BinaryFragment{ + Size: 16, + }, + Err: "16 byte fragment inflated to 20", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + frag := test.Fragment + err := p.ParseBinaryChunk(&frag) + if test.Err != "" { + if err == nil || !strings.Contains(err.Error(), test.Err) { + t.Fatalf("expected error containing %q parsing binary chunk, but got %v", test.Err, err) + } + return + } + if err != nil { + t.Fatalf("unexpected error parsing binary chunk: %v", err) + } + if !reflect.DeepEqual(test.Output, frag.Data) { + t.Errorf("incorrect binary chunk\nexpected: %+v\n actual: %+v", test.Output, frag.Data) + } + }) + } +} + +func TestParseBinaryFragments(t *testing.T) { + tests := map[string]struct { + Input string + File File + + Binary bool + Fragment *BinaryFragment + ReverseFragment *BinaryFragment + Err bool + }{ + "dataWithReverse": { + Input: `GIT binary patch +literal 40 +gcmZQzU|?i` + "`" + `U?w2V48*KJ%mKu_Kr9NxN<eH500b)lkN^Mx + +literal 0 +HcmV?d00001 + +`, + Binary: true, + Fragment: &BinaryFragment{ + Method: BinaryPatchLiteral, + Size: 40, + Data: fib(10, binary.BigEndian), + }, + ReverseFragment: &BinaryFragment{ + Method: BinaryPatchLiteral, + Size: 0, + Data: []byte{}, + }, + }, + "dataWithoutReverse": { + Input: `GIT binary patch +literal 40 +gcmZQzU|?i` + "`" + `U?w2V48*KJ%mKu_Kr9NxN<eH500b)lkN^Mx + +`, + Binary: true, + Fragment: &BinaryFragment{ + Method: BinaryPatchLiteral, + Size: 40, + Data: fib(10, binary.BigEndian), + }, + }, + "noData": { + Input: "Binary files differ\n", + Binary: true, + }, + "text": { + Input: `@@ -1 +1 @@ +-old line ++new line +`, + Binary: false, + }, + "missingData": { + Input: "GIT binary patch\n", + Err: true, + }, + "invalidData": { + Input: `GIT binary patch +literal 20 +TcmZQzU|?i'U?w2V48*Je09XJG + +`, + Err: true, + }, + "invalidReverseData": { + Input: `GIT binary patch +literal 20 +TcmZQzU|?i` + "`" + `U?w2V48*Je09XJG + +literal 0 +zcmV?d00001 + +`, + Err: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + file := test.File + _, err := p.ParseBinaryFragments(&file) + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing binary fragments, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("unexpected error parsing binary fragments: %v", err) + } + if test.Binary != file.IsBinary { + t.Errorf("incorrect binary state: expected %t, actual %t", test.Binary, file.IsBinary) + } + if !reflect.DeepEqual(test.Fragment, file.BinaryFragment) { + t.Errorf("incorrect binary fragment\nexpected: %+v\n actual: %+v", test.Fragment, file.BinaryFragment) + } + if !reflect.DeepEqual(test.ReverseFragment, file.ReverseBinaryFragment) { + t.Errorf("incorrect reverse binary fragment\nexpected: %+v\n actual: %+v", test.ReverseFragment, file.ReverseBinaryFragment) + } + }) + } +} + +func fib(n int, ord binary.ByteOrder) []byte { + buf := make([]byte, 4*n) + for i := 0; i < len(buf); i += 4 { + if i < 8 { + ord.PutUint32(buf[i:], 1) + } else { + ord.PutUint32(buf[i:], ord.Uint32(buf[i-4:])+ord.Uint32(buf[i-8:])) + } + } + return buf +} |
