t12-repo.c 16.9 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 25 26 27 28 29
/*
 * 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.
 */
#include "test_lib.h"
#include "test_helpers.h"

#include "odb.h"
#include "git2/odb_backend.h"
30
#include "repository.h"
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 58 59 60 61 62 63 64 65 66

typedef struct {
	git_odb_backend base;
	int position;
} fake_backend;

git_odb_backend *new_backend(int position)
{
	fake_backend *b;

	b = git__malloc(sizeof(fake_backend));
	if (b == NULL)
		return NULL;

	memset(b, 0x0, sizeof(fake_backend));
	b->position = position;
	return (git_odb_backend *)b;
}

int test_backend_sorting(git_odb *odb)
{
	unsigned int i;

	for (i = 0; i < odb->backends.length; ++i) {
		fake_backend *internal = *((fake_backend **)git_vector_get(&odb->backends, i));

		if (internal == NULL)
			return GIT_ERROR;

		if (internal->position != (int)i)
			return GIT_ERROR;
	}

	return GIT_SUCCESS;
}

67
BEGIN_TEST(odb0, "assure that ODB backends are properly sorted")
68 69 70 71 72 73 74 75 76 77
	git_odb *odb;
	must_pass(git_odb_new(&odb));
	must_pass(git_odb_add_backend(odb, new_backend(0), 5));
	must_pass(git_odb_add_backend(odb, new_backend(2), 3));
	must_pass(git_odb_add_backend(odb, new_backend(1), 4));
	must_pass(git_odb_add_backend(odb, new_backend(3), 1));
	must_pass(test_backend_sorting(odb));
	git_odb_close(odb);
END_TEST

78
BEGIN_TEST(odb1, "assure that alternate backends are properly sorted")
79 80 81 82 83 84 85 86 87 88 89 90 91 92
	git_odb *odb;
	must_pass(git_odb_new(&odb));
	must_pass(git_odb_add_backend(odb, new_backend(0), 5));
	must_pass(git_odb_add_backend(odb, new_backend(2), 3));
	must_pass(git_odb_add_backend(odb, new_backend(1), 4));
	must_pass(git_odb_add_backend(odb, new_backend(3), 1));
	must_pass(git_odb_add_alternate(odb, new_backend(4), 5));
	must_pass(git_odb_add_alternate(odb, new_backend(6), 3));
	must_pass(git_odb_add_alternate(odb, new_backend(5), 4));
	must_pass(git_odb_add_alternate(odb, new_backend(7), 1));
	must_pass(test_backend_sorting(odb));
	git_odb_close(odb);
END_TEST

93 94 95 96

#define STANDARD_REPOSITORY 0
#define BARE_REPOSITORY 1

97 98 99 100 101 102
static int ensure_repository_init(
	const char *working_directory,
	int repository_kind,
	const char *expected_path_index,
	const char *expected_path_repository,
	const char *expected_working_directory)
103 104 105 106
{
	char path_odb[GIT_PATH_MAX];
	git_repository *repo;

Vicent Marti committed
107
	if (git_futils_isdir(working_directory) == GIT_SUCCESS)
108
		return GIT_ERROR;
109

Vicent Marti committed
110
	git_path_join(path_odb, expected_path_repository, GIT_OBJECTS_DIR);
111

112 113 114 115
	if (git_repository_init(&repo, working_directory, repository_kind) < GIT_SUCCESS)
		return GIT_ERROR;

	if (repo->path_workdir != NULL || expected_working_directory != NULL) {
116
		if (git__suffixcmp(repo->path_workdir, expected_working_directory) != 0)
117
			goto cleanup;
118 119
	}

120
	if (git__suffixcmp(repo->path_odb, path_odb) != 0)
121
		goto cleanup;
122

123
	if (git__suffixcmp(repo->path_repository, expected_path_repository) != 0)
124
		goto cleanup;
125 126

	if (repo->path_index != NULL || expected_path_index != NULL) {
127
		if (git__suffixcmp(repo->path_index, expected_path_index) != 0)
128
			goto cleanup;
129

130 131 132 133 134
#ifdef GIT_WIN32
		if ((GetFileAttributes(repo->path_repository) & FILE_ATTRIBUTE_HIDDEN) == 0)
			goto cleanup;
#endif

135 136 137 138 139 140 141
		if (git_repository_is_bare(repo) == 1)
			goto cleanup;
	} else if (git_repository_is_bare(repo) == 0)
			goto cleanup;

	if (git_repository_is_empty(repo) == 0)
		goto cleanup;
142 143

	git_repository_free(repo);
144
	git_futils_rmdir_r(working_directory, 1);
145 146

	return GIT_SUCCESS;
147 148 149

cleanup:
	git_repository_free(repo);
150
	git_futils_rmdir_r(working_directory, 1);
151
	return GIT_ERROR;
152 153
}

