diffiter.c 11.2 KB
Newer Older
Russell Belfer committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include "clar_libgit2.h"
#include "diff_helpers.h"

void test_diff_diffiter__initialize(void)
{
}

void test_diff_diffiter__cleanup(void)
{
	cl_git_sandbox_cleanup();
}

void test_diff_diffiter__create(void)
{
	git_repository *repo = cl_git_sandbox_init("attr");
16
	git_diff *diff;
17
	size_t d, num_d;
Russell Belfer committed
18

19
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
20 21 22

	num_d = git_diff_num_deltas(diff);
	for (d = 0; d < num_d; ++d) {
Russell Belfer committed
23 24
		const git_diff_delta *delta = git_diff_get_delta(diff, d);
		cl_assert(delta != NULL);
25 26
	}

Russell Belfer committed
27 28
	cl_assert(!git_diff_get_delta(diff, num_d));

29
	git_diff_free(diff);
Russell Belfer committed
30 31
}

32
void test_diff_diffiter__iterate_files_1(void)
Russell Belfer committed
33 34
{
	git_repository *repo = cl_git_sandbox_init("attr");
35
	git_diff *diff;
36
	size_t d, num_d;
37
	diff_expects exp = { 0 };
Russell Belfer committed
38

39
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
Russell Belfer committed
40

41 42 43
	num_d = git_diff_num_deltas(diff);

	for (d = 0; d < num_d; ++d) {
Russell Belfer committed
44
		const git_diff_delta *delta = git_diff_get_delta(diff, d);
Russell Belfer committed
45
		cl_assert(delta != NULL);
46 47

		diff_file_cb(delta, (float)d / (float)num_d, &exp);
Russell Belfer committed
48
	}
49
	cl_assert_equal_sz(6, exp.files);
Russell Belfer committed
50

51
	git_diff_free(diff);
Russell Belfer committed
52 53 54 55 56
}

void test_diff_diffiter__iterate_files_2(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
57
	git_diff *diff;
58 59
	size_t d, num_d;
	int count = 0;
Russell Belfer committed
60

61
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
Russell Belfer committed
62

63
	num_d = git_diff_num_deltas(diff);
Russell Belfer committed
64
	cl_assert_equal_i(8, (int)num_d);
65 66

	for (d = 0; d < num_d; ++d) {
Russell Belfer committed
67
		const git_diff_delta *delta = git_diff_get_delta(diff, d);
Russell Belfer committed
68 69 70 71 72
		cl_assert(delta != NULL);
		count++;
	}
	cl_assert_equal_i(8, count);

73
	git_diff_free(diff);
Russell Belfer committed
74 75 76 77 78
}

void test_diff_diffiter__iterate_files_and_hunks(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
79
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
80
	git_diff *diff = NULL;
81 82
	size_t d, num_d;
	int file_count = 0, hunk_count = 0;
Russell Belfer committed
83 84 85 86 87

	opts.context_lines = 3;
	opts.interhunk_lines = 1;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;

88
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
Russell Belfer committed
89

90 91 92
	num_d = git_diff_num_deltas(diff);

	for (d = 0; d < num_d; ++d) {
93
		git_patch *patch;
94 95
		size_t h, num_h;

Russell Belfer committed
96
		cl_git_pass(git_patch_from_diff(&patch, diff, d));
97
		cl_assert(patch);
Russell Belfer committed
98 99 100

		file_count++;

101
		num_h = git_patch_num_hunks(patch);
102 103

		for (h = 0; h < num_h; h++) {
104
			const git_diff_hunk *hunk;
105

106 107
			cl_git_pass(git_patch_get_hunk(&hunk, NULL, patch, h));
			cl_assert(hunk);
108

Russell Belfer committed
109 110
			hunk_count++;
		}
111

112
		git_patch_free(patch);
Russell Belfer committed
113 114 115 116 117
	}

	cl_assert_equal_i(13, file_count);
	cl_assert_equal_i(8, hunk_count);

118
	git_diff_free(diff);
Russell Belfer committed
119
}
120 121 122 123

