1 #include "clar_libgit2.h"
2 #include "git2/sys/repository.h"
3 
4 #include "apply.h"
5 #include "repository.h"
6 
7 #include "../patch/patch_common.h"
8 
9 static git_repository *repo = NULL;
10 static git_diff_options binary_opts = GIT_DIFF_OPTIONS_INIT;
11 
test_apply_fromdiff__initialize(void)12 void test_apply_fromdiff__initialize(void)
13 {
14 	repo = cl_git_sandbox_init("renames");
15 
16 	binary_opts.flags |= GIT_DIFF_SHOW_BINARY;
17 }
18 
test_apply_fromdiff__cleanup(void)19 void test_apply_fromdiff__cleanup(void)
20 {
21 	cl_git_sandbox_cleanup();
22 }
23 
apply_gitbuf(const git_buf * old,const char * oldname,const git_buf * new,const char * newname,const char * patch_expected,const git_diff_options * diff_opts)24 static int apply_gitbuf(
25 	const git_buf *old,
26 	const char *oldname,
27 	const git_buf *new,
28 	const char *newname,
29 	const char *patch_expected,
30 	const git_diff_options *diff_opts)
31 {
32 	git_patch *patch;
33 	git_buf result = GIT_BUF_INIT;
34 	git_buf patchbuf = GIT_BUF_INIT;
35 	char *filename;
36 	unsigned int mode;
37 	int error;
38 
39 	cl_git_pass(git_patch_from_buffers(&patch,
40 		old ? old->ptr : NULL, old ? old->size : 0,
41 		oldname,
42 		new ? new->ptr : NULL, new ? new->size : 0,
43 		newname,
44 		diff_opts));
45 
46 	if (patch_expected) {
47 		cl_git_pass(git_patch_to_buf(&patchbuf, patch));
48 		cl_assert_equal_s(patch_expected, patchbuf.ptr);
49 	}
50 
51 	error = git_apply__patch(&result, &filename, &mode, old ? old->ptr : NULL, old ? old->size : 0, patch, NULL);
52 
53 	if (error == 0 && new == NULL) {
54 		cl_assert_equal_i(0, result.size);
55 		cl_assert_equal_p(NULL, filename);
56 		cl_assert_equal_i(0, mode);
57 	}
58 	else if (error == 0) {
59 		cl_assert_equal_s(new->ptr, result.ptr);
60 		cl_assert_equal_s(newname ? newname : oldname, filename);
61 		cl_assert_equal_i(0100644, mode);
62 	}
63 
64 	git__free(filename);
65 	git_buf_dispose(&result);
66 	git_buf_dispose(&patchbuf);
67 	git_patch_free(patch);
68 
69 	return error;
70 }
71 
apply_buf(const char * old,const char * oldname,const char * new,const char * newname,const char * patch_expected,const git_diff_options * diff_opts)72 static int apply_buf(
73 	const char *old,
74 	const char *oldname,
75 	const char *new,
76 	const char *newname,
77 	const char *patch_expected,
78 	const git_diff_options *diff_opts)
79 {
80 	git_buf o = GIT_BUF_INIT, n = GIT_BUF_INIT,
81 		*optr = NULL, *nptr = NULL;
82 
83 	if (old) {
84 		o.ptr = (char *)old;
85 		o.size = strlen(old);
86 		optr = &o;
87 	}
88 
89 	if (new) {
90 		n.ptr = (char *)new;
91 		n.size = strlen(new);
92 		nptr = &n;
93 	}
94 
95 	return apply_gitbuf(optr, oldname, nptr, newname, patch_expected, diff_opts);
96 }
97 
test_apply_fromdiff__change_middle(void)98 void test_apply_fromdiff__change_middle(void)
99 {
100 	cl_git_pass(apply_buf(
101 		FILE_ORIGINAL, "file.txt",
102 		FILE_CHANGE_MIDDLE, "file.txt",
103 		PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL));
104 }
105 
test_apply_fromdiff__change_middle_nocontext(void)106 void test_apply_fromdiff__change_middle_nocontext(void)
107 {
108 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
109 	diff_opts.context_lines = 0;
110 
111 	cl_git_pass(apply_buf(
112 		FILE_ORIGINAL, "file.txt",
113 		FILE_CHANGE_MIDDLE, "file.txt",
114 		PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT, &diff_opts));
115 }
116 
test_apply_fromdiff__change_firstline(void)117 void test_apply_fromdiff__change_firstline(void)
118 {
119 	cl_git_pass(apply_buf(
120 		FILE_ORIGINAL, "file.txt",
121 		FILE_CHANGE_FIRSTLINE, "file.txt",
122 		PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL));
123 }
124 
test_apply_fromdiff__lastline(void)125 void test_apply_fromdiff__lastline(void)
126 {
127 	cl_git_pass(apply_buf(
128 		FILE_ORIGINAL, "file.txt",
129 		FILE_CHANGE_LASTLINE, "file.txt",
130 		PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL));
131 }
132 
test_apply_fromdiff__change_middle_and_lastline_nocontext(void)133 void test_apply_fromdiff__change_middle_and_lastline_nocontext(void)
134 {
135 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
136 	diff_opts.context_lines = 0;
137 
138 	cl_git_pass(apply_buf(
139 		FILE_ORIGINAL, "file.txt",
140 		FILE_CHANGE_MIDDLE_AND_LASTLINE, "file.txt",
141 		PATCH_ORIGINAL_TO_CHANGE_MIDDLE_AND_LASTLINE_NOCONTEXT, &diff_opts));
142 }
143 
test_apply_fromdiff__prepend(void)144 void test_apply_fromdiff__prepend(void)
145 {
146 	cl_git_pass(apply_buf(
147 		FILE_ORIGINAL, "file.txt",
148 		FILE_PREPEND, "file.txt",
149 		PATCH_ORIGINAL_TO_PREPEND, NULL));
150 }
151 
test_apply_fromdiff__prepend_nocontext(void)152 void test_apply_fromdiff__prepend_nocontext(void)
153 {
154 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
155 	diff_opts.context_lines = 0;
156 
157 	cl_git_pass(apply_buf(
158 		FILE_ORIGINAL, "file.txt",
159 		FILE_PREPEND, "file.txt",
160 		PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts));
161 }
162 
test_apply_fromdiff__prepend_and_change(void)163 void test_apply_fromdiff__prepend_and_change(void)
164 {
165 	cl_git_pass(apply_buf(
166 		FILE_ORIGINAL, "file.txt",
167 		FILE_PREPEND_AND_CHANGE, "file.txt",
168 		PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE, NULL));
169 }
170 
test_apply_fromdiff__prepend_and_change_nocontext(void)171 void test_apply_fromdiff__prepend_and_change_nocontext(void)
172 {
173 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
174 	diff_opts.context_lines = 0;
175 
176 	cl_git_pass(apply_buf(
177 		FILE_ORIGINAL, "file.txt",
178 		FILE_PREPEND_AND_CHANGE, "file.txt",
179 		PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE_NOCONTEXT, &diff_opts));
180 }
181 
test_apply_fromdiff__delete_and_change(void)182 void test_apply_fromdiff__delete_and_change(void)
183 {
184 	cl_git_pass(apply_buf(
185 		FILE_ORIGINAL, "file.txt",
186 		FILE_DELETE_AND_CHANGE, "file.txt",
187 		PATCH_ORIGINAL_TO_DELETE_AND_CHANGE, NULL));
188 }
189 
test_apply_fromdiff__delete_and_change_nocontext(void)190 void test_apply_fromdiff__delete_and_change_nocontext(void)
191 {
192 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
193 	diff_opts.context_lines = 0;
194 
195 	cl_git_pass(apply_buf(
196 		FILE_ORIGINAL, "file.txt",
197 		FILE_DELETE_AND_CHANGE, "file.txt",
198 		PATCH_ORIGINAL_TO_DELETE_AND_CHANGE_NOCONTEXT, &diff_opts));
199 }
200 
test_apply_fromdiff__delete_firstline(void)201 void test_apply_fromdiff__delete_firstline(void)
202 {
203 	cl_git_pass(apply_buf(
204 		FILE_ORIGINAL, "file.txt",
205 		FILE_DELETE_FIRSTLINE, "file.txt",
206 		PATCH_ORIGINAL_TO_DELETE_FIRSTLINE, NULL));
207 }
208 
test_apply_fromdiff__append(void)209 void test_apply_fromdiff__append(void)
210 {
211 	cl_git_pass(apply_buf(
212 		FILE_ORIGINAL, "file.txt",
213 		FILE_APPEND, "file.txt",
214 		PATCH_ORIGINAL_TO_APPEND, NULL));
215 }
216 
test_apply_fromdiff__append_nocontext(void)217 void test_apply_fromdiff__append_nocontext(void)
218 {
219 	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
220 	diff_opts.context_lines = 0;
221 
222 	cl_git_pass(apply_buf(
223 		FILE_ORIGINAL, "file.txt",
224 		FILE_APPEND, "file.txt",
225 		PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts));
226 }
227 
test_apply_fromdiff__prepend_and_append(void)228 void test_apply_fromdiff__prepend_and_append(void)
229 {
230 	cl_git_pass(apply_buf(
231 		FILE_ORIGINAL, "file.txt",
232 		FILE_PREPEND_AND_APPEND, "file.txt",
233 		PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL));
234 }
235 
test_apply_fromdiff__to_empty_file(void)236 void test_apply_fromdiff__to_empty_file(void)
237 {
238 	cl_git_pass(apply_buf(
239 		FILE_ORIGINAL, "file.txt",
240 		"", NULL,
241 		PATCH_ORIGINAL_TO_EMPTY_FILE, NULL));
242 }
243 
test_apply_fromdiff__from_empty_file(void)244 void test_apply_fromdiff__from_empty_file(void)
245 {
246 	cl_git_pass(apply_buf(
247 		"", NULL,
248 		FILE_ORIGINAL, "file.txt",
249 		PATCH_EMPTY_FILE_TO_ORIGINAL, NULL));
250 }
251 
test_apply_fromdiff__add(void)252 void test_apply_fromdiff__add(void)
253 {
254 	cl_git_pass(apply_buf(
255 		NULL, NULL,
256 		FILE_ORIGINAL, "file.txt",
257 		PATCH_ADD_ORIGINAL, NULL));
258 }
259 
test_apply_fromdiff__delete(void)260 void test_apply_fromdiff__delete(void)
261 {
262 	cl_git_pass(apply_buf(
263 		FILE_ORIGINAL, "file.txt",
264 		NULL, NULL,
265 		PATCH_DELETE_ORIGINAL, NULL));
266 }
267 
test_apply_fromdiff__no_change(void)268 void test_apply_fromdiff__no_change(void)
269 {
270 	cl_git_pass(apply_buf(
271 		FILE_ORIGINAL, "file.txt",
272 		FILE_ORIGINAL, "file.txt",
273 		"", NULL));
274 }
275 
test_apply_fromdiff__binary_add(void)276 void test_apply_fromdiff__binary_add(void)
277 {
278 	git_buf newfile = GIT_BUF_INIT;
279 
280 	newfile.ptr = FILE_BINARY_DELTA_MODIFIED;
281 	newfile.size = FILE_BINARY_DELTA_MODIFIED_LEN;
282 
283 	cl_git_pass(apply_gitbuf(
284 		NULL, NULL,
285 		&newfile, "binary.bin",
286 		NULL, &binary_opts));
287 }
288 
test_apply_fromdiff__binary_no_change(void)289 void test_apply_fromdiff__binary_no_change(void)
290 {
291 	git_buf original = GIT_BUF_INIT;
292 
293 	original.ptr = FILE_BINARY_DELTA_ORIGINAL;
294 	original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
295 
296 	cl_git_pass(apply_gitbuf(
297 		&original, "binary.bin",
298 		&original, "binary.bin",
299 		"", &binary_opts));
300 }
301 
test_apply_fromdiff__binary_change_delta(void)302 void test_apply_fromdiff__binary_change_delta(void)
303 {
304 	git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
305 
306 	original.ptr = FILE_BINARY_DELTA_ORIGINAL;
307 	original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
308 
309 	modified.ptr = FILE_BINARY_DELTA_MODIFIED;
310 	modified.size = FILE_BINARY_DELTA_MODIFIED_LEN;
311 
312 	cl_git_pass(apply_gitbuf(
313 		&original, "binary.bin",
314 		&modified, "binary.bin",
315 		NULL, &binary_opts));
316 }
317 
test_apply_fromdiff__binary_change_literal(void)318 void test_apply_fromdiff__binary_change_literal(void)
319 {
320 	git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
321 
322 	original.ptr = FILE_BINARY_LITERAL_ORIGINAL;
323 	original.size = FILE_BINARY_LITERAL_ORIGINAL_LEN;
324 
325 	modified.ptr = FILE_BINARY_LITERAL_MODIFIED;
326 	modified.size = FILE_BINARY_LITERAL_MODIFIED_LEN;
327 
328 	cl_git_pass(apply_gitbuf(
329 		&original, "binary.bin",
330 		&modified, "binary.bin",
331 		NULL, &binary_opts));
332 }
333 
test_apply_fromdiff__binary_delete(void)334 void test_apply_fromdiff__binary_delete(void)
335 {
336 	git_buf original = GIT_BUF_INIT;
337 
338 	original.ptr = FILE_BINARY_DELTA_MODIFIED;
339 	original.size = FILE_BINARY_DELTA_MODIFIED_LEN;
340 
341 	cl_git_pass(apply_gitbuf(
342 		&original, "binary.bin",
343 		NULL, NULL,
344 		NULL, &binary_opts));
345 }
346 
test_apply_fromdiff__patching_correctly_truncates_source(void)347 void test_apply_fromdiff__patching_correctly_truncates_source(void)
348 {
349 	git_buf original = GIT_BUF_INIT, patched = GIT_BUF_INIT;
350 	git_patch *patch;
351 	unsigned int mode;
352 	char *path;
353 
354 	cl_git_pass(git_patch_from_buffers(&patch,
355 					   "foo\nbar", 7, "file.txt",
356 					   "foo\nfoo", 7, "file.txt", NULL));
357 
358 	/*
359 	 * Previously, we would fail to correctly truncate the source buffer if
360 	 * the source has more than one line and ends with a non-newline
361 	 * character. In the following call, we thus truncate the source string
362 	 * in the middle of the second line. Without the bug fixed, we would
363 	 * successfully apply the patch to the source and return success. With
364 	 * the overflow being fixed, we should return an error.
365 	 */
366 	cl_git_fail_with(GIT_EAPPLYFAIL,
367 			 git_apply__patch(&patched, &path, &mode,
368 					  "foo\nbar\n", 5, patch, NULL));
369 
370 	/* Verify that the patch succeeds if we do not truncate */
371 	cl_git_pass(git_apply__patch(&patched, &path, &mode,
372 				     "foo\nbar\n", 7, patch, NULL));
373 
374 	git_buf_dispose(&original);
375 	git_buf_dispose(&patched);
376 	git_patch_free(patch);
377 	git__free(path);
378 }
379