Merge pull request #4265 from pks-t/pks/read-prefix-tests

Read prefix tests
diff --git a/tests/odb/backend/backend_helpers.c b/tests/odb/backend/backend_helpers.c
new file mode 100644
index 0000000..2653702
--- /dev/null
+++ b/tests/odb/backend/backend_helpers.c
@@ -0,0 +1,132 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+#include "backend_helpers.h"
+
+static int search_object(const fake_object **out, fake_backend *fake, const git_oid *oid, size_t len)
+{
+	const fake_object *obj = fake->objects;
+
+	while (obj && obj->oid) {
+		git_oid current_oid;
+
+		git_oid_fromstr(&current_oid, obj->oid);
+
+		if (git_oid_ncmp(&current_oid, oid, len) == 0) {
+			if (out)
+				*out = obj;
+			return 0;
+		}
+
+		obj++;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+	fake_backend *fake;
+
+	fake = (fake_backend *)backend;
+
+	fake->exists_calls++;
+
+	return search_object(NULL, fake, oid, GIT_OID_RAWSZ) == GIT_OK;
+}
+
+static int fake_backend__read(
+	void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
+{
+	const fake_object *obj;
+	fake_backend *fake;
+
+	fake = (fake_backend *)backend;
+
+	fake->read_calls++;
+
+	if (search_object(&obj, fake, oid, GIT_OID_RAWSZ) == 0) {
+		*len_p = strlen(obj->content);
+		*buffer_p = git__strdup(obj->content);
+		*type_p = GIT_OBJ_BLOB;
+		return 0;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static int fake_backend__read_header(
+	size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
+{
+	const fake_object *obj;
+	fake_backend *fake;
+
+	fake = (fake_backend *)backend;
+
+	fake->read_header_calls++;
+
+	if (search_object(&obj, fake, oid, GIT_OID_RAWSZ) == 0) {
+		*len_p = strlen(obj->content);
+		*type_p = GIT_OBJ_BLOB;
+		return 0;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static int fake_backend__read_prefix(
+	git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *short_oid, size_t len)
+{
+	const fake_object *obj;
+	fake_backend *fake;
+
+	fake = (fake_backend *)backend;
+
+	fake->read_prefix_calls++;
+
+	if (search_object(&obj, fake, short_oid, len) == 0) {
+		git_oid_fromstr(out_oid, obj->oid);
+		*len_p = strlen(obj->content);
+		*buffer_p = git__strdup(obj->content);
+		*type_p = GIT_OBJ_BLOB;
+		return 0;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static void fake_backend__free(git_odb_backend *_backend)
+{
+	fake_backend *backend;
+
+	backend = (fake_backend *)_backend;
+
+	git__free(backend);
+}
+
+int build_fake_backend(
+	git_odb_backend **out,
+	const fake_object *objects)
+{
+	fake_backend *backend;
+
+	backend = git__calloc(1, sizeof(fake_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+	backend->parent.refresh = NULL;
+	backend->objects = objects;
+
+	backend->parent.read = fake_backend__read;
+	backend->parent.read_prefix = fake_backend__read_prefix;
+	backend->parent.read_header = fake_backend__read_header;
+	backend->parent.exists = fake_backend__exists;
+	backend->parent.free = &fake_backend__free;
+
+	*out = (git_odb_backend *)backend;
+
+	return 0;
+}
diff --git a/tests/odb/backend/backend_helpers.h b/tests/odb/backend/backend_helpers.h
new file mode 100644
index 0000000..6cc1ce9
--- /dev/null
+++ b/tests/odb/backend/backend_helpers.h
@@ -0,0 +1,21 @@
+#include "git2/sys/odb_backend.h"
+
+typedef struct {
+	const char *oid;
+	const char *content;
+} fake_object;
+
+typedef struct {
+	git_odb_backend parent;
+
+	int exists_calls;
+	int read_calls;
+	int read_header_calls;
+	int read_prefix_calls;
+
+	const fake_object *objects;
+} fake_backend;
+
+int build_fake_backend(
+	git_odb_backend **out,
+	const fake_object *objects);
diff --git a/tests/odb/backend/multiple.c b/tests/odb/backend/multiple.c
new file mode 100644
index 0000000..1c6068d
--- /dev/null
+++ b/tests/odb/backend/multiple.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+
+#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
+static git_repository *_repo;
+static git_odb_object *_obj;
+static fake_backend *_fake_empty;
+static fake_backend *_fake_filled;
+
+static git_oid _existing_oid;
+
+static const fake_object _objects_filled[] = {
+	{ EXISTING_HASH, "" },
+	{ NULL, NULL }
+};
+
+static const fake_object _objects_empty[] = {
+	{ NULL, NULL }
+};
+
+void test_odb_backend_multiple__initialize(void)
+{
+	git_odb_backend *backend;
+
+	git_oid_fromstr(&_existing_oid, EXISTING_HASH);
+
+	_obj = NULL;
+	_repo = cl_git_sandbox_init("testrepo.git");
+
+	cl_git_pass(build_fake_backend(&backend, _objects_filled));
+	_fake_filled = (fake_backend *)backend;
+
+	cl_git_pass(build_fake_backend(&backend, _objects_empty));
+	_fake_empty = (fake_backend *)backend;
+}
+
+void test_odb_backend_multiple__cleanup(void)
+{
+	git_odb_object_free(_obj);
+	cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_multiple__read_with_empty_first_succeeds(void)
+{
+	git_odb *odb;
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50));
+
+	cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid));
+
+	cl_assert_equal_i(1, _fake_filled->read_calls);
+	cl_assert_equal_i(1, _fake_empty->read_calls);
+}
+
+void test_odb_backend_multiple__read_with_first_matching_stops(void)
+{
+	git_odb *odb;
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 10));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+	cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid));
+
+	cl_assert_equal_i(1, _fake_filled->read_calls);
+	cl_assert_equal_i(0, _fake_empty->read_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_empty_succeeds(void)
+{
+	git_odb *odb;
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50));
+
+	cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+	cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+	cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_matching_reads_both(void)
+{
+	git_odb *odb;
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+	cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+	cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+	cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_matching_succeeds_without_hash_verification(void)
+{
+	git_odb *odb;
+
+	git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0);
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10));
+	cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+	cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+	/*
+	 * Both backends should be checked as we have to check
+	 * for collisions
+	 */
+	cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+	cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+
+	git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1);
+}
diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c
index f12ac74..6abc0c6 100644
--- a/tests/odb/backend/nonrefreshing.c
+++ b/tests/odb/backend/nonrefreshing.c
@@ -1,153 +1,41 @@
 #include "clar_libgit2.h"
-#include "git2/sys/odb_backend.h"
 #include "repository.h"
-
-typedef struct fake_backend {
-	git_odb_backend parent;
-
-	git_error_code error_code;
-
-	int exists_calls;
-	int read_calls;
-	int read_header_calls;
-	int read_prefix_calls;
-} fake_backend;
+#include "backend_helpers.h"
 
 static git_repository *_repo;
 static fake_backend *_fake;
-static git_oid _oid;
 
-#define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
-#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
 
-static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
-{
-	fake_backend *fake;
+static const fake_object _objects[] = {
+	{ EXISTING_HASH, "" },
+	{ NULL, NULL }
+};
 
-	GIT_UNUSED(oid);
+static git_oid _nonexisting_oid;
+static git_oid _existing_oid;
 
-	fake = (fake_backend *)backend;
-
-	fake->exists_calls++;
-
-	return (fake->error_code == GIT_OK);
-}
-
-static int fake_backend__read(
-	void **buffer_p, size_t *len_p, git_otype *type_p,
-	git_odb_backend *backend, const git_oid *oid)
-{
-	fake_backend *fake;
-
-	GIT_UNUSED(buffer_p);
-	GIT_UNUSED(len_p);
-	GIT_UNUSED(type_p);
-	GIT_UNUSED(oid);
-
-	fake = (fake_backend *)backend;
-
-	fake->read_calls++;
-
-	*len_p = 0;
-	*buffer_p = NULL;
-	*type_p = GIT_OBJ_BLOB;
-
-	return fake->error_code;
-}
-
-static int fake_backend__read_header(
-	size_t *len_p, git_otype *type_p,
-	git_odb_backend *backend, const git_oid *oid)
-{
-	fake_backend *fake;
-
-	GIT_UNUSED(len_p);
-	GIT_UNUSED(type_p);
-	GIT_UNUSED(oid);
-
-	fake = (fake_backend *)backend;
-
-	fake->read_header_calls++;
-
-	*len_p = 0;
-	*type_p = GIT_OBJ_BLOB;
-
-	return fake->error_code;
-}
-
-static int fake_backend__read_prefix(
-	git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p,
-	git_odb_backend *backend, const git_oid *short_oid,	size_t len)
-{
-	fake_backend *fake;
-
-	GIT_UNUSED(buffer_p);
-	GIT_UNUSED(len_p);
-	GIT_UNUSED(type_p);
-	GIT_UNUSED(short_oid);
-	GIT_UNUSED(len);
-
-	fake = (fake_backend *)backend;
-
-	fake->read_prefix_calls++;
-
-	git_oid_cpy(out_oid, &_oid);
-	*len_p = 0;
-	*buffer_p = NULL;
-	*type_p = GIT_OBJ_BLOB;
-
-	return fake->error_code;
-}
-
-static void fake_backend__free(git_odb_backend *_backend)
-{
-	fake_backend *backend;
-
-	backend = (fake_backend *)_backend;
-
-	git__free(backend);
-}
-
-static int build_fake_backend(
-	git_odb_backend **out,
-	git_error_code error_code)
-{
-	fake_backend *backend;
-
-	backend = git__calloc(1, sizeof(fake_backend));
-	GITERR_CHECK_ALLOC(backend);
-
-	backend->parent.version = GIT_ODB_BACKEND_VERSION;
-
-	backend->parent.refresh = NULL;
-	backend->error_code = error_code;
-
-	backend->parent.read = fake_backend__read;
-	backend->parent.read_prefix = fake_backend__read_prefix;
-	backend->parent.read_header = fake_backend__read_header;
-	backend->parent.exists = fake_backend__exists;
-	backend->parent.free = &fake_backend__free;
-
-	*out = (git_odb_backend *)backend;
-
-	return 0;
-}
-
-static void setup_repository_and_backend(git_error_code error_code, const char *hash)
+static void setup_repository_and_backend(void)
 {
 	git_odb *odb = NULL;
 	git_odb_backend *backend = NULL;
 
 	_repo = cl_git_sandbox_init("testrepo.git");
 
-	cl_git_pass(build_fake_backend(&backend, error_code));
+	cl_git_pass(build_fake_backend(&backend, _objects));
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 	cl_git_pass(git_odb_add_backend(odb, backend, 10));
 
 	_fake = (fake_backend *)backend;
+}
 
-	cl_git_pass(git_oid_fromstr(&_oid, hash));
+void test_odb_backend_nonrefreshing__initialize(void)
+{
+	git_oid_fromstr(&_nonexisting_oid, NONEXISTING_HASH);
+	git_oid_fromstr(&_existing_oid, EXISTING_HASH);
+	setup_repository_and_backend();
 }
 
 void test_odb_backend_nonrefreshing__cleanup(void)
@@ -159,10 +47,8 @@
 {
 	git_odb *odb;
 
-	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
-
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
-	cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
+	cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid));
 
 	cl_assert_equal_i(1, _fake->exists_calls);
 }
