repo.c 11.5 KB
Newer Older
1
#include "clar_libgit2.h"
2 3
#include "fileops.h"
#include "git2/attr.h"
4
#include "attr.h"
5

6
#include "attr_expect.h"
7
#include "git2/sys/repository.h"
8

9 10 11 12
static git_repository *g_repo = NULL;

void test_attr_repo__initialize(void)
{
13
	g_repo = cl_git_sandbox_init("attr");
14 15 16 17
}

void test_attr_repo__cleanup(void)
{
18
	cl_git_sandbox_cleanup();
19 20 21
	g_repo = NULL;
}

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
static struct attr_expected get_one_test_cases[] = {
	{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
	{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
	{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
	{ "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
	{ "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
	{ "root_test2", "repoattr", EXPECT_TRUE, NULL },
	{ "root_test2", "rootattr", EXPECT_FALSE, NULL },
	{ "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
	{ "root_test2", "multiattr", EXPECT_FALSE, NULL },
	{ "root_test3", "repoattr", EXPECT_TRUE, NULL },
	{ "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
	{ "root_test3", "multiattr", EXPECT_STRING, "3" },
	{ "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
	{ "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
	{ "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
	{ "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
	{ "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
	{ "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
	{ "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
	{ "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
	{ "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
	{ "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
	{ "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
	{ "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
	{ "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
	{ "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
	{ "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
	{ "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
	{ "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
	{ "does-not-exist", "foo", EXPECT_STRING, "yes" },
	{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
	{ "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
	{ "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
};

58 59
void test_attr_repo__get_one(void)
{
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
	int i;

	for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
		struct attr_expected *scan = &get_one_test_cases[i];
		const char *value;

		cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
		attr_check_expected(
			scan->expected, scan->expected_str, scan->attr, value);
	}

	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
}

void test_attr_repo__get_one_start_deep(void)
{
	int i;
82

83 84
	for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
		struct attr_expected *scan = &get_one_test_cases[i];
85
		const char *value;
86

87
		cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
88 89
		attr_check_expected(
			scan->expected, scan->expected_str, scan->attr, value);
90
	}
91

92 93 94 95 96 97
	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
	cl_assert(git_attr_cache__is_cached(
		g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
98 99 100 101 102 103 104
}

void test_attr_repo__get_many(void)
{
	const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
	const char *values[4];

105
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
106

107 108 109 110
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_TRUE(values[1]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
111

112
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
113

114 115 116 117
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_FALSE(values[1]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
118

119
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
120

121 122 123
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_TRUE(values[1]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
124
	cl_assert_equal_s("yes", values[3]);
125 126
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
void test_attr_repo__get_many_in_place(void)
{
	const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };

	/* it should be legal to look up values into the same array that has
	 * the attribute names, overwriting each name as the value is found.
	 */

	cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));

	cl_assert(GIT_ATTR_TRUE(vals[0]));
	cl_assert(GIT_ATTR_TRUE(vals[1]));
	cl_assert(GIT_ATTR_UNSPECIFIED(vals[2]));
	cl_assert_equal_s("yes", vals[3]);
}

143
static int count_attrs(
144 145
	const char *name,
	const char *value,
146 147
	void *payload)
{
148 149
	GIT_UNUSED(name);
	GIT_UNUSED(value);
150 151 152

	*((int *)payload) += 1;

153
	return 0;
154 155
}

156 157
#define CANCEL_VALUE 12345

158 159 160 161 162 163 164 165 166 167 168
static int cancel_iteration(
	const char *name,
	const char *value,
	void *payload)
{
	GIT_UNUSED(name);
	GIT_UNUSED(value);

	*((int *)payload) -= 1;

	if (*((int *)payload) < 0)
169
		return CANCEL_VALUE;
170 171 172 173

	return 0;
}

174 175 176 177 178
void test_attr_repo__foreach(void)
{
	int count;

	count = 0;
179 180
	cl_git_pass(git_attr_foreach(
		g_repo, 0, "root_test1", &count_attrs, &count));
181 182 183
	cl_assert(count == 2);

	count = 0;
184
	cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
185 186 187 188
		&count_attrs, &count));
	cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */

	count = 0;
189
	cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
190
		&count_attrs, &count));
191
	cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
192 193 194

	count = 2;
	cl_assert_equal_i(
195
		CANCEL_VALUE, git_attr_foreach(
196 197
			g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
	);
198
}
199 200 201 202 203

void test_attr_repo__manpage_example(void)
{
	const char *value;

204
	cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
205
	cl_assert(GIT_ATTR_TRUE(value));
206

207
	cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
208
	cl_assert(GIT_ATTR_UNSPECIFIED(value));
209

210
	cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
211
	cl_assert(GIT_ATTR_FALSE(value));
212

213
	cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
214
	cl_assert_equal_s("filfre", value);
215

216
	cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
217
	cl_assert(GIT_ATTR_UNSPECIFIED(value));
218 219 220 221 222 223
}

void test_attr_repo__macros(void)
{
	const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" };
	const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
224
	const char *names3[3] = { "macro2", "multi2", "multi3" };
225 226
	const char *values[5];

227
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
228

229 230 231 232 233
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_TRUE(values[1]));
	cl_assert(GIT_ATTR_FALSE(values[2]));
	cl_assert(GIT_ATTR_FALSE(values[3]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
234

235
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
236

237 238 239 240
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_TRUE(values[1]));
	cl_assert(GIT_ATTR_FALSE(values[2]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
241
	cl_assert_equal_s("77", values[4]);
242

243
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
244

245 246
	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert(GIT_ATTR_FALSE(values[1]));
247
	cl_assert_equal_s("answer", values[2]);
248 249 250 251 252 253 254 255
}

void test_attr_repo__bad_macros(void)
{
	const char *names[6] = { "rootattr", "positive", "negative",
		"firstmacro", "secondmacro", "thirdmacro" };
	const char *values[6];

256
	cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
257 258

	/* these three just confirm that the "mymacro" rule ran */
259 260 261
	cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
	cl_assert(GIT_ATTR_TRUE(values[1]));
	cl_assert(GIT_ATTR_FALSE(values[2]));
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284

	/* file contains:
	 *     # let's try some malicious macro defs
	 *     [attr]firstmacro -thirdmacro -secondmacro
	 *     [attr]secondmacro firstmacro -firstmacro
	 *     [attr]thirdmacro secondmacro=hahaha -firstmacro
	 *     macro_bad firstmacro secondmacro thirdmacro
	 *
	 * firstmacro assignment list ends up with:
	 *     -thirdmacro -secondmacro
	 * secondmacro assignment list expands "firstmacro" and ends up with:
	 *     -thirdmacro -secondmacro -firstmacro
	 * thirdmacro assignment don't expand so list ends up with:
	 *     secondmacro="hahaha"
	 *
	 * macro_bad assignment list ends up with:
	 *     -thirdmacro -secondmacro firstmacro &&
	 *     -thirdmacro -secondmacro -firstmacro secondmacro &&
	 *     secondmacro="hahaha" thirdmacro
	 *
	 * so summary results should be:
	 *     -firstmacro secondmacro="hahaha" thirdmacro
	 */
285
	cl_assert(GIT_ATTR_FALSE(values[3]));
286
	cl_assert_equal_s("hahaha", values[4]);
287
	cl_assert(GIT_ATTR_TRUE(values[5]));
288
}
289

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
#define CONTENT "I'm going to be dynamically processed\r\n" \
	"And my line endings...\r\n" \
	"...are going to be\n" \
	"normalized!\r\n"

#define GITATTR "* text=auto\n" \
	"*.txt text\n" \
	"*.data binary\n"

static void add_to_workdir(const char *filename, const char *content)
{
	git_buf buf = GIT_BUF_INIT;

	cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
	cl_git_rewritefile(git_buf_cstr(&buf), content);

	git_buf_free(&buf);
}

static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
{
311
	size_t index_pos;
Ben Straub committed
312
	const git_index_entry *entry;
313 314

	add_to_workdir(filename, CONTENT);
315
	cl_git_pass(git_index_add_bypath(index, filename));
316

317
	cl_assert(!git_index_find(&index_pos, index, filename));
318

Edward Thomson committed
319
	entry = git_index_get_byindex(index, index_pos);
320
	cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha));
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
}

void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
{
	git_index* index;

	cl_git_pass(git_repository_index(&index, g_repo));

	add_to_workdir(".gitattributes", GITATTR);

	assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
	assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
	assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");

	git_index_free(index);
}
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

void test_attr_repo__bare_repo_with_index(void)
{
	const char *names[4] = { "test1", "test2", "test3", "test4" };
	const char *values[4];
	git_index *index;

	cl_git_pass(git_repository_index(&index, g_repo));

	cl_git_mkfile(
		"attr/.gitattributes",
		"*.txt test1 test2=foobar -test3\n"
		"trial.txt -test1 test2=barfoo !test3 test4\n");
	cl_git_pass(git_index_add_bypath(index, ".gitattributes"));
	git_index_free(index);

	cl_must_pass(p_unlink("attr/.gitattributes"));
	cl_assert(!git_path_exists("attr/.gitattributes"));

	cl_git_pass(git_repository_set_bare(g_repo));

	cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names));

	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert_equal_s("foobar", values[1]);
	cl_assert(GIT_ATTR_FALSE(values[2]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));

	cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names));

	cl_assert(GIT_ATTR_FALSE(values[0]));
	cl_assert_equal_s("barfoo", values[1]);
	cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
	cl_assert(GIT_ATTR_TRUE(values[3]));
371 372 373 374 375 376 377

	cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names));

	cl_assert(GIT_ATTR_TRUE(values[0]));
	cl_assert_equal_s("foobar", values[1]);
	cl_assert(GIT_ATTR_FALSE(values[2]));
	cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
378
}