t04-commit.c 16.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2,
 * as published by the Free Software Foundation.
 *
 * In addition to the permissions in the GNU General Public License,
 * the authors give you unlimited permission to link the compiled
 * version of this file into combinations with other programs,
 * and to distribute those combinations without any restriction
 * coming from the use of this file.  (The General Public License
 * restrictions do apply in other respects; for example, they cover
 * modification of the file, and distribution when not linked into
 * a combined executable.)
 *
 * This file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
25 26
#include "test_lib.h"
#include "test_helpers.h"
27

28
#include "commit.h"
29
#include "signature.h"
30 31 32

static char *test_commits_broken[] = {

33
/* empty commit */
34 35
"",

36
/* random garbage */
37 38
"asd97sa9du902e9a0jdsuusad09as9du098709aweu8987sd\n",

39
/* broken endlines 1 */
40 41 42 43 44 45 46
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\r\n\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\r\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
\r\n\
a test commit with broken endlines\r\n",

47
/* broken endlines 2 */
48 49 50 51 52 53 54
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
\
another test commit with broken endlines",

55
/* starting endlines */
56 57 58 59 60 61 62
"\ntree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a test commit with a starting endline\n",

63
/* corrupted commit 1 */
64 65 66
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent 05452d6349abcd67aa396df",

67
/* corrupted commit 2 */
68 69 70
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent ",

71
/* corrupted commit 3 */
72 73 74
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent ",

75
/* corrupted commit 4 */
76 77 78 79 80 81 82
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
par",

};


static char *test_commits_working[] = {
83
/* simple commit with no message */
84
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
85 86 87 88
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n",

89
/* simple commit, no parent */
90
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
91 92 93 94 95
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a simple commit which works\n",

96
/* simple commit, no parent, no newline in message */
97
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
98 99 100
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
101 102 103 104 105 106 107 108
a simple commit which works",

/* simple commit, 1 parent */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent e90810b8df3e80c413d903f631643c716887138d\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
109 110 111
a simple commit which works\n",
};

112
BEGIN_TEST(parse0, "parse the OID line in a commit")
113

114
	git_oid oid;
115

116
#define TEST_OID_PASS(string, header) { \
117 118
	const char *ptr = string;\
	const char *ptr_original = ptr;\
119
	size_t len = strlen(ptr);\
Vicent Marti committed
120
	must_pass(git__parse_oid(&oid, &ptr, ptr + len, header));\
121
	must_be_true(ptr == ptr_original + len);\
122 123
}

124
#define TEST_OID_FAIL(string, header) { \
125
	const char *ptr = string;\
126
	size_t len = strlen(ptr);\
Vicent Marti committed
127
	must_fail(git__parse_oid(&oid, &ptr, ptr + len, header));\