154
BEGIN_TEST(init0, "initialize a standard repo")
155 156
	char path_index[GIT_PATH_MAX], path_repository[GIT_PATH_MAX];

Vicent Marti committed
157 158
	git_path_join(path_repository, TEMP_REPO_FOLDER, GIT_DIR);
	git_path_join(path_index, path_repository, GIT_INDEX_FILE);
159

160 161
	must_pass(ensure_repository_init(TEMP_REPO_FOLDER, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER));
	must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER));
162 163
END_TEST

164
BEGIN_TEST(init1, "initialize a bare repo")
165 166
	char path_repository[GIT_PATH_MAX];

Vicent Marti committed
167
	git_path_join(path_repository, TEMP_REPO_FOLDER, "");
168

169 170
	must_pass(ensure_repository_init(TEMP_REPO_FOLDER, BARE_REPOSITORY, NULL, path_repository, NULL));
	must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, BARE_REPOSITORY, NULL, path_repository, NULL));
171 172
END_TEST

173
BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping out of the current working directory")
174 175 176 177 178
	char path_repository[GIT_PATH_MAX];
	char current_workdir[GIT_PATH_MAX];
	const int mode = 0755; /* or 0777 ? */
	git_repository* repo;

Vicent Marti committed
179
	must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
180

Vicent Marti committed
181 182
	git_path_join(path_repository, TEMP_REPO_FOLDER, "a/b/c/");
	must_pass(git_futils_mkdir_r(path_repository, mode));
183 184 185 186

	must_pass(chdir(path_repository));

	must_pass(git_repository_init(&repo, "../d/e.git", 1));
187 188
	must_pass(git__suffixcmp(repo->path_repository, "/a/b/d/e.git/"));

189 190
	git_repository_free(repo);

191 192 193 194 195
	must_pass(git_repository_open(&repo, "../d/e.git"));

	git_repository_free(repo);

	must_pass(chdir(current_workdir));
196
	must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
197 198
END_TEST

199
#define EMPTY_BARE_REPOSITORY_FOLDER TEST_RESOURCES "/empty_bare.git/"
200 201 202 203 204 205 206 207

BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git")
	git_repository *repo;

	must_pass(copydir_recurs(EMPTY_BARE_REPOSITORY_FOLDER, TEMP_REPO_FOLDER));
	must_pass(remove_placeholders(TEMP_REPO_FOLDER, "dummy-marker.txt"));

	must_pass(git_repository_open(&repo, TEMP_REPO_FOLDER));
208 209
	must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL);
	must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) == NULL);
210 211

	git_repository_free(repo);
212
	must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
213 214
END_TEST

215
#define EMPTY_REPOSITORY_FOLDER TEST_RESOURCES "/empty_standard_repo/.gitted/"
216 217 218 219

BEGIN_TEST(open1, "Open a standard repository that has just been initialized by git")
	git_repository *repo;

220 221
	must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
	must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
222

223
	must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
224 225
	must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL);
	must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) != NULL);
