diff options
author | Jeff Hostetler <jeffhost@microsoft.com> | 2016-01-04 12:12:24 -0500 |
---|---|---|
committer | Edward Thomson <ethomson@github.com> | 2016-03-23 17:17:02 -0400 |
commit | df25daef9b07174124989aaf993cc20c9e9b0660 (patch) | |
tree | af15eb5738451f65dc8d4e45b04c687c06ea4f38 | |
parent | 6788553231699d4bb8e0ea0c05fdf83407bfaf6f (diff) | |
download | libgit2-df25daef9b07174124989aaf993cc20c9e9b0660.tar.gz |
Added clar test for #3568
-rw-r--r-- | tests/diff/racediffiter.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/tests/diff/racediffiter.c b/tests/diff/racediffiter.c new file mode 100644 index 000000000..d364d6b21 --- /dev/null +++ b/tests/diff/racediffiter.c @@ -0,0 +1,129 @@ +/* This test exercises the problem described in +** https://github.com/libgit2/libgit2/pull/3568 +** where deleting a directory during a diff/status +** operation can cause an access violation. +** +** The "test_diff_racediffiter__basic() test confirms +** the normal operation of diff on the given repo. +** +** The "test_diff_racediffiter__racy_rmdir() test +** uses the new diff progress callback to delete +** a directory (after the initial readdir() and +** before the directory itself is visited) causing +** the recursion and iteration to fail. +*/ + +#include "clar_libgit2.h" +#include "diff_helpers.h" + +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) + +void test_diff_racediffiter__initialize(void) +{ +} + +void test_diff_racediffiter__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +typedef struct +{ + const char *path; + git_delta_t t; + +} basic_payload; + +static int notify_cb__basic( + const git_diff *diff_so_far, + const git_diff_delta *delta_to_add, + const char *matched_pathspec, + void *payload) +{ + basic_payload *exp = (basic_payload *)payload; + basic_payload *e; + + GIT_UNUSED(diff_so_far); + GIT_UNUSED(matched_pathspec); + + for (e = exp; e->path; e++) { + if (strcmp(e->path, delta_to_add->new_file.path) == 0) { + cl_assert_equal_i(e->t, delta_to_add->status); + return 0; + } + } + cl_assert(0); + return GIT_ENOTFOUND; +} + +void test_diff_racediffiter__basic(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_repository *repo = cl_git_sandbox_init("diff"); + git_diff *diff; + + basic_payload exp_a[] = { + { "another.txt", GIT_DELTA_MODIFIED }, + { "readme.txt", GIT_DELTA_MODIFIED }, + { "zzzzz/", GIT_DELTA_IGNORED }, + { NULL, 0 } + }; + + cl_must_pass(p_mkdir("diff/zzzzz", 0777)); + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS; + opts.notify_cb = notify_cb__basic; + opts.payload = exp_a; + + cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts)); + + git_diff_free(diff); +} + + +typedef struct { + bool first_time; + const char *dir; + basic_payload *basic_payload; +} racy_payload; + +static int notify_cb__racy_rmdir( + const git_diff *diff_so_far, + const git_diff_delta *delta_to_add, + const char *matched_pathspec, + void *payload) +{ + racy_payload *pay = (racy_payload *)payload; + + if (pay->first_time) { + cl_must_pass(p_rmdir(pay->dir)); + pay->first_time = false; + } + + return notify_cb__basic(diff_so_far, delta_to_add, matched_pathspec, pay->basic_payload); +} + +void test_diff_racediffiter__racy(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_repository *repo = cl_git_sandbox_init("diff"); + git_diff *diff; + + basic_payload exp_a[] = { + { "another.txt", GIT_DELTA_MODIFIED }, + { "readme.txt", GIT_DELTA_MODIFIED }, + { NULL, 0 } + }; + + racy_payload pay = { true, "diff/zzzzz", exp_a }; + + cl_must_pass(p_mkdir("diff/zzzzz", 0777)); + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS; + opts.notify_cb = notify_cb__racy_rmdir; + opts.payload = &pay; + + cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts)); + + git_diff_free(diff); +} |