128 129
}

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
	TEST_OID_PASS("parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent ");
	TEST_OID_PASS("tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
	TEST_OID_PASS("random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading ");
	TEST_OID_PASS("stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading");
	TEST_OID_PASS("tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree ");
	TEST_OID_PASS("tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree ");
	TEST_OID_PASS("tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree ");
	TEST_OID_PASS("tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree ");

	TEST_OID_FAIL("parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent ");
	TEST_OID_FAIL("05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
	TEST_OID_FAIL("parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent ");
	TEST_OID_FAIL("parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent ");
	TEST_OID_FAIL("tree  05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
	TEST_OID_FAIL("parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent ");
	TEST_OID_FAIL("parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent ");
	TEST_OID_FAIL("", "tree ");
	TEST_OID_FAIL("", "");
148 149 150 151 152 153

#undef TEST_OID_PASS
#undef TEST_OID_FAIL

END_TEST

154
BEGIN_TEST(parse1, "parse the signature line in a commit")
155

156
#define TEST_SIGNATURE_PASS(_string, _header, _name, _email, _time, _offset) { \
157
	const char *ptr = _string; \
158
	size_t len = strlen(_string);\
159 160
	git_signature person = {NULL, NULL, {0, 0}}; \
	must_pass(git_signature__parse(&person, &ptr, ptr + len, _header));\
161 162
	must_be_true(strcmp(_name, person.name) == 0);\
	must_be_true(strcmp(_email, person.email) == 0);\
163 164
	must_be_true(_time == person.when.time);\
	must_be_true(_offset == person.when.offset);\
165
	free(person.name); free(person.email);\
166 167
}

168
#define TEST_SIGNATURE_FAIL(_string, _header) { \
169
	const char *ptr = _string; \
170
	size_t len = strlen(_string);\
171 172
	git_signature person = {NULL, NULL, {0, 0}}; \
	must_fail(git_signature__parse(&person, &ptr, ptr + len, _header));\
173
	free(person.name); free(person.email);\
174 175
}

176
	TEST_SIGNATURE_PASS(
177 178 179 180 181 182
		"author Vicent Marti <tanoku@gmail.com> 12345 \n",
		"author ",
		"Vicent Marti",
		"tanoku@gmail.com",
		12345,
		0);
183

184
	TEST_SIGNATURE_PASS(
185 186 187 188
		"author Vicent Marti <> 12345 \n",
		"author ",
		"Vicent Marti",
		"",
189 190
		12345,
		0);
191

192
	TEST_SIGNATURE_PASS(
193
		"author Vicent Marti <tanoku@gmail.com> 231301 +1020\n",
194 195 196
		"author ",
		"Vicent Marti",
		"tanoku@gmail.com",
197 198
		231301,
		620);
199

200
	TEST_SIGNATURE_PASS(
201 202 203 204 205 206
		"author Vicent Marti with an outrageously long name \
		which will probably overflow the buffer <tanoku@gmail.com> 12345 \n",
		"author ",
		"Vicent Marti with an outrageously long name \
		which will probably overflow the buffer",
		"tanoku@gmail.com",
207 208
		12345,
		0);
209

210
	TEST_SIGNATURE_PASS(
211 212 213 214 215 216
		"author Vicent Marti <tanokuwithaveryveryverylongemail\
		whichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n",
		"author ",
		"Vicent Marti",
		"tanokuwithaveryveryverylongemail\
		whichwillprobablyvoverflowtheemailbuffer@gmail.com",
217 218
		12345,
		0);
219

220
	TEST_SIGNATURE_PASS(
221 222 223 224 225 226 227
		"committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n",
		"committer ",
		"Vicent Marti",
		"tanoku@gmail.com",
		123456,
		0);

228
	TEST_SIGNATURE_PASS(
229 230 231 232 233 234 235
		"committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n",
		"committer ",
		"Vicent Marti",
		"tanoku@gmail.com",
		123456,
		60);

236
	TEST_SIGNATURE_PASS(
237 238 239 240 241 242 243
		"committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n",
		"committer ",
		"Vicent Marti",
		"tanoku@gmail.com",
		123456,
		-60);

244
	TEST_SIGNATURE_FAIL(
245 246 247
		"committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n",
		"committer ");

248
	TEST_SIGNATURE_FAIL(
249 250 251
		"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n",
		"committer ");

252
	TEST_SIGNATURE_FAIL(
253 254 255
		"author Vicent Marti <tanoku@gmail.com> 12345 \n",
		"author  ");

256
	TEST_SIGNATURE_FAIL(
257 258 259
		"author Vicent Marti <tanoku@gmail.com> 12345 \n",
		"committer ");

260
	TEST_SIGNATURE_FAIL(
261 262 263
		"author Vicent Marti 12345 \n",
		"author ");

264
	TEST_SIGNATURE_FAIL(
265 266 267
		"author Vicent Marti <broken@email 12345 \n",
		"author ");

268
	TEST_SIGNATURE_FAIL(
269 270 271
		"author Vicent Marti <tanoku@gmail.com> notime \n",
		"author ");

272
	TEST_SIGNATURE_FAIL(
273 274 275
		"author Vicent Marti <tanoku@gmail.com>\n",
		"author ");

276
	TEST_SIGNATURE_FAIL(
277 278 279
		"author ",
		"author ");

280 281
#undef TEST_SIGNATURE_PASS
#undef TEST_SIGNATURE_FAIL
282 283 284

END_TEST

285 286 287
/* External declaration for testing the buffer parsing method */
int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags);

288
BEGIN_TEST(parse2, "parse a whole commit buffer")
289 290 291 292
	const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken);
	const int working_commit_count = sizeof(test_commits_working) / sizeof(*test_commits_working);

	int i;
293
	git_repository *repo;
294

Vicent Marti committed
295
	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
296