226 227

	git_repository_free(repo);
228
	must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
229 230 231
END_TEST


232 233 234 235 236 237 238 239 240
BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of the current working directory")
	char new_current_workdir[GIT_PATH_MAX];
	char current_workdir[GIT_PATH_MAX];
	char path_repository[GIT_PATH_MAX];

	const int mode = 0755; /* or 0777 ? */
	git_repository* repo;

	/* Setup the repository to open */
Vicent Marti committed
241
	must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
242
	strcpy(path_repository, current_workdir);
Vicent Marti committed
243
	git_path_join_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git");
244 245 246
	must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository));

	/* Change the current working directory */
Vicent Marti committed
247 248
	git_path_join(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/");
	must_pass(git_futils_mkdir_r(new_current_workdir, mode));
249 250 251 252 253 254
	must_pass(chdir(new_current_workdir));

	must_pass(git_repository_open(&repo, "../../d/e.git"));

	git_repository_free(repo);

255
	must_pass(chdir(current_workdir));
256
	must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
257
END_TEST
258

259 260 261 262 263 264 265 266 267 268 269 270 271
BEGIN_TEST(empty0, "test if a repository is empty or not")

	git_repository *repo_empty, *repo_normal;

	must_pass(git_repository_open(&repo_normal, REPOSITORY_FOLDER));
	must_be_true(git_repository_is_empty(repo_normal) == 0);
	git_repository_free(repo_normal);

	must_pass(git_repository_open(&repo_empty, EMPTY_BARE_REPOSITORY_FOLDER));
	must_be_true(git_repository_is_empty(repo_empty) == 1);
	git_repository_free(repo_empty);
END_TEST

272 273 274 275 276 277 278
BEGIN_TEST(detached0, "test if HEAD is detached")
	git_repository *repo;
	git_reference *ref;
	git_oid oid;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

279
	must_be_true(git_repository_head_detached(repo) == 0);
280 281 282

	/* detach the HEAD */
	git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd");
283 284
	must_pass(git_reference_create_oid(&ref, repo, "HEAD", &oid, 1));
	must_be_true(git_repository_head_detached(repo) == 1);
285 286

	/* take the reop back to it's original state */
287 288
	must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/master", 1));
	must_be_true(git_repository_head_detached(repo) == 0);
289 290 291 292 293 294 295 296 297 298

	git_repository_free(repo);
END_TEST

BEGIN_TEST(orphan0, "test if HEAD is orphan")
	git_repository *repo;
	git_reference *ref;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

299
	must_be_true(git_repository_head_orphan(repo) == 0);
300 301

	/* orphan HEAD */
302 303
	must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/orphan", 1));
	must_be_true(git_repository_head_orphan(repo) == 1);
304 305

	/* take the reop back to it's original state */
306 307
	must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/master", 1));
	must_be_true(git_repository_head_orphan(repo) == 0);
308 309 310 311

	git_repository_free(repo);
END_TEST

312
#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348

#define SUB_REPOSITORY_FOLDER_NAME "sub_repo"
#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME
#define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub"
#define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub"
#define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub"

#define REPOSITORY_ALTERNATE_FOLDER DISCOVER_FOLDER "/alternate_sub_repo"
#define REPOSITORY_ALTERNATE_FOLDER_SUB REPOSITORY_ALTERNATE_FOLDER "/sub"
#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB "/subsub"
#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/subsubsub"

#define ALTERNATE_MALFORMED_FOLDER1 DISCOVER_FOLDER "/alternate_malformed_repo1"
#define ALTERNATE_MALFORMED_FOLDER2 DISCOVER_FOLDER "/alternate_malformed_repo2"
#define ALTERNATE_MALFORMED_FOLDER3 DISCOVER_FOLDER "/alternate_malformed_repo3"
#define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo"

static int ensure_repository_discover(const char *start_path, const char *ceiling_dirs, const char *expected_path)
{
	int error;
	char found_path[GIT_PATH_MAX];

	error = git_repository_discover(found_path, sizeof(found_path), start_path, 0, ceiling_dirs);
	//across_fs is always 0 as we can't automate the filesystem change tests

	if (error < GIT_SUCCESS)
		return error;

	return strcmp(found_path, expected_path) ? GIT_ERROR : GIT_SUCCESS;
}

static int write_file(const char *path, const char *content)
{
	int error;
	git_file file;

Vicent Marti committed
349 350
	if (git_futils_exists(path) == GIT_SUCCESS) {
		error = p_unlink(path);
351 352 353 354 355

		if (error < GIT_SUCCESS)
			return error;
	}

Vicent Marti committed
356
	file = git_futils_creat_withpath(path, 0644);
357 358 359
	if (file < GIT_SUCCESS)
		return file;

360
	error = p_write(file, content, strlen(content) * sizeof(char));
361

Vicent Marti committed
362
	p_close(file);
363 364 365 366 367 368 369 370 371 372

	return error;
}

//no check is performed on ceiling_dirs length, so be sure it's long enough
static int append_ceiling_dir(char *ceiling_dirs, const char *path)
{
	int len = strlen(ceiling_dirs);
	int error;

373
	error = git_path_prettify_dir(ceiling_dirs + len + (len ? 1 : 0), path, NULL);
374 375 376 377
	if (error < GIT_SUCCESS)
		return git__rethrow(error, "Failed to append ceiling directory.");

	if (len)
378
		ceiling_dirs[len] = GIT_PATH_LIST_SEPARATOR;
379 380 381 382 383 384 385 386 387 388 389 390

	return GIT_SUCCESS;
}

BEGIN_TEST(discover0, "test discover")
	git_repository *repo;
	char ceiling_dirs[GIT_PATH_MAX * 2] = "";
	char repository_path[GIT_PATH_MAX];
	char sub_repository_path[GIT_PATH_MAX];
	char found_path[GIT_PATH_MAX];
	int mode = 0755;

Vicent Marti committed
391
	git_futils_mkdir_r(DISCOVER_FOLDER, mode);
392
	must_pass(append_ceiling_dir(ceiling_dirs, TEMP_REPO_FOLDER));
393

394
	must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
395 396 397

	must_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
	must_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs));
