blob.c 30 KB
Newer Older
1 2 3 4
#include "clar_libgit2.h"
#include "diff_helpers.h"

static git_repository *g_repo = NULL;
nulltoken committed
5
static diff_expects expected;
6
static git_diff_options opts;
7
static git_blob *d, *alien;
8

9 10 11 12 13 14 15 16 17 18 19 20 21 22
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));
}

23 24
void test_diff_blob__initialize(void)
{
25
	git_oid oid;
26

27
	g_repo = cl_git_sandbox_init("attr");
28

29
	cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION));
30 31
	opts.context_lines = 1;

nulltoken committed
32
	memset(&expected, 0, sizeof(expected));
33 34

	/* tests/resources/attr/root_test4.txt */
35 36
	cl_git_pass(git_oid_fromstrn(&oid, "a0f7217a", 8));
	cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &oid, 4));
37 38 39 40

	/* alien.png */
	cl_git_pass(git_oid_fromstrn(&oid, "edf3dcee", 8));
	cl_git_pass(git_blob_lookup_prefix(&alien, g_repo, &oid, 4));
41 42 43 44
}

void test_diff_blob__cleanup(void)
{
45
	git_blob_free(d);
46 47
	d = NULL;

48
	git_blob_free(alien);
49
	alien = NULL;
50

51
	cl_git_sandbox_cleanup();
52 53
}

54 55 56 57 58 59 60 61 62 63 64 65 66 67
static void assert_one_modified(
	int hunks, int lines, int ctxt, int adds, int dels, diff_expects *exp)
{
	cl_assert_equal_i(1, exp->files);
	cl_assert_equal_i(1, exp->file_status[GIT_DELTA_MODIFIED]);
	cl_assert_equal_i(0, exp->files_binary);

	cl_assert_equal_i(hunks, exp->hunks);
	cl_assert_equal_i(lines, exp->lines);
	cl_assert_equal_i(ctxt,  exp->line_ctxt);
	cl_assert_equal_i(adds,  exp->line_adds);
	cl_assert_equal_i(dels,  exp->line_dels);
}

68
void test_diff_blob__can_compare_text_blobs(void)
69
{
70 71
	git_blob *a, *b, *c;
	git_oid a_oid, b_oid, c_oid;
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

	/* tests/resources/attr/root_test1 */
	cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
	cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));

	/* tests/resources/attr/root_test2 */
	cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
	cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));

	/* tests/resources/attr/root_test3 */
	cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
	cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));

	/* Doing the equivalent of a `git diff -U1` on these files */

87
	/* diff on tests/resources/attr/root_test1 */
88
	memset(&expected, 0, sizeof(expected));
89
	cl_git_pass(git_diff_blobs(
90 91
		a, NULL, b, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
92
	assert_one_modified(1, 6, 1, 5, 0, &expected);
93

94 95 96 97 98 99 100
	/* same diff but use direct buffers */
	memset(&expected, 0, sizeof(expected));
	cl_git_pass(git_diff_buffers(
		git_blob_rawcontent(a), (size_t)git_blob_rawsize(a), NULL,
		git_blob_rawcontent(b), (size_t)git_blob_rawsize(b), NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
	assert_one_modified(1, 6, 1, 5, 0, &expected);
101

102
	/* diff on tests/resources/attr/root_test2 */
nulltoken committed
103
	memset(&expected, 0, sizeof(expected));
104
	cl_git_pass(git_diff_blobs(
105 106
		b, NULL, c, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
107
	assert_one_modified(1, 15, 3, 9, 3, &expected);
108

109
	/* diff on tests/resources/attr/root_test3 */
nulltoken committed
110
	memset(&expected, 0, sizeof(expected));
111
	cl_git_pass(git_diff_blobs(
112 113
		a, NULL, c, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
114
	assert_one_modified(1, 13, 0, 12, 1, &expected);
115

nulltoken committed
116
	memset(&expected, 0, sizeof(expected));
117
	cl_git_pass(git_diff_blobs(
118 119
		c, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
120
	assert_one_modified(2, 14, 4, 6, 4, &expected);
121 122 123 124 125 126

	git_blob_free(a);
	git_blob_free(b);
	git_blob_free(c);
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
static void assert_patch_matches_blobs(
	git_patch *p, git_blob *a, git_blob *b,
	int hunks, int l0, int l1, int ctxt, int adds, int dels)
{
	const git_diff_delta *delta;
	size_t tc, ta, td;

	cl_assert(p != NULL);

	delta = git_patch_get_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.id));
	cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
	cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.id));
	cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);

	cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p));

	if (hunks > 0)
		cl_assert_equal_i(l0, git_patch_num_lines_in_hunk(p, 0));
	if (hunks > 1)
		cl_assert_equal_i(l1, git_patch_num_lines_in_hunk(p, 1));

	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
	cl_assert_equal_i(ctxt, (int)tc);
	cl_assert_equal_i(adds, (int)ta);
	cl_assert_equal_i(dels, (int)td);
}

158 159 160 161
void test_diff_blob__can_compare_text_blobs_with_patch(void)
{
	git_blob *a, *b, *c;
	git_oid a_oid, b_oid, c_oid;
162
	git_patch *p;
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

	/* tests/resources/attr/root_test1 */
	cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
	cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));

	/* tests/resources/attr/root_test2 */
	cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
	cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));

	/* tests/resources/attr/root_test3 */
	cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
	cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));

	/* Doing the equivalent of a `git diff -U1` on these files */

	/* diff on tests/resources/attr/root_test1 */
