]> git.proxmox.com Git - libgit2.git/blame - tests-clar/diff/diffiter.c
Rename diff objects and split patch.h
[libgit2.git] / tests-clar / diff / diffiter.c
CommitLineData
f335ecd6
RB
1#include "clar_libgit2.h"
2#include "diff_helpers.h"
3
4void test_diff_diffiter__initialize(void)
5{
6}
7
8void test_diff_diffiter__cleanup(void)
9{
10 cl_git_sandbox_cleanup();
11}
12
13void test_diff_diffiter__create(void)
14{
15 git_repository *repo = cl_git_sandbox_init("attr");
3ff1d123 16 git_diff *diff;
5f69a31f 17 size_t d, num_d;
f335ecd6 18
56c72b75 19 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
5f69a31f
RB
20
21 num_d = git_diff_num_deltas(diff);
22 for (d = 0; d < num_d; ++d) {
bae957b9 23 const git_diff_delta *delta;
3ff1d123 24 cl_git_pass(git_patch_from_diff(NULL, &delta, diff, d));
5f69a31f
RB
25 }
26
3ff1d123 27 git_diff_free(diff);
f335ecd6
RB
28}
29
5173ea92 30void test_diff_diffiter__iterate_files_1(void)
f335ecd6
RB
31{
32 git_repository *repo = cl_git_sandbox_init("attr");
3ff1d123 33 git_diff *diff;
5f69a31f 34 size_t d, num_d;
5173ea92 35 diff_expects exp = { 0 };
f335ecd6 36
56c72b75 37 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
f335ecd6 38
5f69a31f 39 num_d = git_diff_num_deltas(diff);
5f69a31f
RB
40
41 for (d = 0; d < num_d; ++d) {
bae957b9 42 const git_diff_delta *delta;
3ff1d123 43 cl_git_pass(git_patch_from_diff(NULL, &delta, diff, d));
f335ecd6 44 cl_assert(delta != NULL);
5173ea92
RB
45
46 diff_file_cb(delta, (float)d / (float)num_d, &exp);
f335ecd6 47 }
5173ea92 48 cl_assert_equal_sz(6, exp.files);
f335ecd6 49
3ff1d123 50 git_diff_free(diff);
f335ecd6
RB
51}
52
53void test_diff_diffiter__iterate_files_2(void)
54{
55 git_repository *repo = cl_git_sandbox_init("status");
3ff1d123 56 git_diff *diff;
5f69a31f
RB
57 size_t d, num_d;
58 int count = 0;
f335ecd6 59
56c72b75 60 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
f335ecd6 61
5f69a31f 62 num_d = git_diff_num_deltas(diff);
cc5bf359 63 cl_assert_equal_i(8, (int)num_d);
5f69a31f
RB
64
65 for (d = 0; d < num_d; ++d) {
bae957b9 66 const git_diff_delta *delta;
3ff1d123 67 cl_git_pass(git_patch_from_diff(NULL, &delta, diff, d));
f335ecd6
RB
68 cl_assert(delta != NULL);
69 count++;
70 }
f335ecd6
RB
71 cl_assert_equal_i(8, count);
72
3ff1d123 73 git_diff_free(diff);
f335ecd6
RB
74}
75
76void test_diff_diffiter__iterate_files_and_hunks(void)
77{
78 git_repository *repo = cl_git_sandbox_init("status");
2f8d30be 79 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 80 git_diff *diff = NULL;
5f69a31f
RB
81 size_t d, num_d;
82 int file_count = 0, hunk_count = 0;
f335ecd6
RB
83
84 opts.context_lines = 3;
85 opts.interhunk_lines = 1;
86 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
87
56c72b75 88 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
f335ecd6 89
5f69a31f
RB
90 num_d = git_diff_num_deltas(diff);
91
92 for (d = 0; d < num_d; ++d) {
3ff1d123 93 git_patch *patch;
bae957b9 94 const git_diff_delta *delta;
5f69a31f
RB
95 size_t h, num_h;
96
3ff1d123 97 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, d));
f335ecd6 98
f335ecd6 99 cl_assert(delta);
5f69a31f 100 cl_assert(patch);
f335ecd6
RB
101
102 file_count++;
103
3ff1d123 104 num_h = git_patch_num_hunks(patch);
5f69a31f
RB
105
106 for (h = 0; h < num_h; h++) {
3ff1d123 107 const git_diff_hunk *range;
5f69a31f
RB
108 const char *header;
109 size_t header_len, num_l;
110
3ff1d123 111 cl_git_pass(git_patch_get_hunk(
5f69a31f
RB
112 &range, &header, &header_len, &num_l, patch, h));
113
f335ecd6 114 cl_assert(range);
5f69a31f
RB
115 cl_assert(header);
116
f335ecd6
RB
117 hunk_count++;
118 }
5f69a31f 119
3ff1d123 120 git_patch_free(patch);
f335ecd6
RB
121 }
122
f335ecd6
RB
123 cl_assert_equal_i(13, file_count);
124 cl_assert_equal_i(8, hunk_count);
125
3ff1d123 126 git_diff_free(diff);
f335ecd6 127}
1f35e89d
RB
128
129void test_diff_diffiter__max_size_threshold(void)
130{
131 git_repository *repo = cl_git_sandbox_init("status");
2f8d30be 132 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 133 git_diff *diff = NULL;
5f69a31f
RB
134 int file_count = 0, binary_count = 0, hunk_count = 0;
135 size_t d, num_d;
1f35e89d
RB
136
137 opts.context_lines = 3;
138 opts.interhunk_lines = 1;
139 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
140
56c72b75 141 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
5f69a31f 142 num_d = git_diff_num_deltas(diff);
1f35e89d 143
5f69a31f 144 for (d = 0; d < num_d; ++d) {
3ff1d123 145 git_patch *patch;
bae957b9 146 const git_diff_delta *delta;
5f69a31f 147
3ff1d123 148 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, d));
1f35e89d 149 cl_assert(delta);
5f69a31f 150 cl_assert(patch);
1f35e89d
RB
151
152 file_count++;
3ff1d123 153 hunk_count += (int)git_patch_num_hunks(patch);
1f35e89d 154
71a3d27e
RB
155 assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
156 binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1f35e89d 157
3ff1d123 158 git_patch_free(patch);
5f69a31f 159 }
1f35e89d
RB
160
161 cl_assert_equal_i(13, file_count);
162 cl_assert_equal_i(0, binary_count);
163 cl_assert_equal_i(8, hunk_count);
164
3ff1d123 165 git_diff_free(diff);
1f35e89d
RB
166
167 /* try again with low file size threshold */
168
5f69a31f 169 file_count = binary_count = hunk_count = 0;
1f35e89d
RB
170
171 opts.context_lines = 3;
172 opts.interhunk_lines = 1;
173 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
174 opts.max_size = 50; /* treat anything over 50 bytes as binary! */
175
56c72b75 176 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
5f69a31f 177 num_d = git_diff_num_deltas(diff);
1f35e89d 178
5f69a31f 179 for (d = 0; d < num_d; ++d) {
3ff1d123 180 git_patch *patch;
bae957b9 181 const git_diff_delta *delta;
1f35e89d 182
3ff1d123 183 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, d));
1f35e89d 184
5f69a31f 185 file_count++;
3ff1d123 186 hunk_count += (int)git_patch_num_hunks(patch);
1f35e89d 187
71a3d27e
RB
188 assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
189 binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1f35e89d 190
3ff1d123 191 git_patch_free(patch);
5f69a31f 192 }
1f35e89d
RB
193
194 cl_assert_equal_i(13, file_count);
1f35e89d
RB
195 /* Three files are over the 50 byte threshold:
196 * - staged_changes_file_deleted
197 * - staged_changes_modified_file
198 * - staged_new_file_modified_file
199 */
200 cl_assert_equal_i(3, binary_count);
1f35e89d
RB
201 cl_assert_equal_i(5, hunk_count);
202
3ff1d123 203 git_diff_free(diff);
5f69a31f
RB
204}
205
206
207void test_diff_diffiter__iterate_all(void)
208{
209 git_repository *repo = cl_git_sandbox_init("status");
2f8d30be 210 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 211 git_diff *diff = NULL;
5f69a31f
RB
212 diff_expects exp = {0};
213 size_t d, num_d;
214
215 opts.context_lines = 3;
216 opts.interhunk_lines = 1;
217 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
218
56c72b75 219 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
5f69a31f
RB
220
221 num_d = git_diff_num_deltas(diff);
222 for (d = 0; d < num_d; ++d) {
3ff1d123 223 git_patch *patch;
bae957b9 224 const git_diff_delta *delta;
5f69a31f
RB
225 size_t h, num_h;
226
3ff1d123 227 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, d));
5f69a31f
RB
228 cl_assert(patch && delta);
229 exp.files++;
230
3ff1d123 231 num_h = git_patch_num_hunks(patch);
5f69a31f 232 for (h = 0; h < num_h; h++) {
3ff1d123 233 const git_diff_hunk *range;
5f69a31f
RB
234 const char *header;
235 size_t header_len, l, num_l;
236
3ff1d123 237 cl_git_pass(git_patch_get_hunk(
5f69a31f
RB
238 &range, &header, &header_len, &num_l, patch, h));
239 cl_assert(range && header);
240 exp.hunks++;
241
242 for (l = 0; l < num_l; ++l) {
243 char origin;
244 const char *content;
245 size_t content_len;
246
3ff1d123 247 cl_git_pass(git_patch_get_line_in_hunk(
5f69a31f
RB
248 &origin, &content, &content_len, NULL, NULL, patch, h, l));
249 cl_assert(content);
250 exp.lines++;
251 }
252 }
253
3ff1d123 254 git_patch_free(patch);
5f69a31f
RB
255 }
256
257 cl_assert_equal_i(13, exp.files);
258 cl_assert_equal_i(8, exp.hunks);
64286308 259 cl_assert_equal_i(14, exp.lines);
5f69a31f 260
3ff1d123 261 git_diff_free(diff);
5f69a31f
RB
262}
263
3ff1d123 264static void iterate_over_patch(git_patch *patch, diff_expects *exp)
5f69a31f 265{
3ff1d123 266 size_t h, num_h = git_patch_num_hunks(patch), num_l;
5f69a31f
RB
267
268 exp->files++;
cc5bf359 269 exp->hunks += (int)num_h;
5f69a31f
RB
270
271 /* let's iterate in reverse, just because we can! */
64286308 272 for (h = 1, num_l = 0; h <= num_h; ++h)
3ff1d123 273 num_l += git_patch_num_lines_in_hunk(patch, num_h - h);
64286308 274
cc5bf359 275 exp->lines += (int)num_l;
5f69a31f
RB
276}
277
278#define PATCH_CACHE 5
279
280void test_diff_diffiter__iterate_randomly_while_saving_state(void)
281{
282 git_repository *repo = cl_git_sandbox_init("status");
2f8d30be 283 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 284 git_diff *diff = NULL;
5f69a31f 285 diff_expects exp = {0};
3ff1d123 286 git_patch *patches[PATCH_CACHE];
5f69a31f
RB
287 size_t p, d, num_d;
288
289 memset(patches, 0, sizeof(patches));
290
291 opts.context_lines = 3;
292 opts.interhunk_lines = 1;
293 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
294
56c72b75 295 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
5f69a31f
RB
296
297 num_d = git_diff_num_deltas(diff);
298
299 /* To make sure that references counts work for diff and patch objects,
300 * this generates patches and randomly caches them. Only when the patch
301 * is removed from the cache are hunks and lines counted. At the end,
302 * there are still patches in the cache, so free the diff and try to
303 * process remaining patches after the diff is freed.
304 */
305
306 srand(121212);
307 p = rand() % PATCH_CACHE;
308
309 for (d = 0; d < num_d; ++d) {
310 /* take old patch */
3ff1d123 311 git_patch *patch = patches[p];
5f69a31f
RB
312 patches[p] = NULL;
313
314 /* cache new patch */
3ff1d123 315 cl_git_pass(git_patch_from_diff(&patches[p], NULL, diff, d));
5f69a31f
RB
316 cl_assert(patches[p] != NULL);
317
318 /* process old patch if non-NULL */
319 if (patch != NULL) {
320 iterate_over_patch(patch, &exp);
3ff1d123 321 git_patch_free(patch);
5f69a31f
RB
322 }
323
324 p = rand() % PATCH_CACHE;
325 }
326
327 /* free diff list now - refcounts should keep things safe */
3ff1d123 328 git_diff_free(diff);
5f69a31f
RB
329
330 /* process remaining unprocessed patches */
331 for (p = 0; p < PATCH_CACHE; p++) {
3ff1d123 332 git_patch *patch = patches[p];
5f69a31f
RB
333
334 if (patch != NULL) {
335 iterate_over_patch(patch, &exp);
3ff1d123 336 git_patch_free(patch);
5f69a31f
RB
337 }
338 }
1f35e89d 339
5f69a31f
RB
340 /* hopefully it all still added up right */
341 cl_assert_equal_i(13, exp.files);
342 cl_assert_equal_i(8, exp.hunks);
64286308 343 cl_assert_equal_i(14, exp.lines);
1f35e89d 344}
93cf7bb8
RB
345
346/* This output is taken directly from `git diff` on the status test data */
347static const char *expected_patch_text[8] = {
348 /* 0 */
349 "diff --git a/file_deleted b/file_deleted\n"
350 "deleted file mode 100644\n"
351 "index 5452d32..0000000\n"
352 "--- a/file_deleted\n"
353 "+++ /dev/null\n"
354 "@@ -1 +0,0 @@\n"
355 "-file_deleted\n",
356 /* 1 */
357 "diff --git a/modified_file b/modified_file\n"
358 "index 452e424..0a53963 100644\n"
359 "--- a/modified_file\n"
360 "+++ b/modified_file\n"
361 "@@ -1 +1,2 @@\n"
362 " modified_file\n"
363 "+modified_file\n",
364 /* 2 */
365 "diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
366 "deleted file mode 100644\n"
367 "index a6be623..0000000\n"
368 "--- a/staged_changes_file_deleted\n"
369 "+++ /dev/null\n"
370 "@@ -1,2 +0,0 @@\n"
371 "-staged_changes_file_deleted\n"
372 "-staged_changes_file_deleted\n",
373 /* 3 */
374 "diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
375 "index 906ee77..011c344 100644\n"
376 "--- a/staged_changes_modified_file\n"
377 "+++ b/staged_changes_modified_file\n"
378 "@@ -1,2 +1,3 @@\n"
379 " staged_changes_modified_file\n"
380 " staged_changes_modified_file\n"
381 "+staged_changes_modified_file\n",
382 /* 4 */
383 "diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
384 "deleted file mode 100644\n"
385 "index 90b8c29..0000000\n"
386 "--- a/staged_new_file_deleted_file\n"
387 "+++ /dev/null\n"
388 "@@ -1 +0,0 @@\n"
389 "-staged_new_file_deleted_file\n",
390 /* 5 */
391 "diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
392 "index ed06290..8b090c0 100644\n"
393 "--- a/staged_new_file_modified_file\n"
394 "+++ b/staged_new_file_modified_file\n"
395 "@@ -1 +1,2 @@\n"
396 " staged_new_file_modified_file\n"
397 "+staged_new_file_modified_file\n",
398 /* 6 */
399 "diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
400 "deleted file mode 100644\n"
401 "index 1888c80..0000000\n"
402 "--- a/subdir/deleted_file\n"
403 "+++ /dev/null\n"
404 "@@ -1 +0,0 @@\n"
405 "-subdir/deleted_file\n",
406 /* 7 */
407 "diff --git a/subdir/modified_file b/subdir/modified_file\n"
408 "index a619198..57274b7 100644\n"
409 "--- a/subdir/modified_file\n"
410 "+++ b/subdir/modified_file\n"
411 "@@ -1 +1,2 @@\n"
412 " subdir/modified_file\n"
413 "+subdir/modified_file\n"
414};
415
416void test_diff_diffiter__iterate_and_generate_patch_text(void)
417{
418 git_repository *repo = cl_git_sandbox_init("status");
3ff1d123 419 git_diff *diff;
93cf7bb8
RB
420 size_t d, num_d;
421
56c72b75 422 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
93cf7bb8
RB
423
424 num_d = git_diff_num_deltas(diff);
425 cl_assert_equal_i(8, (int)num_d);
426
427 for (d = 0; d < num_d; ++d) {
3ff1d123 428 git_patch *patch;
93cf7bb8
RB
429 char *text;
430
3ff1d123 431 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, d));
93cf7bb8
RB
432 cl_assert(patch != NULL);
433
3ff1d123 434 cl_git_pass(git_patch_to_str(&text, patch));
93cf7bb8
RB
435
436 cl_assert_equal_s(expected_patch_text[d], text);
437
438 git__free(text);
3ff1d123 439 git_patch_free(patch);
93cf7bb8
RB
440 }
441
3ff1d123 442 git_diff_free(diff);
93cf7bb8 443}
2f8d30be
BS
444
445void test_diff_diffiter__checks_options_version(void)
446{
447 git_repository *repo = cl_git_sandbox_init("status");
448 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 449 git_diff *diff = NULL;
2f8d30be
BS
450 const git_error *err;
451
452 opts.version = 0;
453 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
454
56c72b75 455 cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
2f8d30be
BS
456 err = giterr_last();
457 cl_assert_equal_i(GITERR_INVALID, err->klass);
458
459 giterr_clear();
460 opts.version = 1024;
56c72b75 461 cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
2f8d30be
BS
462 err = giterr_last();
463 cl_assert_equal_i(GITERR_INVALID, err->klass);
464}
465