diff options
| author | Ben Straub <bs@github.com> | 2013-09-16 16:12:31 -0700 |
|---|---|---|
| committer | Ben Straub <bs@github.com> | 2013-09-16 16:12:31 -0700 |
| commit | 549931679a77b280eb1f36afeda8930eb31d70f7 (patch) | |
| tree | 2744e3e198ad146bae72f35369bbeb4f8028c8c3 /tests-clar | |
| parent | 1a68c168a6cdbe0db6e44fb582a7026a7d536c9d (diff) | |
| parent | 8821c9aa5baf31e21c21825e8c91c765e6631e7f (diff) | |
| download | libgit2-549931679a77b280eb1f36afeda8930eb31d70f7.tar.gz | |
Merge branch 'development' into blame_rebased
Conflicts:
include/git2.h
Diffstat (limited to 'tests-clar')
98 files changed, 6001 insertions, 413 deletions
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c index 8866fd9bd..4eb1d22fe 100644 --- a/tests-clar/attr/file.c +++ b/tests-clar/attr/file.c @@ -49,7 +49,6 @@ void test_attr_file__match_variants(void) cl_assert(rule); cl_assert_equal_s("pat0", rule->match.pattern); cl_assert(rule->match.length == strlen("pat0")); - cl_assert(rule->match.flags == 0); cl_assert(rule->assigns.length == 1); assign = get_assign(rule,0); cl_assert_equal_s("attr0", assign->name); @@ -59,16 +58,16 @@ void test_attr_file__match_variants(void) rule = get_rule(1); cl_assert_equal_s("pat1", rule->match.pattern); cl_assert(rule->match.length == strlen("pat1")); - cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0); rule = get_rule(2); cl_assert_equal_s("pat2", rule->match.pattern); cl_assert(rule->match.length == strlen("pat2")); - cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0); rule = get_rule(3); cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern); - cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0); rule = get_rule(4); cl_assert_equal_s("pat4.*", rule->match.pattern); @@ -89,7 +88,6 @@ void test_attr_file__match_variants(void) rule = get_rule(8); cl_assert_equal_s("pat8 with spaces", rule->match.pattern); cl_assert(rule->match.length == strlen("pat8 with spaces")); - cl_assert(rule->match.flags == 0); rule = get_rule(9); cl_assert_equal_s("pat9", rule->match.pattern); diff --git a/tests-clar/checkout/checkout_helpers.c b/tests-clar/checkout/checkout_helpers.c index ab93a89bd..f55f7b611 100644 --- a/tests-clar/checkout/checkout_helpers.c +++ b/tests-clar/checkout/checkout_helpers.c @@ -74,8 +74,8 @@ static void check_file_contents_internal( if (strip_cr) strip_cr_from_buf(&buf); - clar__assert_equal_i((int)expected_len, (int)buf.size, file, line, "strlen(expected_content) != strlen(actual_content)", 1); - clar__assert_equal_s(expected_content, buf.ptr, file, line, msg, 1); + clar__assert_equal(file, line, "strlen(expected_content) != strlen(actual_content)", 1, PRIuZ, expected_len, (size_t)buf.size); + clar__assert_equal(file, line, msg, 1, "%s", expected_content, buf.ptr); } void check_file_contents_at_line( @@ -91,3 +91,98 @@ void check_file_contents_nocr_at_line( { check_file_contents_internal(path, expected, true, file, line, msg); } + +int checkout_count_callback( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload) +{ + checkout_counts *ct = payload; + + GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir); + + if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) { + ct->n_conflicts++; + + if (ct->debug) { + if (workdir) { + if (baseline) { + if (target) + fprintf(stderr, "M %s (conflicts with M %s)\n", + workdir->path, target->path); + else + fprintf(stderr, "M %s (conflicts with D %s)\n", + workdir->path, baseline->path); + } else { + if (target) + fprintf(stderr, "Existing %s (conflicts with A %s)\n", + workdir->path, target->path); + else + fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path); + } + } else { + if (baseline) { + if (target) + fprintf(stderr, "D %s (conflicts with M %s)\n", + target->path, baseline->path); + else + fprintf(stderr, "D %s (conflicts with D %s)\n", + baseline->path, baseline->path); + } else { + if (target) + fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path); + else + fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path); + } + } + } + } + + if (why & GIT_CHECKOUT_NOTIFY_DIRTY) { + ct->n_dirty++; + + if (ct->debug) { + if (workdir) + fprintf(stderr, "M %s\n", workdir->path); + else + fprintf(stderr, "D %s\n", baseline->path); + } + } + + if (why & GIT_CHECKOUT_NOTIFY_UPDATED) { + ct->n_updates++; + + if (ct->debug) { + if (baseline) { + if (target) + fprintf(stderr, "update: M %s\n", path); + else + fprintf(stderr, "update: D %s\n", path); + } else { + if (target) + fprintf(stderr, "update: A %s\n", path); + else + fprintf(stderr, "update: this makes no sense %s\n", path); + } + } + } + + if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) { + ct->n_untracked++; + + if (ct->debug) + fprintf(stderr, "? %s\n", path); + } + + if (why & GIT_CHECKOUT_NOTIFY_IGNORED) { + ct->n_ignored++; + + if (ct->debug) + fprintf(stderr, "I %s\n", path); + } + + return 0; +} diff --git a/tests-clar/checkout/checkout_helpers.h b/tests-clar/checkout/checkout_helpers.h index 34053809d..0e8da31d1 100644 --- a/tests-clar/checkout/checkout_helpers.h +++ b/tests-clar/checkout/checkout_helpers.h @@ -19,3 +19,20 @@ extern void check_file_contents_nocr_at_line( #define check_file_contents_nocr(PATH,EXP) \ check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH) + +typedef struct { + int n_conflicts; + int n_dirty; + int n_updates; + int n_untracked; + int n_ignored; + int debug; +} checkout_counts; + +extern int checkout_count_callback( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload); diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index a3a0f8fda..73050d08e 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -26,6 +26,10 @@ void test_checkout_index__initialize(void) void test_checkout_index__cleanup(void) { cl_git_sandbox_cleanup(); + + /* try to remove alternative dir */ + if (git_path_isdir("alternative")) + git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES); } void test_checkout_index__cannot_checkout_a_bare_repository(void) @@ -225,6 +229,7 @@ void test_checkout_index__options_dir_modes(void) struct stat st; git_oid oid; git_commit *commit; + mode_t um; cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); cl_git_pass(git_commit_lookup(&commit, g_repo, &oid)); @@ -236,12 +241,15 @@ void test_checkout_index__options_dir_modes(void) cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + /* umask will influence actual directory creation mode */ + (void)p_umask(um = p_umask(022)); + cl_git_pass(p_stat("./testrepo/a", &st)); - cl_assert_equal_i(st.st_mode & 0777, 0701); + cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o"); /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); - cl_assert_equal_i(st.st_mode & 0777, 0755); + cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o"); git_commit_free(commit); #endif @@ -259,7 +267,7 @@ void test_checkout_index__options_override_file_modes(void) cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(p_stat("./testrepo/new.txt", &st)); - cl_assert_equal_i(st.st_mode & 0777, 0700); + cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o"); #endif } @@ -506,3 +514,105 @@ void test_checkout_index__issue_1397(void) check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf"); } + +void test_checkout_index__target_directory(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* create some files that *would* conflict if we were using the wd */ + cl_git_mkfile("testrepo/README", "I'm in the way!\n"); + cl_git_mkfile("testrepo/new.txt", "my new file\n"); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(4, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_index__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_index *index; + git_object *head = NULL; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_index__cleanup(); + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}")); + cl_git_pass(git_index_read_tree(index, (const git_tree *)head)); + cl_git_pass(git_index_write(index)); + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* fail to checkout a bare repo */ + cl_git_fail(git_checkout_index(g_repo, NULL, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + /* files will have been filtered if needed, so strip CR */ + check_file_contents_nocr("./alternative/README", "hey there\n"); + check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents_nocr("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); + + git_object_free(head); +} + +void test_checkout_index__can_get_repo_from_index(void) +{ + git_index *index; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + + cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt")); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_checkout_index(NULL, index, &opts)); + + check_file_contents("./testrepo/README", "hey there\n"); + check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./testrepo/new.txt", "my new file\n"); + + git_index_free(index); +} diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 462a46c83..fff530cdd 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -24,6 +24,9 @@ void test_checkout_tree__cleanup(void) g_object = NULL; cl_git_sandbox_cleanup(); + + if (git_path_isdir("alternative")) + git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES); } void test_checkout_tree__cannot_checkout_a_non_treeish(void) @@ -442,6 +445,47 @@ void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERG assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c"); } +void test_checkout_tree__donot_update_deleted_file_by_default(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid old_id, new_id; + git_commit *old_commit = NULL, *new_commit = NULL; + git_index *index = NULL; + checkout_counts ct; + + opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + memset(&ct, 0, sizeof(ct)); + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &ct; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id)); + cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD)); + + cl_git_pass(p_unlink("testrepo/branch_file.txt")); + cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt")); + cl_git_pass(git_index_write(index)); + + cl_assert(!git_path_exists("testrepo/branch_file.txt")); + + cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff")); + cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id)); + + + cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts)); + + cl_assert_equal_i(1, ct.n_conflicts); + cl_assert_equal_i(1, ct.n_updates); + + git_commit_free(old_commit); + git_commit_free(new_commit); + git_index_free(index); +} + void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) { git_index *index = NULL; @@ -555,6 +599,8 @@ void test_checkout_tree__fails_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } @@ -587,5 +633,66 @@ void test_checkout_tree__can_continue_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } + +void test_checkout_tree__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_tree__cleanup(); /* cleanup default checkout */ + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD")); + cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_fail(git_checkout_tree(g_repo, g_object, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_tree(g_repo, g_object, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + check_file_contents_nocr("./alternative/README", "hey there\n"); + check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents_nocr("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_tree__extremely_long_file_name(void) +{ + // A utf-8 string with 83 characters, but 249 bytes. + const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97"; + char path[1024]; + + g_opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name")); + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + + sprintf(path, "testrepo/%s.txt", longname); + cl_assert(git_path_exists(path)); + + git_object_free(g_object); + cl_git_pass(git_revparse_single(&g_object, g_repo, "master")); + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + cl_assert(!git_path_exists(path)); +} diff --git a/tests-clar/clar.c b/tests-clar/clar.c index 0eae81bf5..6c7399a54 100644 --- a/tests-clar/clar.c +++ b/tests-clar/clar.c @@ -24,28 +24,59 @@ # define _MAIN_CC __cdecl -# define stat(path, st) _stat(path, st) -# define mkdir(path, mode) _mkdir(path) -# define chdir(path) _chdir(path) -# define access(path, mode) _access(path, mode) -# define strdup(str) _strdup(str) -# define strcasecmp(a,b) _stricmp(a,b) +# ifndef stat +# define stat(path, st) _stat(path, st) +# endif +# ifndef mkdir +# define mkdir(path, mode) _mkdir(path) +# endif +# ifndef chdir +# define chdir(path) _chdir(path) +# endif +# ifndef access +# define access(path, mode) _access(path, mode) +# endif +# ifndef strdup +# define strdup(str) _strdup(str) +# endif +# ifndef strcasecmp +# define strcasecmp(a,b) _stricmp(a,b) +# endif # ifndef __MINGW32__ # pragma comment(lib, "shell32") -# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE) -# define W_OK 02 -# define S_ISDIR(x) ((x & _S_IFDIR) != 0) -# define snprint_eq(buf,sz,fmt,a,b) _snprintf_s(buf,sz,_TRUNCATE,fmt,a,b) +# ifndef strncpy +# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE) +# endif +# ifndef W_OK +# define W_OK 02 +# endif +# ifndef S_ISDIR +# define S_ISDIR(x) ((x & _S_IFDIR) != 0) +# endif +# define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__) # else -# define snprint_eq snprintf +# define p_snprintf snprintf +# endif + +# ifndef PRIuZ +# define PRIuZ "Iu" +# endif +# ifndef PRIxZ +# define PRIxZ "Ix" # endif typedef struct _stat STAT_T; #else # include <sys/wait.h> /* waitpid(2) */ # include <unistd.h> # define _MAIN_CC -# define snprint_eq snprintf +# define p_snprintf snprintf +# ifndef PRIuZ +# define PRIuZ "zu" +# endif +# ifndef PRIxZ +# define PRIxZ "zx" +# endif typedef struct stat STAT_T; #endif @@ -183,10 +214,10 @@ clar_run_test( } static void -clar_run_suite(const struct clar_suite *suite, const char *name) +clar_run_suite(const struct clar_suite *suite, const char *filter) { const struct clar_func *test = suite->tests; - size_t i, namelen; + size_t i, matchlen; if (!suite->enabled) return; @@ -200,21 +231,21 @@ clar_run_suite(const struct clar_suite *suite, const char *name) _clar.active_suite = suite->name; _clar.suite_errors = 0; - if (name) { + if (filter) { size_t suitelen = strlen(suite->name); - namelen = strlen(name); - if (namelen <= suitelen) { - name = NULL; + matchlen = strlen(filter); + if (matchlen <= suitelen) { + filter = NULL; } else { - name += suitelen; - while (*name == ':') - ++name; - namelen = strlen(name); + filter += suitelen; + while (*filter == ':') + ++filter; + matchlen = strlen(filter); } } for (i = 0; i < suite->test_count; ++i) { - if (name && strncmp(test[i].name, name, namelen)) + if (filter && strncmp(test[i].name, filter, matchlen)) continue; _clar.active_test = test[i].name; @@ -230,7 +261,7 @@ clar_usage(const char *arg) { printf("Usage: %s [options]\n\n", arg); printf("Options:\n"); - printf(" -sname\tRun only the suite with `name`\n"); + printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n"); printf(" -iname\tInclude the suite with `name`\n"); printf(" -xname\tExclude the suite with `name`\n"); printf(" -q \tOnly report tests that had an error\n"); @@ -256,21 +287,20 @@ clar_parse_args(int argc, char **argv) case 'x': { /* given suite name */ int offset = (argument[2] == '=') ? 3 : 2, found = 0; char action = argument[1]; - size_t j, len, cmplen; + size_t j, arglen, suitelen, cmplen; argument += offset; - len = strlen(argument); + arglen = strlen(argument); - if (len == 0) + if (arglen == 0) clar_usage(argv[0]); for (j = 0; j < _clar_suite_count; ++j) { - cmplen = strlen(_clar_suites[j].name); - if (cmplen > len) - cmplen = len; + suitelen = strlen(_clar_suites[j].name); + cmplen = (arglen < suitelen) ? arglen : suitelen; if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) { - int exact = !strcmp(argument, _clar_suites[j].name); + int exact = (arglen >= suitelen); ++found; @@ -407,36 +437,66 @@ void clar__assert( clar__fail(file, line, error_msg, description, should_abort); } -void clar__assert_equal_s( - const char *s1, - const char *s2, +void clar__assert_equal( const char *file, int line, const char *err, - int should_abort) + int should_abort, + const char *fmt, + ...) { - int match = (s1 == NULL || s2 == NULL) ? (s1 == s2) : (strcmp(s1, s2) == 0); - - if (!match) { - char buf[4096]; - snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2); - clar__fail(file, line, err, buf, should_abort); + va_list args; + char buf[4096]; + int is_equal = 1; + + va_start(args, fmt); + + if (!strcmp("%s", fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2); + + if (!is_equal) { + if (s1 && s2) { + int pos; + for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos) + /* find differing byte offset */; + p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", + s1, s2, pos); + } else { + p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2); + } + } + } + else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) { + size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t); + is_equal = (sz1 == sz2); + if (!is_equal) { + int offset = p_snprintf(buf, sizeof(buf), fmt, sz1); + strncat(buf, " != ", sizeof(buf) - offset); + p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2); + } + } + else if (!strcmp("%p", fmt)) { + void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *); + is_equal = (p1 == p2); + if (!is_equal) + p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2); + } + else { + int i1 = va_arg(args, int), i2 = va_arg(args, int); + is_equal = (i1 == i2); + if (!is_equal) { + int offset = p_snprintf(buf, sizeof(buf), fmt, i1); + strncat(buf, " != ", sizeof(buf) - offset); + p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2); + } } -} -void clar__assert_equal_i( - int i1, - int i2, - const char *file, - int line, - const char *err, - int should_abort) -{ - if (i1 != i2) { - char buf[128]; - snprint_eq(buf, sizeof(buf), "%d != %d", i1, i2); + va_end(args); + + if (!is_equal) clar__fail(file, line, err, buf, should_abort); - } } void cl_set_cleanup(void (*cleanup)(void *), void *opaque) diff --git a/tests-clar/clar.h b/tests-clar/clar.h index d92318bd4..c40bc7ac9 100644 --- a/tests-clar/clar.h +++ b/tests-clar/clar.h @@ -57,15 +57,18 @@ void cl_fixture_cleanup(const char *fixture_name); /** * Typed assertion macros */ -#define cl_assert_equal_s(s1,s2) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1) -#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1) +#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) +#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) -#define cl_assert_equal_i(i1,i2) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2, 1) -#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1) +#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) +#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) +#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) -#define cl_assert_equal_b(b1,b2) clar__assert_equal_i(!!(b1),!!(b2),__FILE__,__LINE__,#b1 " != " #b2, 1) +#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) -#define cl_assert_equal_p(p1,p2) cl_assert((p1) == (p2)) +#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) + +#define cl_assert_equal_sz(sz1,sz2) clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, (size_t)(sz1), (size_t)(sz2)) void clar__fail( const char *file, @@ -82,7 +85,12 @@ void clar__assert( const char *description, int should_abort); -void clar__assert_equal_s(const char *,const char *,const char *,int,const char *,int); -void clar__assert_equal_i(int,int,const char *,int,const char *,int); +void clar__assert_equal( + const char *file, + int line, + const char *err, + int should_abort, + const char *fmt, + ...); #endif diff --git a/tests-clar/clar/sandbox.h b/tests-clar/clar/sandbox.h index bed3011fe..ee7564148 100644 --- a/tests-clar/clar/sandbox.h +++ b/tests-clar/clar/sandbox.h @@ -18,9 +18,9 @@ static int find_tmp_path(char *buffer, size_t length) { #ifndef _WIN32 - static const size_t var_count = 4; + static const size_t var_count = 5; static const char *env_vars[] = { - "TMPDIR", "TMP", "TEMP", "USERPROFILE" + "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE" }; size_t i; @@ -43,6 +43,10 @@ find_tmp_path(char *buffer, size_t length) } #else + DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length); + if (env_len > 0 && env_len < (DWORD)length) + return 0; + if (GetTempPath((DWORD)length, buffer)) return 0; #endif @@ -61,9 +65,7 @@ static void clar_unsandbox(void) if (_clar_path[0] == '\0') return; -#ifdef _WIN32 chdir(".."); -#endif fs_rm(_clar_path); } diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c index de0e41bf7..340943ca8 100644 --- a/tests-clar/clar_libgit2.c +++ b/tests-clar/clar_libgit2.c @@ -56,23 +56,24 @@ void cl_git_rewritefile(const char *filename, const char *new_content) char *cl_getenv(const char *name) { - wchar_t name_utf16[GIT_WIN_PATH]; + git_win32_path name_utf16; DWORD alloc_len; wchar_t *value_utf16; char *value_utf8; - git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); + git_win32_path_from_c(name_utf16, name); alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0); if (alloc_len <= 0) return NULL; - alloc_len = GIT_WIN_PATH; cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t))); GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - cl_assert(value_utf8 = git__malloc(alloc_len)); - git__utf16_to_8(value_utf8, value_utf16); + alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */ + cl_assert(value_utf8 = git__calloc(alloc_len, 1)); + + git__utf16_to_8(value_utf8, alloc_len, value_utf16); git__free(value_utf16); @@ -81,13 +82,13 @@ char *cl_getenv(const char *name) int cl_setenv(const char *name, const char *value) { - wchar_t name_utf16[GIT_WIN_PATH]; - wchar_t value_utf16[GIT_WIN_PATH]; + git_win32_path name_utf16; + git_win32_path value_utf16; - git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); + git_win32_path_from_c(name_utf16, name); if (value) { - git__utf8_to_16(value_utf16, GIT_WIN_PATH, value); + git_win32_path_from_c(value_utf16, value); cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16)); } else { /* Windows XP returns 0 (failed) when passing NULL for lpValue when @@ -107,12 +108,12 @@ int cl_setenv(const char *name, const char *value) * the source is a directory, a child of the source). */ int cl_rename(const char *source, const char *dest) { - wchar_t source_utf16[GIT_WIN_PATH]; - wchar_t dest_utf16[GIT_WIN_PATH]; + git_win32_path source_utf16; + git_win32_path dest_utf16; unsigned retries = 1; - git__utf8_to_16(source_utf16, GIT_WIN_PATH, source); - git__utf8_to_16(dest_utf16, GIT_WIN_PATH, dest); + git_win32_path_from_c(source_utf16, source); + git_win32_path_from_c(dest_utf16, dest); while (!MoveFileW(source_utf16, dest_utf16)) { /* Only retry if the error is ERROR_ACCESS_DENIED; @@ -343,3 +344,13 @@ void cl_repo_set_bool(git_repository *repo, const char *cfg, int value) cl_git_pass(git_config_set_bool(config, cfg, value != 0)); git_config_free(config); } + +int cl_repo_get_bool(git_repository *repo, const char *cfg) +{ + int val = 0; + git_config *config; + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_get_bool(&val, config, cfg));; + git_config_free(config); + return val; +} diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h index 3fcf45a37..8dcfdee48 100644 --- a/tests-clar/clar_libgit2.h +++ b/tests-clar/clar_libgit2.h @@ -29,7 +29,22 @@ void cl_git_report_failure(int, const char *, int, const char *); -#define cl_assert_equal_sz(sz1,sz2) cl_assert_equal_i((int)sz1, (int)(sz2)) +#define cl_assert_at_line(expr,file,line) \ + clar__assert((expr) != 0, file, line, "Expression is not true: " #expr, NULL, 1) + +GIT_INLINE(void) clar__assert_in_range( + int lo, int val, int hi, + const char *file, int line, const char *err, int should_abort) +{ + if (lo > val || hi < val) { + char buf[128]; + snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi); + clar__fail(file, line, err, buf, should_abort); + } +} + +#define cl_assert_in_range(L,V,H) \ + clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1) /* * Some utility macros for building long strings @@ -71,5 +86,6 @@ int cl_git_remove_placeholders(const char *directory_path, const char *filename) /* config setting helpers */ void cl_repo_set_bool(git_repository *repo, const char *cfg, int value); +int cl_repo_get_bool(git_repository *repo, const char *cfg); #endif diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index 0bda6bcec..0d552d65e 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -1,6 +1,6 @@ #include "clar_libgit2.h" -static const char *_name = "remote.fancy.url"; +static const char *_name = "remote.ab.url"; void test_config_multivar__initialize(void) { @@ -46,20 +46,78 @@ static int cb(const git_config_entry *entry, void *data) return 0; } +static void check_get_multivar_foreach( + git_config *cfg, int expected, int expected_patterned) +{ + int n = 0; + + if (expected > 0) { + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); + cl_assert_equal_i(expected, n); + } else { + cl_assert_equal_i(GIT_ENOTFOUND, + git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); + } + + n = 0; + + if (expected_patterned > 0) { + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n)); + cl_assert_equal_i(expected_patterned, n); + } else { + cl_assert_equal_i(GIT_ENOTFOUND, + git_config_get_multivar_foreach(cfg, _name, "example", cb, &n)); + } +} + +static void check_get_multivar(git_config *cfg, int expected) +{ + git_config_iterator *iter; + git_config_entry *entry; + int n = 0; + + cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL)); + + while (git_config_next(&entry, iter) == 0) + n++; + + cl_assert_equal_i(expected, n); + git_config_iterator_free(iter); + +} + void test_config_multivar__get(void) { git_config *cfg; - int n; cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); + check_get_multivar_foreach(cfg, 2, 1); - n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); - cl_assert(n == 2); + /* add another that has the _name entry */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 1)); + check_get_multivar_foreach(cfg, 3, 2); - n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "example", cb, &n)); - cl_assert(n == 1); + /* add another that does not have the _name entry */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, 1)); + check_get_multivar_foreach(cfg, 3, 2); + + /* add another that does not have the _name entry at the end */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, 1)); + check_get_multivar_foreach(cfg, 3, 2); + + /* drop original file */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, 1)); + check_get_multivar_foreach(cfg, 1, 1); + + /* drop other file with match */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, 1)); + check_get_multivar_foreach(cfg, 0, 0); + + /* reload original file (add different place in order) */ + cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1)); + check_get_multivar_foreach(cfg, 2, 1); + + check_get_multivar(cfg, 2); git_config_free(cfg); } @@ -73,12 +131,12 @@ void test_config_multivar__add(void) cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); - cl_assert(n == 3); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); + cl_assert_equal_i(n, 3); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 1); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); + cl_assert_equal_i(n, 1); git_config_free(cfg); @@ -87,12 +145,12 @@ void test_config_multivar__add(void) cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); - cl_assert(n == 3); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); + cl_assert_equal_i(n, 3); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 1); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); + cl_assert_equal_i(n, 1); git_config_free(cfg); } @@ -107,8 +165,8 @@ void test_config_multivar__add_new(void) cl_git_pass(git_config_set_multivar(cfg, var, "", "variable")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, var, NULL, cb, &n)); - cl_assert(n == 1); + cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n)); + cl_assert_equal_i(n, 1); git_config_free(cfg); } @@ -121,13 +179,13 @@ void test_config_multivar__replace(void) cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); cl_assert(n == 2); cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); cl_assert(n == 2); git_config_free(cfg); @@ -135,7 +193,7 @@ void test_config_multivar__replace(void) cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n)); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); cl_assert(n == 2); git_config_free(cfg); @@ -150,16 +208,16 @@ void test_config_multivar__replace_multiple(void) cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 2); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); + cl_assert_equal_i(n, 2); git_config_free(cfg); cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 2); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); + cl_assert_equal_i(n, 2); git_config_free(cfg); } diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index 9f943d0f6..395f1cfdb 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -245,6 +245,37 @@ void test_config_read__foreach(void) git_config_free(cfg); } +void test_config_read__iterator(void) +{ + git_config *cfg; + git_config_iterator *iter; + git_config_entry *entry; + int count, ret; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + + count = 0; + cl_git_pass(git_config_iterator_new(&iter, cfg)); + + while ((ret = git_config_next(&entry, iter)) == 0) { + count++; + } + + git_config_iterator_free(iter); + cl_assert_equal_i(GIT_ITEROVER, ret); + cl_assert_equal_i(7, count); + + count = 3; + cl_git_pass(git_config_iterator_new(&iter, cfg)); + + git_config_iterator_free(iter); + git_config_free(cfg); +} + static int count_cfg_entries(const git_config_entry *entry, void *payload) { int *count = payload; @@ -288,6 +319,38 @@ void test_config_read__foreach_match(void) git_config_free(cfg); } +static void check_glob_iter(git_config *cfg, const char *regexp, int expected) +{ + git_config_iterator *iter; + git_config_entry *entry; + int count, error; + + cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp)); + + count = 0; + while ((error = git_config_next(&entry, iter)) == 0) + count++; + + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert_equal_i(expected, count); + git_config_iterator_free(iter); +} + +void test_config_read__iterator_glob(void) +{ + git_config *cfg; + + cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9"))); + + check_glob_iter(cfg, "core.*", 3); + check_glob_iter(cfg, "remote\\.ab.*", 2); + check_glob_iter(cfg, ".*url$", 2); + check_glob_iter(cfg, ".*dummy.*", 2); + check_glob_iter(cfg, ".*nomatch.*", 0); + + git_config_free(cfg); +} + void test_config_read__whitespace_not_required_around_assignment(void) { git_config *cfg; @@ -431,10 +494,10 @@ void test_config_read__simple_read_from_specific_level(void) git_config_free(cfg); } -static void clean_empty_config(void *unused) +static void clean_test_config(void *unused) { GIT_UNUSED(unused); - cl_fixture_cleanup("./empty"); + cl_fixture_cleanup("./testconfig"); } void test_config_read__can_load_and_parse_an_empty_config_file(void) @@ -442,10 +505,21 @@ void test_config_read__can_load_and_parse_an_empty_config_file(void) git_config *cfg; int i; - cl_set_cleanup(&clean_empty_config, NULL); - cl_git_mkfile("./empty", ""); - cl_git_pass(git_config_open_ondisk(&cfg, "./empty")); + cl_set_cleanup(&clean_test_config, NULL); + cl_git_mkfile("./testconfig", ""); + cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig")); cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither")); git_config_free(cfg); } + +void test_config_read__corrupt_header(void) +{ + git_config *cfg; + + cl_set_cleanup(&clean_test_config, NULL); + cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\""); + cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig")); + + git_config_free(cfg); +} diff --git a/tests-clar/config/validkeyname.c b/tests-clar/config/validkeyname.c index 03c13d723..33699737b 100644 --- a/tests-clar/config/validkeyname.c +++ b/tests-clar/config/validkeyname.c @@ -28,7 +28,7 @@ static void assert_invalid_config_key_name(const char *name) GIT_EINVALIDSPEC); cl_git_fail_with(git_config_delete_entry(cfg, name), GIT_EINVALIDSPEC); - cl_git_fail_with(git_config_get_multivar(cfg, name, "*", NULL, NULL), + cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL), GIT_EINVALIDSPEC); cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"), GIT_EINVALIDSPEC); diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c index d70612a97..57b02a7d9 100644 --- a/tests-clar/config/write.c +++ b/tests-clar/config/write.c @@ -242,3 +242,20 @@ void test_config_write__can_set_a_value_to_NULL(void) cl_git_sandbox_cleanup(); } + +void test_config_write__can_set_an_empty_value(void) +{ + git_repository *repository; + git_config *config; + const char * str; + + repository = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&config, repository)); + + cl_git_pass(git_config_set_string(config, "core.somevar", "")); + cl_git_pass(git_config_get_string(&str, config, "core.somevar")); + cl_assert_equal_s(str, ""); + + git_config_free(config); + cl_git_sandbox_cleanup(); +} diff --git a/tests-clar/core/bitvec.c b/tests-clar/core/bitvec.c new file mode 100644 index 000000000..48d7b99f0 --- /dev/null +++ b/tests-clar/core/bitvec.c @@ -0,0 +1,64 @@ +#include "clar_libgit2.h" +#include "bitvec.h" + +#if 0 +static void print_bitvec(git_bitvec *bv) +{ + int b; + + if (!bv->length) { + for (b = 63; b >= 0; --b) + fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0); + } else { + for (b = bv->length * 8; b >= 0; --b) + fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0); + } + fprintf(stderr, "\n"); +} +#endif + +static void set_some_bits(git_bitvec *bv, size_t length) +{ + size_t i; + + for (i = 0; i < length; ++i) { + if (i % 3 == 0 || i % 7 == 0) + git_bitvec_set(bv, i, true); + } +} + +static void check_some_bits(git_bitvec *bv, size_t length) +{ + size_t i; + + for (i = 0; i < length; ++i) + cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i)); +} + +void test_core_bitvec__0(void) +{ + git_bitvec bv; + + cl_git_pass(git_bitvec_init(&bv, 32)); + set_some_bits(&bv, 16); + check_some_bits(&bv, 16); + git_bitvec_clear(&bv); + set_some_bits(&bv, 32); + check_some_bits(&bv, 32); + git_bitvec_clear(&bv); + set_some_bits(&bv, 64); + check_some_bits(&bv, 64); + git_bitvec_free(&bv); + + cl_git_pass(git_bitvec_init(&bv, 128)); + set_some_bits(&bv, 32); + check_some_bits(&bv, 32); + set_some_bits(&bv, 128); + check_some_bits(&bv, 128); + git_bitvec_free(&bv); + + cl_git_pass(git_bitvec_init(&bv, 4000)); + set_some_bits(&bv, 4000); + check_some_bits(&bv, 4000); + git_bitvec_free(&bv); +} diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c index 3d8221e04..8a0b6711f 100644 --- a/tests-clar/core/buffer.c +++ b/tests-clar/core/buffer.c @@ -718,6 +718,8 @@ void test_core_buffer__classify_with_utf8(void) size_t data1len = 31; char *data2 = "Internal NUL!!!\000\n\nI see you!\n"; size_t data2len = 29; + char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n"; + size_t data3len = 20; git_buf b; b.ptr = data0; b.size = b.asize = data0len; @@ -731,13 +733,18 @@ void test_core_buffer__classify_with_utf8(void) b.ptr = data2; b.size = b.asize = data2len; cl_assert(git_buf_text_is_binary(&b)); cl_assert(git_buf_text_contains_nul(&b)); + + b.ptr = data3; b.size = b.asize = data3len; + cl_assert(!git_buf_text_is_binary(&b)); + cl_assert(!git_buf_text_contains_nul(&b)); } #define SIMILARITY_TEST_DATA_1 \ - "test data\nright here\ninline\ntada\nneeds more data\nlots of data\n" \ - "is this enough?\nthere has to be enough data to fill the hash array!\n" \ - "Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n" \ - "Let's make sure we've got plenty to go with here.\n smile \n" + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \ + "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \ + "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n" void test_core_buffer__similarity_metric(void) { @@ -761,15 +768,17 @@ void test_core_buffer__similarity_metric(void) cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1)); cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); cl_git_pass(git_buf_sets(&buf, - "Test data\nright here\ninline\ntada\nneeds more data\nlots of data\n" - "is this enough?\nthere has to be enough data to fill the hash array!\n" - "Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n" - "Let's make sure we've got plenty to go with here.\n smile \n")); + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \ + "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \ + "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n" + )); cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); sim = git_hashsig_compare(a, b); - cl_assert(95 < sim && sim < 100); /* expect >95% similarity */ + cl_assert_in_range(95, sim, 100); /* expect >95% similarity */ git_hashsig_free(a); git_hashsig_free(b); @@ -779,12 +788,13 @@ void test_core_buffer__similarity_metric(void) cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1)); cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1 - "and if I add some more, it should still be pretty similar, yes?\n")); + "050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n")); cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); sim = git_hashsig_compare(a, b); + /* 20% lines added ~= 10% lines changed */ - cl_assert(70 < sim && sim < 80); /* expect in the 70-80% similarity range */ + cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */ git_hashsig_free(a); git_hashsig_free(b); @@ -794,15 +804,19 @@ void test_core_buffer__similarity_metric(void) cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1)); cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); cl_git_pass(git_buf_sets(&buf, - "test data\nright here\ninline\ntada\nneeds more data\nlots of data\n" - "is this enough?\nthere has to be enough data to fill the hash array!\n" - "okay, that's half the original\nwhat else can we add?\nmore data\n" - "one more line will complete this\nshort\nlines\ndon't\nmatter\n")); + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "020x\n021\n022\n023\n024\n" \ + "x25\nx26\nx27\nx28\nx29\n" \ + "x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \ + "x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n" + )); cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); sim = git_hashsig_compare(a, b); + /* 50% lines changed */ - cl_assert(40 < sim && sim < 60); /* expect in the 40-60% similarity range */ + cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */ git_hashsig_free(a); git_hashsig_free(b); @@ -891,7 +905,7 @@ void test_core_buffer__similarity_metric_whitespace(void) if (i == j) cl_assert_equal_i(100, sim); else - cl_assert(sim < 30); /* expect pretty different */ + cl_assert_in_range(0, sim, 30); /* pretty different */ } else { cl_assert_equal_i(100, sim); } diff --git a/tests-clar/core/caps.c b/tests-clar/core/caps.c new file mode 100644 index 000000000..68a518ed7 --- /dev/null +++ b/tests-clar/core/caps.c @@ -0,0 +1,31 @@ +#include "clar_libgit2.h" + +void test_core_caps__0(void) +{ + int major, minor, rev, caps; + + git_libgit2_version(&major, &minor, &rev); + cl_assert_equal_i(LIBGIT2_VER_MAJOR, major); + cl_assert_equal_i(LIBGIT2_VER_MINOR, minor); + cl_assert_equal_i(LIBGIT2_VER_REVISION, rev); + + caps = git_libgit2_capabilities(); + +#ifdef GIT_THREADS + cl_assert((caps & GIT_CAP_THREADS) != 0); +#else + cl_assert((caps & GIT_CAP_THREADS) == 0); +#endif + +#if defined(GIT_SSL) || defined(GIT_WINHTTP) + cl_assert((caps & GIT_CAP_HTTPS) != 0); +#else + cl_assert((caps & GIT_CAP_HTTPS) == 0); +#endif + +#if defined(GIT_SSH) + cl_assert((caps & GIT_CAP_SSH) != 0); +#else + cl_assert((caps & GIT_CAP_SSH) == 0); +#endif +} diff --git a/tests-clar/core/mkdir.c b/tests-clar/core/mkdir.c index 1e50b4336..a969e4de2 100644 --- a/tests-clar/core/mkdir.c +++ b/tests-clar/core/mkdir.c @@ -115,9 +115,9 @@ static void check_mode(mode_t expected, mode_t actual) { #ifdef GIT_WIN32 /* chmod on Win32 doesn't support exec bit, not group/world bits */ - cl_assert((expected & 0600) == (actual & 0777)); + cl_assert_equal_i_fmt((expected & 0600), (actual & 0777), "%07o"); #else - cl_assert(expected == (actual & 0777)); + cl_assert_equal_i_fmt(expected, (actual & 0777), "%07o"); #endif } diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c index 407770baa..e584d6115 100644 --- a/tests-clar/core/path.c +++ b/tests-clar/core/path.c @@ -446,16 +446,15 @@ void test_core_path__14_apply_relative(void) cl_git_pass(git_path_apply_relative(&p, "../../../../../..")); cl_assert_equal_s("/this/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../../")); + cl_git_pass(git_path_apply_relative(&p, "../")); cl_assert_equal_s("/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../..")); - cl_assert_equal_s("/", p.ptr); + cl_git_fail(git_path_apply_relative(&p, "../../..")); cl_git_pass(git_buf_sets(&p, "d:/another/test")); - cl_git_pass(git_path_apply_relative(&p, "../../../../..")); + cl_git_pass(git_path_apply_relative(&p, "../..")); cl_assert_equal_s("d:/", p.ptr); cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/.")); @@ -473,8 +472,112 @@ void test_core_path__14_apply_relative(void) cl_git_pass(git_path_apply_relative(&p, "..")); cl_assert_equal_s("https://my.url.com/full/path/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../../")); + cl_git_pass(git_path_apply_relative(&p, "../../../")); cl_assert_equal_s("https://", p.ptr); + + cl_git_pass(git_buf_sets(&p, "../../this/is/relative")); + + cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix")); + cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr); + + cl_git_pass(git_path_apply_relative(&p, "../../../../that")); + cl_assert_equal_s("../../that", p.ptr); + + cl_git_pass(git_path_apply_relative(&p, "../there")); + cl_assert_equal_s("../../there", p.ptr); git_buf_free(&p); } + +static void assert_resolve_relative( + git_buf *buf, const char *expected, const char *path) +{ + cl_git_pass(git_buf_sets(buf, path)); + cl_git_pass(git_path_resolve_relative(buf, 0)); + cl_assert_equal_s(expected, buf->ptr); +} + +void test_core_path__15_resolve_relative(void) +{ + git_buf buf = GIT_BUF_INIT; + + assert_resolve_relative(&buf, "", ""); + assert_resolve_relative(&buf, "", "."); + assert_resolve_relative(&buf, "", "./"); + assert_resolve_relative(&buf, "..", ".."); + assert_resolve_relative(&buf, "../", "../"); + assert_resolve_relative(&buf, "..", "./.."); + assert_resolve_relative(&buf, "../", "./../"); + assert_resolve_relative(&buf, "../", "../."); + assert_resolve_relative(&buf, "../", ".././"); + assert_resolve_relative(&buf, "../..", "../.."); + assert_resolve_relative(&buf, "../../", "../../"); + + assert_resolve_relative(&buf, "/", "/"); + assert_resolve_relative(&buf, "/", "/."); + + assert_resolve_relative(&buf, "", "a/.."); + assert_resolve_relative(&buf, "", "a/../"); + assert_resolve_relative(&buf, "", "a/../."); + + assert_resolve_relative(&buf, "/a", "/a"); + assert_resolve_relative(&buf, "/a/", "/a/."); + assert_resolve_relative(&buf, "/", "/a/../"); + assert_resolve_relative(&buf, "/", "/a/../."); + assert_resolve_relative(&buf, "/", "/a/.././"); + + assert_resolve_relative(&buf, "a", "a"); + assert_resolve_relative(&buf, "a/", "a/"); + assert_resolve_relative(&buf, "a/", "a/."); + assert_resolve_relative(&buf, "a/", "a/./"); + + assert_resolve_relative(&buf, "a/b", "a//b"); + assert_resolve_relative(&buf, "a/b/c", "a/b/c"); + assert_resolve_relative(&buf, "b/c", "./b/c"); + assert_resolve_relative(&buf, "a/c", "a/./c"); + assert_resolve_relative(&buf, "a/b/", "a/b/."); + + assert_resolve_relative(&buf, "/a/b/c", "///a/b/c"); + assert_resolve_relative(&buf, "/", "////"); + assert_resolve_relative(&buf, "/a", "///a"); + assert_resolve_relative(&buf, "/", "///."); + assert_resolve_relative(&buf, "/", "///a/.."); + + assert_resolve_relative(&buf, "../../path", "../../test//../././path"); + assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d"); + + cl_git_pass(git_buf_sets(&buf, "/..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/./..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/.//..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/../.")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/../.././../a")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "////..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + /* things that start with Windows network paths */ +#ifdef GIT_WIN32 + assert_resolve_relative(&buf, "//a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "//a/", "//a/b/.."); + assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c"); + + cl_git_pass(git_buf_sets(&buf, "//a/b/../..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); +#else + assert_resolve_relative(&buf, "/a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "/a/", "//a/b/.."); + assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c"); + assert_resolve_relative(&buf, "/", "//a/b/../.."); +#endif + + git_buf_free(&buf); +} diff --git a/tests-clar/core/posix.c b/tests-clar/core/posix.c new file mode 100644 index 000000000..1cef937cd --- /dev/null +++ b/tests-clar/core/posix.c @@ -0,0 +1,99 @@ +#ifndef _WIN32 +# include <arpa/inet.h> +# include <sys/socket.h> +# include <netinet/in.h> +#else +# include <ws2tcpip.h> +# ifdef _MSC_VER +# pragma comment(lib, "ws2_32") +# endif +#endif + +#include "clar_libgit2.h" +#include "posix.h" + +void test_core_posix__initialize(void) +{ +#ifdef GIT_WIN32 + /* on win32, the WSA context needs to be initialized + * before any socket calls can be performed */ + WSADATA wsd; + + cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd)); + cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2); +#endif +} + +static bool supports_ipv6(void) +{ +#ifdef GIT_WIN32 + /* IPv6 is supported on Vista and newer */ + return git_has_win32_version(6, 0, 0); +#else + return 1; +#endif +} + +void test_core_posix__inet_pton(void) +{ + struct in_addr addr; + struct in6_addr addr6; + size_t i; + + struct in_addr_data { + const char *p; + const uint8_t n[4]; + }; + + struct in6_addr_data { + const char *p; + const uint8_t n[16]; + }; + + static struct in_addr_data in_addr_data[] = { + { "0.0.0.0", { 0, 0, 0, 0 } }, + { "10.42.101.8", { 10, 42, 101, 8 } }, + { "127.0.0.1", { 127, 0, 0, 1 } }, + { "140.177.10.12", { 140, 177, 10, 12 } }, + { "204.232.175.90", { 204, 232, 175, 90 } }, + { "255.255.255.255", { 255, 255, 255, 255 } }, + }; + + static struct in6_addr_data in6_addr_data[] = { + { "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }, + { "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }, + { "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } }, + { "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } }, + { "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } }, + }; + + /* Test some ipv4 addresses */ + for (i = 0; i < 6; i++) { + cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1); + cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0); + } + + /* Test some ipv6 addresses */ + if (supports_ipv6()) + { + for (i = 0; i < 6; i++) { + cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1); + cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0); + } + } + + /* Test some invalid strings */ + cl_assert(p_inet_pton(AF_INET, "", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0); + + /* Test unsupported address families */ + cl_git_fail(p_inet_pton(12, "52.472", NULL)); /* AF_DECnet */ + cl_assert_equal_i(EAFNOSUPPORT, errno); + + cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */ + cl_assert_equal_i(EAFNOSUPPORT, errno); +} diff --git a/tests-clar/core/sortedcache.c b/tests-clar/core/sortedcache.c new file mode 100644 index 000000000..c1869bee0 --- /dev/null +++ b/tests-clar/core/sortedcache.c @@ -0,0 +1,363 @@ +#include "clar_libgit2.h" +#include "sortedcache.h" + +static int name_only_cmp(const void *a, const void *b) +{ + return strcmp(a, b); +} + +void test_core_sortedcache__name_only(void) +{ + git_sortedcache *sc; + void *item; + size_t pos; + + cl_git_pass(git_sortedcache_new( + &sc, 0, NULL, NULL, name_only_cmp, NULL)); + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "iii")); + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(5, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL); + cl_assert_equal_s("aaa", item); + cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL); + cl_assert_equal_s("mmm", item); + cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL); + cl_assert_equal_s("zzz", item); + cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bbb", item); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("iii", item); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("mmm", item); + cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL); + cl_assert_equal_s("zzz", item); + cl_assert(git_sortedcache_entry(sc, 5) == NULL); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii")); + cl_assert_equal_sz(2, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz")); + cl_assert_equal_sz(4, pos); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc")); + + git_sortedcache_clear(sc, true); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + + git_sortedcache_free(sc); +} + +typedef struct { + int value; + char smaller_value; + char path[GIT_FLEX_ARRAY]; +} sortedcache_test_struct; + +static int sortedcache_test_struct_cmp(const void *a_, const void *b_) +{ + const sortedcache_test_struct *a = a_, *b = b_; + return strcmp(a->path, b->path); +} + +static void sortedcache_test_struct_free(void *payload, void *item_) +{ + sortedcache_test_struct *item = item_; + int *count = payload; + (*count)++; + item->smaller_value = 0; +} + +void test_core_sortedcache__in_memory(void) +{ + git_sortedcache *sc; + sortedcache_test_struct *item; + int free_count = 0; + + cl_git_pass(git_sortedcache_new( + &sc, offsetof(sortedcache_test_struct, path), + sortedcache_test_struct_free, &free_count, + sortedcache_test_struct_cmp, NULL)); + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa")); + item->value = 10; + item->smaller_value = 1; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb")); + item->value = 20; + item->smaller_value = 2; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz")); + item->value = 30; + item->smaller_value = 26; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm")); + item->value = 40; + item->smaller_value = 14; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii")); + item->value = 50; + item->smaller_value = 9; + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(5, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_rlock(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL); + cl_assert_equal_s("mmm", item->path); + cl_assert_equal_i(40, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "abc") == NULL); + + /* not on Windows: + * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one + */ + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bbb", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("iii", item->path); + cl_assert_equal_i(50, item->value); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("mmm", item->path); + cl_assert_equal_i(40, item->value); + cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_entry(sc, 5) == NULL); + + git_sortedcache_runlock(sc); + /* git_sortedcache_runlock(sc); */ + + cl_assert_equal_i(0, free_count); + + git_sortedcache_clear(sc, true); + + cl_assert_equal_i(5, free_count); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + + free_count = 0; + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing")); + item->value = 10; + item->smaller_value = 3; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again")); + item->value = 20; + item->smaller_value = 1; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final")); + item->value = 30; + item->smaller_value = 2; + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(3, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL); + cl_assert_equal_s("testing", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL); + cl_assert_equal_s("again", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("again", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(30, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("testing", item->path); + cl_assert_equal_i(10, item->value); + cl_assert(git_sortedcache_entry(sc, 3) == NULL); + + { + size_t pos; + + cl_git_pass(git_sortedcache_wlock(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again")); + + cl_assert_equal_sz(2, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing")); + cl_assert_equal_sz(1, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing")); + + cl_assert_equal_sz(1, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final")); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + + git_sortedcache_wunlock(sc); + } + + git_sortedcache_free(sc); + + cl_assert_equal_i(3, free_count); +} + +static void sortedcache_test_reload(git_sortedcache *sc) +{ + int count = 0; + git_buf buf = GIT_BUF_INIT; + char *scan, *after; + sortedcache_test_struct *item; + + cl_assert(git_sortedcache_lockandload(sc, &buf) > 0); + + git_sortedcache_clear(sc, false); /* clear once we already have lock */ + + for (scan = buf.ptr; *scan; scan = after + 1) { + int val = strtol(scan, &after, 0); + cl_assert(after > scan); + scan = after; + + for (scan = after; git__isspace(*scan); ++scan) /* find start */; + for (after = scan; *after && *after != '\n'; ++after) /* find eol */; + *after = '\0'; + + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan)); + + item->value = val; + item->smaller_value = (char)(count++); + } + + git_sortedcache_wunlock(sc); + + git_buf_free(&buf); +} + +void test_core_sortedcache__on_disk(void) +{ + git_sortedcache *sc; + sortedcache_test_struct *item; + int free_count = 0; + size_t pos; + + cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n"); + + cl_git_pass(git_sortedcache_new( + &sc, offsetof(sortedcache_test_struct, path), + sortedcache_test_struct_free, &free_count, + sortedcache_test_struct_cmp, "cacheitems.txt")); + + /* should need to reload the first time */ + + sortedcache_test_reload(sc); + + /* test what we loaded */ + + cl_assert_equal_sz(3, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL); + cl_assert_equal_s("cde", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bcd", item->path); + cl_assert_equal_i(20, item->value); + cl_assert(git_sortedcache_entry(sc, 3) == NULL); + + /* should not need to reload this time */ + + cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL)); + + /* rewrite ondisk file and reload */ + + cl_assert_equal_i(0, free_count); + + cl_git_rewritefile( + "cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n"); + sortedcache_test_reload(sc); + + cl_assert_equal_i(3, free_count); + + /* test what we loaded */ + + cl_assert_equal_sz(4, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(100, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(10, item->value); + cl_assert(git_sortedcache_lookup(sc, "cde") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(500, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(200, item->value); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc")); + cl_assert_equal_sz(1, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final")); + cl_assert_equal_sz(2, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz")); + cl_assert_equal_sz(3, pos); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing")); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde")); + + git_sortedcache_free(sc); + + cl_assert_equal_i(7, free_count); +} + diff --git a/tests-clar/core/string.c b/tests-clar/core/string.c index bf6ec0a80..ec9575685 100644 --- a/tests-clar/core/string.c +++ b/tests-clar/core/string.c @@ -26,3 +26,16 @@ void test_core_string__1(void) cl_assert(git__suffixcmp("zaz", "ac") > 0); } +/* compare icase sorting with case equality */ +void test_core_string__2(void) +{ + cl_assert(git__strcasesort_cmp("", "") == 0); + cl_assert(git__strcasesort_cmp("foo", "foo") == 0); + cl_assert(git__strcasesort_cmp("foo", "bar") > 0); + cl_assert(git__strcasesort_cmp("bar", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "FOO") > 0); + cl_assert(git__strcasesort_cmp("FOO", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "BAR") > 0); + cl_assert(git__strcasesort_cmp("BAR", "foo") < 0); + cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); +} diff --git a/tests-clar/core/vector.c b/tests-clar/core/vector.c index c9e43a149..db52c004f 100644 --- a/tests-clar/core/vector.c +++ b/tests-clar/core/vector.c @@ -54,7 +54,7 @@ void test_core_vector__2(void) cl_git_pass(git_vector_insert(&x, ptrs[1])); cl_assert(x.length == 5); - git_vector_uniq(&x); + git_vector_uniq(&x, NULL); cl_assert(x.length == 2); git_vector_free(&x); diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c index b12186d98..42b9fcd5f 100644 --- a/tests-clar/diff/blob.c +++ b/tests-clar/diff/blob.c @@ -6,6 +6,20 @@ static diff_expects expected; static git_diff_options opts; static git_blob *d, *alien; +static void quick_diff_blob_to_str( + const git_blob *blob, const char *blob_path, + const char *str, size_t len, const char *str_path) +{ + memset(&expected, 0, sizeof(expected)); + + if (str && !len) + len = strlen(str); + + cl_git_pass(git_diff_blob_to_buffer( + blob, blob_path, str, len, str_path, + &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); +} + void test_diff_blob__initialize(void) { git_oid oid; @@ -59,7 +73,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test1 */ cl_git_pass(git_diff_blobs( - a, b, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, NULL, b, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -74,7 +89,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test2 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - b, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + b, NULL, c, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -89,7 +105,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test3 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - a, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, NULL, c, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -103,7 +120,8 @@ void test_diff_blob__can_compare_text_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - c, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + c, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -125,6 +143,7 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_blob *a, *b, *c; git_oid a_oid, b_oid, c_oid; git_diff_patch *p; + const git_diff_delta *delta; size_t tc, ta, td; /* tests/resources/attr/root_test1 */ @@ -142,10 +161,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) /* Doing the equivalent of a `git diff -U1` on these files */ /* diff on tests/resources/attr/root_test1 */ - cl_git_pass(git_diff_patch_from_blobs(&p, a, b, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, b, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -157,10 +184,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* diff on tests/resources/attr/root_test2 */ - cl_git_pass(git_diff_patch_from_blobs(&p, b, c, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, b, NULL, c, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -172,12 +207,17 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* diff on tests/resources/attr/root_test3 */ - cl_git_pass(git_diff_patch_from_blobs(&p, a, c, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, c, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); - cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); - cl_assert_equal_i(13, git_diff_patch_num_lines_in_hunk(p, 0)); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); cl_assert_equal_i(0, (int)tc); @@ -187,10 +227,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* one more */ - cl_git_pass(git_diff_patch_from_blobs(&p, c, d, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, c, NULL, d, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); + cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0)); cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1)); @@ -212,7 +260,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) git_blob *e = NULL; cl_git_pass(git_diff_blobs( - d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, e, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]); @@ -227,7 +276,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, e, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]); @@ -242,7 +292,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - alien, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, NULL, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -253,7 +304,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - NULL, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + NULL, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -266,13 +318,22 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) { git_blob *e = NULL; git_diff_patch *p; + const git_diff_delta *delta; int line; char origin; - cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size); + cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert_equal_sz(0, delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -286,10 +347,18 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) opts.flags |= GIT_DIFF_REVERSE; - cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, delta->status); + cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert_equal_sz(0, delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -303,20 +372,28 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) opts.flags ^= GIT_DIFF_REVERSE; - cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); - cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, NULL, alien, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); - cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, delta->status); + cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); @@ -332,44 +409,66 @@ static void assert_identical_blobs_comparison(diff_expects *expected) void test_diff_blob__can_compare_identical_blobs(void) { + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_diff_blobs( - d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - cl_assert_equal_i(0, expected.files_binary); assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(0, expected.files_binary); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - NULL, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + NULL, NULL, NULL, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_identical_blobs_comparison(&expected); cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(0, expected.files); /* NULLs mean no callbacks, period */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - alien, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - cl_assert(expected.files_binary > 0); assert_identical_blobs_comparison(&expected); + cl_assert(expected.files_binary > 0); } void test_diff_blob__can_compare_identical_blobs_with_patch(void) { git_diff_patch *p; + const git_diff_delta *delta; - cl_git_pass(git_diff_patch_from_blobs(&p, d, d, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, d, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); + cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d)); + cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d)); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); + cl_assert_equal_sz(0, delta->old_file.size); + cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert_equal_sz(0, delta->new_file.size); + cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, alien, alien, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); @@ -396,14 +495,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4)); cl_git_pass(git_diff_blobs( - alien, heart, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, heart, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - heart, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + heart, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -413,14 +514,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void) { cl_git_pass(git_diff_blobs( - alien, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); } @@ -461,7 +564,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) /* Test with default inter-hunk-context (not set) => default is 0 */ cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -469,7 +573,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) opts.interhunk_lines = 0; memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -477,7 +582,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) opts.interhunk_lines = 1; memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.hunks); @@ -490,7 +596,8 @@ void test_diff_blob__checks_options_version_too_low(void) opts.version = 0; cl_git_fail(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -501,7 +608,8 @@ void test_diff_blob__checks_options_version_too_high(void) opts.version = 1024; cl_git_fail(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -548,10 +656,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void) cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4)); /* diff from blob a to content of b */ - cl_git_pass(git_diff_blob_to_buffer( - a, b_content, strlen(b_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, b_content, 0, NULL); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(0, expected.files_binary); @@ -562,37 +667,25 @@ void test_diff_blob__can_compare_blob_to_buffer(void) cl_assert_equal_i(0, expected.line_dels); /* diff from blob a to content of a */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, a_content, strlen(a_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + quick_diff_blob_to_str(a, NULL, a_content, 0, NULL); assert_identical_blobs_comparison(&expected); /* diff from NULL blob to content of a */ memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - NULL, a_content, strlen(a_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); /* diff from blob a to NULL buffer */ memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, NULL, 0, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, NULL, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED); /* diff with reverse */ opts.flags ^= GIT_DIFF_REVERSE; memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, NULL, 0, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, NULL, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); git_blob_free(a); @@ -613,7 +706,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from blob a to content of b */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, b_content, strlen(b_content), &opts)); + &p, a, NULL, b_content, strlen(b_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); @@ -628,8 +721,9 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) git_diff_patch_free(p); /* diff from blob a to content of a */ + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, a_content, strlen(a_content), &opts)); + &p, a, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); @@ -637,7 +731,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from NULL blob to content of a */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, NULL, a_content, strlen(a_content), &opts)); + &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -646,7 +740,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from blob a to NULL buffer */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, NULL, 0, &opts)); + &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -657,7 +751,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) opts.flags ^= GIT_DIFF_REVERSE; cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, NULL, 0, &opts)); + &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -684,6 +778,8 @@ void test_diff_blob__binary_data_comparisons(void) const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"; size_t bin_len = 33; + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8)); cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4)); @@ -692,44 +788,32 @@ void test_diff_blob__binary_data_comparisons(void) /* non-binary to reference content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(0, expected.files_binary); /* binary to reference content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(1, expected.files_binary); /* non-binary to binary content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL); assert_binary_blobs_comparison(&expected); /* binary to non-binary content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL); assert_binary_blobs_comparison(&expected); /* non-binary to binary blob */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - bin, nonbin, &opts, + bin, NULL, nonbin, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -739,27 +823,18 @@ void test_diff_blob__binary_data_comparisons(void) opts.flags |= GIT_DIFF_FORCE_TEXT; - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); assert_identical_blobs_comparison(&expected); - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL); assert_one_modified_with_lines(&expected, 4); - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL); assert_one_modified_with_lines(&expected, 4); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - bin, nonbin, &opts, + bin, NULL, nonbin, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified_with_lines(&expected, 4); @@ -767,3 +842,227 @@ void test_diff_blob__binary_data_comparisons(void) git_blob_free(bin); git_blob_free(nonbin); } + +void test_diff_blob__using_path_and_attributes(void) +{ + git_config *cfg; + git_blob *bin, *nonbin; + git_oid oid; + const char *nonbin_content = "Hello from the root\n"; + const char *bin_content = + "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"; + size_t bin_len = 33; + const char *changed; + git_diff_patch *p; + char *pout; + + /* set up custom diff drivers and 'diff' attribute mappings for them */ + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1)); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]")); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]")); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_numctx.funcname", "^[0-9]")); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_textnum.funcname", "^[0-9]")); + git_config_free(cfg); + + cl_git_append2file( + "attr/.gitattributes", + "\n\n# test_diff_blob__using_path_and_attributes extra\n\n" + "*.binary diff=iam_binary\n" + "*.textary diff=iam_text\n" + "*.alphary diff=iam_alphactx\n" + "*.textalphary diff=iam_textalpha\n" + "*.textnumary diff=iam_textnum\n" + "*.numary diff=iam_numctx\n\n"); + + opts.context_lines = 0; + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8)); + cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4)); + /* 20b: "Hello from the root\n" */ + + cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8)); + cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4)); + /* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */ + + /* non-binary to reference content */ + + quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL); + assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(0, expected.files_binary); + + /* binary to reference content */ + + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); + assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(1, expected.files_binary); + + /* add some text */ + + changed = "Hello from the root\nMore lines\nAnd more\nGo here\n"; + + quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, expected.files_binary); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(0, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.normal b/zzz.normal\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.normal\n" + "+++ b/zzz.normal\n" + "@@ -1,0 +2,3 @@ Hello from the root\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.binary b/zzz.binary\n" + "index 45141a7..75b0dbb 100644\n" + "Binary files a/zzz.binary and b/zzz.binary differ\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.alphary b/zzz.alphary\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.alphary\n" + "+++ b/zzz.alphary\n" + "@@ -1,0 +2,3 @@ Hello from the root\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.numary b/zzz.numary\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.numary\n" + "+++ b/zzz.numary\n" + "@@ -1,0 +2,3 @@\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n" + * 33 bytes + */ + + changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n"; + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.normal", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.normal b/zzz.normal\n" + "index b435cd5..1604519 100644\n" + "Binary files a/zzz.normal and b/zzz.normal differ\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textary b/zzz.textary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textary\n" + "+++ b/zzz.textary\n" + "@@ -3 +3 @@\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textalphary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textalphary b/zzz.textalphary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textalphary\n" + "+++ b/zzz.textalphary\n" + "@@ -3 +3 @@\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textnumary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textnumary b/zzz.textnumary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textnumary\n" + "+++ b/zzz.textnumary\n" + "@@ -3 +3 @@ 0123456789\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + git_blob_free(nonbin); + git_blob_free(bin); +} diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index 4e23792a6..a5c322b4d 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -70,8 +70,9 @@ int diff_hunk_cb( diff_expects *e = payload; GIT_UNUSED(delta); - GIT_UNUSED(header); - GIT_UNUSED(header_len); + + /* confirm no NUL bytes in header text */ + while (header_len--) cl_assert('\0' != *header++); e->hunks++; e->hunk_old_lines += range->old_lines; diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c index 06ab2ff14..e02dd5c68 100644 --- a/tests-clar/diff/drivers.c +++ b/tests-clar/diff/drivers.c @@ -123,3 +123,34 @@ void test_diff_drivers__patterns(void) git_tree_free(one); } +void test_diff_drivers__long_lines(void) +{ + const char *base = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nisi ligula. Ut viverra enim sed lobortis suscipit.\nPhasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissim risus. Suspendisse at nisi quis turpis fringilla rutrum id sit amet nulla.\nNam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\nMauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\nAliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n"; + git_index *idx; + git_diff_list *diff; + git_diff_patch *patch; + char *actual; + const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n"; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_mkfile("empty_standard_repo/longlines.txt", base); + cl_git_pass(git_repository_index(&idx, g_repo)); + cl_git_pass(git_index_add_bypath(idx, "longlines.txt")); + cl_git_pass(git_index_write(idx)); + git_index_free(idx); + + cl_git_append2file("empty_standard_repo/longlines.txt", "newline\nnewline\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); + cl_assert_equal_sz(1, git_diff_num_deltas(diff)); + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&actual, patch)); + + cl_assert_equal_s(expected, actual); + + free(actual); + git_diff_patch_free(patch); + git_diff_list_free(diff); +} + diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 3f14a0de7..6a33fa990 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -128,6 +128,11 @@ void test_diff_patch__to_string(void) cl_assert_equal_s(expected, text); + cl_assert_equal_sz(31, git_diff_patch_size(patch, 0, 0, 0)); + cl_assert_equal_sz(31, git_diff_patch_size(patch, 1, 0, 0)); + cl_assert_equal_sz(31 + 16, git_diff_patch_size(patch, 1, 1, 0)); + cl_assert_equal_sz(strlen(expected), git_diff_patch_size(patch, 1, 1, 1)); + git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); @@ -408,7 +413,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) static void check_single_patch_stats( git_repository *repo, size_t hunks, - size_t adds, size_t dels, size_t ctxt, + size_t adds, size_t dels, size_t ctxt, size_t *sizes, const char *expected) { git_diff_list *diff; @@ -437,6 +442,18 @@ static void check_single_patch_stats( cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected, text); git__free(text); + + cl_assert_equal_sz( + strlen(expected), git_diff_patch_size(patch, 1, 1, 1)); + } + + if (sizes) { + if (sizes[0]) + cl_assert_equal_sz(sizes[0], git_diff_patch_size(patch, 0, 0, 0)); + if (sizes[1]) + cl_assert_equal_sz(sizes[1], git_diff_patch_size(patch, 1, 0, 0)); + if (sizes[2]) + cl_assert_equal_sz(sizes[2], git_diff_patch_size(patch, 1, 1, 0)); } /* walk lines in hunk with basic sanity checks */ @@ -481,6 +498,23 @@ void test_diff_patch__line_counts_with_eofnl(void) git_buf content = GIT_BUF_INIT; const char *end; git_index *index; + const char *expected = + /* below is pasted output of 'git diff' with fn context removed */ + "diff --git a/songof7cities.txt b/songof7cities.txt\n" + "index 378a7d9..3d0154e 100644\n" + "--- a/songof7cities.txt\n" + "+++ b/songof7cities.txt\n" + "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n" + " \n" + " To the sound of trumpets shall their seed restore my Cities\n" + " Wealthy and well-weaponed, that once more may I behold\n" + "-All the world go softly when it walks before my Cities,\n" + "+#All the world go softly when it walks before my Cities,\n" + " And the horses and the chariots fleeing from them as of old!\n" + " \n" + " -- Rudyard Kipling\n" + "\\ No newline at end of file\n"; + size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 }; g_repo = cl_git_sandbox_init("renames"); @@ -495,14 +529,14 @@ void test_diff_patch__line_counts_with_eofnl(void) git_buf_consume(&content, end); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL); + check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL); /* remove trailing whitespace */ git_buf_rtrim(&content); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL); + check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL); /* add trailing whitespace */ @@ -514,7 +548,7 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_pass(git_buf_putc(&content, '\n')); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL); + check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL); /* no trailing whitespace as context line */ @@ -537,22 +571,7 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_rewritefile("renames/songof7cities.txt", content.ptr); check_single_patch_stats( - g_repo, 1, 1, 1, 6, - /* below is pasted output of 'git diff' with fn context removed */ - "diff --git a/songof7cities.txt b/songof7cities.txt\n" - "index 378a7d9..3d0154e 100644\n" - "--- a/songof7cities.txt\n" - "+++ b/songof7cities.txt\n" - "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n" - " \n" - " To the sound of trumpets shall their seed restore my Cities\n" - " Wealthy and well-weaponed, that once more may I behold\n" - "-All the world go softly when it walks before my Cities,\n" - "+#All the world go softly when it walks before my Cities,\n" - " And the horses and the chariots fleeing from them as of old!\n" - " \n" - " -- Rudyard Kipling\n" - "\\ No newline at end of file\n"); + g_repo, 1, 1, 1, 6, expected_sizes, expected); git_buf_free(&content); git_config_free(cfg); diff --git a/tests-clar/diff/pathspec.c b/tests-clar/diff/pathspec.c new file mode 100644 index 000000000..7b15ea04c --- /dev/null +++ b/tests-clar/diff/pathspec.c @@ -0,0 +1,93 @@ +#include "clar_libgit2.h" +#include "diff_helpers.h" + +static git_repository *g_repo = NULL; + +void test_diff_pathspec__initialize(void) +{ + g_repo = cl_git_sandbox_init("status"); +} + +void test_diff_pathspec__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_diff_pathspec__0(void) +{ + const char *a_commit = "26a125ee"; /* the current HEAD */ + const char *b_commit = "0017bd4a"; /* the start */ + git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); + git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit); + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + git_strarray paths = { NULL, 1 }; + char *path; + git_pathspec *ps; + git_pathspec_match_list *matches; + + cl_assert(a); + cl_assert(b); + + path = "*_file"; + paths.strings = &path; + cl_git_pass(git_pathspec_new(&ps, &paths)); + + cl_git_pass(git_pathspec_match_tree(&matches, a, GIT_PATHSPEC_DEFAULT, ps)); + cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(matches,0)); + cl_assert(git_pathspec_match_list_diff_entry(matches,0) == NULL); + git_pathspec_match_list_free(matches); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, NULL, a, &opts)); + + cl_git_pass(git_pathspec_match_diff( + &matches, diff, GIT_PATHSPEC_DEFAULT, ps)); + cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches)); + cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL); + cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL); + cl_assert_equal_s("current_file", + git_pathspec_match_list_diff_entry(matches,0)->new_file.path); + cl_assert_equal_i(GIT_DELTA_ADDED, + (int)git_pathspec_match_list_diff_entry(matches,0)->status); + git_pathspec_match_list_free(matches); + + git_diff_list_free(diff); + diff = NULL; + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); + + cl_git_pass(git_pathspec_match_diff( + &matches, diff, GIT_PATHSPEC_DEFAULT, ps)); + cl_assert_equal_i(3, (int)git_pathspec_match_list_entrycount(matches)); + cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL); + cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL); + cl_assert_equal_s("subdir/current_file", + git_pathspec_match_list_diff_entry(matches,0)->new_file.path); + cl_assert_equal_i(GIT_DELTA_DELETED, + (int)git_pathspec_match_list_diff_entry(matches,0)->status); + git_pathspec_match_list_free(matches); + + git_diff_list_free(diff); + diff = NULL; + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts)); + + cl_git_pass(git_pathspec_match_diff( + &matches, diff, GIT_PATHSPEC_DEFAULT, ps)); + cl_assert_equal_i(4, (int)git_pathspec_match_list_entrycount(matches)); + cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL); + cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL); + cl_assert_equal_s("modified_file", + git_pathspec_match_list_diff_entry(matches,0)->new_file.path); + cl_assert_equal_i(GIT_DELTA_MODIFIED, + (int)git_pathspec_match_list_diff_entry(matches,0)->status); + git_pathspec_match_list_free(matches); + + git_diff_list_free(diff); + diff = NULL; + + git_tree_free(a); + git_tree_free(b); + git_pathspec_free(ps); +} diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index fd31a3859..b5a9935fd 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -236,6 +236,8 @@ void test_diff_rename__not_exact_match(void) &diff, g_repo, old_tree, new_tree, &diffopts)); opts.flags = GIT_DIFF_FIND_ALL; + opts.break_rewrite_threshold = 70; + cl_git_pass(git_diff_find_similar(diff, &opts)); memset(&exp, 0, sizeof(exp)); @@ -312,8 +314,8 @@ void test_diff_rename__not_exact_match(void) /* the default match algorithm is going to find the internal * whitespace differences in the lines of sixserving.txt to be - * significant enough that this will decide to split it into - * an ADD and a DELETE + * significant enough that this will decide to split it into an ADD + * and a DELETE */ memset(&exp, 0, sizeof(exp)); @@ -480,6 +482,7 @@ void test_diff_rename__working_directory_changes(void) cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE; + opts.rename_threshold = 70; cl_git_pass(git_diff_find_similar(diff, &opts)); memset(&exp, 0, sizeof(exp)); @@ -516,7 +519,7 @@ void test_diff_rename__working_directory_changes(void) cl_git_pass(git_oid_fromstr(&id, blobsha)); cl_git_pass(git_blob_lookup(&blob, g_repo, &id)); cl_git_pass(git_buf_set( - &content, git_blob_rawcontent(blob), git_blob_rawsize(blob))); + &content, git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob))); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); git_blob_free(blob); @@ -650,6 +653,59 @@ void test_diff_rename__file_exchange(void) git_buf_free(&c2); } +void test_diff_rename__file_exchange_three(void) +{ + git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT, c3 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt")); + cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt")); + cl_git_pass(git_futils_readbuffer(&c3, "renames/ikeepsix.txt")); + + cl_git_pass(git_futils_writebuffer(&c1, "renames/ikeepsix.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c3, "renames/songof7cities.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt")); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); + git_buf_free(&c2); + git_buf_free(&c3); +} + void test_diff_rename__file_partial_exchange(void) { git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; @@ -702,7 +758,7 @@ void test_diff_rename__file_partial_exchange(void) git_buf_free(&c2); } -void test_diff_rename__file_split(void) +void test_diff_rename__rename_and_copy_from_same_source(void) { git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; git_index *index; @@ -815,6 +871,8 @@ void test_diff_rename__from_deleted_to_split(void) struct rename_expected { size_t len; + + unsigned int *status; const char **sources; const char **targets; @@ -825,9 +883,11 @@ int test_names_expected(const git_diff_delta *delta, float progress, void *p) { struct rename_expected *expected = p; + GIT_UNUSED(progress); + cl_assert(expected->idx < expected->len); - cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED); + cl_assert_equal_i(delta->status, expected->status[expected->idx]); cl_assert(git__strcmp(expected->sources[expected->idx], delta->old_file.path) == 0); @@ -849,9 +909,10 @@ void test_diff_rename__rejected_match_can_match_others(void) git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT; + unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED }; const char *sources[] = { "Class1.cs", "Class2.cs" }; const char *targets[] = { "ClassA.cs", "ClassB.cs" }; - struct rename_expected expect = { 2, sources, targets }; + struct rename_expected expect = { 2, status, sources, targets }; char *ptr; opts.checkout_strategy = GIT_CHECKOUT_FORCE; @@ -892,11 +953,13 @@ void test_diff_rename__rejected_match_can_match_others(void) cl_git_pass( git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); cl_git_pass( git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + git_diff_list_free(diff); git_tree_free(tree); git_index_free(index); git_reference_free(head); @@ -904,3 +967,317 @@ void test_diff_rename__rejected_match_can_match_others(void) git_buf_free(&one); git_buf_free(&two); } + +static void write_similarity_file_two(const char *filename, size_t b_lines) +{ + git_buf contents = GIT_BUF_INIT; + size_t i; + + for (i = 0; i < b_lines; i++) + git_buf_printf(&contents, "%02d - bbbbb\r\n", (int)(i+1)); + + for (i = b_lines; i < 50; i++) + git_buf_printf(&contents, "%02d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n")); + + cl_git_pass( + git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777)); + + git_buf_free(&contents); +} + +void test_diff_rename__rejected_match_can_match_others_two(void) +{ + git_reference *head, *selfsimilar; + git_index *index; + git_tree *tree; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED }; + const char *sources[] = { "a.txt", "b.txt" }; + const char *targets[] = { "c.txt", "d.txt" }; + struct rename_expected expect = { 2, status, sources, targets }; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_set_target( + &selfsimilar, head, "refs/heads/renames_similar_two")); + cl_git_pass(git_checkout_head(g_repo, &opts)); + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(p_unlink("renames/a.txt")); + cl_git_pass(p_unlink("renames/b.txt")); + + cl_git_pass(git_index_remove_bypath(index, "a.txt")); + cl_git_pass(git_index_remove_bypath(index, "b.txt")); + + write_similarity_file_two("renames/c.txt", 7); + write_similarity_file_two("renames/d.txt", 8); + + cl_git_pass(git_index_add_bypath(index, "c.txt")); + cl_git_pass(git_index_add_bypath(index, "d.txt")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass( + git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + cl_git_pass( + git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_assert(expect.idx > 0); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + git_reference_free(head); + git_reference_free(selfsimilar); +} + +void test_diff_rename__rejected_match_can_match_others_three(void) +{ + git_reference *head, *selfsimilar; + git_index *index; + git_tree *tree; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + + /* Both cannot be renames from a.txt */ + unsigned int status[] = { GIT_DELTA_ADDED, GIT_DELTA_RENAMED }; + const char *sources[] = { "0001.txt", "a.txt" }; + const char *targets[] = { "0001.txt", "0002.txt" }; + struct rename_expected expect = { 2, status, sources, targets }; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_set_target( + &selfsimilar, head, "refs/heads/renames_similar_two")); + cl_git_pass(git_checkout_head(g_repo, &opts)); + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(p_unlink("renames/a.txt")); + + cl_git_pass(git_index_remove_bypath(index, "a.txt")); + + write_similarity_file_two("renames/0001.txt", 7); + write_similarity_file_two("renames/0002.txt", 0); + + cl_git_pass(git_index_add_bypath(index, "0001.txt")); + cl_git_pass(git_index_add_bypath(index, "0002.txt")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass( + git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + cl_git_pass( + git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + + cl_assert(expect.idx == expect.len); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + git_reference_free(head); + git_reference_free(selfsimilar); +} + +void test_diff_rename__can_rename_from_rewrite(void) +{ + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + + unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED }; + const char *sources[] = { "ikeepsix.txt", "songof7cities.txt" }; + const char *targets[] = { "songof7cities.txt", "this-is-a-rename.txt" }; + struct rename_expected expect = { 2, status, sources, targets }; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(p_rename("renames/songof7cities.txt", "renames/this-is-a-rename.txt")); + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/songof7cities.txt")); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "this-is-a-rename.txt")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass( + git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + findopts.flags |= GIT_DIFF_FIND_AND_BREAK_REWRITES | + GIT_DIFF_FIND_REWRITES | + GIT_DIFF_FIND_RENAMES_FROM_REWRITES; + + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + cl_git_pass( + git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + + cl_assert(expect.idx == expect.len); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); +} + +void test_diff_rename__case_changes_are_split(void) +{ + git_index *index; + git_tree *tree; + git_diff_list *diff = NULL; + diff_expects exp; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt")); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); + git_index_free(index); + git_tree_free(tree); +} + +void test_diff_rename__unmodified_can_be_renamed(void) +{ + git_index *index; + git_tree *tree; + git_diff_list *diff = NULL; + diff_expects exp; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt")); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); + git_index_free(index); + git_tree_free(tree); +} + +void test_diff_rename__rewrite_on_single_file(void) +{ + git_index *index; + git_diff_list *diff = NULL; + diff_expects exp; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + + diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + + findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED | + GIT_DIFF_FIND_AND_BREAK_REWRITES | + GIT_DIFF_FIND_RENAMES_FROM_REWRITES; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_rewritefile("renames/ikeepsix.txt", + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + memset(&exp, 0, sizeof(exp)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + git_index_free(index); +} diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c index 6e52a6319..9dcf8194e 100644 --- a/tests-clar/diff/submodules.c +++ b/tests-clar/diff/submodules.c @@ -5,39 +5,17 @@ static git_repository *g_repo = NULL; -static void setup_submodules(void) -{ - g_repo = cl_git_sandbox_init("submodules"); - cl_fixture_sandbox("testrepo.git"); - rewrite_gitmodules(git_repository_workdir(g_repo)); - p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git"); -} - -static void setup_submodules2(void) -{ - g_repo = cl_git_sandbox_init("submod2"); - - cl_fixture_sandbox("submod2_target"); - p_rename("submod2_target/.gitted", "submod2_target/.git"); - - rewrite_gitmodules(git_repository_workdir(g_repo)); - p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git"); - p_rename("submod2/not/.gitted", "submod2/not/.git"); -} - void test_diff_submodules__initialize(void) { } void test_diff_submodules__cleanup(void) { - cl_git_sandbox_cleanup(); - - cl_fixture_cleanup("testrepo.git"); - cl_fixture_cleanup("submod2_target"); + cleanup_fixture_submodules(); } -static void check_diff_patches(git_diff_list *diff, const char **expected) +static void check_diff_patches_at_line( + git_diff_list *diff, const char **expected, const char *file, int line) { const git_diff_delta *delta; git_diff_patch *patch = NULL; @@ -47,23 +25,32 @@ static void check_diff_patches(git_diff_list *diff, const char **expected) for (d = 0; d < num_d; ++d, git_diff_patch_free(patch)) { cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d)); - if (delta->status == GIT_DELTA_UNMODIFIED) + if (delta->status == GIT_DELTA_UNMODIFIED) { + cl_assert_at_line(expected[d] == NULL, file, line); continue; + } if (expected[d] && !strcmp(expected[d], "<SKIP>")) continue; - if (expected[d] && !strcmp(expected[d], "<END>")) - cl_assert(0); + if (expected[d] && !strcmp(expected[d], "<END>")) { + cl_git_pass(git_diff_patch_to_str(&patch_text, patch)); + cl_assert_at_line(!strcmp(expected[d], "<END>"), file, line); + } cl_git_pass(git_diff_patch_to_str(&patch_text, patch)); - cl_assert_equal_s(expected[d], patch_text); + clar__assert_equal( + file, line, "expected diff did not match actual diff", 1, + "%s", expected[d], patch_text); git__free(patch_text); } - cl_assert(expected[d] && !strcmp(expected[d], "<END>")); + cl_assert_at_line(expected[d] && !strcmp(expected[d], "<END>"), file, line); } +#define check_diff_patches(diff, exp) \ + check_diff_patches_at_line(diff, exp, __FILE__, __LINE__) + void test_diff_submodules__unmodified_submodule(void) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; @@ -79,11 +66,12 @@ void test_diff_submodules__unmodified_submodule(void) "<END>" }; - setup_submodules(); + g_repo = setup_fixture_submodules(); opts.flags = GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED; + opts.old_prefix = "a"; opts.new_prefix = "b"; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected); @@ -105,7 +93,7 @@ void test_diff_submodules__dirty_submodule(void) "<END>" }; - setup_submodules(); + g_repo = setup_fixture_submodules(); cl_git_rewritefile("submodules/testrepo/README", "heyheyhey"); cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before"); @@ -113,20 +101,73 @@ void test_diff_submodules__dirty_submodule(void) opts.flags = GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED; + opts.old_prefix = "a"; opts.new_prefix = "b"; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected); git_diff_list_free(diff); } +void test_diff_submodules__dirty_submodule_2(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL, *diff2 = NULL; + char *smpath = "testrepo"; + static const char *expected_none[] = { "<END>" }; + static const char *expected_dirty[] = { + "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */ + "<END>" + }; + + g_repo = setup_fixture_submodules(); + + cl_git_pass(git_submodule_reload_all(g_repo)); + + opts.flags = GIT_DIFF_INCLUDE_UNTRACKED | + GIT_DIFF_INCLUDE_UNTRACKED_CONTENT | + GIT_DIFF_RECURSE_UNTRACKED_DIRS | + GIT_DIFF_DISABLE_PATHSPEC_MATCH; + opts.old_prefix = "a"; opts.new_prefix = "b"; + opts.pathspec.count = 1; + opts.pathspec.strings = &smpath; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_none); + git_diff_list_free(diff); + + cl_git_rewritefile("submodules/testrepo/README", "heyheyhey"); + cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + + { + git_tree *head; + + cl_git_pass(git_repository_head_tree(&head, g_repo)); + cl_git_pass(git_diff_tree_to_index(&diff2, g_repo, head, NULL, &opts)); + cl_git_pass(git_diff_merge(diff, diff2)); + git_diff_list_free(diff2); + git_tree_free(head); + + check_diff_patches(diff, expected_dirty); + } + + git_diff_list_free(diff); + + cl_git_pass(git_submodule_reload_all(g_repo)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + git_diff_list_free(diff); +} + void test_diff_submodules__submod2_index_to_wd(void) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; static const char *expected[] = { "<SKIP>", /* .gitmodules */ - NULL, /* not-submodule */ - NULL, /* not */ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */ @@ -135,9 +176,10 @@ void test_diff_submodules__submod2_index_to_wd(void) "<END>" }; - setup_submodules2(); + g_repo = setup_fixture_submod2(); opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + opts.old_prefix = "a"; opts.new_prefix = "b"; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected); @@ -155,11 +197,12 @@ void test_diff_submodules__submod2_head_to_index(void) "<END>" }; - setup_submodules2(); + g_repo = setup_fixture_submod2(); cl_git_pass(git_repository_head_tree(&head, g_repo)); opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + opts.old_prefix = "a"; opts.new_prefix = "b"; cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts)); check_diff_patches(diff, expected); @@ -167,3 +210,233 @@ void test_diff_submodules__submod2_head_to_index(void) git_tree_free(head); } + +void test_diff_submodules__invalid_cache(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + git_submodule *sm; + char *smpath = "sm_changed_head"; + git_repository *smrepo; + git_index *smindex; + static const char *expected_baseline[] = { + "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ + "<END>" + }; + static const char *expected_unchanged[] = { "<END>" }; + static const char *expected_dirty[] = { + "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247-dirty\n", + "<END>" + }; + static const char *expected_moved[] = { + "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..0910a13 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 0910a13dfa2210496f6c590d75bc360dd11b2a1b\n", + "<END>" + }; + static const char *expected_moved_dirty[] = { + "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..0910a13 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 0910a13dfa2210496f6c590d75bc360dd11b2a1b-dirty\n", + "<END>" + }; + + g_repo = setup_fixture_submod2(); + + opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + opts.old_prefix = "a"; opts.new_prefix = "b"; + opts.pathspec.count = 1; + opts.pathspec.strings = &smpath; + + /* baseline */ + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_baseline); + git_diff_list_free(diff); + + /* update index with new HEAD */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath)); + cl_git_pass(git_submodule_add_to_index(sm, 1)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_unchanged); + git_diff_list_free(diff); + + /* create untracked file in submodule working directory */ + cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello"); + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + git_diff_list_free(diff); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_unchanged); + git_diff_list_free(diff); + + /* modify tracked file in submodule working directory */ + cl_git_append2file( + "submod2/sm_changed_head/file_to_modify", "\nmore stuff\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + git_diff_list_free(diff); + + cl_git_pass(git_submodule_reload_all(g_repo)); + cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + git_diff_list_free(diff); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_unchanged); + git_diff_list_free(diff); + + /* add file to index in submodule */ + cl_git_pass(git_submodule_open(&smrepo, sm)); + cl_git_pass(git_repository_index(&smindex, smrepo)); + cl_git_pass(git_index_add_bypath(smindex, "file_to_modify")); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_dirty); + git_diff_list_free(diff); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_unchanged); + git_diff_list_free(diff); + + /* commit changed index of submodule */ + { + git_object *parent; + git_oid tree_id, commit_id; + git_tree *tree; + git_signature *sig; + git_reference *ref; + + cl_git_pass(git_revparse_ext(&parent, &ref, smrepo, "HEAD")); + cl_git_pass(git_index_write_tree(&tree_id, smindex)); + cl_git_pass(git_index_write(smindex)); + cl_git_pass(git_tree_lookup(&tree, smrepo, &tree_id)); + cl_git_pass(git_signature_new(&sig, "Sm Test", "sm@tester.test", 1372350000, 480)); + cl_git_pass(git_commit_create_v( + &commit_id, smrepo, git_reference_name(ref), sig, sig, + NULL, "Move it", tree, 1, parent)); + git_object_free(parent); + git_tree_free(tree); + git_reference_free(ref); + git_signature_free(sig); + } + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_moved); + git_diff_list_free(diff); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_ALL); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_unchanged); + git_diff_list_free(diff); + + git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_moved_dirty); + git_diff_list_free(diff); + + p_unlink("submod2/sm_changed_head/new_around_here"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_moved); + git_diff_list_free(diff); + + git_index_free(smindex); + git_repository_free(smrepo); +} + +void test_diff_submodules__diff_ignore_options(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + git_config *cfg; + static const char *expected_normal[] = { + "<SKIP>", /* .gitmodules */ + "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */ + "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ + "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */ + "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */ + "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */ + "<END>" + }; + static const char *expected_ignore_all[] = { + "<SKIP>", /* .gitmodules */ + "<END>" + }; + static const char *expected_ignore_dirty[] = { + "<SKIP>", /* .gitmodules */ + "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ + "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */ + "<END>" + }; + + g_repo = setup_fixture_submod2(); + + opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + opts.old_prefix = "a"; opts.new_prefix = "b"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_normal); + git_diff_list_free(diff); + + opts.flags |= GIT_DIFF_IGNORE_SUBMODULES; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_ignore_all); + git_diff_list_free(diff); + + opts.flags &= ~GIT_DIFF_IGNORE_SUBMODULES; + opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_ignore_all); + git_diff_list_free(diff); + + opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_ignore_dirty); + git_diff_list_free(diff); + + opts.ignore_submodules = 0; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", false)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_normal); + git_diff_list_free(diff); + + cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", true)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_ignore_all); + git_diff_list_free(diff); + + cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "none")); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_normal); + git_diff_list_free(diff); + + cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "dirty")); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + check_diff_patches(diff, expected_ignore_dirty); + git_diff_list_free(diff); + + git_config_free(cfg); +} diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 18182ea96..c7fac1e48 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -776,6 +776,7 @@ void test_diff_workdir__submodules(void) opts.flags = GIT_DIFF_INCLUDE_UNTRACKED | + GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS | GIT_DIFF_INCLUDE_UNTRACKED_CONTENT; @@ -806,7 +807,7 @@ void test_diff_workdir__submodules(void) * only significant difference is that those Added items will show up * as Untracked items in the pure libgit2 diff. * - * Then add in the two extra untracked items "not" and "not-submodule" + * Then add in the two extra ignored items "not" and "not-submodule" * to get the 12 files reported here. */ @@ -815,8 +816,8 @@ void test_diff_workdir__submodules(void) cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); - cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(8, exp.file_status[GIT_DELTA_UNTRACKED]); /* the following numbers match "git diff 873585" exactly */ @@ -1109,6 +1110,26 @@ void test_diff_workdir__untracked_directory_scenarios(void) git_diff_list_free(diff); + /* empty directory in empty directory */ + + cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777)); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + /* directory with only ignored files */ cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777)); diff --git a/tests-clar/index/addall.c b/tests-clar/index/addall.c new file mode 100644 index 000000000..00388ee00 --- /dev/null +++ b/tests-clar/index/addall.c @@ -0,0 +1,277 @@ +#include "clar_libgit2.h" +#include "../status/status_helpers.h" +#include "posix.h" +#include "fileops.h" + +git_repository *g_repo = NULL; + +void test_index_addall__initialize(void) +{ +} + +void test_index_addall__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; +} + +#define STATUS_INDEX_FLAGS \ + (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \ + GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \ + GIT_STATUS_INDEX_TYPECHANGE) + +#define STATUS_WT_FLAGS \ + (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \ + GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \ + GIT_STATUS_WT_RENAMED) + +typedef struct { + size_t index_adds; + size_t index_dels; + size_t index_mods; + size_t wt_adds; + size_t wt_dels; + size_t wt_mods; + size_t ignores; +} index_status_counts; + +static int index_status_cb( + const char *path, unsigned int status_flags, void *payload) +{ + index_status_counts *vals = payload; + + /* cb_status__print(path, status_flags, NULL); */ + + GIT_UNUSED(path); + + if (status_flags & GIT_STATUS_INDEX_NEW) + vals->index_adds++; + if (status_flags & GIT_STATUS_INDEX_MODIFIED) + vals->index_mods++; + if (status_flags & GIT_STATUS_INDEX_DELETED) + vals->index_dels++; + if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) + vals->index_mods++; + + if (status_flags & GIT_STATUS_WT_NEW) + vals->wt_adds++; + if (status_flags & GIT_STATUS_WT_MODIFIED) + vals->wt_mods++; + if (status_flags & GIT_STATUS_WT_DELETED) + vals->wt_dels++; + if (status_flags & GIT_STATUS_WT_TYPECHANGE) + vals->wt_mods++; + + if (status_flags & GIT_STATUS_IGNORED) + vals->ignores++; + + return 0; +} + +static void check_status( + git_repository *repo, + size_t index_adds, size_t index_dels, size_t index_mods, + size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores) +{ + index_status_counts vals; + + memset(&vals, 0, sizeof(vals)); + + cl_git_pass(git_status_foreach(repo, index_status_cb, &vals)); + + cl_assert_equal_sz(index_adds, vals.index_adds); + cl_assert_equal_sz(index_dels, vals.index_dels); + cl_assert_equal_sz(index_mods, vals.index_mods); + cl_assert_equal_sz(wt_adds, vals.wt_adds); + cl_assert_equal_sz(wt_dels, vals.wt_dels); + cl_assert_equal_sz(wt_mods, vals.wt_mods); + cl_assert_equal_sz(ignores, vals.ignores); +} + +static void check_stat_data(git_index *index, const char *path, bool match) +{ + const git_index_entry *entry; + struct stat st; + + cl_must_pass(p_lstat(path, &st)); + + /* skip repo base dir name */ + while (*path != '/') + ++path; + ++path; + + entry = git_index_get_bypath(index, path, 0); + cl_assert(entry); + + if (match) { + cl_assert(st.st_ctime == entry->ctime.seconds); + cl_assert(st.st_mtime == entry->mtime.seconds); + cl_assert(st.st_size == entry->file_size); + cl_assert(st.st_uid == entry->uid); + cl_assert(st.st_gid == entry->gid); + cl_assert_equal_i_fmt( + GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o"); + cl_assert_equal_b( + GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode)); + } else { + /* most things will still match */ + cl_assert(st.st_size != entry->file_size); + /* would check mtime, but with second resolution it won't work :( */ + } +} + +static void commit_index_to_head( + git_repository *repo, + const char *commit_message) +{ + git_index *index; + git_oid tree_id, commit_id; + git_tree *tree; + git_signature *sig; + git_commit *parent = NULL; + + git_revparse_single((git_object **)&parent, repo, "HEAD"); + /* it is okay if looking up the HEAD fails */ + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_index_write(index)); /* not needed, but might as well */ + git_index_free(index); + + cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + + cl_git_pass(git_signature_now(&sig, "Testy McTester", "tt@tester.test")); + + cl_git_pass(git_commit_create_v( + &commit_id, repo, "HEAD", sig, sig, + NULL, commit_message, tree, parent ? 1 : 0, parent)); + + git_commit_free(parent); + git_tree_free(tree); + git_signature_free(sig); +} + +void test_index_addall__repo_lifecycle(void) +{ + int error; + git_index *index; + git_strarray paths = { NULL, 0 }; + char *strs[1]; + + cl_git_pass(git_repository_init(&g_repo, "addall", false)); + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_mkfile("addall/file.foo", "a file"); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + + cl_git_mkfile("addall/.gitignore", "*.foo\n"); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile("addall/file.bar", "another file"); + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); + + strs[0] = "file.*"; + paths.strings = strs; + paths.count = 1; + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.bar", true); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + + cl_git_rewritefile("addall/file.bar", "new content for file"); + check_stat_data(index, "addall/file.bar", false); + check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); + + cl_git_mkfile("addall/file.zzz", "yet another one"); + cl_git_mkfile("addall/other.zzz", "yet another one"); + cl_git_mkfile("addall/more.zzz", "yet another one"); + check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_stat_data(index, "addall/file.bar", true); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.zzz", true); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + + commit_index_to_head(g_repo, "first commit"); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* attempt to add an ignored file - does nothing */ + strs[0] = "file.foo"; + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* add with check - should generate error */ + error = git_index_add_all( + index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL); + cl_assert_equal_i(GIT_EINVALIDSPEC, error); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* add with force - should allow */ + cl_git_pass(git_index_add_all( + index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); + check_stat_data(index, "addall/file.foo", true); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + + /* now it's in the index, so regular add should work */ + cl_git_rewritefile("addall/file.foo", "new content for file"); + check_stat_data(index, "addall/file.foo", false); + check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.foo", true); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + + cl_git_pass(git_index_add_bypath(index, "more.zzz")); + check_stat_data(index, "addall/more.zzz", true); + check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); + + cl_git_rewritefile("addall/file.zzz", "new content for file"); + check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); + + cl_git_pass(git_index_add_bypath(index, "file.zzz")); + check_stat_data(index, "addall/file.zzz", true); + check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); + + strs[0] = "*.zzz"; + cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL)); + check_status(g_repo, 1, 1, 0, 4, 0, 0, 0); + + cl_git_pass(git_index_add_bypath(index, "file.zzz")); + check_status(g_repo, 1, 0, 1, 3, 0, 0, 0); + + commit_index_to_head(g_repo, "second commit"); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); + + cl_must_pass(p_unlink("addall/file.zzz")); + check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); + + /* update_all should be able to remove entries */ + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_status(g_repo, 0, 1, 0, 3, 0, 0, 0); + + strs[0] = "*"; + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); + + /* must be able to remove at any position while still updating other files */ + cl_must_pass(p_unlink("addall/.gitignore")); + cl_git_rewritefile("addall/file.zzz", "reconstructed file"); + cl_git_rewritefile("addall/more.zzz", "altered file reality"); + check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_status(g_repo, 2, 1, 0, 1, 0, 0, 0); + /* this behavior actually matches 'git add -u' where "file.zzz" has + * been removed from the index, so when you go to update, even though + * it exists in the HEAD, it is not re-added to the index, leaving it + * as a DELETE when comparing HEAD to index and as an ADD comparing + * index to worktree + */ + + git_index_free(index); +} diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c index 88e374e6e..9090d4d8a 100644 --- a/tests-clar/index/tests.c +++ b/tests-clar/index/tests.c @@ -416,3 +416,71 @@ void test_index_tests__remove_directory(void) git_repository_free(repo); cl_fixture_cleanup("index_test"); } + +void test_index_tests__preserves_case(void) +{ + git_repository *repo; + git_index *index; + const git_index_entry *entry; + int index_caps; + + cl_set_cleanup(&cleanup_myrepo, NULL); + + cl_git_pass(git_repository_init(&repo, "./myrepo", 0)); + cl_git_pass(git_repository_index(&index, repo)); + + index_caps = git_index_caps(index); + + cl_git_rewritefile("myrepo/test.txt", "hey there\n"); + cl_git_pass(git_index_add_bypath(index, "test.txt")); + + cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt")); + cl_git_rewritefile("myrepo/TEST.txt", "hello again\n"); + cl_git_pass(git_index_add_bypath(index, "TEST.txt")); + + if (index_caps & GIT_INDEXCAP_IGNORE_CASE) + cl_assert_equal_i(1, (int)git_index_entrycount(index)); + else + cl_assert_equal_i(2, (int)git_index_entrycount(index)); + + /* Test access by path instead of index */ + cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL); + /* The path should *not* have changed without an explicit remove */ + cl_assert(git__strcmp(entry->path, "test.txt") == 0); + + cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL); + if (index_caps & GIT_INDEXCAP_IGNORE_CASE) + /* The path should *not* have changed without an explicit remove */ + cl_assert(git__strcmp(entry->path, "test.txt") == 0); + else + cl_assert(git__strcmp(entry->path, "TEST.txt") == 0); + + git_index_free(index); + git_repository_free(repo); +} + +void test_index_tests__elocked(void) +{ + git_repository *repo; + git_index *index; + git_filebuf file = GIT_FILEBUF_INIT; + const git_error *err; + int error; + + cl_set_cleanup(&cleanup_myrepo, NULL); + + cl_git_pass(git_repository_init(&repo, "./myrepo", 0)); + cl_git_pass(git_repository_index(&index, repo)); + + /* Lock the index file so we fail to lock it */ + cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0)); + error = git_index_write(index); + cl_assert_equal_i(GIT_ELOCKED, error); + + err = giterr_last(); + cl_assert_equal_i(err->klass, GITERR_INDEX); + + git_filebuf_cleanup(&file); + git_index_free(index); + git_repository_free(repo); +} diff --git a/tests-clar/merge/trees/automerge.c b/tests-clar/merge/trees/automerge.c index 04a7beff6..746ce5068 100644 --- a/tests-clar/merge/trees/automerge.c +++ b/tests-clar/merge/trees/automerge.c @@ -122,7 +122,7 @@ void test_merge_trees_automerge__automerge(void) cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE)); cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB)); - cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0); + cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0); git_index_free(index); git_blob_free(blob); diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c index 3cb8a25d6..c8edd37f5 100644 --- a/tests-clar/network/remote/local.c +++ b/tests-clar/network/remote/local.c @@ -52,7 +52,6 @@ static void connect_to_local_repository(const char *local_repository) cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf))); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - } void test_network_remote_local__connected(void) @@ -75,6 +74,18 @@ void test_network_remote_local__retrieve_advertised_references(void) cl_assert_equal_i(how_many_refs, 28); } +void test_network_remote_local__retrieve_advertised_references_after_disconnect(void) +{ + int how_many_refs = 0; + + connect_to_local_repository(cl_fixture("testrepo.git")); + git_remote_disconnect(remote); + + cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); + + cl_assert_equal_i(how_many_refs, 28); +} + void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void) { int how_many_refs = 0; @@ -158,3 +169,75 @@ void test_network_remote_local__tagopt(void) cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); git_reference_free(ref); } + +void test_network_remote_local__push_to_bare_remote(void) +{ + /* Should be able to push to a bare remote */ + git_remote *localremote; + git_push *push; + + /* Get some commits */ + connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_add_fetch(remote, "master:master")); + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + git_remote_disconnect(remote); + + /* Set up an empty bare repo to push into */ + { + git_repository *localbarerepo; + cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1)); + git_repository_free(localbarerepo); + } + + /* Connect to the bare repo */ + cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localbare.git")); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH)); + + /* Try to push */ + cl_git_pass(git_push_new(&push, localremote)); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); + cl_git_pass(git_push_finish(push)); + cl_assert(git_push_unpack_ok(push)); + + /* Clean up */ + git_push_free(push); + git_remote_free(localremote); + cl_fixture_cleanup("localbare.git"); +} + +void test_network_remote_local__push_to_non_bare_remote(void) +{ + /* Shouldn't be able to push to a non-bare remote */ + git_remote *localremote; + git_push *push; + + /* Get some commits */ + connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_add_fetch(remote, "master:master")); + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + git_remote_disconnect(remote); + + /* Set up an empty non-bare repo to push into */ + { + git_repository *remoterepo = NULL; + cl_git_pass(git_repository_init(&remoterepo, "localnonbare", 0)); + git_repository_free(remoterepo); + } + + /* Connect to the bare repo */ + cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localnonbare")); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH)); + + /* Try to push */ + cl_git_pass(git_push_new(&push, localremote)); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); + cl_git_fail_with(git_push_finish(push), GIT_EBAREREPO); + cl_assert_equal_i(0, git_push_unpack_ok(push)); + + /* Clean up */ + git_push_free(push); + git_remote_free(localremote); + cl_fixture_cleanup("localbare.git"); +} diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 3c4fa96fa..6e0eeeb05 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -243,13 +243,19 @@ void test_network_remote_remotes__list(void) git_config *cfg; cl_git_pass(git_remote_list(&list, _repo)); - cl_assert(list.count == 4); + cl_assert(list.count == 5); git_strarray_free(&list); cl_git_pass(git_repository_config(&cfg, _repo)); + + /* Create a new remote */ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com")); + + /* Update a remote (previously without any url/pushurl entry) */ + cl_git_pass(git_config_set_string(cfg, "remote.no-remote-url.pushurl", "http://example.com")); + cl_git_pass(git_remote_list(&list, _repo)); - cl_assert(list.count == 5); + cl_assert(list.count == 7); git_strarray_free(&list); git_config_free(cfg); @@ -361,13 +367,43 @@ void test_network_remote_remotes__tagopt(void) git_config_free(cfg); } -void test_network_remote_remotes__cannot_load_with_an_empty_url(void) +void test_network_remote_remotes__can_load_with_an_empty_url(void) { git_remote *remote = NULL; - cl_git_fail(git_remote_load(&remote, _repo, "empty-remote-url")); + cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-url")); + + cl_assert(remote->url == NULL); + cl_assert(remote->pushurl == NULL); + + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH)); + + cl_assert(giterr_last() != NULL); cl_assert(giterr_last()->klass == GITERR_INVALID); - cl_assert_equal_p(remote, NULL); + + git_remote_free(remote); +} + +void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void) +{ + git_remote *remote = NULL; + + cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-pushurl")); + + cl_assert(remote->url == NULL); + cl_assert(remote->pushurl == NULL); + + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH)); + + git_remote_free(remote); +} + +void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl(void) +{ + git_remote *remote = NULL; + + cl_git_fail_with( + git_remote_load(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND); } void test_network_remote_remotes__check_structure_version(void) diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c index 042bddab7..2b3954d9c 100644 --- a/tests-clar/object/blob/filter.c +++ b/tests-clar/object/blob/filter.c @@ -5,7 +5,7 @@ #include "buf_text.h" static git_repository *g_repo = NULL; -#define NUM_TEST_OBJECTS 8 +#define NUM_TEST_OBJECTS 9 static git_oid g_oids[NUM_TEST_OBJECTS]; static const char *g_raw[NUM_TEST_OBJECTS] = { "", @@ -15,9 +15,10 @@ static const char *g_raw[NUM_TEST_OBJECTS] = { "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r", "123\n\000\001\002\003\004abc\255\254\253\r\n", "\xEF\xBB\xBFThis is UTF-8\n", + "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n", "\xFE\xFF\x00T\x00h\x00i\x00s\x00!" }; -static git_off_t g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17, -1, 12 }; +static git_off_t g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17, -1, -1, 12 }; static git_buf_text_stats g_stats[NUM_TEST_OBJECTS] = { { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 2, 0, 6, 0 }, @@ -26,6 +27,7 @@ static git_buf_text_stats g_stats[NUM_TEST_OBJECTS] = { { 0, 0, 4, 4, 1, 31, 0 }, { 0, 1, 1, 2, 1, 9, 5 }, { GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 }, + { GIT_BOM_UTF8, 0, 2, 2, 2, 27, 0 }, { GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 }, }; static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = { @@ -36,6 +38,7 @@ static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = { { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 }, { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 }, { "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 }, + { "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n", 0, 29 }, { "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 } }; diff --git a/tests-clar/object/blob/fromchunks.c b/tests-clar/object/blob/fromchunks.c index dc57d4fbe..03ed4efb4 100644 --- a/tests-clar/object/blob/fromchunks.c +++ b/tests-clar/object/blob/fromchunks.c @@ -41,14 +41,46 @@ void test_object_blob_fromchunks__can_create_a_blob_from_a_in_memory_chunk_provi cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); - cl_git_fail(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY)); + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); cl_git_pass(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY)); + cl_assert(git_oid_cmp(&expected_oid, git_object_id(blob)) == 0); + git_object_free(blob); } +void test_object_blob_fromchunks__doesnot_overwrite_an_already_existing_object(void) +{ + git_buf path = GIT_BUF_INIT; + git_buf content = GIT_BUF_INIT; + git_oid expected_oid, oid; + int howmany = 7; + + cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); + + cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); + + /* Let's replace the content of the blob file storage with something else... */ + cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/32/1cbdf08803c744082332332838df6bd160f8f9")); + cl_git_pass(p_unlink(git_buf_cstr(&path))); + cl_git_mkfile(git_buf_cstr(&path), "boom"); + + /* ...request a creation of the same blob... */ + howmany = 7; + cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); + + /* ...and ensure the content of the faked blob file hasn't been altered */ + cl_git_pass(git_futils_readbuffer(&content, git_buf_cstr(&path))); + cl_assert(!git__strcmp("boom", git_buf_cstr(&content))); + + git_buf_free(&path); + git_buf_free(&content); +} + #define GITATTR "* text=auto\n" \ "*.txt text\n" \ "*.data binary\n" diff --git a/tests-clar/object/raw/write.c b/tests-clar/object/raw/write.c index 9709c0302..273f08f2c 100644 --- a/tests-clar/object/raw/write.c +++ b/tests-clar/object/raw/write.c @@ -31,9 +31,9 @@ static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw) int error; cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type)); - stream->write(stream, raw->data, raw->len); - error = stream->finalize_write(oid, stream); - stream->free(stream); + git_odb_stream_write(stream, raw->data, raw->len); + error = git_odb_stream_finalize_write(oid, stream); + git_odb_stream_free(stream); cl_git_pass(error); } diff --git a/tests-clar/object/tree/walk.c b/tests-clar/object/tree/walk.c index b7af4924d..1207e864c 100644 --- a/tests-clar/object/tree/walk.c +++ b/tests-clar/object/tree/walk.c @@ -101,3 +101,77 @@ void test_object_tree_walk__1(void) git_tree_free(tree); } + + +struct treewalk_skip_data { + int files; + int dirs; + const char *skip; + const char *stop; +}; + +static int treewalk_skip_de_cb( + const char *root, const git_tree_entry *entry, void *payload) +{ + struct treewalk_skip_data *data = payload; + const char *name = git_tree_entry_name(entry); + + GIT_UNUSED(root); + + if (git_tree_entry_type(entry) == GIT_OBJ_TREE) + data->dirs++; + else + data->files++; + + if (data->skip && !strcmp(name, data->skip)) + return 1; + else if (data->stop && !strcmp(name, data->stop)) + return -1; + else + return 0; +} + +void test_object_tree_walk__2(void) +{ + git_oid id; + git_tree *tree; + struct treewalk_skip_data data; + + /* look up a deep tree */ + git_oid_fromstr(&id, "ae90f12eea699729ed24555e40b9fd669da12a12"); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + + memset(&data, 0, sizeof(data)); + data.skip = "de"; + + cl_assert_equal_i(0, git_tree_walk( + tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); + cl_assert_equal_i(5, data.files); + cl_assert_equal_i(3, data.dirs); + + memset(&data, 0, sizeof(data)); + data.stop = "3.txt"; + + cl_assert_equal_i(GIT_EUSER, git_tree_walk( + tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); + cl_assert_equal_i(3, data.files); + cl_assert_equal_i(2, data.dirs); + + memset(&data, 0, sizeof(data)); + data.skip = "new.txt"; + + cl_assert_equal_i(0, git_tree_walk( + tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); + cl_assert_equal_i(7, data.files); + cl_assert_equal_i(4, data.dirs); + + memset(&data, 0, sizeof(data)); + data.stop = "new.txt"; + + cl_assert_equal_i(GIT_EUSER, git_tree_walk( + tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); + cl_assert_equal_i(7, data.files); + cl_assert_equal_i(4, data.dirs); + + git_tree_free(tree); +} diff --git a/tests-clar/odb/backend/nonrefreshing.c b/tests-clar/odb/backend/nonrefreshing.c new file mode 100644 index 000000000..b43529479 --- /dev/null +++ b/tests-clar/odb/backend/nonrefreshing.c @@ -0,0 +1,274 @@ +#include "clar_libgit2.h" +#include "git2/sys/odb_backend.h" +#include "repository.h" + +typedef struct fake_backend { + git_odb_backend parent; + + git_error_code error_code; + + int exists_calls; + int read_calls; + int read_header_calls; + int read_prefix_calls; +} fake_backend; + +static git_repository *_repo; +static fake_backend *_fake; +static git_oid _oid; + +static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid) +{ + fake_backend *fake; + + GIT_UNUSED(oid); + + fake = (fake_backend *)backend; + + fake->exists_calls++; + + return (fake->error_code == GIT_OK); +} + +static int fake_backend__read( + void **buffer_p, size_t *len_p, git_otype *type_p, + git_odb_backend *backend, const git_oid *oid) +{ + fake_backend *fake; + + GIT_UNUSED(buffer_p); + GIT_UNUSED(len_p); + GIT_UNUSED(type_p); + GIT_UNUSED(oid); + + fake = (fake_backend *)backend; + + fake->read_calls++; + + *len_p = 0; + *buffer_p = NULL; + *type_p = GIT_OBJ_BLOB; + + return fake->error_code; +} + +static int fake_backend__read_header( + size_t *len_p, git_otype *type_p, + git_odb_backend *backend, const git_oid *oid) +{ + fake_backend *fake; + + GIT_UNUSED(len_p); + GIT_UNUSED(type_p); + GIT_UNUSED(oid); + + fake = (fake_backend *)backend; + + fake->read_header_calls++; + + *len_p = 0; + *type_p = GIT_OBJ_BLOB; + + return fake->error_code; +} + +static int fake_backend__read_prefix( + git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p, + git_odb_backend *backend, const git_oid *short_oid, size_t len) +{ + fake_backend *fake; + + GIT_UNUSED(out_oid); + GIT_UNUSED(buffer_p); + GIT_UNUSED(len_p); + GIT_UNUSED(type_p); + GIT_UNUSED(short_oid); + GIT_UNUSED(len); + + fake = (fake_backend *)backend; + + fake->read_prefix_calls++; + + *len_p = 0; + *buffer_p = NULL; + *type_p = GIT_OBJ_BLOB; + + return fake->error_code; +} + +static void fake_backend__free(git_odb_backend *_backend) +{ + fake_backend *backend; + + backend = (fake_backend *)_backend; + + git__free(backend); +} + +static int build_fake_backend( + git_odb_backend **out, + git_error_code error_code) +{ + fake_backend *backend; + + backend = git__calloc(1, sizeof(fake_backend)); + GITERR_CHECK_ALLOC(backend); + + backend->parent.version = GIT_ODB_BACKEND_VERSION; + + backend->parent.refresh = NULL; + backend->error_code = error_code; + + backend->parent.read = fake_backend__read; + backend->parent.read_prefix = fake_backend__read_prefix; + backend->parent.read_header = fake_backend__read_header; + backend->parent.exists = fake_backend__exists; + backend->parent.free = &fake_backend__free; + + *out = (git_odb_backend *)backend; + + return 0; +} + +static void setup_repository_and_backend(git_error_code error_code) +{ + git_odb *odb = NULL; + git_odb_backend *backend = NULL; + + _repo = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(build_fake_backend(&backend, error_code)); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, backend, 10)); + + _fake = (fake_backend *)backend; + + cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); +} + +void test_odb_backend_nonrefreshing__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void) +{ + git_odb *odb; + + setup_repository_and_backend(GIT_ENOTFOUND); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_b(false, git_odb_exists(odb, &_oid)); + + cl_assert_equal_i(1, _fake->exists_calls); +} + +void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void) +{ + git_object *obj; + + setup_repository_and_backend(GIT_ENOTFOUND); + + cl_git_fail_with( + git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); + + cl_assert_equal_i(1, _fake->read_calls); +} + +void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void) +{ + git_object *obj; + + setup_repository_and_backend(GIT_ENOTFOUND); + + cl_git_fail_with( + git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY), + GIT_ENOTFOUND); + + cl_assert_equal_i(1, _fake->read_prefix_calls); +} + +void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void) +{ + git_odb *odb; + size_t len; + git_otype type; + + setup_repository_and_backend(GIT_ENOTFOUND); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + + cl_git_fail_with( + git_odb_read_header(&len, &type, odb, &_oid), + GIT_ENOTFOUND); + + cl_assert_equal_i(1, _fake->read_header_calls); +} + +void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void) +{ + git_odb *odb; + + setup_repository_and_backend(GIT_OK); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_assert_equal_b(true, git_odb_exists(odb, &_oid)); + + cl_assert_equal_i(1, _fake->exists_calls); +} + +void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void) +{ + git_object *obj; + + setup_repository_and_backend(GIT_OK); + + cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY)); + + cl_assert_equal_i(1, _fake->read_calls); + + git_object_free(obj); +} + +void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void) +{ + git_object *obj; + + setup_repository_and_backend(GIT_OK); + + cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY)); + + cl_assert_equal_i(1, _fake->read_prefix_calls); + + git_object_free(obj); +} + +void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void) +{ + git_odb *odb; + size_t len; + git_otype type; + + setup_repository_and_backend(GIT_OK); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + + cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid)); + + cl_assert_equal_i(1, _fake->read_header_calls); +} + +void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full_oid(void) +{ + git_object *obj; + + setup_repository_and_backend(GIT_ENOTFOUND); + + cl_git_fail_with( + git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), + GIT_ENOTFOUND); + + cl_assert_equal_i(1, _fake->read_calls); +} diff --git a/tests-clar/odb/loose.c b/tests-clar/odb/loose.c index 9539bb24c..eb6b788b7 100644 --- a/tests-clar/odb/loose.c +++ b/tests-clar/odb/loose.c @@ -3,6 +3,11 @@ #include "posix.h" #include "loose_data.h" +#ifdef __ANDROID_API__ +# define S_IREAD S_IRUSR +# define S_IWRITE S_IWUSR +#endif + static void write_object_files(object_data *d) { int fd; diff --git a/tests-clar/odb/mixed.c b/tests-clar/odb/mixed.c index da0ed97d7..51970ceec 100644 --- a/tests-clar/odb/mixed.c +++ b/tests-clar/odb/mixed.c @@ -16,10 +16,78 @@ void test_odb_mixed__cleanup(void) void test_odb_mixed__dup_oid(void) { const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a"; + const char short_hex[] = "ce01362"; git_oid oid; git_odb_object *obj; + cl_git_pass(git_oid_fromstr(&oid, hex)); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ)); git_odb_object_free(obj); + cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1)); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1)); + git_odb_object_free(obj); } +/* some known sha collisions of file content: + * 'aabqhq' and 'aaazvc' with prefix 'dea509d0' (+ '9' and + 'b') + * 'aaeufo' and 'aaaohs' with prefix '81b5bff5' (+ 'f' and + 'b') + * 'aafewy' and 'aaepta' with prefix '739e3c4c' + * 'aahsyn' and 'aadrjg' with prefix '0ddeaded' (+ '9' and + 'e') + */ + +void test_odb_mixed__dup_oid_prefix_0(void) { + char hex[10]; + git_oid oid; + git_odb_object *obj; + + /* ambiguous in the same pack file */ + + strncpy(hex, "dea509d0", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + + strncpy(hex, "dea509d09", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); + + strncpy(hex, "dea509d0b", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); + + /* ambiguous in different pack files */ + + strncpy(hex, "81b5bff5", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + + strncpy(hex, "81b5bff5b", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); + + strncpy(hex, "81b5bff5f", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); + + /* ambiguous in pack file and loose */ + + strncpy(hex, "0ddeaded", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + + strncpy(hex, "0ddeaded9", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); + + strncpy(hex, "0ddeadede", sizeof(hex)); + cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); + cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + git_odb_object_free(obj); +} diff --git a/tests-clar/odb/streamwrite.c b/tests-clar/odb/streamwrite.c new file mode 100644 index 000000000..591a20040 --- /dev/null +++ b/tests-clar/odb/streamwrite.c @@ -0,0 +1,56 @@ +#include "clar_libgit2.h" +#include "git2/odb_backend.h" + +static git_repository *repo; +static git_odb *odb; +static git_odb_stream *stream; + +void test_odb_streamwrite__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_odb(&odb, repo)); + + cl_git_pass(git_odb_open_wstream(&stream, odb, 14, GIT_OBJ_BLOB)); + cl_assert_equal_sz(14, stream->declared_size); +} + +void test_odb_streamwrite__cleanup(void) +{ + git_odb_stream_free(stream); + git_odb_free(odb); + cl_git_sandbox_cleanup(); +} + +void test_odb_streamwrite__can_accept_chunks(void) +{ + git_oid oid; + + cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8)); + cl_assert_equal_sz(8, stream->received_bytes); + + cl_git_pass(git_odb_stream_write(stream, "deadbeef", 6)); + cl_assert_equal_sz(8 + 6, stream->received_bytes); + + cl_git_pass(git_odb_stream_finalize_write(&oid, stream)); +} + +void test_odb_streamwrite__can_detect_missing_bytes(void) +{ + git_oid oid; + + cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8)); + cl_assert_equal_sz(8, stream->received_bytes); + + cl_git_pass(git_odb_stream_write(stream, "deadbeef", 4)); + cl_assert_equal_sz(8 + 4, stream->received_bytes); + + cl_git_fail(git_odb_stream_finalize_write(&oid, stream)); +} + +void test_odb_streamwrite__can_detect_additional_bytes(void) +{ + cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8)); + cl_assert_equal_sz(8, stream->received_bytes); + + cl_git_fail(git_odb_stream_write(stream, "deadbeef", 7)); +} diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c index 5dc7974c7..4c0a28c1c 100644 --- a/tests-clar/online/push.c +++ b/tests-clar/online/push.c @@ -9,6 +9,10 @@ static git_repository *_repo; +static char *_remote_ssh_key; +static char *_remote_ssh_pubkey; +static char *_remote_ssh_passphrase; + static char *_remote_url; static char *_remote_user; static char *_remote_pass; @@ -29,6 +33,7 @@ static git_oid _tag_commit; static git_oid _tag_tree; static git_oid _tag_blob; static git_oid _tag_lightweight; +static git_oid _tag_tag; static int cred_acquire_cb( git_cred **cred, @@ -42,6 +47,9 @@ static int cred_acquire_cb( *((bool*)payload) = true; + if (GIT_CREDTYPE_SSH_PUBLICKEY & allowed_types) + return git_cred_ssh_keyfile_passphrase_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase); + if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 || git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass) < 0) return -1; @@ -272,11 +280,15 @@ void test_online_push__initialize(void) git_oid_fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e"); git_oid_fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498"); git_oid_fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce"); + git_oid_fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5"); /* Remote URL environment variable must be set. User and password are optional. */ _remote_url = cl_getenv("GITTEST_REMOTE_URL"); _remote_user = cl_getenv("GITTEST_REMOTE_USER"); _remote_pass = cl_getenv("GITTEST_REMOTE_PASS"); + _remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); + _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); _remote = NULL; if (_remote_url) { @@ -569,6 +581,16 @@ void test_online_push__tag_lightweight(void) exp_refs, ARRAY_SIZE(exp_refs), 0); } +void test_online_push__tag_to_tag(void) +{ + const char *specs[] = { "refs/tags/tag-tag:refs/tags/tag-tag" }; + push_status exp_stats[] = { { "refs/tags/tag-tag", NULL } }; + expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } }; + do_push(specs, ARRAY_SIZE(specs), + exp_stats, ARRAY_SIZE(exp_stats), + exp_refs, ARRAY_SIZE(exp_refs), 0); +} + void test_online_push__force(void) { const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"}; diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c index 57f9c0e39..433812cb4 100644 --- a/tests-clar/refs/branches/foreach.c +++ b/tests-clar/refs/branches/foreach.c @@ -122,7 +122,7 @@ void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void cl_git_pass(git_branch_foreach(repo, GIT_BRANCH_REMOTE, contains_branch_list_cb, &exp)); assert_branch_has_been_found(exp, "nulltoken/HEAD"); - assert_branch_has_been_found(exp, "nulltoken/HEAD"); + assert_branch_has_been_found(exp, "nulltoken/master"); } static int branch_list_interrupt_cb( diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index c9c2af4a7..de5c0fd3d 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -36,7 +36,7 @@ void test_refs_list__all(void) /* We have exactly 12 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - cl_assert_equal_i((int)ref_list.count, 13); + cl_assert_equal_i((int)ref_list.count, 14); git_strarray_free(&ref_list); } @@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "144344043ba4d4a405da03de3844aa829ae8be0e\n"); cl_git_pass(git_reference_list(&ref_list, g_repo)); - cl_assert_equal_i((int)ref_list.count, 13); + cl_assert_equal_i((int)ref_list.count, 14); git_strarray_free(&ref_list); } diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c index d8d5cc6d0..849a052aa 100644 --- a/tests-clar/refs/pack.c +++ b/tests-clar/refs/pack.c @@ -32,7 +32,7 @@ static void packall(void) void test_refs_pack__empty(void) { - // create a packfile for an empty folder + /* create a packfile for an empty folder */ git_buf temp_path = GIT_BUF_INIT; cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir")); @@ -44,7 +44,7 @@ void test_refs_pack__empty(void) void test_refs_pack__loose(void) { - // create a packfile from all the loose rn a repo + /* create a packfile from all the loose refs in a repo */ git_reference *reference; git_buf temp_path = GIT_BUF_INIT; @@ -77,3 +77,29 @@ void test_refs_pack__loose(void) git_reference_free(reference); git_buf_free(&temp_path); } + +void test_refs_pack__symbolic(void) +{ + /* create a packfile from loose refs skipping symbolic refs */ + int i; + git_oid head; + git_reference *ref; + char name[128]; + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + /* make a bunch of references */ + + for (i = 0; i < 100; ++i) { + snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i); + cl_git_pass(git_reference_symbolic_create( + &ref, g_repo, name, "refs/heads/master", 0)); + git_reference_free(ref); + + snprintf(name, sizeof(name), "refs/heads/direct-%03d", i); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + git_reference_free(ref); + } + + packall(); +} diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c index afb6be008..35cf17e9e 100644 --- a/tests-clar/refs/read.c +++ b/tests-clar/refs/read.c @@ -255,6 +255,22 @@ void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void) assert_is_branch("refs/tags/e90810b", false); } +static void assert_is_tag(const char *name, bool expected_tagness) +{ + git_reference *reference; + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference)); + git_reference_free(reference); +} + +void test_refs_read__can_determine_if_a_reference_is_a_tag(void) +{ + assert_is_tag("refs/tags/e90810b", true); + assert_is_tag("refs/tags/test", true); + assert_is_tag("refs/heads/packed", false); + assert_is_tag("refs/remotes/test/master", false); +} + void test_refs_read__invalid_name_returns_EINVALIDSPEC(void) { git_reference *reference; diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 69d92745c..37d3981bb 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -544,6 +544,37 @@ void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void) GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90")); } +/* + * $ echo "aabqhq" | git hash-object -t blob --stdin + * dea509d0b3cb8ee0650f6ca210bc83f4678851ba + * + * $ echo "aaazvc" | git hash-object -t blob --stdin + * dea509d097ce692e167dfc6a48a7a280cc5e877e + */ +void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void) +{ + git_repository *repo; + git_index *index; + git_object *obj; + + repo = cl_git_sandbox_init("testrepo"); + + cl_git_mkfile("testrepo/one.txt", "aabqhq\n"); + cl_git_mkfile("testrepo/two.txt", "aaazvc\n"); + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_add_bypath(index, "one.txt")); + cl_git_pass(git_index_add_bypath(index, "two.txt")); + + cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS); + + cl_git_pass(git_revparse_single(&obj, repo, "dea509d09")); + + git_object_free(obj); + git_index_free(index); + cl_git_sandbox_cleanup(); +} + void test_refs_revparse__issue_994(void) { git_repository *repo; @@ -738,4 +769,45 @@ void test_refs_revparse__ext_can_expand_short_reference_names(void) "master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "refs/heads/master"); + + test_object_and_ref( + "HEAD", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "refs/heads/master"); + + test_object_and_ref( + "tags/test", + "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", + "refs/tags/test"); +} + +void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void) +{ + test_object_and_ref( + "HEAD~3", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + NULL); + + test_object_and_ref( + "HEAD~0", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL); + + test_object_and_ref( + "HEAD^0", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL); + + test_object_and_ref( + "@{-1}@{0}", + "a4a7dce85cf63874e984719f4fdd239f5145052f", + NULL); +} + +void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void) +{ + test_object_and_ref( + "tags/test:readme.txt", + "0266163a49e280c4f5ed1e08facd36a2bd716bcf", + NULL); } diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c index 8cf73795f..e3fc112b3 100644 --- a/tests-clar/repo/init.c +++ b/tests-clar/repo/init.c @@ -10,10 +10,17 @@ enum repo_mode { }; static git_repository *_repo = NULL; +static mode_t g_umask = 0; void test_repo_init__initialize(void) { _repo = NULL; + + /* load umask if not already loaded */ + if (!g_umask) { + g_umask = p_umask(022); + (void)p_umask(g_umask); + } } static void cleanup_repository(void *path) @@ -263,7 +270,6 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void) void test_repo_init__reinit_overwrites_filemode(void) { - git_config *config; int expected, current_value; #ifdef GIT_WIN32 @@ -284,13 +290,10 @@ void test_repo_init__reinit_overwrites_filemode(void) /* Reinit the repository */ cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1)); - git_repository_config(&config, _repo); /* Ensure the "core.filemode" config value has been reset */ - cl_git_pass(git_config_get_bool(¤t_value, config, "core.filemode")); + current_value = cl_repo_get_bool(_repo, "core.filemode"); cl_assert_equal_i(expected, current_value); - - git_config_free(config); } void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void) @@ -361,6 +364,8 @@ void test_repo_init__extended_1(void) cl_fixture_cleanup("root"); } +#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177) + static void assert_hooks_match( const char *template_dir, const char *repo_dir, @@ -377,14 +382,20 @@ static void assert_hooks_match( cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path)); cl_git_pass(git_path_lstat(actual.ptr, &st)); - cl_assert(expected_st.st_size == st.st_size); + cl_assert_equal_sz(expected_st.st_size, st.st_size); - if (!core_filemode) { - expected_st.st_mode = expected_st.st_mode & ~0111; - st.st_mode = st.st_mode & ~0111; - } + if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) { + mode_t expected_mode = + GIT_MODE_TYPE(expected_st.st_mode) | + (GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask); + + if (!core_filemode) { + CLEAR_FOR_CORE_FILEMODE(expected_mode); + CLEAR_FOR_CORE_FILEMODE(st.st_mode); + } - cl_assert_equal_i((int)expected_st.st_mode, (int)st.st_mode); + cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o"); + } git_buf_free(&expected); git_buf_free(&actual); @@ -402,24 +413,19 @@ static void assert_mode_seems_okay( git_buf_free(&full); if (!core_filemode) { - expect_mode = expect_mode & ~0111; - st.st_mode = st.st_mode & ~0111; + CLEAR_FOR_CORE_FILEMODE(expect_mode); + CLEAR_FOR_CORE_FILEMODE(st.st_mode); expect_setgid = false; } - if (S_ISGID != 0) { - if (expect_setgid) - cl_assert((st.st_mode & S_ISGID) != 0); - else - cl_assert((st.st_mode & S_ISGID) == 0); - } + if (S_ISGID != 0) + cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0); - if ((expect_mode & 0111) != 0) - cl_assert((st.st_mode & 0111) != 0); - else - cl_assert((st.st_mode & 0111) == 0); + cl_assert_equal_b( + GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode)); - cl_assert((expect_mode & 0170000) == (st.st_mode & 0170000)); + cl_assert_equal_i_fmt( + GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o"); } void test_repo_init__extended_with_template(void) @@ -427,6 +433,7 @@ void test_repo_init__extended_with_template(void) git_buf expected = GIT_BUF_INIT; git_buf actual = GIT_BUF_INIT; git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + int filemode; cl_set_cleanup(&cleanup_repository, "templated.git"); @@ -450,13 +457,15 @@ void test_repo_init__extended_with_template(void) git_buf_free(&expected); git_buf_free(&actual); + filemode = cl_repo_get_bool(_repo, "core.filemode"); + assert_hooks_match( cl_fixture("template"), git_repository_path(_repo), - "hooks/update.sample", true); + "hooks/update.sample", filemode); assert_hooks_match( cl_fixture("template"), git_repository_path(_repo), - "hooks/link.sample", true); + "hooks/link.sample", filemode); } void test_repo_init__extended_with_template_and_shared_mode(void) @@ -464,7 +473,6 @@ void test_repo_init__extended_with_template_and_shared_mode(void) git_buf expected = GIT_BUF_INIT; git_buf actual = GIT_BUF_INIT; git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; - git_config *config; int filemode = true; const char *repo_path = NULL; @@ -480,9 +488,7 @@ void test_repo_init__extended_with_template_and_shared_mode(void) cl_assert(!git_repository_is_bare(_repo)); cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/")); - cl_git_pass(git_repository_config(&config, _repo)); - cl_git_pass(git_config_get_bool(&filemode, config, "core.filemode")); - git_config_free(config); + filemode = cl_repo_get_bool(_repo, "core.filemode"); cl_git_pass(git_futils_readbuffer( &expected, cl_fixture("template/description"))); @@ -530,3 +536,63 @@ void test_repo_init__can_reinit_an_initialized_repository(void) git_repository_free(reinit); } + +void test_repo_init__init_with_initial_commit(void) +{ + git_index *index; + + cl_set_cleanup(&cleanup_repository, "committed"); + + /* Initialize the repository */ + cl_git_pass(git_repository_init(&_repo, "committed", 0)); + + /* Init will be automatically created when requested for a new repo */ + cl_git_pass(git_repository_index(&index, _repo)); + + /* Create a file so we can commit it + * + * If you are writing code outside the test suite, you can create this + * file any way that you like, such as: + * FILE *fp = fopen("committed/file.txt", "w"); + * fputs("some stuff\n", fp); + * fclose(fp); + * We like to use the help functions because they do error detection + * in a way that's easily compatible with our test suite. + */ + cl_git_mkfile("committed/file.txt", "some stuff\n"); + + /* Add file to the index */ + cl_git_pass(git_index_add_bypath(index, "file.txt")); + cl_git_pass(git_index_write(index)); + + /* Make sure we're ready to use git_signature_default :-) */ + { + git_config *cfg, *local; + cl_git_pass(git_repository_config(&cfg, _repo)); + cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL)); + cl_git_pass(git_config_set_string(local, "user.name", "Test User")); + cl_git_pass(git_config_set_string(local, "user.email", "t@example.com")); + git_config_free(local); + git_config_free(cfg); + } + + /* Create a commit with the new contents of the index */ + { + git_signature *sig; + git_oid tree_id, commit_id; + git_tree *tree; + + cl_git_pass(git_signature_default(&sig, _repo)); + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id)); + + cl_git_pass(git_commit_create_v( + &commit_id, _repo, "HEAD", sig, sig, + NULL, "First", tree, 0)); + + git_tree_free(tree); + git_signature_free(sig); + } + + git_index_free(index); +} diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 11a7d2a23..1c513e9e7 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -906,6 +906,7 @@ void test_repo_iterator__fs2(void) static const char *expect_base[] = { "heads/br2", "heads/dir", + "heads/long-file-name", "heads/master", "heads/packed-test", "heads/subtrees", @@ -922,6 +923,6 @@ void test_repo_iterator__fs2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", 0, NULL, NULL)); - expect_iterator_items(i, 11, expect_base, 11, expect_base); + expect_iterator_items(i, 12, expect_base, 12, expect_base); git_iterator_free(i); } diff --git a/tests-clar/repo/open.c b/tests-clar/repo/open.c index 840858586..f386612a7 100644 --- a/tests-clar/repo/open.c +++ b/tests-clar/repo/open.c @@ -69,14 +69,23 @@ void test_repo_open__open_with_discover(void) cl_fixture_cleanup("attr"); } +static void make_gitlink_dir(const char *dir, const char *linktext) +{ + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_futils_mkdir(dir, NULL, 0777, GIT_MKDIR_VERIFY_DIR)); + cl_git_pass(git_buf_joinpath(&path, dir, ".git")); + cl_git_rewritefile(path.ptr, linktext); + git_buf_free(&path); +} + void test_repo_open__gitlinked(void) { /* need to have both repo dir and workdir set up correctly */ git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); git_repository *repo2; - cl_must_pass(p_mkdir("alternate", 0777)); - cl_git_mkfile("alternate/.git", "gitdir: ../empty_standard_repo/.git"); + make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git"); cl_git_pass(git_repository_open(&repo2, "alternate")); @@ -193,12 +202,11 @@ void test_repo_open__bad_gitlinks(void) cl_git_sandbox_init("attr"); - cl_git_pass(p_mkdir("alternate", 0777)); cl_git_pass(p_mkdir("invalid", 0777)); cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777)); for (scan = bad_links; *scan != NULL; scan++) { - cl_git_rewritefile("alternate/.git", *scan); + make_gitlink_dir("alternate", *scan); cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL)); } @@ -315,3 +323,52 @@ void test_repo_open__no_config(void) git_repository_free(repo); cl_fixture_cleanup("empty_standard_repo"); } + +void test_repo_open__force_bare(void) +{ + /* need to have both repo dir and workdir set up correctly */ + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_repository *barerepo; + + make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git"); + + cl_assert(!git_repository_is_bare(repo)); + + cl_git_pass(git_repository_open(&barerepo, "alternate")); + cl_assert(!git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(git_repository_open_bare( + &barerepo, "empty_standard_repo/.git")); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "alternate/.git", GIT_REPOSITORY_OPEN_BARE, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(p_mkdir("empty_standard_repo/subdir", 0777)); + cl_git_mkfile("empty_standard_repo/subdir/something.txt", "something"); + + cl_git_fail(git_repository_open_bare( + &barerepo, "empty_standard_repo/subdir")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "empty_standard_repo/subdir", GIT_REPOSITORY_OPEN_BARE, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(p_mkdir("alternate/subdir", 0777)); + cl_git_pass(p_mkdir("alternate/subdir/sub2", 0777)); + cl_git_mkfile("alternate/subdir/sub2/something.txt", "something"); + + cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "alternate/subdir/sub2", GIT_REPOSITORY_OPEN_BARE, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); +} diff --git a/tests-clar/repo/pathspec.c b/tests-clar/repo/pathspec.c new file mode 100644 index 000000000..334066b67 --- /dev/null +++ b/tests-clar/repo/pathspec.c @@ -0,0 +1,385 @@ +#include "clar_libgit2.h" +#include "git2/pathspec.h" + +static git_repository *g_repo; + +void test_repo_pathspec__initialize(void) +{ + g_repo = cl_git_sandbox_init("status"); +} + +void test_repo_pathspec__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static char *str0[] = { "*_file", "new_file", "garbage" }; +static char *str1[] = { "*_FILE", "NEW_FILE", "GARBAGE" }; +static char *str2[] = { "staged_*" }; +static char *str3[] = { "!subdir", "*_file", "new_file" }; +static char *str4[] = { "*" }; +static char *str5[] = { "S*" }; + +void test_repo_pathspec__workdir0(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 0)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES | GIT_PATHSPEC_FAILURES_ONLY, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir1(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_FILE", "NEW_FILE", "GARBAGE" } */ + s.strings = str1; s.count = ARRAY_SIZE(str1); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_IGNORE_CASE, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_fail(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_NO_MATCH_ERROR, ps)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir2(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "staged_*" } */ + s.strings = str2; s.count = ARRAY_SIZE(str2); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_fail(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_NO_MATCH_ERROR, ps)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir3(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "!subdir", "*_file", "new_file" } */ + s.strings = str3; s.count = ARRAY_SIZE(str3); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("new_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7)); + + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir4(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*" } */ + s.strings = str4; s.count = ARRAY_SIZE(str4); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("这", git_pathspec_match_list_entry(m, 12)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + + +void test_repo_pathspec__index0(void) +{ + git_index *idx; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + cl_git_pass(git_repository_index(&idx, g_repo)); + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_index(&m, idx, 0, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("staged_new_file_deleted_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 7)); + cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 8)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 9)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); + git_index_free(idx); +} + +void test_repo_pathspec__index1(void) +{ + /* Currently the USE_CASE and IGNORE_CASE flags don't work on the + * index because the index sort order for the index iterator is + * set by the index itself. I think the correct fix is for the + * index not to embed a global sort order but to support traversal + * in either case sensitive or insensitive order in a stateless + * manner. + * + * Anyhow, as it is, there is no point in doing this test. + */ +#if 0 + git_index *idx; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + cl_git_pass(git_repository_index(&idx, g_repo)); + + /* { "*_FILE", "NEW_FILE", "GARBAGE" } */ + s.strings = str1; s.count = ARRAY_SIZE(str1); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_USE_CASE, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); + git_index_free(idx); +#endif +} + +void test_repo_pathspec__tree0(void) +{ + git_object *tree; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(4, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__tree5(void) +{ + git_object *tree; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "S*" } */ + s.strings = str5; s.count = ARRAY_SIZE(str5); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("subdir.txt", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__in_memory(void) +{ + static char *strings[] = { "one", "two*", "!three*", "*four" }; + git_strarray s = { strings, ARRAY_SIZE(strings) }; + git_pathspec *ps; + + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_assert(git_pathspec_matches_path(ps, 0, "one")); + cl_assert(!git_pathspec_matches_path(ps, 0, "ONE")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_IGNORE_CASE, "ONE")); + cl_assert(git_pathspec_matches_path(ps, 0, "two")); + cl_assert(git_pathspec_matches_path(ps, 0, "two.txt")); + cl_assert(!git_pathspec_matches_path(ps, 0, "three.txt")); + cl_assert(git_pathspec_matches_path(ps, 0, "anything.four")); + cl_assert(!git_pathspec_matches_path(ps, 0, "three.four")); + cl_assert(!git_pathspec_matches_path(ps, 0, "nomatch")); + cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two*")); + cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "anyfour")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "*four")); + + git_pathspec_free(ps); +} diff --git a/tests-clar/resources/config/config11 b/tests-clar/resources/config/config11 index 7331862a5..1d8a74470 100644 --- a/tests-clar/resources/config/config11 +++ b/tests-clar/resources/config/config11 @@ -1,4 +1,4 @@ -[remote "fancy"] +[remote "ab"] url = git://github.com/libgit2/libgit2 url = git://git.example.com/libgit2 diff --git a/tests-clar/resources/duplicate.git/config b/tests-clar/resources/duplicate.git/config index 515f48362..a4ef456cb 100644 --- a/tests-clar/resources/duplicate.git/config +++ b/tests-clar/resources/duplicate.git/config @@ -1,5 +1,5 @@ [core] repositoryformatversion = 0 filemode = true - bare = false + bare = true logallrefupdates = true diff --git a/tests-clar/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea b/tests-clar/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea Binary files differnew file mode 100644 index 000000000..47c2a631a --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea diff --git a/tests-clar/resources/duplicate.git/objects/info/packs b/tests-clar/resources/duplicate.git/objects/info/packs index 3696a7d36..d0fdf905e 100644 --- a/tests-clar/resources/duplicate.git/objects/info/packs +++ b/tests-clar/resources/duplicate.git/objects/info/packs @@ -1,2 +1,3 @@ P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack +P pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx b/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx Binary files differnew file mode 100644 index 000000000..acbed82b6 --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack b/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack Binary files differnew file mode 100644 index 000000000..652b0c91f --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx b/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx Binary files differnew file mode 100644 index 000000000..fff685554 --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack b/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack Binary files differnew file mode 100644 index 000000000..e3e5f0e09 --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx b/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx Binary files differnew file mode 100644 index 000000000..9f78f6e0f --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack b/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack Binary files differnew file mode 100644 index 000000000..d1dd3b61a --- /dev/null +++ b/tests-clar/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack diff --git a/tests-clar/resources/duplicate.git/refs/heads/dummy-marker.txt b/tests-clar/resources/duplicate.git/refs/heads/dummy-marker.txt new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/tests-clar/resources/duplicate.git/refs/heads/dummy-marker.txt @@ -0,0 +1 @@ +dummy diff --git a/tests-clar/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 b/tests-clar/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 new file mode 100644 index 000000000..0bc57f266 --- /dev/null +++ b/tests-clar/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 @@ -0,0 +1,2 @@ +x%] +0S͏B)}
@bnf`f`>3(nibC0hQ6BMv2&-M0Q)+
tNsE*;}JϲN픹(th@#B˺C?TÅoyk7
\ No newline at end of file diff --git a/tests-clar/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 b/tests-clar/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 Binary files differnew file mode 100644 index 000000000..b7b81d5e3 --- /dev/null +++ b/tests-clar/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit-two b/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit-two new file mode 100644 index 000000000..abb365080 --- /dev/null +++ b/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit-two @@ -0,0 +1 @@ +36f79b2846017d3761e0a02d0bccd573e0f90c57 diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-tag b/tests-clar/resources/push_src/.gitted/refs/tags/tag-tag new file mode 100644 index 000000000..d6cd74804 --- /dev/null +++ b/tests-clar/resources/push_src/.gitted/refs/tags/tag-tag @@ -0,0 +1 @@ +eea4f2705eeec2db3813f2430829afce99cd00b5 diff --git a/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 new file mode 100644 index 000000000..01801ed11 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 @@ -0,0 +1,5 @@ +xEͱ
@QbWq
H_&{]yYX`='흶=ZohzF + + + +MhBЄ&4 MhB3ьf4hF3юKx
\ No newline at end of file diff --git a/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 new file mode 100644 index 000000000..a98d14ee7 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 @@ -0,0 +1,3 @@ +xmM +0`9\@OdE@&4hLI"]c}bծaBORvΡ5" +b0[kLopͭU˺SC; 8hsF_le2}ɩ-!Dg4*IDO;!~)>m䮔~*D
\ No newline at end of file diff --git a/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f Binary files differnew file mode 100644 index 000000000..90e107fa2 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f diff --git a/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b Binary files differnew file mode 100644 index 000000000..2fb025080 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two new file mode 100644 index 000000000..4ee5d04e1 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two @@ -0,0 +1 @@ +50e90273af7d826ff0a95865bcd3ba8412c447d9 diff --git a/tests-clar/resources/submodules/gitmodules b/tests-clar/resources/submodules/gitmodules index 1262f8bb0..2798b696c 100644 --- a/tests-clar/resources/submodules/gitmodules +++ b/tests-clar/resources/submodules/gitmodules @@ -1,3 +1,6 @@ [submodule "testrepo"] path = testrepo + url = +[submodule ""] + path = testrepo url =
\ No newline at end of file diff --git a/tests-clar/resources/testrepo.git/config b/tests-clar/resources/testrepo.git/config index 904a4e3f3..dfab4eeb4 100644 --- a/tests-clar/resources/testrepo.git/config +++ b/tests-clar/resources/testrepo.git/config @@ -10,7 +10,11 @@ url = git://github.com/libgit2/libgit2 [remote "empty-remote-url"] url = - + pushurl = +[remote "empty-remote-pushurl"] + pushurl = +[remote "no-remote-url"] + fetch = [remote "test_with_pushurl"] url = git://github.com/libgit2/fetchlibgit2 pushurl = git://github.com/libgit2/pushlibgit2 diff --git a/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 Binary files differnew file mode 100644 index 000000000..ee7c78174 --- /dev/null +++ b/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 diff --git a/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e b/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e Binary files differnew file mode 100644 index 000000000..197685b86 --- /dev/null +++ b/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e diff --git a/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a Binary files differnew file mode 100644 index 000000000..db778aaae --- /dev/null +++ b/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name b/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name new file mode 100644 index 000000000..1f942a746 --- /dev/null +++ b/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name @@ -0,0 +1 @@ +6b377958d8c6a4906e8573b53672a1a23a4e8ce6 diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index e82776260..6d55aed54 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -98,27 +98,46 @@ static int test_walk(git_revwalk *walk, const git_oid *root, return test_walk_only(walk, possible_results, results_count); } -static git_repository *_repo; -static git_revwalk *_walk; +static git_repository *_repo = NULL; +static git_revwalk *_walk = NULL; +static const char *_fixture = NULL; void test_revwalk_basic__initialize(void) { - cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); - cl_git_pass(git_revwalk_new(&_walk, _repo)); } void test_revwalk_basic__cleanup(void) { git_revwalk_free(_walk); - _walk = NULL; - git_repository_free(_repo); + + if (_fixture) + cl_git_sandbox_cleanup(); + else + git_repository_free(_repo); + + _fixture = NULL; _repo = NULL; + _walk = NULL; +} + +static void revwalk_basic_setup_walk(const char *fixture) +{ + if (fixture) { + _fixture = fixture; + _repo = cl_git_sandbox_init(fixture); + } else { + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); + } + + cl_git_pass(git_revwalk_new(&_walk, _repo)); } void test_revwalk_basic__sorting_modes(void) { git_oid id; + revwalk_basic_setup_walk(NULL); + git_oid_fromstr(&id, commit_head); cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME, commit_sorting_time, 1)); @@ -132,6 +151,8 @@ void test_revwalk_basic__glob_heads(void) int i = 0; git_oid oid; + revwalk_basic_setup_walk(NULL); + cl_git_pass(git_revwalk_push_glob(_walk, "heads")); while (git_revwalk_next(&oid, _walk) == 0) { @@ -142,11 +163,30 @@ void test_revwalk_basic__glob_heads(void) cl_assert(i == 14); } +void test_revwalk_basic__glob_heads_with_invalid(void) +{ + int i; + git_oid oid; + + revwalk_basic_setup_walk("testrepo"); + + cl_git_mkfile("testrepo/.git/refs/heads/garbage", "not-a-ref"); + cl_git_pass(git_revwalk_push_glob(_walk, "heads")); + + for (i = 0; !git_revwalk_next(&oid, _walk); ++i) + /* walking */; + + /* git log --branches --oneline | wc -l => 16 */ + cl_assert_equal_i(17, i); +} + void test_revwalk_basic__push_head(void) { int i = 0; git_oid oid; + revwalk_basic_setup_walk(NULL); + cl_git_pass(git_revwalk_push_head(_walk)); while (git_revwalk_next(&oid, _walk) == 0) { @@ -162,6 +202,8 @@ void test_revwalk_basic__push_head_hide_ref(void) int i = 0; git_oid oid; + revwalk_basic_setup_walk(NULL); + cl_git_pass(git_revwalk_push_head(_walk)); cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test")); @@ -178,6 +220,8 @@ void test_revwalk_basic__push_head_hide_ref_nobase(void) int i = 0; git_oid oid; + revwalk_basic_setup_walk(NULL); + cl_git_pass(git_revwalk_push_head(_walk)); cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed")); @@ -193,12 +237,16 @@ void test_revwalk_basic__disallow_non_commit(void) { git_oid oid; + revwalk_basic_setup_walk(NULL); + cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91")); cl_git_fail(git_revwalk_push(_walk, &oid)); } void test_revwalk_basic__push_range(void) { + revwalk_basic_setup_walk(NULL); + git_revwalk_reset(_walk); git_revwalk_sorting(_walk, 0); cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e")); diff --git a/tests-clar/revwalk/mergebase.c b/tests-clar/revwalk/mergebase.c index e2617ab0e..a2dbbc738 100644 --- a/tests-clar/revwalk/mergebase.c +++ b/tests-clar/revwalk/mergebase.c @@ -123,6 +123,18 @@ void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void) cl_assert_equal_sz(4, behind); } +void test_revwalk_mergebase__prefer_youngest_merge_base(void) +{ + git_oid result, one, two, expected; + + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f ")); + cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + + cl_git_pass(git_merge_base(&result, _repo, &one, &two)); + cl_assert(git_oid_cmp(&result, &expected) == 0); +} + void test_revwalk_mergebase__no_off_by_one_missing(void) { git_oid result, one, two; diff --git a/tests-clar/revwalk/simplify.c b/tests-clar/revwalk/simplify.c new file mode 100644 index 000000000..c94952105 --- /dev/null +++ b/tests-clar/revwalk/simplify.c @@ -0,0 +1,51 @@ +#include "clar_libgit2.h" + +/* + * a4a7dce [0] Merge branch 'master' into br2 + |\ + | * 9fd738e [1] a fourth commit + | * 4a202b3 [2] a third commit + * | c47800c [3] branch commit one + |/ + * 5b5b025 [5] another commit + * 8496071 [4] testing +*/ +static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f"; + +static const char *expected_str[] = { + "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */ + "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */ + "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */ + "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */ +}; + +void test_revwalk_simplify__first_parent(void) +{ + git_repository *repo; + git_revwalk *walk; + git_oid id, expected[4]; + int i, error; + + for (i = 0; i < 4; i++) { + git_oid_fromstr(&expected[i], expected_str[i]); + } + + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_revwalk_new(&walk, repo)); + + git_oid_fromstr(&id, commit_head); + cl_git_pass(git_revwalk_push(walk, &id)); + git_revwalk_simplify_first_parent(walk); + + i = 0; + while ((error = git_revwalk_next(&id, walk)) == 0) { + git_oid_cmp(&id, &expected[i]); + i++; + } + + cl_assert_equal_i(i, 4); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_revwalk_free(walk); + git_repository_free(repo); +} diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c index 4f6879cfc..acdc8fb58 100644 --- a/tests-clar/status/ignore.c +++ b/tests-clar/status/ignore.c @@ -459,3 +459,124 @@ void test_status_ignore__automatically_ignore_bad_files(void) cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c")); cl_assert(!ignored); } + +void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void) +{ + status_entry_single st; + char *test_cases[] = { + "!file", + "#blah", + "[blah]", + "[attr]", + "[attr]blah", + NULL + }; + int i; + + for (i = 0; *(test_cases + i) != NULL; i++) { + git_buf file = GIT_BUF_INIT; + char *file_name = *(test_cases + i); + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name)); + cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!"); + + memset(&st, 0, sizeof(st)); + cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); + cl_assert(st.count == 1); + cl_assert(st.status == GIT_STATUS_WT_NEW); + + cl_git_pass(git_status_file(&st.status, repo, file_name)); + cl_assert(st.status == GIT_STATUS_WT_NEW); + + cl_git_sandbox_cleanup(); + git_buf_free(&file); + } +} + +void test_status_ignore__issue_1766_negated_ignores(void) +{ + int ignored = 0; + unsigned int status; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_futils_mkdir_r( + "empty_standard_repo/a", NULL, 0775)); + cl_git_mkfile( + "empty_standard_repo/a/.gitignore", "*\n!.gitignore\n"); + cl_git_mkfile( + "empty_standard_repo/a/ignoreme", "I should be ignored\n"); + + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore")); + cl_assert(!ignored); + + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme")); + cl_assert(ignored); + + cl_git_pass(git_futils_mkdir_r( + "empty_standard_repo/b", NULL, 0775)); + cl_git_mkfile( + "empty_standard_repo/b/.gitignore", "*\n!.gitignore\n"); + cl_git_mkfile( + "empty_standard_repo/b/ignoreme", "I should be ignored\n"); + + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/.gitignore")); + cl_assert(!ignored); + + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/ignoreme")); + cl_assert(ignored); + + /* shouldn't have changed results from first couple either */ + + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore")); + cl_assert(!ignored); + cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme")); + cl_assert(ignored); + + /* status should find the two ignore files and nothing else */ + + cl_git_pass(git_status_file(&status, g_repo, "a/.gitignore")); + cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status); + + cl_git_pass(git_status_file(&status, g_repo, "a/ignoreme")); + cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status); + + cl_git_pass(git_status_file(&status, g_repo, "b/.gitignore")); + cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status); + + cl_git_pass(git_status_file(&status, g_repo, "b/ignoreme")); + cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status); + + { + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *paths[] = { + "a/.gitignore", + "a/ignoreme", + "b/.gitignore", + "b/ignoreme", + }; + static const unsigned int statuses[] = { + GIT_STATUS_WT_NEW, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, + GIT_STATUS_IGNORED, + }; + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 4; + counts.expected_paths = paths; + counts.expected_statuses = statuses; + + opts.flags = GIT_STATUS_OPT_DEFAULTS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + } +} + diff --git a/tests-clar/status/renames.c b/tests-clar/status/renames.c new file mode 100644 index 000000000..d72e563bf --- /dev/null +++ b/tests-clar/status/renames.c @@ -0,0 +1,555 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "path.h" +#include "posix.h" +#include "status_helpers.h" +#include "util.h" +#include "status.h" + +static git_repository *g_repo = NULL; + +void test_status_renames__initialize(void) +{ + g_repo = cl_git_sandbox_init("renames"); +} + +void test_status_renames__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void rename_file(git_repository *repo, const char *oldname, const char *newname) +{ + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + + git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); + git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); + + cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); + + git_buf_free(&oldpath); + git_buf_free(&newpath); +} + +static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname) +{ + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + + git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); + git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); + + cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); + cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!"); + + git_buf_free(&oldpath); + git_buf_free(&newpath); +} + +struct status_entry { + git_status_t status; + const char *oldname; + const char *newname; +}; + +static void test_status( + git_status_list *status_list, + struct status_entry *expected_list, + size_t expected_len) +{ + const git_status_entry *actual; + const struct status_entry *expected; + const char *oldname, *newname; + size_t i; + + cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list)); + + for (i = 0; i < expected_len; i++) { + actual = git_status_byindex(status_list, i); + expected = &expected_list[i]; + + cl_assert_equal_i((int)expected->status, (int)actual->status); + + oldname = actual->head_to_index ? actual->head_to_index->old_file.path : + actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL; + + newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path : + actual->head_to_index ? actual->head_to_index->new_file.path : NULL; + + if (oldname) + cl_assert(git__strcmp(oldname, expected->oldname) == 0); + else + cl_assert(expected->oldname == NULL); + + if (newname) + cl_assert(git__strcmp(newname, expected->newname) == 0); + else + cl_assert(expected->newname == NULL); + } +} + +void test_status_renames__head2index_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "newname.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "newname.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__head2index_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "untimely.txt", "bbb.txt" }, + { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" }, + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "ddd.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt"); + rename_file(g_repo, "songof7cities.txt", "ccc.txt"); + rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ddd.txt")); + cl_git_pass(git_index_add_bypath(index, "aaa.txt")); + cl_git_pass(git_index_add_bypath(index, "ccc.txt")); + cl_git_pass(git_index_add_bypath(index, "bbb.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__head2index_no_rename_from_rewrite(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" }, + { GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "_temp_.txt"); + rename_file(g_repo, "sixserving.txt", "ikeepsix.txt"); + rename_file(g_repo, "_temp_.txt", "sixserving.txt"); + + cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 2); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__head2index_rename_from_rewrite(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" }, + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "_temp_.txt"); + rename_file(g_repo, "sixserving.txt", "ikeepsix.txt"); + rename_file(g_repo, "_temp_.txt", "sixserving.txt"); + + cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 2); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__index2workdir_one(void) +{ + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + rename_file(g_repo, "ikeepsix.txt", "newname.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); +} + +void test_status_renames__index2workdir_two(void) +{ + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "untimely.txt", "bbb.txt" }, + { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" }, + { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + rename_file(g_repo, "ikeepsix.txt", "ddd.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt"); + rename_file(g_repo, "songof7cities.txt", "ccc.txt"); + rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); +} + +void test_status_renames__index2workdir_rename_from_rewrite(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" }, + { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "_temp_.txt"); + rename_file(g_repo, "sixserving.txt", "ikeepsix.txt"); + rename_file(g_repo, "_temp_.txt", "sixserving.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 2); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "ikeepsix.txt", "newname-workdir.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "newname-index.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "newname-index.txt")); + cl_git_pass(git_index_write(index)); + + rename_file(g_repo, "newname-index.txt", "newname-workdir.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "ikeepsix-both.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "sixserving-index.txt" }, + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "songof7cities.txt", "songof7cities-workdir.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimely-both.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt"); + rename_file(g_repo, "untimely.txt", "untimely-index.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely-index.txt")); + cl_git_pass(git_index_write(index)); + + rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt"); + rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt"); + rename_file(g_repo, "untimely-index.txt", "untimely-both.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); + + git_index_free(index); +} + + +void test_status_renames__both_rename_from_rewrite(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "songof7cities.txt", "ikeepsix.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "ikeepsix.txt", "sixserving.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "sixserving.txt", "songof7cities.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "_temp_.txt"); + rename_file(g_repo, "sixserving.txt", "ikeepsix.txt"); + rename_file(g_repo, "songof7cities.txt", "sixserving.txt"); + rename_file(g_repo, "_temp_.txt", "songof7cities.txt"); + + cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_write(index)); + + rename_file(g_repo, "songof7cities.txt", "_temp_.txt"); + rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt"); + rename_file(g_repo, "sixserving.txt", "ikeepsix.txt"); + rename_file(g_repo, "_temp_.txt", "sixserving.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 3); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__rewrites_only_for_renames(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_rewritefile("renames/ikeepsix.txt", + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n" \ + "This is enough content for the file to be rewritten.\n"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_casechange_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int index_caps; + struct status_entry expected_icase[] = { + { GIT_STATUS_INDEX_RENAMED, + "ikeepsix.txt", "IKeepSix.txt" }, + }; + struct status_entry expected_case[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "ikeepsix.txt", "IKEEPSIX.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + index_caps = git_index_caps(index); + + rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt")); + cl_git_pass(git_index_write(index)); + + /* on a case-insensitive file system, this change won't matter. + * on a case-sensitive one, it will. + */ + rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + + test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + expected_icase : expected_case, 1); + + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_casechange_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int index_caps; + struct status_entry expected_icase[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "IKeepSix.txt" }, + { GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "sixserving.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED, + "songof7cities.txt", "songof7.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimeliest.txt" } + }; + struct status_entry expected_case[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "ikeepsix.txt" }, + { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED, + "sixserving.txt", "SixServing.txt" }, + { GIT_STATUS_INDEX_RENAMED | + GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED, + "songof7cities.txt", "SONGOF7.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimeliest.txt" } + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + index_caps = git_index_caps(index); + + rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt"); + rename_file(g_repo, "songof7cities.txt", "songof7.txt"); + rename_file(g_repo, "untimely.txt", "untimelier.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_add_bypath(index, "songof7.txt")); + cl_git_pass(git_index_add_bypath(index, "untimelier.txt")); + cl_git_pass(git_index_write(index)); + + rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt"); + rename_file(g_repo, "sixserving.txt", "SixServing.txt"); + rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt"); + rename_file(g_repo, "untimelier.txt", "untimeliest.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + + test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + expected_icase : expected_case, 4); + + git_status_list_free(statuslist); + + git_index_free(index); +} diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h index a41bde7c2..8ad4235fd 100644 --- a/tests-clar/status/status_data.h +++ b/tests-clar/status/status_data.h @@ -1,5 +1,9 @@ #include "status_helpers.h" +// A utf-8 string with 83 characters, but 249 bytes. +static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97"; + + /* entries for a plain copy of tests/resources/status */ static const char *entry_paths0[] = { @@ -250,3 +254,73 @@ static const unsigned int entry_statuses4[] = { }; static const int entry_count4 = 23; + + +/* entries for a copy of tests/resources/status with options + * passed to the status call in order to only get the differences + * between the HEAD and the index (changes to be committed) + */ + +static const char *entry_paths5[] = { + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", +}; + +static const unsigned int entry_statuses5[] = { + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_NEW, +}; + +static const int entry_count5 = 8; + + +/* entries for a copy of tests/resources/status with options + * passed to the status call in order to only get the differences + * between the workdir and the index (changes not staged, untracked files) + */ + +static const char *entry_paths6[] = { + "file_deleted", + "ignored_file", + "modified_file", + "new_file", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_modified_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + "subdir/deleted_file", + "subdir/modified_file", + "subdir/new_file", + "\xe8\xbf\x99", +}; + +static const unsigned int entry_statuses6[] = { + GIT_STATUS_WT_DELETED, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, +}; + +static const int entry_count6 = 13; diff --git a/tests-clar/status/status_helpers.c b/tests-clar/status/status_helpers.c index f073c2491..902b65c4f 100644 --- a/tests-clar/status/status_helpers.c +++ b/tests-clar/status/status_helpers.c @@ -6,6 +6,9 @@ int cb_status__normal( { status_entry_counts *counts = payload; + if (counts->debug) + cb_status__print(path, status_flags, NULL); + if (counts->entry_count >= counts->expected_entry_count) { counts->wrong_status_flags_count++; goto exit; diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h index ae1469e79..f1f009e02 100644 --- a/tests-clar/status/status_helpers.h +++ b/tests-clar/status/status_helpers.h @@ -8,6 +8,7 @@ typedef struct { const unsigned int* expected_statuses; const char** expected_paths; int expected_entry_count; + bool debug; } status_entry_counts; /* cb_status__normal takes payload of "status_entry_counts *" */ diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c index af8707721..7bfef503f 100644 --- a/tests-clar/status/submodules.c +++ b/tests-clar/status/submodules.c @@ -9,25 +9,19 @@ static git_repository *g_repo = NULL; void test_status_submodules__initialize(void) { - g_repo = cl_git_sandbox_init("submodules"); - - cl_fixture_sandbox("testrepo.git"); - - rewrite_gitmodules(git_repository_workdir(g_repo)); - - p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git"); } void test_status_submodules__cleanup(void) { - cl_git_sandbox_cleanup(); - cl_fixture_cleanup("testrepo.git"); + cleanup_fixture_submodules(); } void test_status_submodules__api(void) { git_submodule *sm; + g_repo = setup_fixture_submodules(); + cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND); cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND); @@ -42,6 +36,8 @@ void test_status_submodules__0(void) { int counts = 0; + g_repo = setup_fixture_submodules(); + cl_assert(git_path_isdir("submodules/.git")); cl_assert(git_path_isdir("submodules/testrepo/.git")); cl_assert(git_path_isfile("submodules/.gitmodules")); @@ -86,6 +82,8 @@ void test_status_submodules__1(void) { status_entry_counts counts; + g_repo = setup_fixture_submodules(); + cl_assert(git_path_isdir("submodules/.git")); cl_assert(git_path_isdir("submodules/testrepo/.git")); cl_assert(git_path_isfile("submodules/.gitmodules")); @@ -104,6 +102,7 @@ void test_status_submodules__1(void) void test_status_submodules__single_file(void) { unsigned int status = 0; + g_repo = setup_fixture_submodules(); cl_git_pass( git_status_file(&status, g_repo, "testrepo") ); cl_assert(!status); } @@ -134,6 +133,8 @@ void test_status_submodules__moved_head(void) GIT_STATUS_WT_NEW }; + g_repo = setup_fixture_submodules(); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); cl_git_pass(git_submodule_open(&smrepo, sm)); @@ -192,6 +193,8 @@ void test_status_submodules__dirty_workdir_only(void) GIT_STATUS_WT_NEW }; + g_repo = setup_fixture_submodules(); + cl_git_rewritefile("submodules/testrepo/README", "heyheyhey"); cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before"); diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 13335843b..be7398cb6 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -40,6 +40,48 @@ void test_status_worktree__whole_repository(void) cl_assert_equal_i(0, counts.wrong_sorted_path); } +void assert_show(const int entry_counts, const char *entry_paths[], + const unsigned int entry_statuses[], git_status_show_t show) +{ + status_entry_counts counts; + git_repository *repo = cl_git_sandbox_init("status"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = entry_counts; + counts.expected_paths = entry_paths; + counts.expected_statuses = entry_statuses; + + opts.flags = GIT_STATUS_OPT_DEFAULTS; + opts.show = show; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) + ); + + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} + +void test_status_worktree__show_index_and_workdir(void) +{ + assert_show(entry_count0, entry_paths0, entry_statuses0, + GIT_STATUS_SHOW_INDEX_AND_WORKDIR); +} + +void test_status_worktree__show_index_only(void) +{ + assert_show(entry_count5, entry_paths5, entry_statuses5, + GIT_STATUS_SHOW_INDEX_ONLY); +} + +void test_status_worktree__show_workdir_only(void) +{ + assert_show(entry_count6, entry_paths6, entry_statuses6, + GIT_STATUS_SHOW_WORKDIR_ONLY); +} + /* this test is equivalent to t18-status.c:statuscb1 */ void test_status_worktree__empty_repository(void) { @@ -695,3 +737,163 @@ void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracke /* Actually returns GIT_STATUS_IGNORED on Windows */ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); } + +void test_status_worktree__simple_delete(void) +{ + git_repository *repo = cl_git_sandbox_init("renames"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int count; + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | + GIT_STATUS_OPT_EXCLUDE_SUBMODULES | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + count = 0; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); + cl_assert_equal_i(0, count); + + cl_must_pass(p_unlink("renames/untimely.txt")); + + count = 0; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); + cl_assert_equal_i(1, count); +} + +void test_status_worktree__simple_delete_indexed(void) +{ + git_repository *repo = cl_git_sandbox_init("renames"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *status; + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | + GIT_STATUS_OPT_EXCLUDE_SUBMODULES | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + cl_assert_equal_sz(0, git_status_list_entrycount(status)); + git_status_list_free(status); + + cl_must_pass(p_unlink("renames/untimely.txt")); + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + cl_assert_equal_sz(1, git_status_list_entrycount(status)); + cl_assert_equal_i( + GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status); + git_status_list_free(status); +} + +static const char *icase_paths[] = { "B", "c", "g", "H" }; +static unsigned int icase_statuses[] = { + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, +}; + +static const char *case_paths[] = { "B", "H", "c", "g" }; +static unsigned int case_statuses[] = { + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED, +}; + +void test_status_worktree__sorting_by_case(void) +{ + git_repository *repo = cl_git_sandbox_init("icase"); + git_index *index; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + bool native_ignore_case; + status_entry_counts counts; + + cl_git_pass(git_repository_index(&index, repo)); + native_ignore_case = + (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0; + git_index_free(index); + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 0; + counts.expected_paths = NULL; + counts.expected_statuses = NULL; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + cl_git_rewritefile("icase/B", "new stuff"); + cl_must_pass(p_unlink("icase/c")); + cl_git_rewritefile("icase/g", "new stuff"); + cl_must_pass(p_unlink("icase/H")); + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + if (native_ignore_case) { + counts.expected_paths = icase_paths; + counts.expected_statuses = icase_statuses; + } else { + counts.expected_paths = case_paths; + counts.expected_statuses = case_statuses; + } + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + counts.expected_paths = case_paths; + counts.expected_statuses = case_statuses; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY; + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + counts.expected_paths = icase_paths; + counts.expected_statuses = icase_statuses; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} + +void test_status_worktree__long_filenames(void) +{ + char path[260*4+1]; + const char *expected_paths[] = {path}; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts = {0}; + + // Create directory with amazingly long filename + sprintf(path, "empty_standard_repo/%s", longname); + cl_git_pass(git_futils_mkdir_r(path, NULL, 0777)); + sprintf(path, "empty_standard_repo/%s/foo", longname); + cl_git_mkfile(path, "dummy"); + + sprintf(path, "%s/foo", longname); + counts.expected_entry_count = 1; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = GIT_STATUS_OPT_DEFAULTS; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} + diff --git a/tests-clar/stress/diff.c b/tests-clar/stress/diff.c new file mode 100644 index 000000000..0524aa108 --- /dev/null +++ b/tests-clar/stress/diff.c @@ -0,0 +1,164 @@ +#include "clar_libgit2.h" +#include "../diff/diff_helpers.h" + +static git_repository *g_repo = NULL; + +void test_stress_diff__initialize(void) +{ +} + +void test_stress_diff__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +#define ANOTHER_POEM \ +"OH, glorious are the guarded heights\nWhere guardian souls abide—\nSelf-exiled from our gross delights—\nAbove, beyond, outside:\nAn ampler arc their spirit swings—\nCommands a juster view—\nWe have their word for all these things,\nNo doubt their words are true.\n\nYet we, the bond slaves of our day,\nWhom dirt and danger press—\nCo-heirs of insolence, delay,\nAnd leagued unfaithfulness—\nSuch is our need must seek indeed\nAnd, having found, engage\nThe men who merely do the work\nFor which they draw the wage.\n\nFrom forge and farm and mine and bench,\nDeck, altar, outpost lone—\nMill, school, battalion, counter, trench,\nRail, senate, sheepfold, throne—\nCreation's cry goes up on high\nFrom age to cheated age:\n\"Send us the men who do the work\n\"For which they draw the wage!\"\n" + +static void test_with_many(int expected_new) +{ + git_index *index; + git_tree *tree, *new_tree; + git_diff_list *diff = NULL; + diff_expects exp; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt")); + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, NULL, NULL, &exp)); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(expected_new + 2, exp.files); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, NULL, NULL, &exp)); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(expected_new + 1, exp.files); + + git_diff_list_free(diff); + + { + git_object *parent; + git_signature *sig; + git_oid tree_id, commit_id; + git_reference *ref; + + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&new_tree, g_repo, &tree_id)); + + cl_git_pass(git_revparse_ext(&parent, &ref, g_repo, "HEAD")); + cl_git_pass(git_signature_new( + &sig, "Sm Test", "sm@tester.test", 1372350000, 480)); + + cl_git_pass(git_commit_create_v( + &commit_id, g_repo, git_reference_name(ref), sig, sig, + NULL, "yoyoyo", new_tree, 1, parent)); + + git_object_free(parent); + git_reference_free(ref); + git_signature_free(sig); + } + + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree, new_tree, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, NULL, NULL, &exp)); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(expected_new + 2, exp.files); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, NULL, NULL, &exp)); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(expected_new + 1, exp.files); + + git_diff_list_free(diff); + + git_tree_free(new_tree); + git_tree_free(tree); + git_index_free(index); +} + +void test_stress_diff__rename_big_files(void) +{ + git_index *index; + char tmp[64]; + int i, j; + git_buf b = GIT_BUF_INIT; + + g_repo = cl_git_sandbox_init("renames"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + for (i = 0; i < 100; i += 1) { + snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + for (j = i * 256; j > 0; --j) + git_buf_printf(&b, "more content %d\n", i); + cl_git_mkfile(tmp, b.ptr); + } + + for (i = 0; i < 100; i += 1) { + snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/"))); + } + + git_buf_free(&b); + git_index_free(index); + + test_with_many(100); +} + +void test_stress_diff__rename_many_files(void) +{ + git_index *index; + char tmp[64]; + int i; + git_buf b = GIT_BUF_INIT; + + g_repo = cl_git_sandbox_init("renames"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + git_buf_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0); + + for (i = 0; i < 2500; i += 1) { + snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + snprintf(b.ptr, 9, "%08d", i); + b.ptr[8] = '\n'; + cl_git_mkfile(tmp, b.ptr); + } + git_buf_free(&b); + + for (i = 0; i < 2500; i += 1) { + snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/"))); + } + + git_index_free(index); + + test_with_many(2500); +} diff --git a/tests-clar/submodule/lookup.c b/tests-clar/submodule/lookup.c index acf8f6462..013bbdf96 100644 --- a/tests-clar/submodule/lookup.c +++ b/tests-clar/submodule/lookup.c @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "submodule_helpers.h" #include "posix.h" +#include "git2/sys/repository.h" static git_repository *g_repo = NULL; @@ -112,3 +113,73 @@ void test_submodule_lookup__foreach(void) cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); cl_assert_equal_i(8, data.count); } + +void test_submodule_lookup__lookup_even_with_orphaned_head(void) +{ + git_reference *orphan; + git_submodule *sm; + + /* orphan the head */ + cl_git_pass(git_reference_symbolic_create( + &orphan, g_repo, "HEAD", "refs/heads/garbage", 1)); + git_reference_free(orphan); + + /* lookup existing */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); + cl_assert(sm); + + /* lookup pending change in .gitmodules that is not in HEAD */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); + cl_assert(sm); + + /* lookup pending change in .gitmodules that is neither in HEAD nor index */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); + cl_assert(sm); + + /* lookup git repo subdir that is not added as submodule */ + cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); + + /* lookup existing directory that is not a submodule */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); + + /* lookup existing file that is not a submodule */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file")); + + /* lookup non-existent item */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file")); +} + +void test_submodule_lookup__lookup_even_with_missing_index(void) +{ + git_index *idx; + git_submodule *sm; + + /* give the repo an empty index */ + cl_git_pass(git_index_new(&idx)); + git_repository_set_index(g_repo, idx); + git_index_free(idx); + + /* lookup existing */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); + cl_assert(sm); + + /* lookup pending change in .gitmodules that is not in HEAD */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); + cl_assert(sm); + + /* lookup pending change in .gitmodules that is neither in HEAD nor index */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); + cl_assert(sm); + + /* lookup git repo subdir that is not added as submodule */ + cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); + + /* lookup existing directory that is not a submodule */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); + + /* lookup existing file that is not a submodule */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file")); + + /* lookup non-existent item */ + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file")); +} diff --git a/tests-clar/submodule/modify.c b/tests-clar/submodule/modify.c index 94eb3738a..c0498ce6e 100644 --- a/tests-clar/submodule/modify.c +++ b/tests-clar/submodule/modify.c @@ -204,10 +204,10 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_set_url(sm1, old_url)); cl_assert_equal_i( (int)GIT_SUBMODULE_IGNORE_UNTRACKED, - (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT)); + (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET)); cl_assert_equal_i( (int)GIT_SUBMODULE_UPDATE_REBASE, - (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT)); + (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET)); cl_assert_equal_i( 1, git_submodule_set_fetch_recurse_submodules(sm1, old_fetchrecurse)); @@ -228,10 +228,10 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_save(sm1)); /* attempt to "revert" values */ - git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT); - git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT); + git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET); + git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET); - /* but ignore and update should NOT revert because the DEFAULT + /* but ignore and update should NOT revert because the RESET * should now be the newly saved value... */ cl_assert_equal_i( diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c index 39c83a0b7..7b29ac288 100644 --- a/tests-clar/submodule/status.c +++ b/tests-clar/submodule/status.c @@ -9,21 +9,12 @@ static git_repository *g_repo = NULL; void test_submodule_status__initialize(void) { - g_repo = cl_git_sandbox_init("submod2"); - - cl_fixture_sandbox("submod2_target"); - p_rename("submod2_target/.gitted", "submod2_target/.git"); - - /* must create submod2_target before rewrite so prettify will work */ - rewrite_gitmodules(git_repository_workdir(g_repo)); - p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git"); - p_rename("submod2/not/.gitted", "submod2/not/.git"); + g_repo = setup_fixture_submod2(); } void test_submodule_status__cleanup(void) { - cl_git_sandbox_cleanup(); - cl_fixture_cleanup("submod2_target"); + cleanup_fixture_submodules(); } void test_submodule_status__unchanged(void) @@ -326,6 +317,7 @@ void test_submodule_status__ignore_all(void) typedef struct { size_t counter; const char **paths; + int *statuses; } submodule_expectations; static int confirm_submodule_status( @@ -336,6 +328,7 @@ static int confirm_submodule_status( while (git__suffixcmp(exp->paths[exp->counter], "/") == 0) exp->counter++; + cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags); cl_assert_equal_s(exp->paths[exp->counter++], path); GIT_UNUSED(status_flags); @@ -365,7 +358,24 @@ void test_submodule_status__iterator(void) "sm_unchanged", NULL }; - submodule_expectations exp = { 0, expected }; + static int expected_flags[] = { + GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */ + 0, /* "just_a_dir/" will be skipped */ + GIT_STATUS_CURRENT, /* "just_a_dir/contents" */ + GIT_STATUS_CURRENT, /* "just_a_file" */ + GIT_STATUS_IGNORED, /* "not" (contains .git) */ + GIT_STATUS_IGNORED, /* "not-submodule" (contains .git) */ + GIT_STATUS_CURRENT, /* "README.txt */ + GIT_STATUS_INDEX_NEW, /* "sm_added_and_uncommited" */ + GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */ + GIT_STATUS_WT_MODIFIED, /* "sm_changed_head" */ + GIT_STATUS_WT_MODIFIED, /* "sm_changed_index" */ + GIT_STATUS_WT_MODIFIED, /* "sm_changed_untracked_file" */ + GIT_STATUS_WT_MODIFIED, /* "sm_missing_commits" */ + GIT_STATUS_CURRENT, /* "sm_unchanged" */ + 0 + }; + submodule_expectations exp = { 0, expected, expected_flags }; git_status_options opts = GIT_STATUS_OPTIONS_INIT; cl_git_pass(git_iterator_for_workdir(&iter, g_repo, @@ -376,9 +386,13 @@ void test_submodule_status__iterator(void) git_iterator_free(iter); - opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_INCLUDE_UNMODIFIED | + GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; - cl_git_pass(git_status_foreach_ext(g_repo, &opts, confirm_submodule_status, &exp)); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, confirm_submodule_status, &exp)); } void test_submodule_status__untracked_dirs_containing_ignored_files(void) diff --git a/tests-clar/submodule/submodule_helpers.c b/tests-clar/submodule/submodule_helpers.c index 0c3e79f71..a7807522b 100644 --- a/tests-clar/submodule/submodule_helpers.c +++ b/tests-clar/submodule/submodule_helpers.c @@ -82,3 +82,38 @@ void rewrite_gitmodules(const char *workdir) git_buf_free(&out_f); git_buf_free(&path); } + +git_repository *setup_fixture_submodules(void) +{ + git_repository *repo = cl_git_sandbox_init("submodules"); + + cl_fixture_sandbox("testrepo.git"); + + rewrite_gitmodules(git_repository_workdir(repo)); + p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git"); + + return repo; +} + +git_repository *setup_fixture_submod2(void) +{ + git_repository *repo = cl_git_sandbox_init("submod2"); + + cl_fixture_sandbox("submod2_target"); + p_rename("submod2_target/.gitted", "submod2_target/.git"); + + rewrite_gitmodules(git_repository_workdir(repo)); + p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git"); + p_rename("submod2/not/.gitted", "submod2/not/.git"); + + return repo; +} + +void cleanup_fixture_submodules(void) +{ + cl_git_sandbox_cleanup(); + + /* just try to clean up both possible extras */ + cl_fixture_cleanup("testrepo.git"); + cl_fixture_cleanup("submod2_target"); +} diff --git a/tests-clar/submodule/submodule_helpers.h b/tests-clar/submodule/submodule_helpers.h index 6b76a832e..1de15ca17 100644 --- a/tests-clar/submodule/submodule_helpers.h +++ b/tests-clar/submodule/submodule_helpers.h @@ -1,2 +1,5 @@ extern void rewrite_gitmodules(const char *workdir); +extern git_repository *setup_fixture_submodules(void); +extern git_repository *setup_fixture_submod2(void); +extern void cleanup_fixture_submodules(void); diff --git a/tests-clar/threads/refdb.c b/tests-clar/threads/refdb.c new file mode 100644 index 000000000..f8d76cb9b --- /dev/null +++ b/tests-clar/threads/refdb.c @@ -0,0 +1,211 @@ +#include "clar_libgit2.h" +#include "git2/refdb.h" +#include "refdb.h" + +static git_repository *g_repo; +static int g_expected = 0; + +void test_threads_refdb__initialize(void) +{ + g_repo = NULL; +} + +void test_threads_refdb__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +#define REPEAT 20 +#define THREADS 20 + +static void *iterate_refs(void *arg) +{ + git_reference_iterator *i; + git_reference *ref; + int count = 0; + + cl_git_pass(git_reference_iterator_new(&i, g_repo)); + + for (count = 0; !git_reference_next(&ref, i); ++count) { + cl_assert(ref != NULL); + git_reference_free(ref); + } + + if (g_expected > 0) + cl_assert_equal_i(g_expected, count); + + git_reference_iterator_free(i); + + return arg; +} + +void test_threads_refdb__iterator(void) +{ + int r, t; + git_thread th[THREADS]; + int id[THREADS]; + git_oid head; + git_reference *ref; + char name[128]; + git_refdb *refdb; + + g_repo = cl_git_sandbox_init("testrepo2"); + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + /* make a bunch of references */ + + for (r = 0; r < 200; ++r) { + snprintf(name, sizeof(name), "refs/heads/direct-%03d", r); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + git_reference_free(ref); + } + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + + g_expected = 206; + + for (r = 0; r < REPEAT; ++r) { + g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */ + + for (t = 0; t < THREADS; ++t) { + id[t] = t; +#ifdef GIT_THREADS + cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t])); +#else + th[t] = t; + iterate_refs(&id[t]); +#endif + } + +#ifdef GIT_THREADS + for (t = 0; t < THREADS; ++t) { + cl_git_pass(git_thread_join(th[t], NULL)); + } +#endif + + memset(th, 0, sizeof(th)); + } +} + +static void *create_refs(void *arg) +{ + int *id = arg, i; + git_oid head; + char name[128]; + git_reference *ref[10]; + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + for (i = 0; i < 10; ++i) { + snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i); + cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0)); + + if (i == 5) { + git_refdb *refdb; + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + } + } + + for (i = 0; i < 10; ++i) + git_reference_free(ref[i]); + + return arg; +} + +static void *delete_refs(void *arg) +{ + int *id = arg, i; + git_reference *ref; + char name[128]; + + for (i = 0; i < 10; ++i) { + snprintf( + name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i); + + if (!git_reference_lookup(&ref, g_repo, name)) { + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + } + + if (i == 5) { + git_refdb *refdb; + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + } + } + + return arg; +} + +void test_threads_refdb__edit_while_iterate(void) +{ + int r, t; + git_thread th[THREADS]; + int id[THREADS]; + git_oid head; + git_reference *ref; + char name[128]; + git_refdb *refdb; + + g_repo = cl_git_sandbox_init("testrepo2"); + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + /* make a bunch of references */ + + for (r = 0; r < 50; ++r) { + snprintf(name, sizeof(name), "refs/heads/starter-%03d", r); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + git_reference_free(ref); + } + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + + g_expected = -1; + + g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */ + + for (t = 0; t < THREADS; ++t) { + void *(*fn)(void *arg); + + switch (t & 0x3) { + case 0: fn = create_refs; break; + case 1: fn = delete_refs; break; + default: fn = iterate_refs; break; + } + + id[t] = t; +#ifdef GIT_THREADS + cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); +#else + th[t] = t; + fn(&id[t]); +#endif + } + +#ifdef GIT_THREADS + for (t = 0; t < THREADS; ++t) { + cl_git_pass(git_thread_join(th[t], NULL)); + } + + memset(th, 0, sizeof(th)); + + for (t = 0; t < THREADS; ++t) { + id[t] = t; + cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t])); + } + + for (t = 0; t < THREADS; ++t) { + cl_git_pass(git_thread_join(th[t], NULL)); + } +#endif +} diff --git a/tests-clar/valgrind-supp-mac.txt b/tests-clar/valgrind-supp-mac.txt index 297b11e62..fcc7ede86 100644 --- a/tests-clar/valgrind-supp-mac.txt +++ b/tests-clar/valgrind-supp-mac.txt @@ -113,24 +113,18 @@ { mac-ssl-leak-1 Memcheck:Leak - fun:malloc - fun:CRYPTO_malloc ... fun:ERR_load_strings } { mac-ssl-leak-2 Memcheck:Leak - fun:malloc - fun:CRYPTO_malloc ... fun:SSL_library_init } { mac-ssl-leak-3 Memcheck:Leak - fun:malloc - fun:strdup ... fun:si_module_with_name fun:getaddrinfo @@ -144,6 +138,14 @@ fun:ssl3_get_server_certificate } { + mac-ssl-leak-5 + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:ERR_put_error +} +{ clar-printf-buf Memcheck:Leak fun:malloc |
