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