fork of go-gitdiff with jj support

Add error tests for BinaryFragment#Apply

These cover some of the possible errors with delta fragments. Many of
the same tests could be implemented slightly easier by testing the
helper functions, but the infrastructure is already set up to test the
full function.

This is made easier by the bin.go CLI, which can parse and encode binary
patch data. Once parsed, fragments were manipulated with truncate and
dd, encoded, and placed in the patch files.

+52 -13
gitdiff/apply_test.go
··· 6 6 "io" 7 7 "io/ioutil" 8 8 "path/filepath" 9 + "strings" 9 10 "testing" 10 11 ) 11 12 ··· 85 86 var dst bytes.Buffer 86 87 err = frag.ApplyStrict(&dst, NewLineReader(bytes.NewReader(src), 0)) 87 88 if test.Err != nil { 88 - if err == nil { 89 - t.Fatalf("expected error applying fragment, but got nil") 90 - } 91 - if !errors.Is(err, test.Err) { 92 - t.Fatalf("incorrect apply error: expected: %T (%v), actual: %T (%v)", test.Err, test.Err, err, err) 93 - } 89 + checkApplyError(t, test.Err, err) 94 90 return 95 91 } 96 92 if err != nil { ··· 107 103 func TestBinaryFragmentApply(t *testing.T) { 108 104 tests := map[string]struct { 109 105 Files applyFiles 110 - Err error 106 + Err interface{} 111 107 }{ 112 108 "literalCreate": {Files: getApplyFiles("bin_fragment_literal_create")}, 113 109 "literalModify": {Files: getApplyFiles("bin_fragment_literal_modify")}, 114 110 "deltaModify": {Files: getApplyFiles("bin_fragment_delta_modify")}, 115 111 "deltaModifyLarge": {Files: getApplyFiles("bin_fragment_delta_modify_large")}, 112 + 113 + "errorIncompleteAdd": { 114 + Files: applyFiles{ 115 + Src: "bin_fragment_delta_error.src", 116 + Patch: "bin_fragment_delta_error_incomplete_add.patch", 117 + }, 118 + Err: "incomplete add", 119 + }, 120 + "errorIncompleteCopy": { 121 + Files: applyFiles{ 122 + Src: "bin_fragment_delta_error.src", 123 + Patch: "bin_fragment_delta_error_incomplete_copy.patch", 124 + }, 125 + Err: "incomplete copy", 126 + }, 127 + "errorSrcSize": { 128 + Files: applyFiles{ 129 + Src: "bin_fragment_delta_error.src", 130 + Patch: "bin_fragment_delta_error_src_size.patch", 131 + }, 132 + Err: &Conflict{}, 133 + }, 134 + "errorDstSize": { 135 + Files: applyFiles{ 136 + Src: "bin_fragment_delta_error.src", 137 + Patch: "bin_fragment_delta_error_dst_size.patch", 138 + }, 139 + Err: "insufficient or extra data", 140 + }, 116 141 } 117 142 118 143 for name, test := range tests { ··· 135 160 var dst bytes.Buffer 136 161 err = frag.Apply(&dst, bytes.NewReader(src)) 137 162 if test.Err != nil { 138 - if err == nil { 139 - t.Fatalf("expected error applying fragment, but got nil") 140 - } 141 - if !errors.Is(err, test.Err) { 142 - t.Fatalf("incorrect apply error: expected: %T (%v), actual: %T (%v)", test.Err, test.Err, err, err) 143 - } 163 + checkApplyError(t, test.Err, err) 144 164 return 145 165 } 146 166 if err != nil { ··· 151 171 t.Errorf("incorrect result after apply\nexpected:\n%x\nactual:\n%x", out, dst.Bytes()) 152 172 } 153 173 }) 174 + } 175 + } 176 + 177 + func checkApplyError(t *testing.T, terr interface{}, err error) { 178 + if err == nil { 179 + t.Fatalf("expected error applying fragment, but got nil") 180 + } 181 + 182 + switch terr := terr.(type) { 183 + case string: 184 + if !strings.Contains(err.Error(), terr) { 185 + t.Fatalf("incorrect apply error: %q does not contain %q", err.Error(), terr) 186 + } 187 + case error: 188 + if !errors.Is(err, terr) { 189 + t.Fatalf("incorrect apply error: expected: %T (%v), actual: %T (%v)", terr, terr, err, err) 190 + } 191 + default: 192 + t.Fatalf("unsupported error type: %T", terr) 154 193 } 155 194 } 156 195
+124
gitdiff/testdata/apply/bin.go
··· 1 + //+build ignore 2 + 3 + // bin.go is a helper CLI to manipulate binary diff data for testing purposes. 4 + // It can decode patches generated by git using the standard parsing functions 5 + // or it can encode binary data back into the format expected by Git. It 6 + // operates on stdin writes results (possibly binary) to stdout. 7 + 8 + package main 9 + 10 + import ( 11 + "bytes" 12 + "compress/zlib" 13 + "encoding/binary" 14 + "flag" 15 + "io/ioutil" 16 + "log" 17 + "os" 18 + "strings" 19 + 20 + "github.com/bluekeyes/go-gitdiff/gitdiff" 21 + ) 22 + 23 + var ( 24 + b85Powers = []uint32{52200625, 614125, 7225, 85, 1} 25 + b85Alpha = []byte( 26 + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "!#$%&()*+-;<=>?@^_`{|}~", 27 + ) 28 + ) 29 + 30 + var mode string 31 + 32 + func base85Encode(data []byte) []byte { 33 + chunks, remaining := len(data)/4, len(data)%4 34 + if remaining > 0 { 35 + data = append(data, make([]byte, 4-remaining)...) 36 + chunks++ 37 + } 38 + 39 + var n int 40 + out := make([]byte, 5*chunks) 41 + 42 + for i := 0; i < len(data); i += 4 { 43 + v := binary.BigEndian.Uint32(data[i : i+4]) 44 + for j := 0; j < 5; j++ { 45 + p := v / b85Powers[j] 46 + out[n+j] = b85Alpha[p] 47 + v -= b85Powers[j] * p 48 + } 49 + n += 5 50 + } 51 + 52 + return out 53 + } 54 + 55 + func compress(data []byte) ([]byte, error) { 56 + var b bytes.Buffer 57 + w := zlib.NewWriter(&b) 58 + 59 + if _, err := w.Write(data); err != nil { 60 + return nil, err 61 + } 62 + if err := w.Close(); err != nil { 63 + return nil, err 64 + } 65 + 66 + return b.Bytes(), nil 67 + } 68 + 69 + func wrap(data []byte) string { 70 + var s strings.Builder 71 + for i := 0; i < len(data); i += 52 { 72 + c := 52 73 + if c > len(data)-i { 74 + c = len(data) - i 75 + } 76 + b := (c / 5) * 4 77 + 78 + if b <= 26 { 79 + s.WriteByte(byte('A' + b - 1)) 80 + } else { 81 + s.WriteByte(byte('a' + b - 27)) 82 + } 83 + s.Write(data[i : i+c]) 84 + s.WriteByte('\n') 85 + } 86 + return s.String() 87 + } 88 + 89 + func init() { 90 + flag.StringVar(&mode, "mode", "parse", "operation mode, one of 'parse' or 'encode'") 91 + } 92 + 93 + func main() { 94 + flag.Parse() 95 + 96 + switch mode { 97 + case "parse": 98 + files, _, err := gitdiff.Parse(os.Stdin) 99 + if err != nil { 100 + log.Fatalf("failed to parse file: %v", err) 101 + } 102 + if len(files) != 1 { 103 + log.Fatalf("patch contains more than one file: %d", len(files)) 104 + } 105 + if files[0].BinaryFragment == nil { 106 + log.Fatalf("patch file does not contain a binary fragment") 107 + } 108 + os.Stdout.Write(files[0].BinaryFragment.Data) 109 + 110 + case "encode": 111 + data, err := ioutil.ReadAll(os.Stdin) 112 + if err != nil { 113 + log.Fatalf("failed to read input: %v", err) 114 + } 115 + data, err = compress(data) 116 + if err != nil { 117 + log.Fatalf("failed to compress data: %v", err) 118 + } 119 + os.Stdout.WriteString(wrap(base85Encode(data))) 120 + 121 + default: 122 + log.Fatalf("unknown mode: %s", mode) 123 + } 124 + }
gitdiff/testdata/apply/bin_fragment_delta_error.src