398
	git_repository_free(repo);
399 400

	must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
Vicent Marti committed
401
	must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
402 403
	must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));

Vicent Marti committed
404
	must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
405 406 407 408
	must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path));

Vicent Marti committed
409
	must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
410 411 412 413 414 415 416 417
	must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
	must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
	must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));

Vicent Marti committed
418
	must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
419
	must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"));
Vicent Marti committed
420
	must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode));
421
	must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"));
Vicent Marti committed
422
	must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode));
423
	must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"));
Vicent Marti committed
424
	must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
425 426 427 428
	must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"));
	must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
	must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
	must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
429
	must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
430 431 432 433 434

	must_pass(append_ceiling_dir(ceiling_dirs, SUB_REPOSITORY_FOLDER));
	//this must pass as ceiling_directories cannot predent the current
	//working directory to be checked
	must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
435 436 437
	must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs));
	must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
	must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));
438 439 440 441 442 443 444

	//.gitfile redirection should not be affected by ceiling directories
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
	must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));

445
	must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
446
	git_repository_free(repo);
447 448
END_TEST

449 450 451 452 453
BEGIN_SUITE(repository)
	ADD_TEST(odb0);
	ADD_TEST(odb1);
	ADD_TEST(init0);
	ADD_TEST(init1);
454
	ADD_TEST(init2);
455
	ADD_TEST(open0);
456
	ADD_TEST(open1);
457
	ADD_TEST(open2);
458
	ADD_TEST(empty0);
459 460
	ADD_TEST(detached0);
	ADD_TEST(orphan0);
461
	ADD_TEST(discover0);
462
END_SUITE
463