fork of go-gitdiff with jj support
at v0.0.0 6.2 kB view raw
1package gitdiff 2 3import ( 4 "bytes" 5 "errors" 6 "io" 7 "io/ioutil" 8 "path/filepath" 9 "testing" 10) 11 12func TestApplyTextFragment(t *testing.T) { 13 tests := map[string]applyTest{ 14 "createFile": {Files: getApplyFiles("text_fragment_new")}, 15 "deleteFile": {Files: getApplyFiles("text_fragment_delete_all")}, 16 17 "addStart": {Files: getApplyFiles("text_fragment_add_start")}, 18 "addMiddle": {Files: getApplyFiles("text_fragment_add_middle")}, 19 "addEnd": {Files: getApplyFiles("text_fragment_add_end")}, 20 "addEndNoEOL": {Files: getApplyFiles("text_fragment_add_end_noeol")}, 21 22 "changeStart": {Files: getApplyFiles("text_fragment_change_start")}, 23 "changeMiddle": {Files: getApplyFiles("text_fragment_change_middle")}, 24 "changeEnd": {Files: getApplyFiles("text_fragment_change_end")}, 25 "changeEndEOL": {Files: getApplyFiles("text_fragment_change_end_eol")}, 26 "changeExact": {Files: getApplyFiles("text_fragment_change_exact")}, 27 "changeSingleNoEOL": {Files: getApplyFiles("text_fragment_change_single_noeol")}, 28 29 "errorShortSrcBefore": { 30 Files: applyFiles{ 31 Src: "text_fragment_error.src", 32 Patch: "text_fragment_error_short_src_before.patch", 33 }, 34 Err: io.ErrUnexpectedEOF, 35 }, 36 "errorShortSrc": { 37 Files: applyFiles{ 38 Src: "text_fragment_error.src", 39 Patch: "text_fragment_error_short_src.patch", 40 }, 41 Err: io.ErrUnexpectedEOF, 42 }, 43 "errorContextConflict": { 44 Files: applyFiles{ 45 Src: "text_fragment_error.src", 46 Patch: "text_fragment_error_context_conflict.patch", 47 }, 48 Err: &Conflict{}, 49 }, 50 "errorDeleteConflict": { 51 Files: applyFiles{ 52 Src: "text_fragment_error.src", 53 Patch: "text_fragment_error_delete_conflict.patch", 54 }, 55 Err: &Conflict{}, 56 }, 57 "errorNewFile": { 58 Files: applyFiles{ 59 Src: "text_fragment_error.src", 60 Patch: "text_fragment_error_new_file.patch", 61 }, 62 Err: &Conflict{}, 63 }, 64 } 65 66 for name, test := range tests { 67 t.Run(name, func(t *testing.T) { 68 test.run(t, func(dst io.Writer, src io.ReaderAt, file *File) error { 69 if len(file.TextFragments) != 1 { 70 t.Fatalf("patch should contain exactly one fragment, but it has %d", len(file.TextFragments)) 71 } 72 applier := NewTextApplier(dst, src) 73 return applier.ApplyFragment(file.TextFragments[0]) 74 }) 75 }) 76 } 77} 78 79func TestApplyBinaryFragment(t *testing.T) { 80 tests := map[string]applyTest{ 81 "literalCreate": {Files: getApplyFiles("bin_fragment_literal_create")}, 82 "literalModify": {Files: getApplyFiles("bin_fragment_literal_modify")}, 83 "deltaModify": {Files: getApplyFiles("bin_fragment_delta_modify")}, 84 "deltaModifyLarge": {Files: getApplyFiles("bin_fragment_delta_modify_large")}, 85 86 "errorIncompleteAdd": { 87 Files: applyFiles{ 88 Src: "bin_fragment_delta_error.src", 89 Patch: "bin_fragment_delta_error_incomplete_add.patch", 90 }, 91 Err: "incomplete add", 92 }, 93 "errorIncompleteCopy": { 94 Files: applyFiles{ 95 Src: "bin_fragment_delta_error.src", 96 Patch: "bin_fragment_delta_error_incomplete_copy.patch", 97 }, 98 Err: "incomplete copy", 99 }, 100 "errorSrcSize": { 101 Files: applyFiles{ 102 Src: "bin_fragment_delta_error.src", 103 Patch: "bin_fragment_delta_error_src_size.patch", 104 }, 105 Err: &Conflict{}, 106 }, 107 "errorDstSize": { 108 Files: applyFiles{ 109 Src: "bin_fragment_delta_error.src", 110 Patch: "bin_fragment_delta_error_dst_size.patch", 111 }, 112 Err: "insufficient or extra data", 113 }, 114 } 115 116 for name, test := range tests { 117 t.Run(name, func(t *testing.T) { 118 test.run(t, func(dst io.Writer, src io.ReaderAt, file *File) error { 119 applier := NewBinaryApplier(dst, src) 120 return applier.ApplyFragment(file.BinaryFragment) 121 }) 122 }) 123 } 124} 125 126func TestApplyFile(t *testing.T) { 127 tests := map[string]applyTest{ 128 "textModify": { 129 Files: applyFiles{ 130 Src: "file_text.src", 131 Patch: "file_text_modify.patch", 132 Out: "file_text_modify.out", 133 }, 134 }, 135 "textDelete": { 136 Files: applyFiles{ 137 Src: "file_text.src", 138 Patch: "file_text_delete.patch", 139 Out: "file_text_delete.out", 140 }, 141 }, 142 "textErrorPartialDelete": { 143 Files: applyFiles{ 144 Src: "file_text.src", 145 Patch: "file_text_error_partial_delete.patch", 146 }, 147 Err: &Conflict{}, 148 }, 149 "binaryModify": { 150 Files: getApplyFiles("file_bin_modify"), 151 }, 152 "modeChange": { 153 Files: getApplyFiles("file_mode_change"), 154 }, 155 } 156 157 for name, test := range tests { 158 t.Run(name, func(t *testing.T) { 159 test.run(t, func(dst io.Writer, src io.ReaderAt, file *File) error { 160 return Apply(dst, src, file) 161 }) 162 }) 163 } 164} 165 166type applyTest struct { 167 Files applyFiles 168 Err interface{} 169} 170 171func (at applyTest) run(t *testing.T, apply func(io.Writer, io.ReaderAt, *File) error) { 172 src, patch, out := at.Files.Load(t) 173 174 files, _, err := Parse(bytes.NewReader(patch)) 175 if err != nil { 176 t.Fatalf("failed to parse patch file: %v", err) 177 } 178 if len(files) != 1 { 179 t.Fatalf("patch should contain exactly one file, but it has %d", len(files)) 180 } 181 182 var dst bytes.Buffer 183 err = apply(&dst, bytes.NewReader(src), files[0]) 184 if at.Err != nil { 185 assertError(t, at.Err, err, "applying fragment") 186 return 187 } 188 if err != nil { 189 var aerr *ApplyError 190 if errors.As(err, &aerr) { 191 t.Fatalf("unexpected error applying: at %d: fragment %d at %d: %v", aerr.Line, aerr.Fragment, aerr.FragmentLine, err) 192 } else { 193 t.Fatalf("unexpected error applying: %v", err) 194 } 195 } 196 197 if !bytes.Equal(out, dst.Bytes()) { 198 t.Errorf("incorrect result after apply\nexpected:\n%q\nactual:\n%q", out, dst.Bytes()) 199 } 200} 201 202type applyFiles struct { 203 Src string 204 Patch string 205 Out string 206} 207 208func getApplyFiles(name string) applyFiles { 209 return applyFiles{ 210 Src: name + ".src", 211 Patch: name + ".patch", 212 Out: name + ".out", 213 } 214} 215 216func (f applyFiles) Load(t *testing.T) (src []byte, patch []byte, out []byte) { 217 load := func(name, kind string) []byte { 218 d, err := ioutil.ReadFile(filepath.Join("testdata", "apply", name)) 219 if err != nil { 220 t.Fatalf("failed to read %s file: %v", kind, err) 221 } 222 return d 223 } 224 225 if f.Src != "" { 226 src = load(f.Src, "source") 227 } 228 if f.Patch != "" { 229 patch = load(f.Patch, "patch") 230 } 231 if f.Out != "" { 232 out = load(f.Out, "output") 233 } 234 return 235}