179
	cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
180
	assert_patch_matches_blobs(p, a, b, 1, 6, 0, 1, 5, 0);
181
	git_patch_free(p);
182 183

	/* diff on tests/resources/attr/root_test2 */
184
	cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
185
	assert_patch_matches_blobs(p, b, c, 1, 15, 0, 3, 9, 3);
186
	git_patch_free(p);
187 188

	/* diff on tests/resources/attr/root_test3 */
189
	cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
190
	assert_patch_matches_blobs(p, a, c, 1, 13, 0, 0, 12, 1);
191
	git_patch_free(p);
192 193

	/* one more */
194
	cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
195
	assert_patch_matches_blobs(p, c, d, 2, 5, 9, 4, 6, 4);
196
	git_patch_free(p);
197 198 199 200 201 202

	git_blob_free(a);
	git_blob_free(b);
	git_blob_free(c);
}

203 204 205 206 207
void test_diff_blob__can_compare_against_null_blobs(void)
{
	git_blob *e = NULL;

	cl_git_pass(git_diff_blobs(
208 209
		d, NULL, e, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
210

Russell Belfer committed
211
	cl_assert_equal_i(1, expected.files);
212 213
	cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
	cl_assert_equal_i(0, expected.files_binary);
214

Russell Belfer committed
215 216 217 218
	cl_assert_equal_i(1, expected.hunks);
	cl_assert_equal_i(14, expected.hunk_old_lines);
	cl_assert_equal_i(14, expected.lines);
	cl_assert_equal_i(14, expected.line_dels);
219 220

	opts.flags |= GIT_DIFF_REVERSE;
nulltoken committed
221
	memset(&expected, 0, sizeof(expected));
222 223

	cl_git_pass(git_diff_blobs(
224 225
		d, NULL, e, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
226

Russell Belfer committed
227
	cl_assert_equal_i(1, expected.files);
228 229
	cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
	cl_assert_equal_i(0, expected.files_binary);
230

Russell Belfer committed
231 232 233 234
	cl_assert_equal_i(1, expected.hunks);
	cl_assert_equal_i(14, expected.hunk_new_lines);
	cl_assert_equal_i(14, expected.lines);
	cl_assert_equal_i(14, expected.line_adds);
235 236

	opts.flags ^= GIT_DIFF_REVERSE;
nulltoken committed
237
	memset(&expected, 0, sizeof(expected));
238 239

	cl_git_pass(git_diff_blobs(
240 241
		alien, NULL, NULL, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
242

Russell Belfer committed
243
	cl_assert_equal_i(1, expected.files);
244 245
	cl_assert_equal_i(1, expected.files_binary);
	cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
Russell Belfer committed
246 247
	cl_assert_equal_i(0, expected.hunks);
	cl_assert_equal_i(0, expected.lines);
248

nulltoken committed
249
	memset(&expected, 0, sizeof(expected));
250 251

	cl_git_pass(git_diff_blobs(
252 253
		NULL, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
254

Russell Belfer committed
255
	cl_assert_equal_i(1, expected.files);
256 257
	cl_assert_equal_i(1, expected.files_binary);
	cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
Russell Belfer committed
258 259
	cl_assert_equal_i(0, expected.hunks);
	cl_assert_equal_i(0, expected.lines);
260 261
}

262 263 264
void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
{
	git_blob *e = NULL;
265
	git_patch *p;
266
	const git_diff_delta *delta;
267 268
	const git_diff_line *line;
	int l, max_l;
269

270
	cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
271 272

	cl_assert(p != NULL);
273

Russell Belfer committed
274
	delta = git_patch_get_delta(p);
275 276
	cl_assert(delta != NULL);
	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
277
	cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.id));
278
	cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
279
	cl_assert(git_oid_iszero(&delta->new_file.id));
280 281
	cl_assert_equal_sz(0, delta->new_file.size);

282 283
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
284

285 286 287 288
	max_l = git_patch_num_lines_in_hunk(p, 0);
	for (l = 0; l < max_l; ++l) {
		cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
		cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
289 290
	}

291
	git_patch_free(p);
292 293 294

	opts.flags |= GIT_DIFF_REVERSE;

295
	cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
296 297

	cl_assert(p != NULL);
298

Russell Belfer committed
299
	delta = git_patch_get_delta(p);
300 301
	cl_assert(delta != NULL);
	cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
302
	cl_assert(git_oid_iszero(&delta->old_file.id));
303
	cl_assert_equal_sz(0, delta->old_file.size);
304
	cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.id));
305 306
	cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);

307 308
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
309

310 311 312 313
	max_l = git_patch_num_lines_in_hunk(p, 0);
	for (l = 0; l < max_l; ++l) {
		cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
		cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
314 315
	}

316
	git_patch_free(p);
317 318 319

	opts.flags ^= GIT_DIFF_REVERSE;

320
	cl_git_pass(git_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
321 322

	cl_assert(p != NULL);
323

Russell Belfer committed
324
	delta = git_patch_get_delta(p);
325 326 327 328
	cl_assert(delta != NULL);
	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
	cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);

329
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
330

331
	git_patch_free(p);
332

333
	cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
334 335

	cl_assert(p != NULL);
336

Russell Belfer committed
337
	delta = git_patch_get_delta(p);
338 339 340 341
	cl_assert(delta != NULL);
	cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
	cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);

342
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
343

344
	git_patch_free(p);
345 346
}

347
static void assert_identical_blobs_comparison(diff_expects *expected)
348
{
349 350 351 352
	cl_assert_equal_i(1, expected->files);
	cl_assert_equal_i(1, expected->file_status[GIT_DELTA_UNMODIFIED]);
	cl_assert_equal_i(0, expected->hunks);
	cl_assert_equal_i(0, expected->lines);
353 354 355 356
}

void test_diff_blob__can_compare_identical_blobs(void)
{
357 358
	opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;

359
	cl_git_pass(git_diff_blobs(
360 361
		d, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
362

363
	assert_identical_blobs_comparison(&expected);
364
	cl_assert_equal_i(0, expected.files_binary);
365

nulltoken committed
366
	memset(&expected, 0, sizeof(expected));
367
	cl_git_pass(git_diff_blobs(
368 369
		NULL, NULL, NULL, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
370

371
	assert_identical_blobs_comparison(&expected);
372
	cl_assert_equal_i(0, expected.files_binary);
373

nulltoken committed
374
	memset(&expected, 0, sizeof(expected));
375
	cl_git_pass(git_diff_blobs(
376 377
		alien, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
378

379
	assert_identical_blobs_comparison(&expected);
380
	cl_assert(expected.files_binary > 0);
381 382
}

383 384
void test_diff_blob__can_compare_identical_blobs_with_patch(void)
{
385
	git_patch *p;
386
	const git_diff_delta *delta;
387

388
	cl_git_pass(git_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
389
	cl_assert(p != NULL);
390

Russell Belfer committed
391
	delta = git_patch_get_delta(p);
392 393 394
	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));
395
	cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.id));
396
	cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
397
	cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.id));
398

399 400
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
	git_patch_free(p);
401

402
	cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
403
	cl_assert(p != NULL);
404

Russell Belfer committed
405
	delta = git_patch_get_delta(p);
406 407 408
	cl_assert(delta != NULL);
	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
	cl_assert_equal_sz(0, delta->old_file.size);
409
	cl_assert(git_oid_iszero(&delta->old_file.id));
410
	cl_assert_equal_sz(0, delta->new_file.size);
411
	cl_assert(git_oid_iszero(&delta->new_file.id));
412

413 414
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
	git_patch_free(p);
415

416
	cl_git_pass(git_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
417
	cl_assert(p != NULL);
Russell Belfer committed
418
	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
419 420
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
	git_patch_free(p);
421 422
}

423
static void assert_binary_blobs_comparison(diff_expects *expected)
424
{
425
	cl_assert(expected->files_binary > 0);
426

427 428 429 430
	cl_assert_equal_i(1, expected->files);
	cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
	cl_assert_equal_i(0, expected->hunks);
	cl_assert_equal_i(0, expected->lines);
431 432 433 434 435 436 437 438 439 440 441 442
}

void test_diff_blob__can_compare_two_binary_blobs(void)
{
	git_blob *heart;
	git_oid h_oid;

	/* heart.png */
	cl_git_pass(git_oid_fromstrn(&h_oid, "de863bff", 8));
	cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4));

	cl_git_pass(git_diff_blobs(
443 444
		alien, NULL, heart, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
445

446
	assert_binary_blobs_comparison(&expected);
447

nulltoken committed
448
	memset(&expected, 0, sizeof(expected));
449 450

	cl_git_pass(git_diff_blobs(
451 452
		heart, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
453

454
	assert_binary_blobs_comparison(&expected);
455 456 457 458 459 460 461

	git_blob_free(heart);
}

void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void)
{
	cl_git_pass(git_diff_blobs(
462 463
		alien, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
464

465
	assert_binary_blobs_comparison(&expected);
466

nulltoken committed
467
	memset(&expected, 0, sizeof(expected));
468 469

	cl_git_pass(git_diff_blobs(
470 471
		d, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
472

473
	assert_binary_blobs_comparison(&expected);
474
}
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

/*
 * $ git diff fe773770 a0f7217
 * diff --git a/fe773770 b/a0f7217
 * index fe77377..a0f7217 100644
 * --- a/fe773770
 * +++ b/a0f7217
 * @@ -1,6 +1,6 @@
 *  Here is some stuff at the start
 * 
 * -This should go in one hunk
 * +This should go in one hunk (first)
 * 
 *  Some additional lines
 * 
 * @@ -8,7 +8,7 @@ Down here below the other lines
 * 
 *  With even more at the end
 * 
 * -Followed by a second hunk of stuff
 * +Followed by a second hunk of stuff (second)
 * 
 *  That happens down here
 */
void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
{
	git_blob *old_d;
	git_oid old_d_oid;

	opts.context_lines = 3;

	/* tests/resources/attr/root_test1 from commit f5b0af1 */
	cl_git_pass(git_oid_fromstrn(&old_d_oid, "fe773770", 8));
	cl_git_pass(git_blob_lookup_prefix(&old_d, g_repo, &old_d_oid, 4));

	/* Test with default inter-hunk-context (not set) => default is 0 */
	cl_git_pass(git_diff_blobs(
512 513
		old_d, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
514

Russell Belfer committed
515
	cl_assert_equal_i(2, expected.hunks);
516 517 518 519 520

	/* Test with inter-hunk-context explicitly set to 0 */
	opts.interhunk_lines = 0;
	memset(&expected, 0, sizeof(expected));
	cl_git_pass(git_diff_blobs(
521 522
		old_d, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
523

Russell Belfer committed
524
	cl_assert_equal_i(2, expected.hunks);
525 526 527 528 529

	/* Test with inter-hunk-context explicitly set to 1 */
	opts.interhunk_lines = 1;
	memset(&expected, 0, sizeof(expected));
	cl_git_pass(git_diff_blobs(
530 531
		old_d, NULL, d, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
532

Russell Belfer committed
533
	cl_assert_equal_i(1, expected.hunks);
534 535 536

	git_blob_free(old_d);
}
537 538 539 540 541 542 543

void test_diff_blob__checks_options_version_too_low(void)
{
	const git_error *err;

	opts.version = 0;
	cl_git_fail(git_diff_blobs(
544 545
		d, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
546 547 548 549 550 551 552 553 554 555
	err = giterr_last();
	cl_assert_equal_i(GITERR_INVALID, err->klass);
}

void test_diff_blob__checks_options_version_too_high(void)
{
	const git_error *err;

	opts.version = 1024;
	cl_git_fail(git_diff_blobs(
556 557
		d, NULL, alien, NULL, &opts,
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
558 559 560
	err = giterr_last();
	cl_assert_equal_i(GITERR_INVALID, err->klass);
}
561 562 563 564 565 566 567 568 569 570 571 572

void test_diff_blob__can_correctly_detect_a_binary_blob_as_binary(void)
{
	/* alien.png */
	cl_assert_equal_i(true, git_blob_is_binary(alien));
}

void test_diff_blob__can_correctly_detect_a_textual_blob_as_non_binary(void)
{
	/* tests/resources/attr/root_test4.txt */
	cl_assert_equal_i(false, git_blob_is_binary(d));
}
573 574 575 576 577

/*
 * git_diff_blob_to_buffer tests
 */

578 579 580 581 582 583 584 585 586 587 588 589 590 591
static void assert_changed_single_one_line_file(
	diff_expects *expected, git_delta_t mod)
{
	cl_assert_equal_i(1, expected->files);
	cl_assert_equal_i(1, expected->file_status[mod]);
	cl_assert_equal_i(1, expected->hunks);
	cl_assert_equal_i(1, expected->lines);

	if (mod == GIT_DELTA_ADDED)
		cl_assert_equal_i(1, expected->line_adds);
	else if (mod == GIT_DELTA_DELETED)
		cl_assert_equal_i(1, expected->line_dels);
}

592 593 594 595 596 597 598 599 600 601 602 603
void test_diff_blob__can_compare_blob_to_buffer(void)
{
	git_blob *a;
	git_oid a_oid;
	const char *a_content = "Hello from the root\n";
	const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";

	/* tests/resources/attr/root_test1 */
	cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
	cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));

	/* diff from blob a to content of b */
604
	quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
605
	assert_one_modified(1, 6, 1, 5, 0, &expected);
606 607

	/* diff from blob a to content of a */
608 609
	opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
	quick_diff_blob_to_str(a, NULL, a_content, 0, NULL);
610 611
	assert_identical_blobs_comparison(&expected);

612
	/* diff from NULL blob to content of a */
613
	memset(&expected, 0, sizeof(expected));
614
	quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL);
615
	assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
616 617 618

	/* diff from blob a to NULL buffer */
	memset(&expected, 0, sizeof(expected));
619
	quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
620
	assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED);
621 622 623 624 625

	/* diff with reverse */
	opts.flags ^= GIT_DIFF_REVERSE;

	memset(&expected, 0, sizeof(expected));
626
	quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
627
	assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
628 629 630

	git_blob_free(a);
}
631

632 633
void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
{
634
	git_patch *p;
635 636 637 638 639 640 641 642 643 644 645
	git_blob *a;
	git_oid a_oid;
	const char *a_content = "Hello from the root\n";
	const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
	size_t tc, ta, td;

	/* tests/resources/attr/root_test1 */
	cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
	cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));

	/* diff from blob a to content of b */
646
	cl_git_pass(git_patch_from_blob_and_buffer(
647
		&p, a, NULL, b_content, strlen(b_content), NULL, &opts));
648 649

	cl_assert(p != NULL);
Russell Belfer committed
650
	cl_assert_equal_i(GIT_DELTA_MODIFIED, git_patch_get_delta(p)->status);
651 652
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
653

654
	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
655 656 657 658
	cl_assert_equal_i(1, (int)tc);
	cl_assert_equal_i(5, (int)ta);
	cl_assert_equal_i(0, (int)td);

659
	git_patch_free(p);
660 661

	/* diff from blob a to content of a */
662
	opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
663
	cl_git_pass(git_patch_from_blob_and_buffer(
664
		&p, a, NULL, a_content, strlen(a_content), NULL, &opts));
665
	cl_assert(p != NULL);
Russell Belfer committed
666
	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
667 668
	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
	git_patch_free(p);
669 670

	/* diff from NULL blob to content of a */
671
	cl_git_pass(git_patch_from_blob_and_buffer(
672
		&p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
673
	cl_assert(p != NULL);
Russell Belfer committed
674
	cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
675 676 677
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
	git_patch_free(p);
678 679

	/* diff from blob a to NULL buffer */
680
	cl_git_pass(git_patch_from_blob_and_buffer(
681
		&p, a, NULL, NULL, 0, NULL, &opts));
682
	cl_assert(p != NULL);
Russell Belfer committed
683
	cl_assert_equal_i(GIT_DELTA_DELETED, git_patch_get_delta(p)->status);
684 685 686
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
	git_patch_free(p);
687 688 689 690

	/* diff with reverse */
	opts.flags ^= GIT_DIFF_REVERSE;

691
	cl_git_pass(git_patch_from_blob_and_buffer(
692
		&p, a, NULL, NULL, 0, NULL, &opts));
693
	cl_assert(p != NULL);
Russell Belfer committed
694
	cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
695 696 697
	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
	git_patch_free(p);
698 699 700

	git_blob_free(a);
}
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718

static void assert_one_modified_with_lines(diff_expects *expected, int lines)
{
	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(lines, expected->lines);
}

void test_diff_blob__binary_data_comparisons(void)
{
	git_blob *bin, *nonbin;
	git_oid oid;
	const char *nonbin_content = "Hello from the root\n";
	size_t nonbin_len = 20;
	const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
	size_t bin_len = 33;

719 720
	opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;

721 722 723 724 725 726 727 728
	cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
	cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));

	cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
	cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));

	/* non-binary to reference content */

729
	quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL);
730 731 732 733 734
	assert_identical_blobs_comparison(&expected);
	cl_assert_equal_i(0, expected.files_binary);

	/* binary to reference content */

735
	quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
736 737 738 739 740 741
	assert_identical_blobs_comparison(&expected);

	cl_assert_equal_i(1, expected.files_binary);

	/* non-binary to binary content */

742
	quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
743 744 745 746
	assert_binary_blobs_comparison(&expected);

	/* binary to non-binary content */

747
	quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
748 749 750 751 752 753
	assert_binary_blobs_comparison(&expected);

	/* non-binary to binary blob */

	memset(&expected, 0, sizeof(expected));
	cl_git_pass(git_diff_blobs(
754
		bin, NULL, nonbin, NULL, &opts,
755 756 757 758 759 760 761 762 763
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
	assert_binary_blobs_comparison(&expected);

	/*
	 * repeat with FORCE_TEXT
	 */

	opts.flags |= GIT_DIFF_FORCE_TEXT;

764
	quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
765 766
	assert_identical_blobs_comparison(&expected);

767
	quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
768 769
	assert_one_modified_with_lines(&expected, 4);

770
	quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
771 772 773 774
	assert_one_modified_with_lines(&expected, 4);

	memset(&expected, 0, sizeof(expected));
	cl_git_pass(git_diff_blobs(
775
		bin, NULL, nonbin, NULL, &opts,
776 777 778 779 780 781 782
		diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
	assert_one_modified_with_lines(&expected, 4);

	/* cleanup */
	git_blob_free(bin);
	git_blob_free(nonbin);
}
783 784 785 786 787 788 789 790 791 792 793

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;
794
	git_patch *p;
Nicolas Hake committed
795
	git_buf buf = GIT_BUF_INIT;
796 797 798 799 800 801 802

	/* 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(
803
		cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z].*$"));
804 805
	cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0));
	cl_git_pass(git_config_set_string(
806
		cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z].*$"));
807
	cl_git_pass(git_config_set_string(
808
		cfg, "diff.iam_numctx.funcname", "^[0-9][0-9]*"));
809 810
	cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0));
	cl_git_pass(git_config_set_string(
811
		cfg, "diff.iam_textnum.funcname", "^[0-9][0-9]*"));
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
	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);
852
	assert_one_modified(1, 3, 0, 3, 0, &expected);
853 854 855 856 857 858 859 860 861

	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);

	quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL);
862
	assert_one_modified(1, 3, 0, 3, 0, &expected);
863 864

	quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL);
865
	assert_one_modified(1, 3, 0, 3, 0, &expected);
866

867
	cl_git_pass(git_patch_from_blob_and_buffer(
868
		&p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
Nicolas Hake committed
869
	cl_git_pass(git_patch_to_buf(&buf, p));
870 871 872 873 874 875 876 877
	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"
Nicolas Hake committed
878 879
		"+Go here\n", buf.ptr);
	git_buf_clear(&buf);
880
	git_patch_free(p);
881

882
	cl_git_pass(git_patch_from_blob_and_buffer(
883
		&p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
Nicolas Hake committed
884
	cl_git_pass(git_patch_to_buf(&buf, p));
885 886 887
	cl_assert_equal_s(
		"diff --git a/zzz.binary b/zzz.binary\n"
		"index 45141a7..75b0dbb 100644\n"
Nicolas Hake committed
888 889
		"Binary files a/zzz.binary and b/zzz.binary differ\n", buf.ptr);
	git_buf_clear(&buf);
890
	git_patch_free(p);
891

892
	cl_git_pass(git_patch_from_blob_and_buffer(
893
		&p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
Nicolas Hake committed
894
	cl_git_pass(git_patch_to_buf(&buf, p));
895 896 897 898 899 900 901 902
	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"
Nicolas Hake committed
903 904
		"+Go here\n", buf.ptr);
	git_buf_clear(&buf);
905
	git_patch_free(p);
906

907
	cl_git_pass(git_patch_from_blob_and_buffer(
908
		&p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
Nicolas Hake committed
909
	cl_git_pass(git_patch_to_buf(&buf, p));
910 911 912 913 914 915 916 917
	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"
Nicolas Hake committed
918 919
		"+Go here\n", buf.ptr);
	git_buf_clear(&buf);
920
	git_patch_free(p);
921 922 923 924 925 926 927

	/* "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";

928
	cl_git_pass(git_patch_from_blob_and_buffer(
929
		&p, bin, "zzz.normal", changed, 37, NULL, &opts));
Nicolas Hake committed
930
	cl_git_pass(git_patch_to_buf(&buf, p));
931 932 933
	cl_assert_equal_s(
		"diff --git a/zzz.normal b/zzz.normal\n"
		"index b435cd5..1604519 100644\n"
Nicolas Hake committed
934 935
		"Binary files a/zzz.normal and b/zzz.normal differ\n", buf.ptr);
	git_buf_clear(&buf);
936
	git_patch_free(p);
937

938
	cl_git_pass(git_patch_from_blob_and_buffer(
939
		&p, bin, "zzz.textary", changed, 37, NULL, &opts));
Nicolas Hake committed
940
	cl_git_pass(git_patch_to_buf(&buf, p));
941 942 943 944 945 946 947
	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"
Nicolas Hake committed
948 949
		"+replace a line\n", buf.ptr);
	git_buf_clear(&buf);
950
	git_patch_free(p);
951

952
	cl_git_pass(git_patch_from_blob_and_buffer(
953
		&p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
Nicolas Hake committed
954
	cl_git_pass(git_patch_to_buf(&buf, p));
955 956 957 958 959 960 961
	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"
Nicolas Hake committed
962 963
		"+replace a line\n", buf.ptr);
	git_buf_clear(&buf);
964
	git_patch_free(p);
965

966
	cl_git_pass(git_patch_from_blob_and_buffer(
967
		&p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
Nicolas Hake committed
968
	cl_git_pass(git_patch_to_buf(&buf, p));
969 970 971 972 973 974 975
	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"
Nicolas Hake committed
976 977
		"+replace a line\n", buf.ptr);
	git_buf_clear(&buf);
978
	git_patch_free(p);
979

Nicolas Hake committed
980
	git_buf_free(&buf);
981 982 983
	git_blob_free(nonbin);
	git_blob_free(bin);
}
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

void test_diff_blob__can_compare_buffer_to_buffer(void)
{
	const char *a = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n";
	const char *b = "a\nB\nc\nd\nE\nF\nh\nj\nk\n";

	opts.interhunk_lines = 0;
	opts.context_lines = 0;

	memset(&expected, 0, sizeof(expected));

	cl_git_pass(git_diff_buffers(
		a, strlen(a), NULL, b, strlen(b), NULL,
		&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
	assert_one_modified(4, 9, 0, 4, 5, &expected);

	opts.flags ^= GIT_DIFF_REVERSE;

	memset(&expected, 0, sizeof(expected));

	cl_git_pass(git_diff_buffers(
		a, strlen(a), NULL, b, strlen(b), NULL,
		&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
	assert_one_modified(4, 9, 0, 5, 4, &expected);
}