@@ -171,10 +57,8 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
-
 	cl_git_fail_with(
-		git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
+		git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJ_ANY),
 		GIT_ENOTFOUND);
 
 	cl_assert_equal_i(1, _fake->read_calls);
@@ -184,10 +68,8 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
-
 	cl_git_fail_with(
-		git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
+		git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJ_ANY),
 		GIT_ENOTFOUND);
 
 	cl_assert_equal_i(1, _fake->read_prefix_calls);
@@ -199,12 +81,10 @@
 	size_t len;
 	git_otype type;
 
-	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
-
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 
 	cl_git_fail_with(
-		git_odb_read_header(&len, &type, odb, &_oid),
+		git_odb_read_header(&len, &type, odb, &_nonexisting_oid),
 		GIT_ENOTFOUND);
 
 	cl_assert_equal_i(1, _fake->read_header_calls);
@@ -214,10 +94,8 @@
 {
 	git_odb *odb;
 
-	setup_repository_and_backend(GIT_OK, HASH);
-
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
-	cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
+	cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid));
 
 	cl_assert_equal_i(1, _fake->exists_calls);
 }
@@ -226,9 +104,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_OK, EMPTY_HASH);
-
-	cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
+	cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJ_ANY));
 
 	cl_assert_equal_i(1, _fake->read_calls);
 
@@ -239,9 +115,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_OK, EMPTY_HASH);
-
-	cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
+	cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJ_ANY));
 
 	cl_assert_equal_i(1, _fake->read_prefix_calls);
 
@@ -254,11 +128,9 @@
 	size_t len;
 	git_otype type;
 
-	setup_repository_and_backend(GIT_OK, HASH);
-
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 
-	cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid));
+	cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid));
 
 	cl_assert_equal_i(1, _fake->read_header_calls);
 }
@@ -267,8 +139,6 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
-
 	cl_git_fail_with(
 		git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
 		GIT_ENOTFOUND);