diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2021-11-16 23:29:22 -0500 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2022-02-22 22:07:45 -0500 |
commit | 3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb (patch) | |
tree | fd6368a72944571c51627b40c592e7d58e0036e1 /tests/libgit2/diff/diff_helpers.c | |
parent | 91ba089663f5efc3bd4ba14a5099372cf5ce57a6 (diff) | |
download | libgit2-3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb.tar.gz |
refactor: `tests` is now `tests/libgit2`
Like we want to separate libgit2 and utility source code, we want to
separate libgit2 and utility tests. Start by moving all the tests into
libgit2.
Diffstat (limited to 'tests/libgit2/diff/diff_helpers.c')
-rw-r--r-- | tests/libgit2/diff/diff_helpers.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/tests/libgit2/diff/diff_helpers.c b/tests/libgit2/diff/diff_helpers.c new file mode 100644 index 000000000..e9900339f --- /dev/null +++ b/tests/libgit2/diff/diff_helpers.c @@ -0,0 +1,316 @@ +#include "clar_libgit2.h" +#include "diff_helpers.h" +#include "git2/sys/diff.h" + +git_tree *resolve_commit_oid_to_tree( + git_repository *repo, + const char *partial_oid) +{ + size_t len = strlen(partial_oid); + git_oid oid; + git_object *obj = NULL; + git_tree *tree = NULL; + + if (git_oid_fromstrn(&oid, partial_oid, len) == 0) + cl_git_pass(git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJECT_ANY)); + + cl_git_pass(git_object_peel((git_object **) &tree, obj, GIT_OBJECT_TREE)); + git_object_free(obj); + return tree; +} + +static char diff_pick_suffix(int mode) +{ + if (S_ISDIR(mode)) + return '/'; + else if (GIT_PERMS_IS_EXEC(mode)) + return '*'; + else + return ' '; +} + +static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress) +{ + char code = git_diff_status_char(delta->status); + char old_suffix = diff_pick_suffix(delta->old_file.mode); + char new_suffix = diff_pick_suffix(delta->new_file.mode); + + fprintf(fp, "%c\t%s", code, delta->old_file.path); + + if ((delta->old_file.path != delta->new_file.path && + strcmp(delta->old_file.path, delta->new_file.path) != 0) || + (delta->old_file.mode != delta->new_file.mode && + delta->old_file.mode != 0 && delta->new_file.mode != 0)) + fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix); + else if (old_suffix != ' ') + fprintf(fp, "%c", old_suffix); + + fprintf(fp, "\t[%.2f]\n", progress); +} + +int diff_file_cb( + const git_diff_delta *delta, + float progress, + void *payload) +{ + diff_expects *e = payload; + + if (e->debug) + fprintf_delta(stderr, delta, progress); + + if (e->names) + cl_assert_equal_s(e->names[e->files], delta->old_file.path); + if (e->statuses) + cl_assert_equal_i(e->statuses[e->files], (int)delta->status); + + e->files++; + + if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) + e->files_binary++; + + cl_assert(delta->status <= GIT_DELTA_CONFLICTED); + + e->file_status[delta->status] += 1; + + return 0; +} + +int diff_print_file_cb( + const git_diff_delta *delta, + float progress, + void *payload) +{ + if (!payload) { + fprintf_delta(stderr, delta, progress); + return 0; + } + + if (!((diff_expects *)payload)->debug) + fprintf_delta(stderr, delta, progress); + + return diff_file_cb(delta, progress, payload); +} + +int diff_binary_cb( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *payload) +{ + GIT_UNUSED(delta); + GIT_UNUSED(binary); + GIT_UNUSED(payload); + + return 0; +} + +int diff_hunk_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + void *payload) +{ + diff_expects *e = payload; + const char *scan = hunk->header, *scan_end = scan + hunk->header_len; + + GIT_UNUSED(delta); + + /* confirm no NUL bytes in header text */ + while (scan < scan_end) + cl_assert('\0' != *scan++); + + e->hunks++; + e->hunk_old_lines += hunk->old_lines; + e->hunk_new_lines += hunk->new_lines; + return 0; +} + +int diff_line_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload) +{ + diff_expects *e = payload; + + GIT_UNUSED(delta); + GIT_UNUSED(hunk); + + e->lines++; + switch (line->origin) { + case GIT_DIFF_LINE_CONTEXT: + case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */ + e->line_ctxt++; + break; + case GIT_DIFF_LINE_ADDITION: + case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */ + e->line_adds++; + break; + case GIT_DIFF_LINE_DELETION: + case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */ + e->line_dels++; + break; + default: + break; + } + return 0; +} + +int diff_foreach_via_iterator( + git_diff *diff, + git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb line_cb, + void *data) +{ + size_t d, num_d = git_diff_num_deltas(diff); + + GIT_UNUSED(binary_cb); + + for (d = 0; d < num_d; ++d) { + git_patch *patch; + const git_diff_delta *delta; + size_t h, num_h; + + cl_git_pass(git_patch_from_diff(&patch, diff, d)); + cl_assert((delta = git_patch_get_delta(patch)) != NULL); + + /* call file_cb for this file */ + if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) { + git_patch_free(patch); + goto abort; + } + + /* if there are no changes, then the patch will be NULL */ + if (!patch) { + cl_assert(delta->status == GIT_DELTA_UNMODIFIED || + (delta->flags & GIT_DIFF_FLAG_BINARY) != 0); + continue; + } + + if (!hunk_cb && !line_cb) { + git_patch_free(patch); + continue; + } + + num_h = git_patch_num_hunks(patch); + + for (h = 0; h < num_h; h++) { + const git_diff_hunk *hunk; + size_t l, num_l; + + cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h)); + + if (hunk_cb && hunk_cb(delta, hunk, data) != 0) { + git_patch_free(patch); + goto abort; + } + + for (l = 0; l < num_l; ++l) { + const git_diff_line *line; + + cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l)); + + if (line_cb && + line_cb(delta, hunk, line, data) != 0) { + git_patch_free(patch); + goto abort; + } + } + } + + git_patch_free(patch); + } + + return 0; + +abort: + git_error_clear(); + return GIT_EUSER; +} + +void diff_print(FILE *fp, git_diff *diff) +{ + cl_git_pass( + git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, + git_diff_print_callback__to_file_handle, fp ? fp : stderr)); +} + +void diff_print_raw(FILE *fp, git_diff *diff) +{ + cl_git_pass( + git_diff_print(diff, GIT_DIFF_FORMAT_RAW, + git_diff_print_callback__to_file_handle, fp ? fp : stderr)); +} + +static size_t num_modified_deltas(git_diff *diff) +{ + const git_diff_delta *delta; + size_t i, cnt = 0; + + for (i = 0; i < git_diff_num_deltas(diff); i++) { + delta = git_diff_get_delta(diff, i); + + if (delta->status != GIT_DELTA_UNMODIFIED) + cnt++; + } + + return cnt; +} + +void diff_assert_equal(git_diff *a, git_diff *b) +{ + const git_diff_delta *ad, *bd; + size_t i, j; + + assert(a && b); + + cl_assert_equal_i(num_modified_deltas(a), num_modified_deltas(b)); + + for (i = 0, j = 0; + i < git_diff_num_deltas(a) && j < git_diff_num_deltas(b); ) { + + ad = git_diff_get_delta(a, i); + bd = git_diff_get_delta(b, j); + + if (ad->status == GIT_DELTA_UNMODIFIED) { + i++; + continue; + } + if (bd->status == GIT_DELTA_UNMODIFIED) { + j++; + continue; + } + + cl_assert_equal_i(ad->status, bd->status); + cl_assert_equal_i(ad->flags, bd->flags); + cl_assert_equal_i(ad->similarity, bd->similarity); + cl_assert_equal_i(ad->nfiles, bd->nfiles); + + /* Don't examine the size or the flags of the deltas; + * computed deltas have sizes (parsed deltas do not) and + * computed deltas will have flags of `VALID_ID` and + * `EXISTS` (parsed deltas will not query the ODB.) + */ + + /* an empty id indicates that it wasn't presented, because + * the diff was identical. (eg, pure rename, mode change only, etc) + */ + if (ad->old_file.id_abbrev && bd->old_file.id_abbrev) { + cl_assert_equal_i(ad->old_file.id_abbrev, bd->old_file.id_abbrev); + cl_assert_equal_oid(&ad->old_file.id, &bd->old_file.id); + cl_assert_equal_i(ad->old_file.mode, bd->old_file.mode); + } + cl_assert_equal_s(ad->old_file.path, bd->old_file.path); + + if (ad->new_file.id_abbrev && bd->new_file.id_abbrev) { + cl_assert_equal_oid(&ad->new_file.id, &bd->new_file.id); + cl_assert_equal_i(ad->new_file.id_abbrev, bd->new_file.id_abbrev); + cl_assert_equal_i(ad->new_file.mode, bd->new_file.mode); + } + cl_assert_equal_s(ad->new_file.path, bd->new_file.path); + + i++; + j++; + } +} + |