void test_diff_diffiter__max_size_threshold(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
124
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
125
	git_diff *diff = NULL;
126 127
	int file_count = 0, binary_count = 0, hunk_count = 0;
	size_t d, num_d;
128 129 130 131 132

	opts.context_lines = 3;
	opts.interhunk_lines = 1;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;

133
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
134
	num_d = git_diff_num_deltas(diff);
135

136
	for (d = 0; d < num_d; ++d) {
137
		git_patch *patch;
138
		const git_diff_delta *delta;
139

Russell Belfer committed
140
		cl_git_pass(git_patch_from_diff(&patch, diff, d));
141
		cl_assert(patch);
Russell Belfer committed
142 143
		delta = git_patch_get_delta(patch);
		cl_assert(delta);
144 145

		file_count++;
146
		hunk_count += (int)git_patch_num_hunks(patch);
147

148 149
		assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
		binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
150

151
		git_patch_free(patch);
152
	}
153 154 155 156 157

	cl_assert_equal_i(13, file_count);
	cl_assert_equal_i(0, binary_count);
	cl_assert_equal_i(8, hunk_count);

158
	git_diff_free(diff);
159 160 161

	/* try again with low file size threshold */

162
	file_count = binary_count = hunk_count = 0;
163 164 165 166 167 168

	opts.context_lines = 3;
	opts.interhunk_lines = 1;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
	opts.max_size = 50; /* treat anything over 50 bytes as binary! */

169
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
170
	num_d = git_diff_num_deltas(diff);
171

172
	for (d = 0; d < num_d; ++d) {
173
		git_patch *patch;
174
		const git_diff_delta *delta;
175

Russell Belfer committed
176 177
		cl_git_pass(git_patch_from_diff(&patch, diff, d));
		delta = git_patch_get_delta(patch);
178

179
		file_count++;
180
		hunk_count += (int)git_patch_num_hunks(patch);
181

182 183
		assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
		binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
184

185
		git_patch_free(patch);
186
	}
187 188 189 190 191 192 193 194 195 196

	cl_assert_equal_i(13, file_count);
	/* Three files are over the 50 byte threshold:
	 * - staged_changes_file_deleted
	 * - staged_changes_modified_file
	 * - staged_new_file_modified_file
	 */
	cl_assert_equal_i(3, binary_count);
	cl_assert_equal_i(5, hunk_count);

197
	git_diff_free(diff);
198 199 200 201 202 203
}


void test_diff_diffiter__iterate_all(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
204
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
205
	git_diff *diff = NULL;
206 207 208 209 210 211 212
	diff_expects exp = {0};
	size_t d, num_d;

	opts.context_lines = 3;
	opts.interhunk_lines = 1;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;

213
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
214 215 216

	num_d = git_diff_num_deltas(diff);
	for (d = 0; d < num_d; ++d) {
217
		git_patch *patch;
218 219
		size_t h, num_h;

Russell Belfer committed
220 221
		cl_git_pass(git_patch_from_diff(&patch, diff, d));
		cl_assert(patch);
222 223
		exp.files++;

224
		num_h = git_patch_num_hunks(patch);
225
		for (h = 0; h < num_h; h++) {
226
			const git_diff_hunk *range;
227
			size_t l, num_l;
228

229 230
			cl_git_pass(git_patch_get_hunk(&range, &num_l, patch, h));
			cl_assert(range);
231 232 233
			exp.hunks++;

			for (l = 0; l < num_l; ++l) {
234
				const git_diff_line *line;
235

236 237
				cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
				cl_assert(line && line->content);
238 239 240 241
				exp.lines++;
			}
		}

242
		git_patch_free(patch);
243 244 245 246
	}

	cl_assert_equal_i(13, exp.files);
	cl_assert_equal_i(8, exp.hunks);
247
	cl_assert_equal_i(14, exp.lines);
248

249
	git_diff_free(diff);
250 251
}

252
static void iterate_over_patch(git_patch *patch, diff_expects *exp)
253
{
254
	size_t h, num_h = git_patch_num_hunks(patch), num_l;
255 256

	exp->files++;
Russell Belfer committed
257
	exp->hunks += (int)num_h;
258 259

	/* let's iterate in reverse, just because we can! */
260
	for (h = 1, num_l = 0; h <= num_h; ++h)
261
		num_l += git_patch_num_lines_in_hunk(patch, num_h - h);
262

Russell Belfer committed
263
	exp->lines += (int)num_l;
264 265 266 267 268 269 270
}

#define PATCH_CACHE 5

void test_diff_diffiter__iterate_randomly_while_saving_state(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
271
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
272
	git_diff *diff = NULL;
273
	diff_expects exp = {0};
274
	git_patch *patches[PATCH_CACHE];
275 276 277 278 279 280 281 282
	size_t p, d, num_d;

	memset(patches, 0, sizeof(patches));

	opts.context_lines = 3;
	opts.interhunk_lines = 1;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;

283
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

	num_d = git_diff_num_deltas(diff);

	/* To make sure that references counts work for diff and patch objects,
	 * this generates patches and randomly caches them.  Only when the patch
	 * is removed from the cache are hunks and lines counted.  At the end,
	 * there are still patches in the cache, so free the diff and try to
	 * process remaining patches after the diff is freed.
	 */

	srand(121212);
	p = rand() % PATCH_CACHE;

	for (d = 0; d < num_d; ++d) {
		/* take old patch */
299
		git_patch *patch = patches[p];
300 301 302
		patches[p] = NULL;

		/* cache new patch */
Russell Belfer committed
303
		cl_git_pass(git_patch_from_diff(&patches[p], diff, d));
304 305 306 307 308
		cl_assert(patches[p] != NULL);

		/* process old patch if non-NULL */
		if (patch != NULL) {
			iterate_over_patch(patch, &exp);
309
			git_patch_free(patch);
310 311 312 313 314 315
		}

		p = rand() % PATCH_CACHE;
	}

	/* free diff list now - refcounts should keep things safe */
316
	git_diff_free(diff);
317 318 319

	/* process remaining unprocessed patches */
	for (p = 0; p < PATCH_CACHE; p++) {
320
		git_patch *patch = patches[p];
321 322 323

		if (patch != NULL) {
			iterate_over_patch(patch, &exp);
324
			git_patch_free(patch);
325 326
		}
	}
327

328 329 330
	/* hopefully it all still added up right */
	cl_assert_equal_i(13, exp.files);
	cl_assert_equal_i(8, exp.hunks);
331
	cl_assert_equal_i(14, exp.lines);
332
}
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

