Blame tests/diff/racediffiter.c

Packit Service 20376f
/* This test exercises the problem described in
Packit Service 20376f
** https://github.com/libgit2/libgit2/pull/3568
Packit Service 20376f
** where deleting a directory during a diff/status
Packit Service 20376f
** operation can cause an access violation.
Packit Service 20376f
**
Packit Service 20376f
** The "test_diff_racediffiter__basic() test confirms
Packit Service 20376f
** the normal operation of diff on the given repo.
Packit Service 20376f
**
Packit Service 20376f
** The "test_diff_racediffiter__racy_rmdir() test
Packit Service 20376f
** uses the new diff progress callback to delete
Packit Service 20376f
** a directory (after the initial readdir() and
Packit Service 20376f
** before the directory itself is visited) causing
Packit Service 20376f
** the recursion and iteration to fail.
Packit Service 20376f
*/
Packit Service 20376f
Packit Service 20376f
#include "clar_libgit2.h"
Packit Service 20376f
#include "diff_helpers.h"
Packit Service 20376f
Packit Service 20376f
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
Packit Service 20376f
Packit Service 20376f
void test_diff_racediffiter__initialize(void)
Packit Service 20376f
{
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void test_diff_racediffiter__cleanup(void)
Packit Service 20376f
{
Packit Service 20376f
	cl_git_sandbox_cleanup();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct
Packit Service 20376f
{
Packit Service 20376f
	const char *path;
Packit Service 20376f
	git_delta_t t;
Packit Service 20376f
Packit Service 20376f
} basic_payload;
Packit Service 20376f
Packit Service 20376f
static int notify_cb__basic(
Packit Service 20376f
	const git_diff *diff_so_far,
Packit Service 20376f
	const git_diff_delta *delta_to_add,
Packit Service 20376f
	const char *matched_pathspec,
Packit Service 20376f
	void *payload)
Packit Service 20376f
{
Packit Service 20376f
	basic_payload *exp = (basic_payload *)payload;
Packit Service 20376f
	basic_payload *e;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(diff_so_far);
Packit Service 20376f
	GIT_UNUSED(matched_pathspec);
Packit Service 20376f
Packit Service 20376f
	for (e = exp; e->path; e++) {
Packit Service 20376f
		if (strcmp(e->path, delta_to_add->new_file.path) == 0) {
Packit Service 20376f
			cl_assert_equal_i(e->t, delta_to_add->status);
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	cl_assert(0);
Packit Service 20376f
	return GIT_ENOTFOUND;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void test_diff_racediffiter__basic(void)
Packit Service 20376f
{
Packit Service 20376f
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	git_repository *repo = cl_git_sandbox_init("diff");
Packit Service 20376f
	git_diff *diff;
Packit Service 20376f
Packit Service 20376f
	basic_payload exp_a[] = {
Packit Service 20376f
		{ "another.txt", GIT_DELTA_MODIFIED },
Packit Service 20376f
		{ "readme.txt", GIT_DELTA_MODIFIED },
Packit Service 20376f
		{ "zzzzz/", GIT_DELTA_IGNORED },
Packit Service 20376f
		{ NULL, 0 }
Packit Service 20376f
	};
Packit Service 20376f
Packit Service 20376f
	cl_must_pass(p_mkdir("diff/zzzzz", 0777));
Packit Service 20376f
Packit Service 20376f
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
Packit Service 20376f
	opts.notify_cb = notify_cb__basic;
Packit Service 20376f
	opts.payload = exp_a;
Packit Service 20376f
Packit Service 20376f
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
Packit Service 20376f
Packit Service 20376f
	git_diff_free(diff);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	bool first_time;
Packit Service 20376f
	const char *dir;
Packit Service 20376f
	basic_payload *basic_payload;
Packit Service 20376f
} racy_payload;
Packit Service 20376f
Packit Service 20376f
static int notify_cb__racy_rmdir(
Packit Service 20376f
	const git_diff *diff_so_far,
Packit Service 20376f
	const git_diff_delta *delta_to_add,
Packit Service 20376f
	const char *matched_pathspec,
Packit Service 20376f
	void *payload)
Packit Service 20376f
{
Packit Service 20376f
	racy_payload *pay = (racy_payload *)payload;
Packit Service 20376f
Packit Service 20376f
	if (pay->first_time) {
Packit Service 20376f
		cl_must_pass(p_rmdir(pay->dir));
Packit Service 20376f
		pay->first_time = false;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return notify_cb__basic(diff_so_far, delta_to_add, matched_pathspec, pay->basic_payload);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void test_diff_racediffiter__racy(void)
Packit Service 20376f
{
Packit Service 20376f
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	git_repository *repo = cl_git_sandbox_init("diff");
Packit Service 20376f
	git_diff *diff;
Packit Service 20376f
Packit Service 20376f
	basic_payload exp_a[] = {
Packit Service 20376f
		{ "another.txt", GIT_DELTA_MODIFIED },
Packit Service 20376f
		{ "readme.txt", GIT_DELTA_MODIFIED },
Packit Service 20376f
		{ NULL, 0 }
Packit Service 20376f
	};
Packit Service 20376f
Packit Service 20376f
	racy_payload pay = { true, "diff/zzzzz", exp_a };
Packit Service 20376f
Packit Service 20376f
	cl_must_pass(p_mkdir("diff/zzzzz", 0777));
Packit Service 20376f
Packit Service 20376f
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
Packit Service 20376f
	opts.notify_cb = notify_cb__racy_rmdir;
Packit Service 20376f
	opts.payload = &pay;
Packit Service 20376f
Packit Service 20376f
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
Packit Service 20376f
Packit Service 20376f
	git_diff_free(diff);
Packit Service 20376f
}