]> git.proxmox.com Git - libgit2.git/blob - tests/libgit2/diff/diff_helpers.c
New upstream version 1.5.0+ds
[libgit2.git] / tests / libgit2 / diff / diff_helpers.c
1 #include "clar_libgit2.h"
2 #include "diff_helpers.h"
3 #include "git2/sys/diff.h"
4
5 git_tree *resolve_commit_oid_to_tree(
6 git_repository *repo,
7 const char *partial_oid)
8 {
9 size_t len = strlen(partial_oid);
10 git_oid oid;
11 git_object *obj = NULL;
12 git_tree *tree = NULL;
13
14 if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
15 cl_git_pass(git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJECT_ANY));
16
17 cl_git_pass(git_object_peel((git_object **) &tree, obj, GIT_OBJECT_TREE));
18 git_object_free(obj);
19 return tree;
20 }
21
22 static char diff_pick_suffix(int mode)
23 {
24 if (S_ISDIR(mode))
25 return '/';
26 else if (GIT_PERMS_IS_EXEC(mode))
27 return '*';
28 else
29 return ' ';
30 }
31
32 static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
33 {
34 char code = git_diff_status_char(delta->status);
35 char old_suffix = diff_pick_suffix(delta->old_file.mode);
36 char new_suffix = diff_pick_suffix(delta->new_file.mode);
37
38 fprintf(fp, "%c\t%s", code, delta->old_file.path);
39
40 if ((delta->old_file.path != delta->new_file.path &&
41 strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
42 (delta->old_file.mode != delta->new_file.mode &&
43 delta->old_file.mode != 0 && delta->new_file.mode != 0))
44 fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
45 else if (old_suffix != ' ')
46 fprintf(fp, "%c", old_suffix);
47
48 fprintf(fp, "\t[%.2f]\n", progress);
49 }
50
51 int diff_file_cb(
52 const git_diff_delta *delta,
53 float progress,
54 void *payload)
55 {
56 diff_expects *e = payload;
57
58 if (e->debug)
59 fprintf_delta(stderr, delta, progress);
60
61 if (e->names)
62 cl_assert_equal_s(e->names[e->files], delta->old_file.path);
63 if (e->statuses)
64 cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
65
66 e->files++;
67
68 if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
69 e->files_binary++;
70
71 cl_assert(delta->status <= GIT_DELTA_CONFLICTED);
72
73 e->file_status[delta->status] += 1;
74
75 return 0;
76 }
77
78 int diff_print_file_cb(
79 const git_diff_delta *delta,
80 float progress,
81 void *payload)
82 {
83 if (!payload) {
84 fprintf_delta(stderr, delta, progress);
85 return 0;
86 }
87
88 if (!((diff_expects *)payload)->debug)
89 fprintf_delta(stderr, delta, progress);
90
91 return diff_file_cb(delta, progress, payload);
92 }
93
94 int diff_binary_cb(
95 const git_diff_delta *delta,
96 const git_diff_binary *binary,
97 void *payload)
98 {
99 GIT_UNUSED(delta);
100 GIT_UNUSED(binary);
101 GIT_UNUSED(payload);
102
103 return 0;
104 }
105
106 int diff_hunk_cb(
107 const git_diff_delta *delta,
108 const git_diff_hunk *hunk,
109 void *payload)
110 {
111 diff_expects *e = payload;
112 const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
113
114 GIT_UNUSED(delta);
115
116 /* confirm no NUL bytes in header text */
117 while (scan < scan_end)
118 cl_assert('\0' != *scan++);
119
120 e->hunks++;
121 e->hunk_old_lines += hunk->old_lines;
122 e->hunk_new_lines += hunk->new_lines;
123 return 0;
124 }
125
126 int diff_line_cb(
127 const git_diff_delta *delta,
128 const git_diff_hunk *hunk,
129 const git_diff_line *line,
130 void *payload)
131 {
132 diff_expects *e = payload;
133
134 GIT_UNUSED(delta);
135 GIT_UNUSED(hunk);
136
137 e->lines++;
138 switch (line->origin) {
139 case GIT_DIFF_LINE_CONTEXT:
140 case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
141 e->line_ctxt++;
142 break;
143 case GIT_DIFF_LINE_ADDITION:
144 case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
145 e->line_adds++;
146 break;
147 case GIT_DIFF_LINE_DELETION:
148 case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
149 e->line_dels++;
150 break;
151 default:
152 break;
153 }
154 return 0;
155 }
156
157 int diff_foreach_via_iterator(
158 git_diff *diff,
159 git_diff_file_cb file_cb,
160 git_diff_binary_cb binary_cb,
161 git_diff_hunk_cb hunk_cb,
162 git_diff_line_cb line_cb,
163 void *data)
164 {
165 size_t d, num_d = git_diff_num_deltas(diff);
166
167 GIT_UNUSED(binary_cb);
168
169 for (d = 0; d < num_d; ++d) {
170 git_patch *patch;
171 const git_diff_delta *delta;
172 size_t h, num_h;
173
174 cl_git_pass(git_patch_from_diff(&patch, diff, d));
175 cl_assert((delta = git_patch_get_delta(patch)) != NULL);
176
177 /* call file_cb for this file */
178 if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
179 git_patch_free(patch);
180 goto abort;
181 }
182
183 /* if there are no changes, then the patch will be NULL */
184 if (!patch) {
185 cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
186 (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
187 continue;
188 }
189
190 if (!hunk_cb && !line_cb) {
191 git_patch_free(patch);
192 continue;
193 }
194
195 num_h = git_patch_num_hunks(patch);
196
197 for (h = 0; h < num_h; h++) {
198 const git_diff_hunk *hunk;
199 size_t l, num_l;
200
201 cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
202
203 if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
204 git_patch_free(patch);
205 goto abort;
206 }
207
208 for (l = 0; l < num_l; ++l) {
209 const git_diff_line *line;
210
211 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
212
213 if (line_cb &&
214 line_cb(delta, hunk, line, data) != 0) {
215 git_patch_free(patch);
216 goto abort;
217 }
218 }
219 }
220
221 git_patch_free(patch);
222 }
223
224 return 0;
225
226 abort:
227 git_error_clear();
228 return GIT_EUSER;
229 }
230
231 void diff_print(FILE *fp, git_diff *diff)
232 {
233 cl_git_pass(
234 git_diff_print(diff, GIT_DIFF_FORMAT_PATCH,
235 git_diff_print_callback__to_file_handle, fp ? fp : stderr));
236 }
237
238 void diff_print_raw(FILE *fp, git_diff *diff)
239 {
240 cl_git_pass(
241 git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
242 git_diff_print_callback__to_file_handle, fp ? fp : stderr));
243 }
244
245 static size_t num_modified_deltas(git_diff *diff)
246 {
247 const git_diff_delta *delta;
248 size_t i, cnt = 0;
249
250 for (i = 0; i < git_diff_num_deltas(diff); i++) {
251 delta = git_diff_get_delta(diff, i);
252
253 if (delta->status != GIT_DELTA_UNMODIFIED)
254 cnt++;
255 }
256
257 return cnt;
258 }
259
260 void diff_assert_equal(git_diff *a, git_diff *b)
261 {
262 const git_diff_delta *ad, *bd;
263 size_t i, j;
264
265 assert(a && b);
266
267 cl_assert_equal_i(num_modified_deltas(a), num_modified_deltas(b));
268
269 for (i = 0, j = 0;
270 i < git_diff_num_deltas(a) && j < git_diff_num_deltas(b); ) {
271
272 ad = git_diff_get_delta(a, i);
273 bd = git_diff_get_delta(b, j);
274
275 if (ad->status == GIT_DELTA_UNMODIFIED) {
276 i++;
277 continue;
278 }
279 if (bd->status == GIT_DELTA_UNMODIFIED) {
280 j++;
281 continue;
282 }
283
284 cl_assert_equal_i(ad->status, bd->status);
285 cl_assert_equal_i(ad->flags, bd->flags);
286 cl_assert_equal_i(ad->similarity, bd->similarity);
287 cl_assert_equal_i(ad->nfiles, bd->nfiles);
288
289 /* Don't examine the size or the flags of the deltas;
290 * computed deltas have sizes (parsed deltas do not) and
291 * computed deltas will have flags of `VALID_ID` and
292 * `EXISTS` (parsed deltas will not query the ODB.)
293 */
294
295 /* an empty id indicates that it wasn't presented, because
296 * the diff was identical. (eg, pure rename, mode change only, etc)
297 */
298 if (ad->old_file.id_abbrev && bd->old_file.id_abbrev) {
299 cl_assert_equal_i(ad->old_file.id_abbrev, bd->old_file.id_abbrev);
300 cl_assert_equal_oid(&ad->old_file.id, &bd->old_file.id);
301 cl_assert_equal_i(ad->old_file.mode, bd->old_file.mode);
302 }
303 cl_assert_equal_s(ad->old_file.path, bd->old_file.path);
304
305 if (ad->new_file.id_abbrev && bd->new_file.id_abbrev) {
306 cl_assert_equal_oid(&ad->new_file.id, &bd->new_file.id);
307 cl_assert_equal_i(ad->new_file.id_abbrev, bd->new_file.id_abbrev);
308 cl_assert_equal_i(ad->new_file.mode, bd->new_file.mode);
309 }
310 cl_assert_equal_s(ad->new_file.path, bd->new_file.path);
311
312 i++;
313 j++;
314 }
315 }
316