]> git.proxmox.com Git - libgit2.git/blame - tests-clar/diff/patch.c
Rename diff objects and split patch.h
[libgit2.git] / tests-clar / diff / patch.c
CommitLineData
eb3d71a5 1#include "clar_libgit2.h"
1384b688
RB
2#include "git2/sys/repository.h"
3
eb3d71a5 4#include "diff_helpers.h"
805c476c
RB
5#include "repository.h"
6#include "buf_text.h"
eb3d71a5 7
8static git_repository *g_repo = NULL;
9
10void test_diff_patch__initialize(void)
11{
eb3d71a5 12}
13
14void test_diff_patch__cleanup(void)
15{
16 cl_git_sandbox_cleanup();
17}
18
1d2dd864 19#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
eb3d71a5 20 "deleted file mode 100644\n" \
21 "index e8ee89e..0000000\n" \
22 "--- a/subdir.txt\n" \
23 "+++ /dev/null\n"
24
1d2dd864 25#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
26
eb3d71a5 27static int check_removal_cb(
bae957b9 28 const git_diff_delta *delta,
3ff1d123 29 const git_diff_hunk *range,
eb3d71a5 30 char line_origin,
1d2dd864 31 const char *formatted_output,
793c4385
RB
32 size_t output_len,
33 void *payload)
eb3d71a5 34{
793c4385 35 GIT_UNUSED(payload);
52877c89 36 GIT_UNUSED(output_len);
eb3d71a5 37
1d2dd864 38 switch (line_origin) {
39 case GIT_DIFF_LINE_FILE_HDR:
40 cl_assert_equal_s(EXPECTED_HEADER, formatted_output);
41 cl_assert(range == NULL);
42 goto check_delta;
43
44 case GIT_DIFF_LINE_HUNK_HDR:
45 cl_assert_equal_s(EXPECTED_HUNK, formatted_output);
46 /* Fall through */
47
48 case GIT_DIFF_LINE_CONTEXT:
49 case GIT_DIFF_LINE_DELETION:
50 goto check_range;
51
52 default:
53 /* unexpected code path */
54 return -1;
55 }
56
57check_range:
58 cl_assert(range != NULL);
59 cl_assert_equal_i(1, range->old_start);
60 cl_assert_equal_i(2, range->old_lines);
61 cl_assert_equal_i(0, range->new_start);
62 cl_assert_equal_i(0, range->new_lines);
eb3d71a5 63
1d2dd864 64check_delta:
16b83019
RB
65 cl_assert_equal_s("subdir.txt", delta->old_file.path);
66 cl_assert_equal_s("subdir.txt", delta->new_file.path);
1d2dd864 67 cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
eb3d71a5 68
1d2dd864 69 return 0;
eb3d71a5 70}
71
72void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
73{
74 /*
75 * $ git diff 26a125e..735b6a2
76 * diff --git a/subdir.txt b/subdir.txt
77 * deleted file mode 100644
78 * index e8ee89e..0000000
79 * --- a/subdir.txt
80 * +++ /dev/null
81 * @@ -1,2 +0,0 @@
82 * -Is it a bird?
83 * -Is it a plane?
84 */
85
86 const char *one_sha = "26a125e";
87 const char *another_sha = "735b6a2";
88 git_tree *one, *another;
3ff1d123 89 git_diff *diff;
eb3d71a5 90
805c476c
RB
91 g_repo = cl_git_sandbox_init("status");
92
eb3d71a5 93 one = resolve_commit_oid_to_tree(g_repo, one_sha);
94 another = resolve_commit_oid_to_tree(g_repo, another_sha);
95
5735bf5e 96 cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
eb3d71a5 97
793c4385 98 cl_git_pass(git_diff_print_patch(diff, check_removal_cb, NULL));
eb3d71a5 99
3ff1d123 100 git_diff_free(diff);
eb3d71a5 101
102 git_tree_free(another);
103 git_tree_free(one);
104}
93cf7bb8
RB
105
106void test_diff_patch__to_string(void)
107{
108 const char *one_sha = "26a125e";
109 const char *another_sha = "735b6a2";
110 git_tree *one, *another;
3ff1d123
RB
111 git_diff *diff;
112 git_patch *patch;
93cf7bb8
RB
113 char *text;
114 const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
115
805c476c
RB
116 g_repo = cl_git_sandbox_init("status");
117
93cf7bb8
RB
118 one = resolve_commit_oid_to_tree(g_repo, one_sha);
119 another = resolve_commit_oid_to_tree(g_repo, another_sha);
120
5735bf5e 121 cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
93cf7bb8 122
a8122b5d 123 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
93cf7bb8 124
3ff1d123 125 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, 0));
93cf7bb8 126
3ff1d123 127 cl_git_pass(git_patch_to_str(&text, patch));
93cf7bb8
RB
128
129 cl_assert_equal_s(expected, text);
130
3ff1d123
RB
131 cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
132 cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
133 cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
134 cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
b4a4cf24 135
93cf7bb8 136 git__free(text);
3ff1d123
RB
137 git_patch_free(patch);
138 git_diff_free(diff);
93cf7bb8
RB
139 git_tree_free(another);
140 git_tree_free(one);
141}
805c476c 142
b1ff7004
RB
143void test_diff_patch__config_options(void)
144{
145 const char *one_sha = "26a125e"; /* current HEAD */
146 git_tree *one;
147 git_config *cfg;
3ff1d123
RB
148 git_diff *diff;
149 git_patch *patch;
b1ff7004
RB
150 char *text;
151 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
152 char *onefile = "staged_changes_modified_file";
153 const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
154 const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
155 const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
156 const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
157
158 g_repo = cl_git_sandbox_init("status");
159 cl_git_pass(git_repository_config(&cfg, g_repo));
160 one = resolve_commit_oid_to_tree(g_repo, one_sha);
161 opts.pathspec.count = 1;
162 opts.pathspec.strings = &onefile;
163
164
165 cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
166
167 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
168
169 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
3ff1d123
RB
170 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, 0));
171 cl_git_pass(git_patch_to_str(&text, patch));
b1ff7004
RB
172 cl_assert_equal_s(expected1, text);
173
174 git__free(text);
3ff1d123
RB
175 git_patch_free(patch);
176 git_diff_free(diff);
b1ff7004
RB
177
178 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
179
180 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
3ff1d123
RB
181 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, 0));
182 cl_git_pass(git_patch_to_str(&text, patch));
b1ff7004
RB
183 cl_assert_equal_s(expected2, text);
184
185 git__free(text);
3ff1d123
RB
186 git_patch_free(patch);
187 git_diff_free(diff);
b1ff7004
RB
188
189
190 cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
191
192 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
193
194 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
3ff1d123
RB
195 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, 0));
196 cl_git_pass(git_patch_to_str(&text, patch));
b1ff7004
RB
197 cl_assert_equal_s(expected3, text);
198
199 git__free(text);
3ff1d123
RB
200 git_patch_free(patch);
201 git_diff_free(diff);
b1ff7004
RB
202
203
204 cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
205
206 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
207
208 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
3ff1d123
RB
209 cl_git_pass(git_patch_from_diff(&patch, NULL, diff, 0));
210 cl_git_pass(git_patch_to_str(&text, patch));
b1ff7004
RB
211 cl_assert_equal_s(expected4, text);
212
213 git__free(text);
3ff1d123
RB
214 git_patch_free(patch);
215 git_diff_free(diff);
b1ff7004
RB
216
217 git_tree_free(one);
218 git_config_free(cfg);
219}
220
805c476c
RB
221void test_diff_patch__hunks_have_correct_line_numbers(void)
222{
7d46b34b 223 git_config *cfg;
805c476c 224 git_tree *head;
7d46b34b 225 git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
3ff1d123
RB
226 git_diff *diff;
227 git_patch *patch;
805c476c 228 const git_diff_delta *delta;
3ff1d123 229 const git_diff_hunk *range;
805c476c
RB
230 const char *hdr, *text;
231 size_t hdrlen, hunklen, textlen;
232 char origin;
233 int oldno, newno;
c2f602f8 234 git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
6f9d5ce8 235 const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
805c476c
RB
236
237 g_repo = cl_git_sandbox_init("renames");
238
487fc724
RB
239 cl_git_pass(git_config_new(&cfg));
240 git_repository_set_config(g_repo, cfg);
5173ea92
RB
241 git_config_free(cfg);
242
867f7c9b 243 git_repository_reinit_filesystem(g_repo, false);
7d46b34b 244
c2f602f8
RB
245 cl_git_pass(
246 git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
247
6f9d5ce8 248 cl_git_rewritefile("renames/songof7cities.txt", new_content);
805c476c
RB
249
250 cl_git_pass(git_repository_head_tree(&head, g_repo));
251
7d46b34b 252 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
805c476c
RB
253
254 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
255
3ff1d123 256 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, 0));
805c476c
RB
257
258 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
3ff1d123 259 cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
805c476c
RB
260
261 /* check hunk 0 */
262
263 cl_git_pass(
3ff1d123 264 git_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
805c476c
RB
265
266 cl_assert_equal_i(18, (int)hunklen);
267
268 cl_assert_equal_i(6, (int)range->old_start);
269 cl_assert_equal_i(15, (int)range->old_lines);
270 cl_assert_equal_i(6, (int)range->new_start);
271 cl_assert_equal_i(9, (int)range->new_lines);
272
3ff1d123 273 cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
805c476c 274
3ff1d123 275 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
276 &origin, &text, &textlen, &oldno, &newno, patch, 0, 0));
277 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
c2f602f8
RB
278 cl_git_pass(git_buf_set(&actual, text, textlen));
279 cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
805c476c
RB
280 cl_assert_equal_i(6, oldno);
281 cl_assert_equal_i(6, newno);
282
3ff1d123 283 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
284 &origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
285 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
c2f602f8
RB
286 cl_git_pass(git_buf_set(&actual, text, textlen));
287 cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
805c476c
RB
288 cl_assert_equal_i(9, oldno);
289 cl_assert_equal_i(-1, newno);
290
3ff1d123 291 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
292 &origin, &text, &textlen, &oldno, &newno, patch, 0, 12));
293 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
c2f602f8
RB
294 cl_git_pass(git_buf_set(&actual, text, textlen));
295 cl_assert_equal_s("This is some new text;\n", actual.ptr);
805c476c
RB
296 cl_assert_equal_i(-1, oldno);
297 cl_assert_equal_i(9, newno);
298
299 /* check hunk 1 */
300
301 cl_git_pass(
3ff1d123 302 git_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 1));
805c476c
RB
303
304 cl_assert_equal_i(18, (int)hunklen);
305
306 cl_assert_equal_i(31, (int)range->old_start);
307 cl_assert_equal_i(15, (int)range->old_lines);
308 cl_assert_equal_i(25, (int)range->new_start);
309 cl_assert_equal_i(9, (int)range->new_lines);
310
3ff1d123 311 cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
805c476c 312
3ff1d123 313 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
314 &origin, &text, &textlen, &oldno, &newno, patch, 1, 0));
315 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
c2f602f8
RB
316 cl_git_pass(git_buf_set(&actual, text, textlen));
317 cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
805c476c
RB
318 cl_assert_equal_i(31, oldno);
319 cl_assert_equal_i(25, newno);
320
3ff1d123 321 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
322 &origin, &text, &textlen, &oldno, &newno, patch, 1, 3));
323 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
c2f602f8
RB
324 cl_git_pass(git_buf_set(&actual, text, textlen));
325 cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
805c476c
RB
326 cl_assert_equal_i(34, oldno);
327 cl_assert_equal_i(-1, newno);
328
3ff1d123 329 cl_git_pass(git_patch_get_line_in_hunk(
805c476c
RB
330 &origin, &text, &textlen, &oldno, &newno, patch, 1, 12));
331 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
c2f602f8
RB
332 cl_git_pass(git_buf_set(&actual, text, textlen));
333 cl_assert_equal_s("Another replacement;\n", actual.ptr);
805c476c
RB
334 cl_assert_equal_i(-1, oldno);
335 cl_assert_equal_i(28, newno);
336
3ff1d123
RB
337 git_patch_free(patch);
338 git_diff_free(diff);
c2f602f8
RB
339
340 /* Let's check line numbers when there is no newline */
341
342 git_buf_rtrim(&old_content);
343 cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
344
345 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
346
347 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
348
3ff1d123 349 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, 0));
c2f602f8
RB
350
351 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
3ff1d123 352 cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
c2f602f8
RB
353
354 /* check hunk 0 */
355
356 cl_git_pass(
3ff1d123 357 git_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
c2f602f8
RB
358
359 cl_assert_equal_i(6, (int)hunklen);
360
361 cl_assert_equal_i(46, (int)range->old_start);
362 cl_assert_equal_i(4, (int)range->old_lines);
363 cl_assert_equal_i(46, (int)range->new_start);
364 cl_assert_equal_i(4, (int)range->new_lines);
365
3ff1d123 366 cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
c2f602f8 367
3ff1d123 368 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
369 &origin, &text, &textlen, &oldno, &newno, patch, 0, 1));
370 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
371 cl_git_pass(git_buf_set(&actual, text, textlen));
372 cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
373 cl_assert_equal_i(47, oldno);
374 cl_assert_equal_i(47, newno);
375
3ff1d123 376 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
377 &origin, &text, &textlen, &oldno, &newno, patch, 0, 2));
378 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
379 cl_git_pass(git_buf_set(&actual, text, textlen));
380 cl_assert_equal_s("\n", actual.ptr);
381 cl_assert_equal_i(48, oldno);
382 cl_assert_equal_i(48, newno);
383
3ff1d123 384 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
385 &origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
386 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
387 cl_git_pass(git_buf_set(&actual, text, textlen));
388 cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
389 cl_assert_equal_i(49, oldno);
390 cl_assert_equal_i(-1, newno);
391
3ff1d123 392 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
393 &origin, &text, &textlen, &oldno, &newno, patch, 0, 4));
394 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
395 cl_git_pass(git_buf_set(&actual, text, textlen));
396 cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
397 cl_assert_equal_i(-1, oldno);
398 cl_assert_equal_i(49, newno);
399
3ff1d123 400 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
401 &origin, &text, &textlen, &oldno, &newno, patch, 0, 5));
402 cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)origin);
403 cl_git_pass(git_buf_set(&actual, text, textlen));
404 cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
405 cl_assert_equal_i(-1, oldno);
406 cl_assert_equal_i(49, newno);
407
3ff1d123
RB
408 git_patch_free(patch);
409 git_diff_free(diff);
c2f602f8
RB
410
411 git_buf_free(&actual);
412 git_buf_free(&old_content);
805c476c
RB
413 git_tree_free(head);
414}
f1e2735c
RB
415
416static void check_single_patch_stats(
fd96f98e 417 git_repository *repo, size_t hunks,
197b8966 418 size_t adds, size_t dels, size_t ctxt, size_t *sizes,
fd96f98e 419 const char *expected)
f1e2735c 420{
3ff1d123
RB
421 git_diff *diff;
422 git_patch *patch;
f1e2735c 423 const git_diff_delta *delta;
e35e2684 424 size_t actual_ctxt, actual_adds, actual_dels;
f1e2735c
RB
425
426 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
427
428 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
429
3ff1d123 430 cl_git_pass(git_patch_from_diff(&patch, &delta, diff, 0));
f1e2735c
RB
431 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
432
3ff1d123 433 cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
f1e2735c 434
3ff1d123 435 cl_git_pass( git_patch_line_stats(
e35e2684 436 &actual_ctxt, &actual_adds, &actual_dels, patch) );
f1e2735c 437
e35e2684 438 cl_assert_equal_sz(ctxt, actual_ctxt);
3ad05221 439 cl_assert_equal_sz(adds, actual_adds);
440 cl_assert_equal_sz(dels, actual_dels);
f1e2735c 441
fd96f98e
RB
442 if (expected != NULL) {
443 char *text;
3ff1d123 444 cl_git_pass(git_patch_to_str(&text, patch));
fd96f98e
RB
445 cl_assert_equal_s(expected, text);
446 git__free(text);
fd96f98e 447
b4a4cf24 448 cl_assert_equal_sz(
3ff1d123 449 strlen(expected), git_patch_size(patch, 1, 1, 1));
197b8966
RB
450 }
451
452 if (sizes) {
453 if (sizes[0])
3ff1d123 454 cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
197b8966 455 if (sizes[1])
3ff1d123 456 cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
197b8966 457 if (sizes[2])
3ff1d123 458 cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
197b8966 459 }
b4a4cf24 460
c2f602f8
RB
461 /* walk lines in hunk with basic sanity checks */
462 for (; hunks > 0; --hunks) {
463 size_t i, max_i;
464 int lastoldno = -1, oldno, lastnewno = -1, newno;
465 char origin;
466
3ff1d123 467 max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
c2f602f8
RB
468
469 for (i = 0; i < max_i; ++i) {
470 int expected = 1;
471
3ff1d123 472 cl_git_pass(git_patch_get_line_in_hunk(
c2f602f8
RB
473 &origin, NULL, NULL, &oldno, &newno, patch, hunks - 1, i));
474
475 if (origin == GIT_DIFF_LINE_ADD_EOFNL ||
476 origin == GIT_DIFF_LINE_DEL_EOFNL ||
477 origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
478 expected = 0;
479
480 if (oldno >= 0) {
481 if (lastoldno >= 0)
482 cl_assert_equal_i(expected, oldno - lastoldno);
483 lastoldno = oldno;
484 }
485 if (newno >= 0) {
486 if (lastnewno >= 0)
487 cl_assert_equal_i(expected, newno - lastnewno);
488 lastnewno = newno;
489 }
490 }
491 }
492
3ff1d123
RB
493 git_patch_free(patch);
494 git_diff_free(diff);
f1e2735c
RB
495}
496
497void test_diff_patch__line_counts_with_eofnl(void)
498{
7d46b34b 499 git_config *cfg;
f1e2735c
RB
500 git_buf content = GIT_BUF_INIT;
501 const char *end;
502 git_index *index;
197b8966
RB
503 const char *expected =
504 /* below is pasted output of 'git diff' with fn context removed */
505 "diff --git a/songof7cities.txt b/songof7cities.txt\n"
506 "index 378a7d9..3d0154e 100644\n"
507 "--- a/songof7cities.txt\n"
508 "+++ b/songof7cities.txt\n"
509 "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
510 " \n"
511 " To the sound of trumpets shall their seed restore my Cities\n"
512 " Wealthy and well-weaponed, that once more may I behold\n"
513 "-All the world go softly when it walks before my Cities,\n"
514 "+#All the world go softly when it walks before my Cities,\n"
515 " And the horses and the chariots fleeing from them as of old!\n"
516 " \n"
517 " -- Rudyard Kipling\n"
518 "\\ No newline at end of file\n";
519 size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
f1e2735c
RB
520
521 g_repo = cl_git_sandbox_init("renames");
522
487fc724
RB
523 cl_git_pass(git_config_new(&cfg));
524 git_repository_set_config(g_repo, cfg);
5173ea92
RB
525 git_config_free(cfg);
526
867f7c9b 527 git_repository_reinit_filesystem(g_repo, false);
7d46b34b 528
6f9d5ce8 529 cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
f1e2735c
RB
530
531 /* remove first line */
532
533 end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
534 git_buf_consume(&content, end);
6f9d5ce8 535 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 536
197b8966 537 check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL);
f1e2735c
RB
538
539 /* remove trailing whitespace */
540
541 git_buf_rtrim(&content);
6f9d5ce8 542 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 543
197b8966 544 check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL);
f1e2735c
RB
545
546 /* add trailing whitespace */
547
548 cl_git_pass(git_repository_index(&index, g_repo));
6f9d5ce8 549 cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
f1e2735c
RB
550 cl_git_pass(git_index_write(index));
551 git_index_free(index);
552
553 cl_git_pass(git_buf_putc(&content, '\n'));
6f9d5ce8 554 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 555
197b8966 556 check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL);
e35e2684
RB
557
558 /* no trailing whitespace as context line */
559
560 {
561 /* walk back a couple lines, make space and insert char */
562 char *scan = content.ptr + content.size;
563 int i;
564
565 for (i = 0; i < 5; ++i) {
566 for (--scan; scan > content.ptr && *scan != '\n'; --scan)
567 /* seek to prev \n */;
568 }
569 cl_assert(scan > content.ptr);
570
571 /* overwrite trailing \n with right-shifted content */
572 memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
573 /* insert '#' char into space we created */
574 scan[1] = '#';
575 }
576 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
577
fd96f98e 578 check_single_patch_stats(
197b8966 579 g_repo, 1, 1, 1, 6, expected_sizes, expected);
3bf68be4
RB
580
581 git_buf_free(&content);
f1e2735c 582}