/* This output is taken directly from `git diff` on the status test data */
static const char *expected_patch_text[8] = {
	/* 0 */
	"diff --git a/file_deleted b/file_deleted\n"
	"deleted file mode 100644\n"
	"index 5452d32..0000000\n"
	"--- a/file_deleted\n"
	"+++ /dev/null\n"
	"@@ -1 +0,0 @@\n"
	"-file_deleted\n",
	/* 1 */
	"diff --git a/modified_file b/modified_file\n"
	"index 452e424..0a53963 100644\n"
	"--- a/modified_file\n"
	"+++ b/modified_file\n"
	"@@ -1 +1,2 @@\n"
	" modified_file\n"
	"+modified_file\n",
	/* 2 */
	"diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
	"deleted file mode 100644\n"
	"index a6be623..0000000\n"
	"--- a/staged_changes_file_deleted\n"
	"+++ /dev/null\n"
	"@@ -1,2 +0,0 @@\n"
	"-staged_changes_file_deleted\n"
	"-staged_changes_file_deleted\n",
	/* 3 */
	"diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
	"index 906ee77..011c344 100644\n"
	"--- a/staged_changes_modified_file\n"
	"+++ b/staged_changes_modified_file\n"
	"@@ -1,2 +1,3 @@\n"
	" staged_changes_modified_file\n"
	" staged_changes_modified_file\n"
	"+staged_changes_modified_file\n",
	/* 4 */
	"diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
	"deleted file mode 100644\n"
	"index 90b8c29..0000000\n"
	"--- a/staged_new_file_deleted_file\n"
	"+++ /dev/null\n"
	"@@ -1 +0,0 @@\n"
	"-staged_new_file_deleted_file\n",
	/* 5 */
	"diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
	"index ed06290..8b090c0 100644\n"
	"--- a/staged_new_file_modified_file\n"
	"+++ b/staged_new_file_modified_file\n"
	"@@ -1 +1,2 @@\n"
	" staged_new_file_modified_file\n"
	"+staged_new_file_modified_file\n",
	/* 6 */
	"diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
	"deleted file mode 100644\n"
	"index 1888c80..0000000\n"
	"--- a/subdir/deleted_file\n"
	"+++ /dev/null\n"
	"@@ -1 +0,0 @@\n"
	"-subdir/deleted_file\n",
	/* 7 */
	"diff --git a/subdir/modified_file b/subdir/modified_file\n"
	"index a619198..57274b7 100644\n"
	"--- a/subdir/modified_file\n"
	"+++ b/subdir/modified_file\n"
	"@@ -1 +1,2 @@\n"
	" subdir/modified_file\n"
	"+subdir/modified_file\n"
};

void test_diff_diffiter__iterate_and_generate_patch_text(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
407
	git_diff *diff;
408 409
	size_t d, num_d;

410
	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
411 412 413 414 415

	num_d = git_diff_num_deltas(diff);
	cl_assert_equal_i(8, (int)num_d);

	for (d = 0; d < num_d; ++d) {
416
		git_patch *patch;
Nicolas Hake committed
417
		git_buf buf = GIT_BUF_INIT;
418

Russell Belfer committed
419
		cl_git_pass(git_patch_from_diff(&patch, diff, d));
420 421
		cl_assert(patch != NULL);

Nicolas Hake committed
422
		cl_git_pass(git_patch_to_buf(&buf, patch));
423

Nicolas Hake committed
424
		cl_assert_equal_s(expected_patch_text[d], buf.ptr);
425

Nicolas Hake committed
426
		git_buf_free(&buf);
427
		git_patch_free(patch);
428 429
	}

430
	git_diff_free(diff);
431
}
432 433 434 435 436

void test_diff_diffiter__checks_options_version(void)
{
	git_repository *repo = cl_git_sandbox_init("status");
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
437
	git_diff *diff = NULL;
438 439 440 441 442
	const git_error *err;

	opts.version = 0;
	opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;

443
	cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
444 445 446 447 448
	err = giterr_last();
	cl_assert_equal_i(GITERR_INVALID, err->klass);

	giterr_clear();
	opts.version = 1024;
449
	cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
450 451 452 453
	err = giterr_last();
	cl_assert_equal_i(GITERR_INVALID, err->klass);
}