297 298 299 300
	for (i = 0; i < broken_commit_count; ++i) {
		git_commit *commit;
		commit = git__malloc(sizeof(git_commit));
		memset(commit, 0x0, sizeof(git_commit));
301
		commit->object.repo = repo;
302

303
		must_fail(commit_parse_buffer(
304 305 306
					commit,
					test_commits_broken[i],
					strlen(test_commits_broken[i]),
307
					0x1)
308
				);
309

310 311
		git_commit__free(commit);
	}
312

313 314
	for (i = 0; i < working_commit_count; ++i) {
		git_commit *commit;
Vicent Marti committed
315

316 317
		commit = git__malloc(sizeof(git_commit));
		memset(commit, 0x0, sizeof(git_commit));
318
		commit->object.repo = repo;
319

320
		must_pass(commit_parse_buffer(
321 322 323
					commit,
					test_commits_working[i],
					strlen(test_commits_working[i]),
324 325 326
					0x0)
				);

Vicent Marti committed
327 328 329
		git_commit__free(commit);

		commit = git__malloc(sizeof(git_commit));
330 331 332 333 334 335 336
		memset(commit, 0x0, sizeof(git_commit));
		commit->object.repo = repo;

		must_pass(commit_parse_buffer(
					commit,
					test_commits_working[i],
					strlen(test_commits_working[i]),
337
					0x1)
338
				);
339

340 341
		git_commit__free(commit);
	}
342

343
	git_repository_free(repo);
344
END_TEST
345 346 347 348 349 350 351 352 353 354

static const char *commit_ids[] = {
	"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
	"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
	"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
	"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
	"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
	"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
};

355
BEGIN_TEST(details0, "query the details on a parsed commit")
356 357 358 359 360 361 362 363 364 365 366 367 368
	const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);

	unsigned int i;
	git_repository *repo;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
	
	for (i = 0; i < commit_count; ++i) {
		git_oid id;
		git_commit *commit;

		const git_signature *author, *committer;
		const char *message, *message_short;
369
		git_time_t commit_time;
370
		unsigned int parents, p;
371
		git_commit *parent = NULL, *old_parent = NULL;
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392

		git_oid_mkstr(&id, commit_ids[i]);

		must_pass(git_commit_lookup(&commit, repo, &id));

		message = git_commit_message(commit);
		message_short = git_commit_message_short(commit);
		author = git_commit_author(commit);
		committer = git_commit_committer(commit);
		commit_time = git_commit_time(commit);
		parents = git_commit_parentcount(commit);

		must_be_true(strcmp(author->name, "Scott Chacon") == 0);
		must_be_true(strcmp(author->email, "schacon@gmail.com") == 0);
		must_be_true(strcmp(committer->name, "Scott Chacon") == 0);
		must_be_true(strcmp(committer->email, "schacon@gmail.com") == 0);
		must_be_true(strchr(message, '\n') != NULL);
		must_be_true(strchr(message_short, '\n') == NULL);
		must_be_true(commit_time > 0);
		must_be_true(parents <= 2);
		for (p = 0;p < parents;p++) {
393 394 395 396
			if (old_parent != NULL)
				git_commit_close(old_parent);

			old_parent = parent;
397
			must_pass(git_commit_parent(&parent, commit, p));
398 399 400
			must_be_true(parent != NULL);
			must_be_true(git_commit_author(parent) != NULL); // is it really a commit?
		}
401 402 403
		git_commit_close(old_parent);
		git_commit_close(parent);

404
		must_fail(git_commit_parent(&parent, commit, parents));
405
		git_commit_close(commit);
406 407 408 409 410 411 412 413 414 415 416 417
	}

	git_repository_free(repo);
END_TEST

#define COMMITTER_NAME "Vicent Marti"
#define COMMITTER_EMAIL "vicent@github.com"
#define COMMIT_MESSAGE "This commit has been created in memory\n\
This is a commit created in memory and it will be written back to disk\n"

static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";

Vicent Marti committed
418

419
BEGIN_TEST(write0, "write a new commit object from memory to disk")
420
	git_repository *repo;
Vicent Marti committed
421 422
	git_commit *commit;
	git_oid tree_id, parent_id, commit_id;
423 424 425 426 427 428
	const git_signature *author, *committer;
	/* char hex_oid[41]; */

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));