This is a binary file and will not be displayed.

+5
gitdiff/testdata/apply/bin_fragment_delta_error_dst_size.patch
··· 1 + diff --git a/gitdiff/testdata/apply/bin_fragment_delta_error.src b/gitdiff/testdata/apply/bin_fragment_delta_error.src 2 + GIT binary patch 3 + delta 18 4 + fc${itY+{<=z`_4AtEhVK$zKyatN;N30RR6$D+j^= 5 +
+5
gitdiff/testdata/apply/bin_fragment_delta_error_incomplete_add.patch
··· 1 + diff --git a/gitdiff/testdata/apply/bin_fragment_delta_error.src b/gitdiff/testdata/apply/bin_fragment_delta_error.src 2 + GIT binary patch 3 + delta 11 4 + Xc${itY+{_?z`_4As|XMP0RR6K8UwQc 5 +
+5
gitdiff/testdata/apply/bin_fragment_delta_error_incomplete_copy.patch
··· 1 + diff --git a/gitdiff/testdata/apply/bin_fragment_delta_error.src b/gitdiff/testdata/apply/bin_fragment_delta_error.src 2 + GIT binary patch 3 + delta 17 4 + fc${itY+{_?z`_4AtEhVK$zKya00961|Nl5!2ZsOv 5 +
+5
gitdiff/testdata/apply/bin_fragment_delta_error_src_size.patch
··· 1 + diff --git a/gitdiff/testdata/apply/bin_fragment_delta_error.src b/gitdiff/testdata/apply/bin_fragment_delta_error.src 2 + GIT binary patch 3 + delta 18 4 + fc${itYGRz=z`_4AtEhVK$zKyatN;N30RR6$EeFB? 5 +