Vicent Marti committed
429 430
	git_oid_mkstr(&tree_id, tree_oid);
	git_oid_mkstr(&parent_id, commit_ids[4]);
431

Vicent Marti committed
432
	/* create signatures */
433 434 435 436 437 438
	committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60);
	must_be_true(committer != NULL);

	author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90);
	must_be_true(author != NULL);

Vicent Marti committed
439 440 441 442 443 444 445 446 447
	must_pass(git_commit_create_v(
		&commit_id, /* out id */
		repo,
		NULL, /* do not update the HEAD */
		author,
		committer,
		COMMIT_MESSAGE,
		&tree_id,
		1, &parent_id));
448 449 450 451

	git_signature_free((git_signature *)committer);
	git_signature_free((git_signature *)author);

Vicent Marti committed
452 453
	must_pass(git_commit_lookup(&commit, repo, &commit_id));

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
	/* Check attributes were set correctly */
	author = git_commit_author(commit);
	must_be_true(author != NULL);
	must_be_true(strcmp(author->name, COMMITTER_NAME) == 0);
	must_be_true(strcmp(author->email, COMMITTER_EMAIL) == 0);
	must_be_true(author->when.time == 987654321);
	must_be_true(author->when.offset == 90);

	committer = git_commit_committer(commit);
	must_be_true(committer != NULL);
	must_be_true(strcmp(committer->name, COMMITTER_NAME) == 0);
	must_be_true(strcmp(committer->email, COMMITTER_EMAIL) == 0);
	must_be_true(committer->when.time == 123456789);
	must_be_true(committer->when.offset == 60);

	must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0);

	must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));

473
	git_commit_close(commit);
474 475 476
	git_repository_free(repo);
END_TEST

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 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
#define ROOT_COMMIT_MESSAGE "This is a root commit\n\
This is a root commit and should be the only one in this branch\n"

BEGIN_TEST(root0, "create a root commit")
	git_repository *repo;
	git_commit *commit;
	git_oid tree_id, commit_id;
	const git_oid *branch_oid;
	const git_signature *author, *committer;
	const char *branch_name = "refs/heads/root-commit-branch";
	git_reference *head, *branch;
	char *head_old;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

	git_oid_mkstr(&tree_id, tree_oid);

	/* create signatures */
	committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60);
	must_be_true(committer != NULL);

	author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90);
	must_be_true(author != NULL);

	/* First we need to update HEAD so it points to our non-existant branch */
	must_pass(git_reference_lookup(&head, repo, "HEAD"));
	must_be_true(git_reference_type(head) == GIT_REF_SYMBOLIC);
	head_old = git__strdup(git_reference_target(head));
	must_be_true(head_old != NULL);

	must_pass(git_reference_set_target(head, branch_name));

	must_pass(git_commit_create_v(
		&commit_id, /* out id */
		repo,
		"HEAD",
		author,
		committer,
		ROOT_COMMIT_MESSAGE,
		&tree_id,
		0));

	git_signature_free((git_signature *)committer);
	git_signature_free((git_signature *)author);

	/*
	 * The fact that creating a commit works has already been
	 * tested. Here we just make sure it's our commit and that it was
	 * written as a root commit.
	 */
	must_pass(git_commit_lookup(&commit, repo, &commit_id));
	must_be_true(git_commit_parentcount(commit) == 0);
	must_pass(git_reference_lookup(&branch, repo, branch_name));
	branch_oid = git_reference_oid(branch);
	must_pass(git_oid_cmp(branch_oid, &commit_id));
	must_be_true(!strcmp(git_commit_message(commit), ROOT_COMMIT_MESSAGE));

	/* Remove the data we just added to the repo */
	must_pass(git_reference_lookup(&head, repo, "HEAD"));
	must_pass(git_reference_set_target(head, head_old));
	must_pass(git_reference_delete(branch));
	must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
	free(head_old);
	git_commit_close(commit);
	git_repository_free(repo);
END_TEST
543

544 545 546 547 548
BEGIN_SUITE(commit)
	ADD_TEST(parse0);
	ADD_TEST(parse1);
	ADD_TEST(parse2);
	ADD_TEST(details0);
Vicent Marti committed
549

550
	ADD_TEST(write0);
Vicent Marti committed
551
	//ADD_TEST(write1);
552 553

	ADD_TEST(root0);
554
END_SUITE