diff options
| author | Edward Thomson <ethomson@edwardthomson.com> | 2021-11-16 23:29:22 -0500 |
|---|---|---|
| committer | Edward Thomson <ethomson@edwardthomson.com> | 2022-02-22 22:07:45 -0500 |
| commit | 3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb (patch) | |
| tree | fd6368a72944571c51627b40c592e7d58e0036e1 /tests/libgit2/repo | |
| parent | 91ba089663f5efc3bd4ba14a5099372cf5ce57a6 (diff) | |
| download | libgit2-3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb.tar.gz | |
refactor: `tests` is now `tests/libgit2`
Like we want to separate libgit2 and utility source code, we want to
separate libgit2 and utility tests. Start by moving all the tests into
libgit2.
Diffstat (limited to 'tests/libgit2/repo')
| -rw-r--r-- | tests/libgit2/repo/config.c | 211 | ||||
| -rw-r--r-- | tests/libgit2/repo/discover.c | 210 | ||||
| -rw-r--r-- | tests/libgit2/repo/env.c | 277 | ||||
| -rw-r--r-- | tests/libgit2/repo/extensions.c | 72 | ||||
| -rw-r--r-- | tests/libgit2/repo/getters.c | 53 | ||||
| -rw-r--r-- | tests/libgit2/repo/hashfile.c | 171 | ||||
| -rw-r--r-- | tests/libgit2/repo/head.c | 182 | ||||
| -rw-r--r-- | tests/libgit2/repo/headtree.c | 53 | ||||
| -rw-r--r-- | tests/libgit2/repo/init.c | 738 | ||||
| -rw-r--r-- | tests/libgit2/repo/message.c | 39 | ||||
| -rw-r--r-- | tests/libgit2/repo/new.c | 27 | ||||
| -rw-r--r-- | tests/libgit2/repo/open.c | 455 | ||||
| -rw-r--r-- | tests/libgit2/repo/pathspec.c | 385 | ||||
| -rw-r--r-- | tests/libgit2/repo/repo_helpers.c | 37 | ||||
| -rw-r--r-- | tests/libgit2/repo/repo_helpers.h | 7 | ||||
| -rw-r--r-- | tests/libgit2/repo/reservedname.c | 132 | ||||
| -rw-r--r-- | tests/libgit2/repo/setters.c | 108 | ||||
| -rw-r--r-- | tests/libgit2/repo/shallow.c | 39 | ||||
| -rw-r--r-- | tests/libgit2/repo/state.c | 131 | ||||
| -rw-r--r-- | tests/libgit2/repo/template.c | 305 |
20 files changed, 3632 insertions, 0 deletions
diff --git a/tests/libgit2/repo/config.c b/tests/libgit2/repo/config.c new file mode 100644 index 000000000..ee7e43dff --- /dev/null +++ b/tests/libgit2/repo/config.c @@ -0,0 +1,211 @@ +#include "clar_libgit2.h" +#include "sysdir.h" +#include "futils.h" +#include <ctype.h> + +static git_str path = GIT_STR_INIT; + +void test_repo_config__initialize(void) +{ + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename( + "empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + git_str_clear(&path); + + cl_must_pass(p_mkdir("alternate", 0777)); + cl_git_pass(git_fs_path_prettify(&path, "alternate", NULL)); +} + +void test_repo_config__cleanup(void) +{ + cl_sandbox_set_search_path_defaults(); + + git_str_dispose(&path); + + cl_git_pass( + git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_assert(!git_fs_path_isdir("alternate")); + + cl_fixture_cleanup("empty_standard_repo"); + +} + +void test_repo_config__can_open_global_when_there_is_no_file(void) +{ + git_repository *repo; + git_config *config, *global; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_open_level( + &global, config, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_set_string(global, "test.set", "42")); + + git_config_free(global); + git_config_free(config); + git_repository_free(repo); +} + +void test_repo_config__can_open_missing_global_with_separators(void) +{ + git_repository *repo; + git_config *config, *global; + + cl_git_pass(git_str_printf( + &path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy")); + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + git_str_dispose(&path); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_open_level( + &global, config, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_set_string(global, "test.set", "42")); + + git_config_free(global); + git_config_free(config); + git_repository_free(repo); +} + +#include "repository.h" + +void test_repo_config__read_with_no_configs_at_all(void) +{ + git_repository *repo; + int val; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + /* with none */ + + cl_must_pass(p_unlink("empty_standard_repo/.git/config")); + cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config")); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(GIT_ABBREV_DEFAULT, val); + git_repository_free(repo); + + /* with no local config, just system */ + + cl_sandbox_set_search_path_defaults(); + + cl_must_pass(p_mkdir("alternate/1", 0777)); + cl_git_pass(git_str_joinpath(&path, path.ptr, "1")); + cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(10, val); + git_repository_free(repo); + + /* with just xdg + system */ + + cl_must_pass(p_mkdir("alternate/2", 0777)); + path.ptr[path.size - 1] = '2'; + cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(20, val); + git_repository_free(repo); + + /* with global + xdg + system */ + + cl_must_pass(p_mkdir("alternate/3", 0777)); + path.ptr[path.size - 1] = '3'; + cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(30, val); + git_repository_free(repo); + + /* with all configs */ + + cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n"); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(40, val); + git_repository_free(repo); + + /* with all configs but delete the files ? */ + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(40, val); + + cl_must_pass(p_unlink("empty_standard_repo/.git/config")); + cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config")); + + cl_must_pass(p_unlink("alternate/1/gitconfig")); + cl_assert(!git_fs_path_isfile("alternate/1/gitconfig")); + + cl_must_pass(p_unlink("alternate/2/config")); + cl_assert(!git_fs_path_isfile("alternate/2/config")); + + cl_must_pass(p_unlink("alternate/3/.gitconfig")); + cl_assert(!git_fs_path_isfile("alternate/3/.gitconfig")); + + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(40, val); + git_repository_free(repo); + + /* reopen */ + + cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config")); + cl_assert(!git_fs_path_isfile("alternate/3/.gitconfig")); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__configmap_lookup_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV)); + cl_assert_equal_i(7, val); + git_repository_free(repo); + + cl_assert(!git_fs_path_exists("empty_standard_repo/.git/config")); + cl_assert(!git_fs_path_exists("alternate/3/.gitconfig")); +} diff --git a/tests/libgit2/repo/discover.c b/tests/libgit2/repo/discover.c new file mode 100644 index 000000000..523fdf8e3 --- /dev/null +++ b/tests/libgit2/repo/discover.c @@ -0,0 +1,210 @@ +#include "clar_libgit2.h" + +#include "odb.h" +#include "futils.h" +#include "repository.h" + +#define TEMP_REPO_FOLDER "temprepo/" +#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git" + +#define SUB_REPOSITORY_FOLDER_NAME "sub_repo" +#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME +#define SUB_REPOSITORY_GITDIR SUB_REPOSITORY_FOLDER "/.git" +#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 void ensure_repository_discover(const char *start_path, + const char *ceiling_dirs, + const char *expected_path) +{ + git_buf found_path = GIT_BUF_INIT; + git_str resolved = GIT_STR_INIT; + + git_str_attach(&resolved, p_realpath(expected_path, NULL), 0); + cl_assert(resolved.size > 0); + cl_git_pass(git_fs_path_to_dir(&resolved)); + cl_git_pass(git_repository_discover(&found_path, start_path, 1, ceiling_dirs)); + + cl_assert_equal_s(found_path.ptr, resolved.ptr); + + git_str_dispose(&resolved); + git_buf_dispose(&found_path); +} + +static void write_file(const char *path, const char *content) +{ + git_file file; + int error; + + if (git_fs_path_exists(path)) { + cl_git_pass(p_unlink(path)); + } + + file = git_futils_creat_withpath(path, 0777, 0666); + cl_assert(file >= 0); + + error = p_write(file, content, strlen(content) * sizeof(char)); + p_close(file); + cl_git_pass(error); +} + +/*no check is performed on ceiling_dirs length, so be sure it's long enough */ +static void append_ceiling_dir(git_str *ceiling_dirs, const char *path) +{ + git_str pretty_path = GIT_STR_INIT; + char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' }; + + cl_git_pass(git_fs_path_prettify_dir(&pretty_path, path, NULL)); + + if (ceiling_dirs->size > 0) + git_str_puts(ceiling_dirs, ceiling_separator); + + git_str_puts(ceiling_dirs, pretty_path.ptr); + + git_str_dispose(&pretty_path); + cl_assert(git_str_oom(ceiling_dirs) == 0); +} + +static git_buf discovered; +static git_str ceiling_dirs; + +void test_repo_discover__initialize(void) +{ + git_repository *repo; + const mode_t mode = 0777; + git_futils_mkdir_r(DISCOVER_FOLDER, mode); + + git_str_init(&ceiling_dirs, 0); + append_ceiling_dir(&ceiling_dirs, TEMP_REPO_FOLDER); + + cl_git_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1)); + git_repository_free(repo); + + cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0)); + cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); + cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); + + cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode)); + write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); + write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); + write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"); + + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode)); + write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode)); + write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode)); + write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode)); + write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"); + + git_repository_free(repo); +} + +void test_repo_discover__cleanup(void) +{ + git_buf_dispose(&discovered); + git_str_dispose(&ceiling_dirs); + cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_repo_discover__discovering_repo_with_exact_path_succeeds(void) +{ + cl_git_pass(git_repository_discover(&discovered, DISCOVER_FOLDER, 0, ceiling_dirs.ptr)); + cl_git_pass(git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs.ptr)); +} + +void test_repo_discover__discovering_nonexistent_dir_fails(void) +{ + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, DISCOVER_FOLDER "-nonexistent", 0, NULL)); +} + +void test_repo_discover__discovering_repo_with_subdirectory_succeeds(void) +{ + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); +} + +void test_repo_discover__discovering_repository_with_alternative_gitdir_succeeds(void) +{ + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER); +} + +void test_repo_discover__discovering_repository_with_malformed_alternative_gitdir_fails(void) +{ + cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs.ptr)); + cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs.ptr)); + cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs.ptr)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs.ptr)); +} + +void test_repo_discover__discovering_repository_with_ceiling(void) +{ + append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER_SUB); + + /* this must pass as ceiling_directories cannot prevent the current + * working directory to be checked */ + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr)); +} + +void test_repo_discover__other_ceiling(void) +{ + append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER); + + /* this must pass as ceiling_directories cannot predent the current + * working directory to be checked */ + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs.ptr)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr)); +} + +void test_repo_discover__ceiling_should_not_affect_gitdir_redirection(void) +{ + append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER); + + /* gitfile redirection should not be affected by ceiling directories */ + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER); +} + +void test_repo_discover__discovery_starting_at_file_succeeds(void) +{ + int fd; + + cl_assert((fd = p_creat(SUB_REPOSITORY_FOLDER "/file", 0600)) >= 0); + cl_assert(p_close(fd) == 0); + + ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); +} + +void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void) +{ +#ifdef GIT_WIN32 + git_buf out = GIT_BUF_INIT; + cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL)); + cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL)); +#endif +} diff --git a/tests/libgit2/repo/env.c b/tests/libgit2/repo/env.c new file mode 100644 index 000000000..e3e522480 --- /dev/null +++ b/tests/libgit2/repo/env.c @@ -0,0 +1,277 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "sysdir.h" +#include <ctype.h> + +static void clear_git_env(void) +{ + cl_setenv("GIT_DIR", NULL); + cl_setenv("GIT_CEILING_DIRECTORIES", NULL); + cl_setenv("GIT_INDEX_FILE", NULL); + cl_setenv("GIT_NAMESPACE", NULL); + cl_setenv("GIT_OBJECT_DIRECTORY", NULL); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); + cl_setenv("GIT_WORK_TREE", NULL); + cl_setenv("GIT_COMMON_DIR", NULL); +} + +void test_repo_env__initialize(void) +{ + clear_git_env(); +} + +void test_repo_env__cleanup(void) +{ + cl_git_sandbox_cleanup(); + + if (git_fs_path_isdir("attr")) + git_futils_rmdir_r("attr", NULL, GIT_RMDIR_REMOVE_FILES); + if (git_fs_path_isdir("testrepo.git")) + git_futils_rmdir_r("testrepo.git", NULL, GIT_RMDIR_REMOVE_FILES); + if (git_fs_path_isdir("peeled.git")) + git_futils_rmdir_r("peeled.git", NULL, GIT_RMDIR_REMOVE_FILES); + + clear_git_env(); +} + +static int GIT_FORMAT_PRINTF(2, 3) cl_setenv_printf(const char *name, const char *fmt, ...) +{ + int ret; + va_list args; + git_str buf = GIT_STR_INIT; + + va_start(args, fmt); + cl_git_pass(git_str_vprintf(&buf, fmt, args)); + va_end(args); + + ret = cl_setenv(name, git_str_cstr(&buf)); + git_str_dispose(&buf); + return ret; +} + +/* Helper functions for test_repo_open__env, passing through the file and line + * from the caller rather than those of the helper. The expression strings + * distinguish between the possible failures within the helper. */ + +static void env_pass_(const char *path, const char *file, const char *func, int line) +{ + git_repository *repo; + cl_git_expect(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line); + cl_git_expect(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line); + cl_assert_at_line(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0, file, func, line); + cl_assert_at_line(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0, file, func, line); + cl_assert_at_line(!git_repository_is_bare(repo), file, func, line); + git_repository_free(repo); +} +#define env_pass(path) env_pass_((path), __FILE__, __func__, __LINE__) + +#define cl_git_fail_at_line(expr, file, func, line) clar__assert((expr) < 0, file, func, line, "Expected function call to fail: " #expr, NULL, 1) + +static void env_fail_(const char *path, const char *file, const char *func, int line) +{ + git_repository *repo; + cl_git_fail_at_line(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, func, line); + cl_git_fail_at_line(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, func, line); +} +#define env_fail(path) env_fail_((path), __FILE__, __func__, __LINE__) + +static void env_cd_( + const char *path, + void (*passfail_)(const char *, const char *, const char *, int), + const char *file, const char *func, int line) +{ + git_str cwd_buf = GIT_STR_INIT; + cl_git_pass(git_fs_path_prettify_dir(&cwd_buf, ".", NULL)); + cl_must_pass(p_chdir(path)); + passfail_(NULL, file, func, line); + cl_must_pass(p_chdir(git_str_cstr(&cwd_buf))); + git_str_dispose(&cwd_buf); +} +#define env_cd_pass(path) env_cd_((path), env_pass_, __FILE__, __func__, __LINE__) +#define env_cd_fail(path) env_cd_((path), env_fail_, __FILE__, __func__, __LINE__) + +static void env_check_objects_(bool a, bool t, bool p, const char *file, const char *func, int line) +{ + git_repository *repo; + git_oid oid_a, oid_t, oid_p; + git_object *object; + cl_git_pass(git_oid_fromstr(&oid_a, "45141a79a77842c59a63229403220a4e4be74e3d")); + cl_git_pass(git_oid_fromstr(&oid_t, "1385f264afb75a56a5bec74243be9b367ba4ca08")); + cl_git_pass(git_oid_fromstr(&oid_p, "0df1a5865c8abfc09f1f2182e6a31be550e99f07")); + cl_git_expect(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line); + + if (a) { + cl_git_expect(git_object_lookup(&object, repo, &oid_a, GIT_OBJECT_BLOB), 0, file, func, line); + git_object_free(object); + } else { + cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_a, GIT_OBJECT_BLOB), file, func, line); + } + + if (t) { + cl_git_expect(git_object_lookup(&object, repo, &oid_t, GIT_OBJECT_BLOB), 0, file, func, line); + git_object_free(object); + } else { + cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_t, GIT_OBJECT_BLOB), file, func, line); + } + + if (p) { + cl_git_expect(git_object_lookup(&object, repo, &oid_p, GIT_OBJECT_COMMIT), 0, file, func, line); + git_object_free(object); + } else { + cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_p, GIT_OBJECT_COMMIT), file, func, line); + } + + git_repository_free(repo); +} +#define env_check_objects(a, t, t2) env_check_objects_((a), (t), (t2), __FILE__, __func__, __LINE__) + +void test_repo_env__open(void) +{ + git_repository *repo = NULL; + git_str repo_dir_buf = GIT_STR_INIT; + const char *repo_dir = NULL; + git_index *index = NULL; + const char *t_obj = "testrepo.git/objects"; + const char *p_obj = "peeled.git/objects"; + + clear_git_env(); + + cl_fixture_sandbox("attr"); + cl_fixture_sandbox("testrepo.git"); + cl_fixture_sandbox("peeled.git"); + cl_git_pass(p_rename("attr/.gitted", "attr/.git")); + + cl_git_pass(git_fs_path_prettify_dir(&repo_dir_buf, "attr", NULL)); + repo_dir = git_str_cstr(&repo_dir_buf); + + /* GIT_DIR that doesn't exist */ + cl_setenv("GIT_DIR", "does-not-exist"); + env_fail(NULL); + /* Explicit start_path overrides GIT_DIR */ + env_pass("attr"); + env_pass("attr/.git"); + env_pass("attr/sub"); + env_pass("attr/sub/sub"); + + /* GIT_DIR with relative paths */ + cl_setenv("GIT_DIR", "attr/.git"); + env_pass(NULL); + cl_setenv("GIT_DIR", "attr"); + env_fail(NULL); + cl_setenv("GIT_DIR", "attr/sub"); + env_fail(NULL); + cl_setenv("GIT_DIR", "attr/sub/sub"); + env_fail(NULL); + + /* GIT_DIR with absolute paths */ + cl_setenv_printf("GIT_DIR", "%s/.git", repo_dir); + env_pass(NULL); + cl_setenv("GIT_DIR", repo_dir); + env_fail(NULL); + cl_setenv_printf("GIT_DIR", "%s/sub", repo_dir); + env_fail(NULL); + cl_setenv_printf("GIT_DIR", "%s/sub/sub", repo_dir); + env_fail(NULL); + cl_setenv("GIT_DIR", NULL); + + /* Searching from the current directory */ + env_cd_pass("attr"); + env_cd_pass("attr/.git"); + env_cd_pass("attr/sub"); + env_cd_pass("attr/sub/sub"); + + /* A ceiling directory blocks searches from ascending into that + * directory, but doesn't block the start_path itself. */ + cl_setenv("GIT_CEILING_DIRECTORIES", repo_dir); + env_cd_pass("attr"); + env_cd_fail("attr/sub"); + env_cd_fail("attr/sub/sub"); + + cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub", repo_dir); + env_cd_pass("attr"); + env_cd_pass("attr/sub"); + env_cd_fail("attr/sub/sub"); + + /* Multiple ceiling directories */ + cl_setenv_printf("GIT_CEILING_DIRECTORIES", "123%c%s/sub%cabc", + GIT_PATH_LIST_SEPARATOR, repo_dir, GIT_PATH_LIST_SEPARATOR); + env_cd_pass("attr"); + env_cd_pass("attr/sub"); + env_cd_fail("attr/sub/sub"); + + cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub", + repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); + env_cd_pass("attr"); + env_cd_fail("attr/sub"); + env_cd_fail("attr/sub/sub"); + + cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub%c%s", + repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); + env_cd_pass("attr"); + env_cd_fail("attr/sub"); + env_cd_fail("attr/sub/sub"); + + cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub/sub", + repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); + env_cd_pass("attr"); + env_cd_fail("attr/sub"); + env_cd_fail("attr/sub/sub"); + + cl_setenv("GIT_CEILING_DIRECTORIES", NULL); + + /* Index files */ + cl_setenv("GIT_INDEX_FILE", cl_fixture("gitgit.index")); + cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL)); + cl_git_pass(git_repository_index(&index, repo)); + cl_assert_equal_s(git_index_path(index), cl_fixture("gitgit.index")); + cl_assert_equal_i(git_index_entrycount(index), 1437); + git_index_free(index); + git_repository_free(repo); + cl_setenv("GIT_INDEX_FILE", NULL); + + /* Namespaces */ + cl_setenv("GIT_NAMESPACE", "some-namespace"); + cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL)); + cl_assert_equal_s(git_repository_get_namespace(repo), "some-namespace"); + git_repository_free(repo); + cl_setenv("GIT_NAMESPACE", NULL); + + /* Object directories and alternates */ + env_check_objects(true, false, false); + + cl_setenv("GIT_OBJECT_DIRECTORY", t_obj); + env_check_objects(false, true, false); + cl_setenv("GIT_OBJECT_DIRECTORY", NULL); + + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", t_obj); + env_check_objects(true, true, false); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); + + cl_setenv("GIT_OBJECT_DIRECTORY", p_obj); + env_check_objects(false, false, true); + cl_setenv("GIT_OBJECT_DIRECTORY", NULL); + + cl_setenv("GIT_OBJECT_DIRECTORY", t_obj); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", p_obj); + env_check_objects(false, true, true); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); + cl_setenv("GIT_OBJECT_DIRECTORY", NULL); + + cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES", + "%s%c%s", t_obj, GIT_PATH_LIST_SEPARATOR, p_obj); + env_check_objects(true, true, true); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); + + cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES", + "%s%c%s", p_obj, GIT_PATH_LIST_SEPARATOR, t_obj); + env_check_objects(true, true, true); + cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); + + cl_fixture_cleanup("peeled.git"); + cl_fixture_cleanup("testrepo.git"); + cl_fixture_cleanup("attr"); + + git_str_dispose(&repo_dir_buf); + + clear_git_env(); +} diff --git a/tests/libgit2/repo/extensions.c b/tests/libgit2/repo/extensions.c new file mode 100644 index 000000000..e7772acd5 --- /dev/null +++ b/tests/libgit2/repo/extensions.c @@ -0,0 +1,72 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "sysdir.h" +#include <ctype.h> + +git_repository *repo; + +void test_repo_extensions__initialize(void) +{ + git_config *config; + + repo = cl_git_sandbox_init("empty_bare.git"); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1)); + git_config_free(config); +} + +void test_repo_extensions__cleanup(void) +{ + cl_git_sandbox_cleanup(); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0)); +} + +void test_repo_extensions__builtin(void) +{ + git_repository *extended; + + cl_repo_set_string(repo, "extensions.noop", "foobar"); + + cl_git_pass(git_repository_open(&extended, "empty_bare.git")); + cl_assert(git_repository_path(extended) != NULL); + cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0); + git_repository_free(extended); +} + +void test_repo_extensions__negate_builtin(void) +{ + const char *in[] = { "foo", "!noop", "baz" }; + git_repository *extended; + + cl_repo_set_string(repo, "extensions.noop", "foobar"); + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in))); + + cl_git_fail(git_repository_open(&extended, "empty_bare.git")); + git_repository_free(extended); +} + +void test_repo_extensions__unsupported(void) +{ + git_repository *extended = NULL; + + cl_repo_set_string(repo, "extensions.unknown", "foobar"); + + cl_git_fail(git_repository_open(&extended, "empty_bare.git")); + git_repository_free(extended); +} + +void test_repo_extensions__adds_extension(void) +{ + const char *in[] = { "foo", "!noop", "newextension", "baz" }; + git_repository *extended; + + cl_repo_set_string(repo, "extensions.newextension", "foobar"); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in))); + + cl_git_pass(git_repository_open(&extended, "empty_bare.git")); + cl_assert(git_repository_path(extended) != NULL); + cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0); + git_repository_free(extended); +} diff --git a/tests/libgit2/repo/getters.c b/tests/libgit2/repo/getters.c new file mode 100644 index 000000000..d401bb832 --- /dev/null +++ b/tests/libgit2/repo/getters.c @@ -0,0 +1,53 @@ +#include "clar_libgit2.h" +#include "repo/repo_helpers.h" + +void test_repo_getters__is_empty_correctly_deals_with_pristine_looking_repos(void) +{ + git_repository *repo; + + repo = cl_git_sandbox_init("empty_bare.git"); + cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt"); + + cl_assert_equal_i(true, git_repository_is_empty(repo)); + + cl_git_sandbox_cleanup(); +} + +void test_repo_getters__is_empty_can_detect_used_repositories(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + cl_assert_equal_i(false, git_repository_is_empty(repo)); + + git_repository_free(repo); +} + +void test_repo_getters__is_empty_can_detect_repositories_with_defaultbranch_config_empty(void) +{ + git_repository *repo; + + create_tmp_global_config("tmp_global_path", "init.defaultBranch", ""); + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + cl_assert_equal_i(false, git_repository_is_empty(repo)); + + git_repository_free(repo); +} + +void test_repo_getters__retrieving_the_odb_honors_the_refcount(void) +{ + git_odb *odb; + git_repository *repo; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + cl_git_pass(git_repository_odb(&odb, repo)); + cl_assert(((git_refcount *)odb)->refcount.val == 2); + + git_repository_free(repo); + cl_assert(((git_refcount *)odb)->refcount.val == 1); + + git_odb_free(odb); +} diff --git a/tests/libgit2/repo/hashfile.c b/tests/libgit2/repo/hashfile.c new file mode 100644 index 000000000..e23bb77f9 --- /dev/null +++ b/tests/libgit2/repo/hashfile.c @@ -0,0 +1,171 @@ +#include "clar_libgit2.h" + +static git_repository *_repo; + +void test_repo_hashfile__initialize(void) +{ + _repo = cl_git_sandbox_init("status"); +} + +void test_repo_hashfile__cleanup(void) +{ + cl_fixture_cleanup("absolute"); + cl_git_sandbox_cleanup(); + _repo = NULL; +} + +void test_repo_hashfile__simple(void) +{ + git_oid a, b; + git_str full = GIT_STR_INIT; + + /* hash with repo relative path */ + cl_git_pass(git_odb_hashfile(&a, "status/current_file", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "current_file", GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + cl_git_pass(git_str_joinpath(&full, git_repository_workdir(_repo), "current_file")); + + /* hash with full path */ + cl_git_pass(git_odb_hashfile(&a, full.ptr, GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + /* hash with invalid type */ + cl_git_fail(git_odb_hashfile(&a, full.ptr, GIT_OBJECT_ANY)); + cl_git_fail(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJECT_OFS_DELTA, NULL)); + + git_str_dispose(&full); +} + +void test_repo_hashfile__filtered_in_workdir(void) +{ + git_str root = GIT_STR_INIT, txt = GIT_STR_INIT, bin = GIT_STR_INIT; + char cwd[GIT_PATH_MAX]; + git_oid a, b; + + cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX)); + cl_must_pass(p_mkdir("absolute", 0777)); + cl_git_pass(git_str_joinpath(&root, cwd, "status")); + cl_git_pass(git_str_joinpath(&txt, root.ptr, "testfile.txt")); + cl_git_pass(git_str_joinpath(&bin, root.ptr, "testfile.bin")); + + cl_repo_set_bool(_repo, "core.autocrlf", true); + + cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n"); + + /* create some sample content with CRLF in it */ + cl_git_mkfile("status/testfile.txt", "content\r\n"); + cl_git_mkfile("status/testfile.bin", "other\r\nstuff\r\n"); + + /* not equal hashes because of filtering */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, NULL)); + cl_assert(git_oid_cmp(&a, &b)); + + /* not equal hashes because of filtering when specified by absolute path */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL)); + cl_assert(git_oid_cmp(&a, &b)); + + /* equal hashes because filter is binary */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + /* equal hashes because filter is binary when specified by absolute path */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + /* equal hashes when 'as_file' points to binary filtering */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, "foo.bin")); + cl_assert_equal_oid(&a, &b); + + /* equal hashes when 'as_file' points to binary filtering (absolute path) */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "foo.bin")); + cl_assert_equal_oid(&a, &b); + + /* not equal hashes when 'as_file' points to text filtering */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, "foo.txt")); + cl_assert(git_oid_cmp(&a, &b)); + + /* not equal hashes when 'as_file' points to text filtering */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "foo.txt")); + cl_assert(git_oid_cmp(&a, &b)); + + /* equal hashes when 'as_file' is empty and turns off filtering */ + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, "")); + cl_assert_equal_oid(&a, &b); + + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, "")); + cl_assert_equal_oid(&a, &b); + + cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "")); + cl_assert_equal_oid(&a, &b); + + cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "")); + cl_assert_equal_oid(&a, &b); + + /* some hash type failures */ + cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0)); + cl_git_fail(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_ANY, NULL)); + + git_str_dispose(&txt); + git_str_dispose(&bin); + git_str_dispose(&root); +} + +void test_repo_hashfile__filtered_outside_workdir(void) +{ + git_str root = GIT_STR_INIT, txt = GIT_STR_INIT, bin = GIT_STR_INIT; + char cwd[GIT_PATH_MAX]; + git_oid a, b; + + cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX)); + cl_must_pass(p_mkdir("absolute", 0777)); + cl_git_pass(git_str_joinpath(&root, cwd, "absolute")); + cl_git_pass(git_str_joinpath(&txt, root.ptr, "testfile.txt")); + cl_git_pass(git_str_joinpath(&bin, root.ptr, "testfile.bin")); + + cl_repo_set_bool(_repo, "core.autocrlf", true); + cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n"); + + /* create some sample content with CRLF in it */ + cl_git_mkfile("absolute/testfile.txt", "content\r\n"); + cl_git_mkfile("absolute/testfile.bin", "other\r\nstuff\r\n"); + + /* not equal hashes because of filtering */ + cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "testfile.txt")); + cl_assert(git_oid_cmp(&a, &b)); + + /* equal hashes because filter is binary */ + cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "testfile.bin")); + cl_assert_equal_oid(&a, &b); + + /* + * equal hashes because no filtering occurs for absolute paths outside the working + * directory unless as_path is specified + */ + cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB)); + cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL)); + cl_assert_equal_oid(&a, &b); + + git_str_dispose(&txt); + git_str_dispose(&bin); + git_str_dispose(&root); +} diff --git a/tests/libgit2/repo/head.c b/tests/libgit2/repo/head.c new file mode 100644 index 000000000..822990555 --- /dev/null +++ b/tests/libgit2/repo/head.c @@ -0,0 +1,182 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "repo_helpers.h" +#include "posix.h" +#include "git2/annotated_commit.h" + +static const char *g_email = "foo@example.com"; +static git_repository *repo; + +void test_repo_head__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_set_ident(repo, "Foo Bar", g_email)); +} + +void test_repo_head__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_repo_head__unborn_head(void) +{ + git_reference *ref; + + cl_git_pass(git_repository_head_detached(repo)); + + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_assert(git_repository_head_unborn(repo) == 1); + + + /* take the repo back to it's original state */ + cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1, NULL)); + cl_assert(git_repository_head_unborn(repo) == 0); + + git_reference_free(ref); +} + +void test_repo_head__set_head_Attaches_HEAD_to_un_unborn_branch_when_the_branch_doesnt_exist(void) +{ + git_reference *head; + + cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet")); + + cl_assert_equal_i(false, git_repository_head_detached(repo)); + + cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo)); +} + +void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void) +{ + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet")); +} + +void test_repo_head__set_head_Fails_when_the_reference_points_to_a_non_commitish(void) +{ + cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob")); +} + +void test_repo_head__set_head_Attaches_HEAD_when_the_reference_points_to_a_branch(void) +{ + git_reference *head; + + cl_git_pass(git_repository_set_head(repo, "refs/heads/br2")); + + cl_assert_equal_i(false, git_repository_head_detached(repo)); + + cl_git_pass(git_repository_head(&head, repo)); + cl_assert_equal_s("refs/heads/br2", git_reference_name(head)); + + git_reference_free(head); +} + +static void assert_head_is_correctly_detached(void) +{ + git_reference *head; + git_object *commit; + + cl_assert_equal_i(true, git_repository_head_detached(repo)); + + cl_git_pass(git_repository_head(&head, repo)); + + cl_git_pass(git_object_lookup(&commit, repo, git_reference_target(head), GIT_OBJECT_COMMIT)); + + git_object_free(commit); + git_reference_free(head); +} + +void test_repo_head__set_head_Detaches_HEAD_when_the_reference_doesnt_point_to_a_branch(void) +{ + cl_git_pass(git_repository_set_head(repo, "refs/tags/test")); + + cl_assert_equal_i(true, git_repository_head_detached(repo)); + + assert_head_is_correctly_detached(); +} + +void test_repo_head__set_head_detached_Return_ENOTFOUND_when_the_object_doesnt_exist(void) +{ + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid)); +} + +void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void) +{ + git_object *blob; + + cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob")); + + cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob))); + + git_object_free(blob); +} + +void test_repo_head__set_head_detached_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void) +{ + git_object *tag; + + cl_git_pass(git_revparse_single(&tag, repo, "tags/test")); + cl_assert_equal_i(GIT_OBJECT_TAG, git_object_type(tag)); + + cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag))); + + assert_head_is_correctly_detached(); + + git_object_free(tag); +} + +void test_repo_head__detach_head_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void) +{ + cl_assert_equal_i(false, git_repository_head_detached(repo)); + + cl_git_pass(git_repository_detach_head(repo)); + + assert_head_is_correctly_detached(); +} + +void test_repo_head__detach_head_Fails_if_HEAD_and_point_to_a_non_commitish(void) +{ + git_reference *head; + + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1, NULL)); + + cl_git_fail(git_repository_detach_head(repo)); + + git_reference_free(head); +} + +void test_repo_head__detaching_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void) +{ + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo)); +} + +void test_repo_head__retrieving_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void) +{ + git_reference *head; + + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo)); +} + +void test_repo_head__retrieving_a_missing_head_returns_GIT_ENOTFOUND(void) +{ + git_reference *head; + + delete_head(repo); + + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head(&head, repo)); +} + +void test_repo_head__can_tell_if_an_unborn_head_is_detached(void) +{ + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(false, git_repository_head_detached(repo)); +} diff --git a/tests/libgit2/repo/headtree.c b/tests/libgit2/repo/headtree.c new file mode 100644 index 000000000..e899ac399 --- /dev/null +++ b/tests/libgit2/repo/headtree.c @@ -0,0 +1,53 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "repo_helpers.h" +#include "posix.h" + +static git_repository *repo; +static git_tree *tree; + +void test_repo_headtree__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + tree = NULL; +} + +void test_repo_headtree__cleanup(void) +{ + git_tree_free(tree); + cl_git_sandbox_cleanup(); +} + +void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void) +{ + cl_git_pass(git_repository_detach_head(repo)); + + cl_git_pass(git_repository_head_tree(&tree, repo)); + + cl_assert(git_oid_streq(git_tree_id(tree), "az")); +} + +void test_repo_headtree__can_retrieve_the_root_tree_from_a_non_detached_head(void) +{ + cl_assert_equal_i(false, git_repository_head_detached(repo)); + + cl_git_pass(git_repository_head_tree(&tree, repo)); + + cl_assert(git_oid_streq(git_tree_id(tree), "az")); +} + +void test_repo_headtree__when_head_is_unborn_returns_EUNBORNBRANCH(void) +{ + make_head_unborn(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(true, git_repository_head_unborn(repo)); + + cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head_tree(&tree, repo)); +} + +void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void) +{ + delete_head(repo); + + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head_tree(&tree, repo)); +} diff --git a/tests/libgit2/repo/init.c b/tests/libgit2/repo/init.c new file mode 100644 index 000000000..7cf6742ca --- /dev/null +++ b/tests/libgit2/repo/init.c @@ -0,0 +1,738 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "repository.h" +#include "config.h" +#include "path.h" +#include "config/config_helpers.h" +#include "repo/repo_helpers.h" + +enum repo_mode { + STANDARD_REPOSITORY = 0, + BARE_REPOSITORY = 1 +}; + +static git_repository *g_repo = NULL; +static git_str g_global_path = GIT_STR_INIT; + +void test_repo_init__initialize(void) +{ + g_repo = NULL; + + git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, + &g_global_path); +} + +void test_repo_init__cleanup(void) +{ + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, + g_global_path.ptr); + git_str_dispose(&g_global_path); + + cl_fixture_cleanup("tmp_global_path"); +} + +static void cleanup_repository(void *path) +{ + git_repository_free(g_repo); + g_repo = NULL; + + cl_fixture_cleanup((const char *)path); +} + +static void ensure_repository_init( + const char *working_directory, + int is_bare, + const char *expected_path_repository, + const char *expected_working_directory) +{ + const char *workdir; + + cl_assert(!git_fs_path_isdir(working_directory)); + + cl_git_pass(git_repository_init(&g_repo, working_directory, is_bare)); + + workdir = git_repository_workdir(g_repo); + if (workdir != NULL || expected_working_directory != NULL) { + cl_assert( + git__suffixcmp(workdir, expected_working_directory) == 0 + ); + } + + cl_assert( + git__suffixcmp(git_repository_path(g_repo), expected_path_repository) == 0 + ); + + cl_assert(git_repository_is_bare(g_repo) == is_bare); + +#ifdef GIT_WIN32 + if (!is_bare) { + DWORD fattrs = GetFileAttributes(git_repository_path(g_repo)); + cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0); + } +#endif + + cl_assert(git_repository_is_empty(g_repo)); +} + +void test_repo_init__standard_repo(void) +{ + cl_set_cleanup(&cleanup_repository, "testrepo"); + ensure_repository_init("testrepo/", 0, "testrepo/.git/", "testrepo/"); +} + +void test_repo_init__standard_repo_noslash(void) +{ + cl_set_cleanup(&cleanup_repository, "testrepo"); + ensure_repository_init("testrepo", 0, "testrepo/.git/", "testrepo/"); +} + +void test_repo_init__bare_repo(void) +{ + cl_set_cleanup(&cleanup_repository, "testrepo.git"); + ensure_repository_init("testrepo.git/", 1, "testrepo.git/", NULL); +} + +void test_repo_init__bare_repo_noslash(void) +{ + cl_set_cleanup(&cleanup_repository, "testrepo.git"); + ensure_repository_init("testrepo.git", 1, "testrepo.git/", NULL); +} + +void test_repo_init__bare_repo_escaping_current_workdir(void) +{ + git_str path_repository = GIT_STR_INIT; + git_str path_current_workdir = GIT_STR_INIT; + + cl_git_pass(git_fs_path_prettify_dir(&path_current_workdir, ".", NULL)); + + cl_git_pass(git_str_joinpath(&path_repository, git_str_cstr(&path_current_workdir), "a/b/c")); + cl_git_pass(git_futils_mkdir_r(git_str_cstr(&path_repository), GIT_DIR_MODE)); + + /* Change the current working directory */ + cl_git_pass(chdir(git_str_cstr(&path_repository))); + + /* Initialize a bare repo with a relative path escaping out of the current working directory */ + cl_git_pass(git_repository_init(&g_repo, "../d/e.git", 1)); + cl_git_pass(git__suffixcmp(git_repository_path(g_repo), "/a/b/d/e.git/")); + + git_repository_free(g_repo); + g_repo = NULL; + + /* Open a bare repo with a relative path escaping out of the current working directory */ + cl_git_pass(git_repository_open(&g_repo, "../d/e.git")); + + cl_git_pass(chdir(git_str_cstr(&path_current_workdir))); + + git_str_dispose(&path_current_workdir); + git_str_dispose(&path_repository); + + cleanup_repository("a"); +} + +void test_repo_init__reinit_bare_repo(void) +{ + cl_set_cleanup(&cleanup_repository, "reinit.git"); + + /* Initialize the repository */ + cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1)); + git_repository_free(g_repo); + g_repo = NULL; + + /* Reinitialize the repository */ + cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1)); +} + +void test_repo_init__reinit_too_recent_bare_repo(void) +{ + git_config *config; + + /* Initialize the repository */ + cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1)); + git_repository_config(&config, g_repo); + + /* + * Hack the config of the repository to make it look like it has + * been created by a recenter version of git/libgit2 + */ + cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42)); + + git_config_free(config); + git_repository_free(g_repo); + g_repo = NULL; + + /* Try to reinitialize the repository */ + cl_git_fail(git_repository_init(&g_repo, "reinit.git", 1)); + + cl_fixture_cleanup("reinit.git"); +} + +void test_repo_init__additional_templates(void) +{ + git_str path = GIT_STR_INIT; + + cl_set_cleanup(&cleanup_repository, "tester"); + + ensure_repository_init("tester", 0, "tester/.git/", "tester/"); + + cl_git_pass( + git_str_joinpath(&path, git_repository_path(g_repo), "description")); + cl_assert(git_fs_path_isfile(git_str_cstr(&path))); + + cl_git_pass( + git_str_joinpath(&path, git_repository_path(g_repo), "info/exclude")); + cl_assert(git_fs_path_isfile(git_str_cstr(&path))); + + cl_git_pass( + git_str_joinpath(&path, git_repository_path(g_repo), "hooks")); + cl_assert(git_fs_path_isdir(git_str_cstr(&path))); + /* won't confirm specific contents of hooks dir since it may vary */ + + git_str_dispose(&path); +} + +static void assert_config_entry_on_init_bytype( + const char *config_key, int expected_value, bool is_bare) +{ + git_config *config; + int error, current_value; + const char *repo_path = is_bare ? + "config_entry/test.bare.git" : "config_entry/test.non.bare.git"; + + cl_set_cleanup(&cleanup_repository, "config_entry"); + + cl_git_pass(git_repository_init(&g_repo, repo_path, is_bare)); + + cl_git_pass(git_repository_config(&config, g_repo)); + error = git_config_get_bool(¤t_value, config, config_key); + git_config_free(config); + + if (expected_value >= 0) { + cl_assert_equal_i(0, error); + cl_assert_equal_i(expected_value, current_value); + } else { + cl_assert_equal_i(expected_value, error); + } +} + +static void assert_config_entry_on_init( + const char *config_key, int expected_value) +{ + assert_config_entry_on_init_bytype(config_key, expected_value, true); + git_repository_free(g_repo); + g_repo = NULL; + + assert_config_entry_on_init_bytype(config_key, expected_value, false); +} + +void test_repo_init__detect_filemode(void) +{ + assert_config_entry_on_init("core.filemode", cl_is_chmod_supported()); +} + +void test_repo_init__detect_ignorecase(void) +{ + struct stat st; + bool found_without_match; + + cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666); + found_without_match = (p_stat("Testcaps", &st) == 0); + cl_must_pass(p_unlink("testCAPS")); + + assert_config_entry_on_init( + "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND); +} + +/* + * Windows: if the filesystem supports symlinks (because we're running + * as administrator, or because the user has opted into it for normal + * users) then we can also opt-in explicitly by settings `core.symlinks` + * in the global config. Symlinks remain off by default. + */ + +void test_repo_init__symlinks_win32_enabled_by_global_config(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + git_config *config, *repo_config; + int val; + + if (!git_fs_path_supports_symlinks("link")) + cl_skip(); + + create_tmp_global_config("tmp_global_config", "core.symlinks", "true"); + + /* + * Create a new repository (can't use `assert_config_on_init` since we + * want to examine configuration levels with more granularity.) + */ + cl_git_pass(git_repository_init(&g_repo, "config_entry/test.non.bare.git", false)); + + /* Ensure that core.symlinks remains set (via the global config). */ + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_get_bool(&val, config, "core.symlinks")); + cl_assert_equal_i(1, val); + + /* + * Ensure that the repository config does not set core.symlinks. + * It should remain inherited. + */ + cl_git_pass(git_config_open_level(&repo_config, config, GIT_CONFIG_LEVEL_LOCAL)); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&val, repo_config, "core.symlinks")); + git_config_free(repo_config); + + git_config_free(config); + + git_repository_free(g_repo); + g_repo = NULL; +#endif +} + +void test_repo_init__symlinks_win32_off_by_default(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + assert_config_entry_on_init("core.symlinks", false); +#endif +} + +void test_repo_init__symlinks_posix_detected(void) +{ +#ifdef GIT_WIN32 + cl_skip(); +#else + assert_config_entry_on_init( + "core.symlinks", git_fs_path_supports_symlinks("link") ? GIT_ENOTFOUND : false); +#endif +} + +void test_repo_init__detect_precompose_unicode_required(void) +{ +#ifdef GIT_USE_ICONV + char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn"; + struct stat st; + bool found_with_nfd; + + cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666); + found_with_nfd = (p_stat(decomposed, &st) == 0); + cl_must_pass(p_unlink(composed)); + + assert_config_entry_on_init("core.precomposeunicode", found_with_nfd); +#else + assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND); +#endif +} + +void test_repo_init__reinit_doesnot_overwrite_ignorecase(void) +{ + git_config *config; + int current_value; + + /* Init a new repo */ + cl_set_cleanup(&cleanup_repository, "not.overwrite.git"); + cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1)); + + /* Change the "core.ignorecase" config value to something unlikely */ + git_repository_config(&config, g_repo); + git_config_set_int32(config, "core.ignorecase", 42); + git_config_free(config); + git_repository_free(g_repo); + g_repo = NULL; + + /* Reinit the repository */ + cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1)); + git_repository_config(&config, g_repo); + + /* Ensure the "core.ignorecase" config value hasn't been updated */ + cl_git_pass(git_config_get_int32(¤t_value, config, "core.ignorecase")); + cl_assert_equal_i(42, current_value); + + git_config_free(config); +} + +void test_repo_init__reinit_overwrites_filemode(void) +{ + int expected = cl_is_chmod_supported(), current_value; + + /* Init a new repo */ + cl_set_cleanup(&cleanup_repository, "overwrite.git"); + cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1)); + + /* Change the "core.filemode" config value to something unlikely */ + cl_repo_set_bool(g_repo, "core.filemode", !expected); + + git_repository_free(g_repo); + g_repo = NULL; + + /* Reinit the repository */ + cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1)); + + /* Ensure the "core.filemode" config value has been reset */ + current_value = cl_repo_get_bool(g_repo, "core.filemode"); + cl_assert_equal_i(expected, current_value); +} + +void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void) +{ + assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true); + git_repository_free(g_repo); + assert_config_entry_on_init_bytype("core.logallrefupdates", true, false); +} + +void test_repo_init__extended_0(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + /* without MKDIR this should fail */ + cl_git_fail(git_repository_init_ext(&g_repo, "extended", &opts)); + + /* make the directory first, then it should succeed */ + cl_git_pass(git_futils_mkdir("extended", 0775, 0)); + cl_git_pass(git_repository_init_ext(&g_repo, "extended", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/extended/")); + cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/extended/.git/")); + cl_assert(!git_repository_is_bare(g_repo)); + cl_assert(git_repository_is_empty(g_repo)); + + cleanup_repository("extended"); +} + +void test_repo_init__extended_1(void) +{ + git_reference *ref; + git_remote *remote; + struct stat st; + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP; + opts.workdir_path = "../c_wd"; + opts.description = "Awesomest test repository evah"; + opts.initial_head = "development"; + opts.origin_url = "https://github.com/libgit2/libgit2.git"; + + cl_git_pass(git_repository_init_ext(&g_repo, "root/b/c.git", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/c.git/")); + cl_assert(git_fs_path_isfile("root/b/c_wd/.git")); + cl_assert(!git_repository_is_bare(g_repo)); + /* repo will not be counted as empty because we set head to "development" */ + cl_assert(!git_repository_is_empty(g_repo)); + + cl_git_pass(git_fs_path_lstat(git_repository_path(g_repo), &st)); + cl_assert(S_ISDIR(st.st_mode)); + if (cl_is_chmod_supported()) + cl_assert((S_ISGID & st.st_mode) == S_ISGID); + else + cl_assert((S_ISGID & st.st_mode) == 0); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC); + cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_remote_lookup(&remote, g_repo, "origin")); + cl_assert_equal_s("origin", git_remote_name(remote)); + cl_assert_equal_s(opts.origin_url, git_remote_url(remote)); + git_remote_free(remote); + + git_repository_free(g_repo); + cl_fixture_cleanup("root"); +} + +void test_repo_init__relative_gitdir(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_str dot_git_content = GIT_STR_INIT; + + opts.workdir_path = "../c_wd"; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(g_repo)); + cl_assert(git_repository_is_empty(g_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + assert_config_entry_value(g_repo, "core.worktree", "../c_wd/"); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_str_dispose(&dot_git_content); + cleanup_repository("root"); +} + +void test_repo_init__relative_gitdir_2(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_str dot_git_content = GIT_STR_INIT; + git_str full_path = GIT_STR_INIT; + + cl_git_pass(git_fs_path_prettify(&full_path, ".", NULL)); + cl_git_pass(git_str_joinpath(&full_path, full_path.ptr, "root/b/c_wd")); + + opts.workdir_path = full_path.ptr; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts)); + git_str_dispose(&full_path); + + cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(g_repo)); + cl_assert(git_repository_is_empty(g_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + assert_config_entry_value(g_repo, "core.worktree", "../c_wd/"); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_str_dispose(&dot_git_content); + cleanup_repository("root"); +} + +void test_repo_init__can_reinit_an_initialized_repository(void) +{ + git_repository *reinit; + + cl_set_cleanup(&cleanup_repository, "extended"); + + cl_git_pass(git_futils_mkdir("extended", 0775, 0)); + cl_git_pass(git_repository_init(&g_repo, "extended", false)); + + cl_git_pass(git_repository_init(&reinit, "extended", false)); + + cl_assert_equal_s(git_repository_path(g_repo), git_repository_path(reinit)); + + git_repository_free(reinit); +} + +void test_repo_init__init_with_initial_commit(void) +{ + git_index *index; + + cl_set_cleanup(&cleanup_repository, "committed"); + + /* Initialize the repository */ + cl_git_pass(git_repository_init(&g_repo, "committed", 0)); + + /* Index will be automatically created when requested for a new repo */ + cl_git_pass(git_repository_index(&index, g_repo)); + + /* Create a file so we can commit it + * + * If you are writing code outside the test suite, you can create this + * file any way that you like, such as: + * FILE *fp = fopen("committed/file.txt", "w"); + * fputs("some stuff\n", fp); + * fclose(fp); + * We like to use the help functions because they do error detection + * in a way that's easily compatible with our test suite. + */ + cl_git_mkfile("committed/file.txt", "some stuff\n"); + + /* Add file to the index */ + cl_git_pass(git_index_add_bypath(index, "file.txt")); + cl_git_pass(git_index_write(index)); + + /* Intentionally not using cl_repo_commit_from_index here so this code + * can be used as an example of how an initial commit is typically + * made to a repository... + */ + + /* Make sure we're ready to use git_signature_default :-) */ + { + git_config *cfg, *local; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL)); + cl_git_pass(git_config_set_string(local, "user.name", "Test User")); + cl_git_pass(git_config_set_string(local, "user.email", "t@example.com")); + git_config_free(local); + git_config_free(cfg); + } + + /* Create a commit with the new contents of the index */ + { + git_signature *sig; + git_oid tree_id, commit_id; + git_tree *tree; + + cl_git_pass(git_signature_default(&sig, g_repo)); + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_commit_create_v( + &commit_id, g_repo, "HEAD", sig, sig, + NULL, "First", tree, 0)); + + git_tree_free(tree); + git_signature_free(sig); + } + + git_index_free(index); +} + +void test_repo_init__at_filesystem_root(void) +{ + git_repository *repo; + const char *sandbox = clar_sandbox_path(); + git_str root = GIT_STR_INIT; + int root_len; + + if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE")) + cl_skip(); + + root_len = git_fs_path_root(sandbox); + cl_assert(root_len >= 0); + + git_str_put(&root, sandbox, root_len+1); + git_str_joinpath(&root, root.ptr, "libgit2_test_dir"); + + cl_assert(!git_fs_path_exists(root.ptr)); + + cl_git_pass(git_repository_init(&repo, root.ptr, 0)); + cl_assert(git_fs_path_isdir(root.ptr)); + cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + + git_str_dispose(&root); + git_repository_free(repo); +} + +void test_repo_init__nonexisting_directory(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_repository *repo; + + /* + * If creating a repo with non-existing parent directories, then libgit2 + * will by default create the complete directory hierarchy if using + * `git_repository_init`. Thus, let's use the extended version and not + * set the `GIT_REPOSITORY_INIT_MKPATH` flag. + */ + cl_git_fail(git_repository_init_ext(&repo, "nonexisting/path", &opts)); +} + +void test_repo_init__nonexisting_root(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + + /* + * This really only depends on the nonexistence of the Q: drive. We + * cannot implement the equivalent test on Unix systems, as there is + * fundamentally no path that is disconnected from the root directory. + */ + cl_git_fail(git_repository_init(&repo, "Q:/non/existent/path", 0)); + cl_git_fail(git_repository_init(&repo, "Q:\\non\\existent\\path", 0)); +#else + clar__skip(); +#endif +} + +void test_repo_init__unwriteable_directory(void) +{ +#ifndef GIT_WIN32 + git_repository *repo; + + if (geteuid() == 0) + clar__skip(); + + /* + * Create a non-writeable directory so that we cannot create directories + * inside of it. The root user has CAP_DAC_OVERRIDE, so he doesn't care + * for the directory permissions and thus we need to skip the test if + * run as root user. + */ + cl_must_pass(p_mkdir("unwriteable", 0444)); + cl_git_fail(git_repository_init(&repo, "unwriteable/repo", 0)); + cl_must_pass(p_rmdir("unwriteable")); +#else + clar__skip(); +#endif +} + +void test_repo_init__defaultbranch_config(void) +{ + git_reference *head; + + cl_set_cleanup(&cleanup_repository, "repo"); + + create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch"); + + cl_git_pass(git_repository_init(&g_repo, "repo", 0)); + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + + cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head)); + + git_reference_free(head); +} + +void test_repo_init__defaultbranch_config_empty(void) +{ + git_reference *head; + + cl_set_cleanup(&cleanup_repository, "repo"); + + create_tmp_global_config("tmp_global_path", "init.defaultbranch", ""); + + cl_git_pass(git_repository_init(&g_repo, "repo", 0)); + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); + + git_reference_free(head); +} + +void test_repo_init__longpath(void) +{ +#ifdef GIT_WIN32 + size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_HEXSZ; + size_t max, i; + git_str path = GIT_STR_INIT; + git_repository *one = NULL, *two = NULL; + + /* + * Files within repositories need to fit within MAX_PATH; + * that means a repo path must be at most (MAX_PATH - 18). + */ + cl_git_pass(git_str_puts(&path, clar_sandbox_path())); + cl_git_pass(git_str_putc(&path, '/')); + + max = ((MAX_PATH) - path.size) - padding; + + for (i = 0; i < max - 1; i++) + cl_git_pass(git_str_putc(&path, 'a')); + + cl_git_pass(git_repository_init(&one, path.ptr, 1)); + + /* Paths longer than this are rejected */ + cl_git_pass(git_str_putc(&path, 'z')); + cl_git_fail(git_repository_init(&two, path.ptr, 1)); + + git_repository_free(one); + git_repository_free(two); + git_str_dispose(&path); +#endif +} diff --git a/tests/libgit2/repo/message.c b/tests/libgit2/repo/message.c new file mode 100644 index 000000000..6241f48f9 --- /dev/null +++ b/tests/libgit2/repo/message.c @@ -0,0 +1,39 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "posix.h" + +static git_repository *_repo; + +void test_repo_message__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_repo_message__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_repo_message__none(void) +{ + git_buf actual = GIT_BUF_INIT; + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo)); +} + +void test_repo_message__message(void) +{ + git_str path = GIT_STR_INIT; + git_buf actual = GIT_BUF_INIT; + const char expected[] = "Test\n\nThis is a test of the emergency broadcast system\n"; + + cl_git_pass(git_str_joinpath(&path, git_repository_path(_repo), "MERGE_MSG")); + cl_git_mkfile(git_str_cstr(&path), expected); + + cl_git_pass(git_repository_message(&actual, _repo)); + cl_assert_equal_s(expected, actual.ptr); + git_buf_dispose(&actual); + + cl_git_pass(p_unlink(git_str_cstr(&path))); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo)); + git_str_dispose(&path); +} diff --git a/tests/libgit2/repo/new.c b/tests/libgit2/repo/new.c new file mode 100644 index 000000000..d77e903f6 --- /dev/null +++ b/tests/libgit2/repo/new.c @@ -0,0 +1,27 @@ +#include "clar_libgit2.h" +#include "git2/sys/repository.h" + +void test_repo_new__has_nothing(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_new(&repo)); + cl_assert_equal_b(true, git_repository_is_bare(repo)); + cl_assert_equal_p(NULL, git_repository_path(repo)); + cl_assert_equal_p(NULL, git_repository_workdir(repo)); + git_repository_free(repo); +} + +void test_repo_new__is_bare_until_workdir_set(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_new(&repo)); + cl_assert_equal_b(true, git_repository_is_bare(repo)); + + cl_git_pass(git_repository_set_workdir(repo, clar_sandbox_path(), 0)); + cl_assert_equal_b(false, git_repository_is_bare(repo)); + + git_repository_free(repo); +} + diff --git a/tests/libgit2/repo/open.c b/tests/libgit2/repo/open.c new file mode 100644 index 000000000..f7ed2c373 --- /dev/null +++ b/tests/libgit2/repo/open.c @@ -0,0 +1,455 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "sysdir.h" +#include <ctype.h> + + +void test_repo_open__cleanup(void) +{ + cl_git_sandbox_cleanup(); + + if (git_fs_path_isdir("alternate")) + git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES); +} + +void test_repo_open__bare_empty_repo(void) +{ + git_repository *repo = cl_git_sandbox_init("empty_bare.git"); + + cl_assert(git_repository_path(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0); + cl_assert(git_repository_workdir(repo) == NULL); +} + +void test_repo_open__format_version_1(void) +{ + git_repository *repo; + git_config *config; + + repo = cl_git_sandbox_init("empty_bare.git"); + + cl_git_pass(git_repository_open(&repo, "empty_bare.git")); + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1)); + + git_config_free(config); + git_repository_free(repo); + + cl_git_pass(git_repository_open(&repo, "empty_bare.git")); + cl_assert(git_repository_path(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0); + git_repository_free(repo); +} + +void test_repo_open__standard_empty_repo_through_gitdir(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_open(&repo, cl_fixture("empty_standard_repo/.gitted"))); + + cl_assert(git_repository_path(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0); + + cl_assert(git_repository_workdir(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0); + + git_repository_free(repo); +} + +void test_repo_open__standard_empty_repo_through_workdir(void) +{ + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_assert(git_repository_path(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0); + + cl_assert(git_repository_workdir(repo) != NULL); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0); +} + + +void test_repo_open__open_with_discover(void) +{ + static const char *variants[] = { + "attr", "attr/", "attr/.git", "attr/.git/", + "attr/sub", "attr/sub/", "attr/sub/sub", "attr/sub/sub/", + NULL + }; + git_repository *repo; + const char **scan; + + cl_fixture_sandbox("attr"); + cl_git_pass(p_rename("attr/.gitted", "attr/.git")); + + for (scan = variants; *scan != NULL; scan++) { + cl_git_pass(git_repository_open_ext(&repo, *scan, 0, NULL)); + cl_assert(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0); + git_repository_free(repo); + } + + cl_fixture_cleanup("attr"); +} + +void test_repo_open__check_if_repository(void) +{ + cl_git_sandbox_init("empty_standard_repo"); + + /* Pass NULL for the output parameter to check for but not open the repo */ + cl_git_pass(git_repository_open_ext(NULL, "empty_standard_repo", 0, NULL)); + cl_git_fail(git_repository_open_ext(NULL, "repo_does_not_exist", 0, NULL)); + + cl_fixture_cleanup("empty_standard_repo"); +} + +static void make_gitlink_dir(const char *dir, const char *linktext) +{ + git_str path = GIT_STR_INIT; + + cl_git_pass(git_futils_mkdir(dir, 0777, GIT_MKDIR_VERIFY_DIR)); + cl_git_pass(git_str_joinpath(&path, dir, ".git")); + cl_git_rewritefile(path.ptr, linktext); + git_str_dispose(&path); +} + +void test_repo_open__gitlinked(void) +{ + /* need to have both repo dir and workdir set up correctly */ + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_repository *repo2; + + make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git"); + + cl_git_pass(git_repository_open(&repo2, "alternate")); + + cl_assert(git_repository_path(repo2) != NULL); + cl_assert_(git__suffixcmp(git_repository_path(repo2), "empty_standard_repo/.git/") == 0, git_repository_path(repo2)); + cl_assert_equal_s(git_repository_path(repo), git_repository_path(repo2)); + + cl_assert(git_repository_workdir(repo2) != NULL); + cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2)); + + git_repository_free(repo2); +} + +void test_repo_open__with_symlinked_config(void) +{ +#ifndef GIT_WIN32 + git_str path = GIT_STR_INIT; + git_repository *repo; + git_config *cfg; + int32_t value; + + cl_git_sandbox_init("empty_standard_repo"); + + /* Setup .gitconfig as symlink */ + cl_git_pass(git_futils_mkdir_r("home", 0777)); + cl_git_mkfile("home/.gitconfig.linked", "[global]\ntest = 4567\n"); + cl_must_pass(symlink(".gitconfig.linked", "home/.gitconfig")); + cl_git_pass(git_fs_path_prettify(&path, "home", NULL)); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_get_int32(&value, cfg, "global.test")); + cl_assert_equal_i(4567, value); + + git_config_free(cfg); + git_repository_free(repo); + cl_git_pass(git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); + cl_sandbox_set_search_path_defaults(); + git_str_dispose(&path); +#endif +} + +void test_repo_open__from_git_new_workdir(void) +{ +#ifndef GIT_WIN32 + /* The git-new-workdir script that ships with git sets up a bunch of + * symlinks to create a second workdir that shares the object db with + * another checkout. Libgit2 can open a repo that has been configured + * this way. + */ + + git_repository *repo2; + git_str link_tgt = GIT_STR_INIT, link = GIT_STR_INIT, body = GIT_STR_INIT; + const char **scan; + int link_fd; + static const char *links[] = { + "config", "refs", "logs/refs", "objects", "info", "hooks", + "packed-refs", "remotes", "rr-cache", "svn", NULL + }; + static const char *copies[] = { + "HEAD", NULL + }; + + cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(p_mkdir("alternate", 0777)); + cl_git_pass(p_mkdir("alternate/.git", 0777)); + + for (scan = links; *scan != NULL; scan++) { + git_str_joinpath(&link_tgt, "empty_standard_repo/.git", *scan); + if (git_fs_path_exists(link_tgt.ptr)) { + git_str_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan); + git_str_joinpath(&link, "alternate/.git", *scan); + if (strchr(*scan, '/')) + git_futils_mkpath2file(link.ptr, 0777); + cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno)); + } + } + for (scan = copies; *scan != NULL; scan++) { + git_str_joinpath(&link_tgt, "empty_standard_repo/.git", *scan); + if (git_fs_path_exists(link_tgt.ptr)) { + git_str_joinpath(&link, "alternate/.git", *scan); + cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr)); + + cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0); + cl_must_pass(p_write(link_fd, body.ptr, body.size)); + p_close(link_fd); + } + } + + git_str_dispose(&link_tgt); + git_str_dispose(&link); + git_str_dispose(&body); + + + cl_git_pass(git_repository_open(&repo2, "alternate")); + + cl_assert(git_repository_path(repo2) != NULL); + cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2)); + + cl_assert(git_repository_workdir(repo2) != NULL); + cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2)); + + git_repository_free(repo2); +#else + cl_skip(); +#endif +} + +void test_repo_open__failures(void) +{ + git_repository *base, *repo; + git_str ceiling = GIT_STR_INIT; + + base = cl_git_sandbox_init("attr"); + cl_git_pass(git_str_sets(&ceiling, git_repository_workdir(base))); + + /* fail with no searching */ + cl_git_fail(git_repository_open(&repo, "attr/sub")); + cl_git_fail(git_repository_open_ext( + &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)); + + /* fail with ceiling too low */ + cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr)); + cl_git_pass(git_str_joinpath(&ceiling, ceiling.ptr, "sub")); + cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr)); + + /* fail with no repo */ + cl_git_pass(p_mkdir("alternate", 0777)); + cl_git_pass(p_mkdir("alternate/.git", 0777)); + cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL)); + cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL)); + + /* fail with no searching and no appending .git */ + cl_git_fail(git_repository_open_ext( + &repo, "attr", + GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_NO_DOTGIT, + NULL)); + + git_str_dispose(&ceiling); +} + +void test_repo_open__bad_gitlinks(void) +{ + git_repository *repo; + static const char *bad_links[] = { + "garbage\n", "gitdir", "gitdir:\n", "gitdir: foobar", + "gitdir: ../invalid", "gitdir: ../invalid2", + "gitdir: ../attr/.git with extra stuff", + NULL + }; + const char **scan; + + cl_git_sandbox_init("attr"); + + cl_git_pass(p_mkdir("invalid", 0777)); + cl_git_pass(git_futils_mkdir_r("invalid2/.git", 0777)); + + for (scan = bad_links; *scan != NULL; scan++) { + make_gitlink_dir("alternate", *scan); + repo = NULL; + cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL)); + cl_assert(repo == NULL); + } + + git_futils_rmdir_r("invalid", NULL, GIT_RMDIR_REMOVE_FILES); + git_futils_rmdir_r("invalid2", NULL, GIT_RMDIR_REMOVE_FILES); +} + +#ifdef GIT_WIN32 +static void unposix_path(git_str *path) +{ + char *src, *tgt; + + src = tgt = path->ptr; + + /* convert "/d/..." to "d:\..." */ + if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') { + *tgt++ = src[1]; + *tgt++ = ':'; + *tgt++ = '\\'; + src += 3; + } + + while (*src) { + *tgt++ = (*src == '/') ? '\\' : *src; + src++; + } + + *tgt = '\0'; +} +#endif + +void test_repo_open__win32_path(void) +{ +#ifdef GIT_WIN32 + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"), *repo2; + git_str winpath = GIT_STR_INIT; + static const char *repo_path = "empty_standard_repo/.git/"; + static const char *repo_wd = "empty_standard_repo/"; + + cl_assert(git__suffixcmp(git_repository_path(repo), repo_path) == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo), repo_wd) == 0); + + cl_git_pass(git_str_sets(&winpath, git_repository_path(repo))); + unposix_path(&winpath); + cl_git_pass(git_repository_open(&repo2, winpath.ptr)); + cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); + git_repository_free(repo2); + + cl_git_pass(git_str_sets(&winpath, git_repository_path(repo))); + git_str_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */ + unposix_path(&winpath); + cl_git_pass(git_repository_open(&repo2, winpath.ptr)); + cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); + git_repository_free(repo2); + + cl_git_pass(git_str_sets(&winpath, git_repository_workdir(repo))); + unposix_path(&winpath); + cl_git_pass(git_repository_open(&repo2, winpath.ptr)); + cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); + git_repository_free(repo2); + + cl_git_pass(git_str_sets(&winpath, git_repository_workdir(repo))); + git_str_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */ + unposix_path(&winpath); + cl_git_pass(git_repository_open(&repo2, winpath.ptr)); + cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0); + cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0); + git_repository_free(repo2); + + git_str_dispose(&winpath); +#endif +} + +void test_repo_open__opening_a_non_existing_repository_returns_ENOTFOUND(void) +{ + git_repository *repo; + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_open(&repo, "i-do-not/exist")); +} + +void test_repo_open__no_config(void) +{ + git_str path = GIT_STR_INIT; + git_repository *repo; + git_config *config; + + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename( + "empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + /* remove local config */ + cl_git_pass(git_futils_rmdir_r( + "empty_standard_repo/.git/config", NULL, GIT_RMDIR_REMOVE_FILES)); + + /* isolate from system level configs */ + cl_must_pass(p_mkdir("alternate", 0777)); + cl_git_pass(git_fs_path_prettify(&path, "alternate", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + git_str_dispose(&path); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_set_string(config, "test.set", "42")); + + git_config_free(config); + git_repository_free(repo); + cl_fixture_cleanup("empty_standard_repo"); + + cl_sandbox_set_search_path_defaults(); +} + +void test_repo_open__force_bare(void) +{ + /* need to have both repo dir and workdir set up correctly */ + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_repository *barerepo; + + make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git"); + + cl_assert(!git_repository_is_bare(repo)); + + cl_git_pass(git_repository_open(&barerepo, "alternate")); + cl_assert(!git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(git_repository_open_bare( + &barerepo, "empty_standard_repo/.git")); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "alternate/.git", GIT_REPOSITORY_OPEN_BARE, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(p_mkdir("empty_standard_repo/subdir", 0777)); + cl_git_mkfile("empty_standard_repo/subdir/something.txt", "something"); + + cl_git_fail(git_repository_open_bare( + &barerepo, "empty_standard_repo/subdir")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "empty_standard_repo/subdir", GIT_REPOSITORY_OPEN_BARE, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); + + cl_git_pass(p_mkdir("alternate/subdir", 0777)); + cl_git_pass(p_mkdir("alternate/subdir/sub2", 0777)); + cl_git_mkfile("alternate/subdir/sub2/something.txt", "something"); + + cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2")); + + cl_git_pass(git_repository_open_ext( + &barerepo, "alternate/subdir/sub2", + GIT_REPOSITORY_OPEN_BARE|GIT_REPOSITORY_OPEN_CROSS_FS, NULL)); + cl_assert(git_repository_is_bare(barerepo)); + git_repository_free(barerepo); +} + diff --git a/tests/libgit2/repo/pathspec.c b/tests/libgit2/repo/pathspec.c new file mode 100644 index 000000000..5b86662bc --- /dev/null +++ b/tests/libgit2/repo/pathspec.c @@ -0,0 +1,385 @@ +#include "clar_libgit2.h" +#include "git2/pathspec.h" + +static git_repository *g_repo; + +void test_repo_pathspec__initialize(void) +{ + g_repo = cl_git_sandbox_init("status"); +} + +void test_repo_pathspec__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static char *str0[] = { "*_file", "new_file", "garbage" }; +static char *str1[] = { "*_FILE", "NEW_FILE", "GARBAGE" }; +static char *str2[] = { "staged_*" }; +static char *str3[] = { "!subdir", "*_file", "new_file" }; +static char *str4[] = { "*" }; +static char *str5[] = { "S*" }; + +void test_repo_pathspec__workdir0(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 0)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES | GIT_PATHSPEC_FAILURES_ONLY, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir1(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_FILE", "NEW_FILE", "GARBAGE" } */ + s.strings = str1; s.count = ARRAY_SIZE(str1); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_IGNORE_CASE, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_fail(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_NO_MATCH_ERROR, ps)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir2(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "staged_*" } */ + s.strings = str2; s.count = ARRAY_SIZE(str2); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_fail(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_NO_MATCH_ERROR, ps)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir3(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "!subdir", "*_file", "new_file" } */ + s.strings = str3; s.count = ARRAY_SIZE(str3); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("new_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7)); + + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__workdir4(void) +{ + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*" } */ + s.strings = str4; s.count = ARRAY_SIZE(str4); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); + cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("\xE8\xBF\x99", git_pathspec_match_list_entry(m, 12)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); +} + + +void test_repo_pathspec__index0(void) +{ + git_index *idx; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + cl_git_pass(git_repository_index(&idx, g_repo)); + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_index(&m, idx, 0, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("staged_new_file_deleted_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 7)); + cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 8)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 9)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); + git_index_free(idx); +} + +void test_repo_pathspec__index1(void) +{ + /* Currently the USE_CASE and IGNORE_CASE flags don't work on the + * index because the index sort order for the index iterator is + * set by the index itself. I think the correct fix is for the + * index not to embed a global sort order but to support traversal + * in either case sensitive or insensitive order in a stateless + * manner. + * + * Anyhow, as it is, there is no point in doing this test. + */ +#if 0 + git_index *idx; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + cl_git_pass(git_repository_index(&idx, g_repo)); + + /* { "*_FILE", "NEW_FILE", "GARBAGE" } */ + s.strings = str1; s.count = ARRAY_SIZE(str1); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_USE_CASE, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_index(&m, idx, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_pathspec_free(ps); + git_index_free(idx); +#endif +} + +void test_repo_pathspec__tree0(void) +{ + git_object *tree; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "*_file", "new_file", "garbage" } */ + s.strings = str0; s.count = ARRAY_SIZE(str0); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(4, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1)); + cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7)); + cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m)); + cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0)); + cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1)); + cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__tree5(void) +{ + git_object *tree; + git_strarray s; + git_pathspec *ps; + git_pathspec_match_list *m; + + /* { "S*" } */ + s.strings = str5; s.count = ARRAY_SIZE(str5); + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree, + GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps)); + cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m)); + cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0)); + cl_assert_equal_s("subdir.txt", git_pathspec_match_list_entry(m, 5)); + cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6)); + cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m)); + git_pathspec_match_list_free(m); + + git_object_free(tree); + + git_pathspec_free(ps); +} + +void test_repo_pathspec__in_memory(void) +{ + static char *strings[] = { "one", "two*", "!three*", "*four" }; + git_strarray s = { strings, ARRAY_SIZE(strings) }; + git_pathspec *ps; + + cl_git_pass(git_pathspec_new(&ps, &s)); + + cl_assert(git_pathspec_matches_path(ps, 0, "one")); + cl_assert(!git_pathspec_matches_path(ps, 0, "ONE")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_IGNORE_CASE, "ONE")); + cl_assert(git_pathspec_matches_path(ps, 0, "two")); + cl_assert(git_pathspec_matches_path(ps, 0, "two.txt")); + cl_assert(!git_pathspec_matches_path(ps, 0, "three.txt")); + cl_assert(git_pathspec_matches_path(ps, 0, "anything.four")); + cl_assert(!git_pathspec_matches_path(ps, 0, "three.four")); + cl_assert(!git_pathspec_matches_path(ps, 0, "nomatch")); + cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two*")); + cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "anyfour")); + cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "*four")); + + git_pathspec_free(ps); +} diff --git a/tests/libgit2/repo/repo_helpers.c b/tests/libgit2/repo/repo_helpers.c new file mode 100644 index 000000000..1efde70a5 --- /dev/null +++ b/tests/libgit2/repo/repo_helpers.c @@ -0,0 +1,37 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "repo_helpers.h" +#include "posix.h" + +void make_head_unborn(git_repository* repo, const char *target) +{ + git_reference *head; + + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1, NULL)); + git_reference_free(head); +} + +void delete_head(git_repository* repo) +{ + git_str head_path = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&head_path, git_repository_path(repo), GIT_HEAD_FILE)); + cl_git_pass(p_unlink(git_str_cstr(&head_path))); + + git_str_dispose(&head_path); +} + +void create_tmp_global_config(const char *dirname, const char *key, const char *val) +{ + git_str path = GIT_STR_INIT; + git_config *config; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, + GIT_CONFIG_LEVEL_GLOBAL, dirname)); + cl_must_pass(p_mkdir(dirname, 0777)); + cl_git_pass(git_str_joinpath(&path, dirname, ".gitconfig")); + cl_git_pass(git_config_open_ondisk(&config, path.ptr)); + cl_git_pass(git_config_set_string(config, key, val)); + git_config_free(config); + git_str_dispose(&path); +} diff --git a/tests/libgit2/repo/repo_helpers.h b/tests/libgit2/repo/repo_helpers.h new file mode 100644 index 000000000..a93bf36ae --- /dev/null +++ b/tests/libgit2/repo/repo_helpers.h @@ -0,0 +1,7 @@ +#include "common.h" + +#define NON_EXISTING_HEAD "refs/heads/hide/and/seek" + +extern void make_head_unborn(git_repository* repo, const char *target); +extern void delete_head(git_repository* repo); +extern void create_tmp_global_config(const char *path, const char *key, const char *val); diff --git a/tests/libgit2/repo/reservedname.c b/tests/libgit2/repo/reservedname.c new file mode 100644 index 000000000..245d8625a --- /dev/null +++ b/tests/libgit2/repo/reservedname.c @@ -0,0 +1,132 @@ +#include "clar_libgit2.h" +#include "../submodule/submodule_helpers.h" +#include "repository.h" + +void test_repo_reservedname__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_repo_reservedname__includes_shortname_on_win32(void) +{ + git_repository *repo; + git_str *reserved; + size_t reserved_len; + + repo = cl_git_sandbox_init("nasty"); + cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, false)); + +#ifdef GIT_WIN32 + cl_assert_equal_i(2, reserved_len); + cl_assert_equal_s(".git", reserved[0].ptr); + cl_assert_equal_s("GIT~1", reserved[1].ptr); +#else + cl_assert_equal_i(1, reserved_len); + cl_assert_equal_s(".git", reserved[0].ptr); +#endif +} + +void test_repo_reservedname__includes_shortname_when_requested(void) +{ + git_repository *repo; + git_str *reserved; + size_t reserved_len; + + repo = cl_git_sandbox_init("nasty"); + cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true)); + + cl_assert_equal_i(2, reserved_len); + cl_assert_equal_s(".git", reserved[0].ptr); + cl_assert_equal_s("GIT~1", reserved[1].ptr); +} + +/* Ensures that custom shortnames are included: creates a GIT~1 so that the + * .git folder itself will have to be named GIT~2 + */ +void test_repo_reservedname__custom_shortname_recognized(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + git_str *reserved; + size_t reserved_len; + + if (!cl_sandbox_supports_8dot3()) + clar__skip(); + + repo = cl_git_sandbox_init("nasty"); + + cl_must_pass(p_rename("nasty/.git", "nasty/_temp")); + cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666); + cl_must_pass(p_rename("nasty/_temp", "nasty/.git")); + + cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true)); + + cl_assert_equal_i(3, reserved_len); + cl_assert_equal_s(".git", reserved[0].ptr); + cl_assert_equal_s("GIT~1", reserved[1].ptr); + cl_assert_equal_s("GIT~2", reserved[2].ptr); +#endif +} + +/* When looking at the short name for a submodule, we need to prevent + * people from overwriting the `.git` file in the submodule working + * directory itself. We don't want to look at the actual repository + * path, since it will be in the super's repository above us, and + * typically named with the name of our subrepository. Consequently, + * preventing access to the short name of the actual repository path + * would prevent us from creating files with the same name as the + * subrepo. (Eg, a submodule named "libgit2" could not contain a file + * named "libgit2", which would be unfortunate.) + */ +void test_repo_reservedname__submodule_pointer(void) +{ +#ifdef GIT_WIN32 + git_repository *super_repo, *sub_repo; + git_submodule *sub; + git_str *sub_reserved; + size_t sub_reserved_len; + + if (!cl_sandbox_supports_8dot3()) + clar__skip(); + + super_repo = setup_fixture_submod2(); + + assert_submodule_exists(super_repo, "sm_unchanged"); + + cl_git_pass(git_submodule_lookup(&sub, super_repo, "sm_unchanged")); + cl_git_pass(git_submodule_open(&sub_repo, sub)); + + cl_assert(git_repository__reserved_names(&sub_reserved, &sub_reserved_len, sub_repo, true)); + + cl_assert_equal_i(2, sub_reserved_len); + cl_assert_equal_s(".git", sub_reserved[0].ptr); + cl_assert_equal_s("GIT~1", sub_reserved[1].ptr); + + git_submodule_free(sub); + git_repository_free(sub_repo); +#endif +} + +/* Like the `submodule_pointer` test (above), this ensures that we do not + * follow the gitlink to the submodule's repository location and treat that + * as a reserved name. This tests at an initial submodule update, where the + * submodule repo is being created. + */ +void test_repo_reservedname__submodule_pointer_during_create(void) +{ + git_repository *repo; + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + git_str url = GIT_STR_INIT; + + repo = setup_fixture_super(); + + cl_git_pass(git_str_joinpath(&url, clar_sandbox_path(), "sub.git")); + cl_repo_set_string(repo, "submodule.sub.url", url.ptr); + + cl_git_pass(git_submodule_lookup(&sm, repo, "sub")); + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + git_submodule_free(sm); + git_str_dispose(&url); +} diff --git a/tests/libgit2/repo/setters.c b/tests/libgit2/repo/setters.c new file mode 100644 index 000000000..9a965dec6 --- /dev/null +++ b/tests/libgit2/repo/setters.c @@ -0,0 +1,108 @@ +#include "clar_libgit2.h" +#include "git2/sys/repository.h" + +#include "posix.h" +#include "util.h" +#include "path.h" +#include "futils.h" + +static git_repository *repo; + +void test_repo_setters__initialize(void) +{ + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&repo, "testrepo.git")); + cl_must_pass(p_mkdir("new_workdir", 0777)); +} + +void test_repo_setters__cleanup(void) +{ + git_repository_free(repo); + repo = NULL; + + cl_fixture_cleanup("testrepo.git"); + cl_fixture_cleanup("new_workdir"); +} + +void test_repo_setters__setting_a_workdir_turns_a_bare_repository_into_a_standard_one(void) +{ + cl_assert(git_repository_is_bare(repo) == 1); + + cl_assert(git_repository_workdir(repo) == NULL); + cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false)); + + cl_assert(git_repository_workdir(repo) != NULL); + cl_assert(git_repository_is_bare(repo) == 0); +} + +void test_repo_setters__setting_a_workdir_prettifies_its_path(void) +{ + cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false)); + + cl_assert(git__suffixcmp(git_repository_workdir(repo), "new_workdir/") == 0); +} + +void test_repo_setters__setting_a_workdir_creates_a_gitlink(void) +{ + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + git_str content = GIT_STR_INIT; + + cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", true)); + + cl_assert(git_fs_path_isfile("./new_workdir/.git")); + + cl_git_pass(git_futils_readbuffer(&content, "./new_workdir/.git")); + cl_assert(git__prefixcmp(git_str_cstr(&content), "gitdir: ") == 0); + cl_assert(git__suffixcmp(git_str_cstr(&content), "testrepo.git/") == 0); + git_str_dispose(&content); + + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.worktree")); + cl_assert(git__suffixcmp(buf.ptr, "new_workdir/") == 0); + + git_buf_dispose(&buf); + git_config_free(cfg); +} + +void test_repo_setters__setting_a_new_index_on_a_repo_which_has_already_loaded_one_properly_honors_the_refcount(void) +{ + git_index *new_index; + + cl_git_pass(git_index_open(&new_index, "./my-index")); + cl_assert(((git_refcount *)new_index)->refcount.val == 1); + + git_repository_set_index(repo, new_index); + cl_assert(((git_refcount *)new_index)->refcount.val == 2); + + git_repository_free(repo); + cl_assert(((git_refcount *)new_index)->refcount.val == 1); + + git_index_free(new_index); + + /* + * Ensure the cleanup method won't try to free the repo as it's already been taken care of + */ + repo = NULL; +} + +void test_repo_setters__setting_a_new_odb_on_a_repo_which_already_loaded_one_properly_honors_the_refcount(void) +{ + git_odb *new_odb; + + cl_git_pass(git_odb_open(&new_odb, "./testrepo.git/objects")); + cl_assert(((git_refcount *)new_odb)->refcount.val == 1); + + git_repository_set_odb(repo, new_odb); + cl_assert(((git_refcount *)new_odb)->refcount.val == 2); + + git_repository_free(repo); + cl_assert(((git_refcount *)new_odb)->refcount.val == 1); + + git_odb_free(new_odb); + + /* + * Ensure the cleanup method won't try to free the repo as it's already been taken care of + */ + repo = NULL; +} diff --git a/tests/libgit2/repo/shallow.c b/tests/libgit2/repo/shallow.c new file mode 100644 index 000000000..adb7a9e44 --- /dev/null +++ b/tests/libgit2/repo/shallow.c @@ -0,0 +1,39 @@ +#include "clar_libgit2.h" +#include "futils.h" + +static git_repository *g_repo; + +void test_repo_shallow__initialize(void) +{ +} + +void test_repo_shallow__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_repo_shallow__no_shallow_file(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); +} + +void test_repo_shallow__empty_shallow_file(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_mkfile("testrepo.git/shallow", ""); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); +} + +void test_repo_shallow__shallow_repo(void) +{ + g_repo = cl_git_sandbox_init("shallow.git"); + cl_assert_equal_i(1, git_repository_is_shallow(g_repo)); +} + +void test_repo_shallow__clears_errors(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); + cl_assert_equal_p(NULL, git_error_last()); +} diff --git a/tests/libgit2/repo/state.c b/tests/libgit2/repo/state.c new file mode 100644 index 000000000..92b272dce --- /dev/null +++ b/tests/libgit2/repo/state.c @@ -0,0 +1,131 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "posix.h" +#include "futils.h" + +static git_repository *_repo; +static git_str _path; + +void test_repo_state__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_repo_state__cleanup(void) +{ + cl_git_sandbox_cleanup(); + git_str_dispose(&_path); +} + +static void setup_simple_state(const char *filename) +{ + cl_git_pass(git_str_joinpath(&_path, git_repository_path(_repo), filename)); + git_futils_mkpath2file(git_str_cstr(&_path), 0777); + cl_git_mkfile(git_str_cstr(&_path), "dummy"); +} + +static void assert_repo_state(git_repository_state_t state) +{ + cl_assert_equal_i(state, git_repository_state(_repo)); +} + +void test_repo_state__none_with_HEAD_attached(void) +{ + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__none_with_HEAD_detached(void) +{ + cl_git_pass(git_repository_detach_head(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__merge(void) +{ + setup_simple_state(GIT_MERGE_HEAD_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__revert(void) +{ + setup_simple_state(GIT_REVERT_HEAD_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_REVERT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__revert_sequence(void) +{ + setup_simple_state(GIT_REVERT_HEAD_FILE); + setup_simple_state(GIT_SEQUENCER_TODO_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_REVERT_SEQUENCE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__cherry_pick(void) +{ + setup_simple_state(GIT_CHERRYPICK_HEAD_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__cherrypick_sequence(void) +{ + setup_simple_state(GIT_CHERRYPICK_HEAD_FILE); + setup_simple_state(GIT_SEQUENCER_TODO_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__bisect(void) +{ + setup_simple_state(GIT_BISECT_LOG_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_BISECT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__rebase_interactive(void) +{ + setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__rebase_merge(void) +{ + setup_simple_state(GIT_REBASE_MERGE_DIR "whatever"); + assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__rebase(void) +{ + setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__apply_mailbox(void) +{ + setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + +void test_repo_state__apply_mailbox_or_rebase(void) +{ + setup_simple_state(GIT_REBASE_APPLY_DIR "whatever"); + assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} diff --git a/tests/libgit2/repo/template.c b/tests/libgit2/repo/template.c new file mode 100644 index 000000000..e8fe266cf --- /dev/null +++ b/tests/libgit2/repo/template.c @@ -0,0 +1,305 @@ +#include "clar_libgit2.h" + +#include "futils.h" +#include "repo/repo_helpers.h" + +#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177) + +static git_repository *_repo = NULL; +static mode_t g_umask = 0; +static git_str _global_path = GIT_STR_INIT; + +static const char *fixture_repo; +static const char *fixture_templates; + +void test_repo_template__initialize(void) +{ + _repo = NULL; + + /* load umask if not already loaded */ + if (!g_umask) { + g_umask = p_umask(022); + (void)p_umask(g_umask); + } +} + +void test_repo_template__cleanup(void) +{ + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, + _global_path.ptr); + git_str_dispose(&_global_path); + + cl_fixture_cleanup("tmp_global_path"); + + if (fixture_repo) { + cl_fixture_cleanup(fixture_repo); + fixture_repo = NULL; + } + + if (fixture_templates) { + cl_fixture_cleanup(fixture_templates); + fixture_templates = NULL; + } + + git_repository_free(_repo); + _repo = NULL; +} + +static void assert_hooks_match( + const char *template_dir, + const char *repo_dir, + const char *hook_path, + bool core_filemode) +{ + git_str expected = GIT_STR_INIT; + git_str actual = GIT_STR_INIT; + struct stat expected_st, st; + + cl_git_pass(git_str_joinpath(&expected, template_dir, hook_path)); + cl_git_pass(git_fs_path_lstat(expected.ptr, &expected_st)); + + cl_git_pass(git_str_joinpath(&actual, repo_dir, hook_path)); + cl_git_pass(git_fs_path_lstat(actual.ptr, &st)); + + cl_assert(expected_st.st_size == st.st_size); + + if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) { + mode_t expected_mode = + GIT_MODE_TYPE(expected_st.st_mode) | + (GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask); + + if (!core_filemode) { + CLEAR_FOR_CORE_FILEMODE(expected_mode); + CLEAR_FOR_CORE_FILEMODE(st.st_mode); + } + + cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o"); + } + + git_str_dispose(&expected); + git_str_dispose(&actual); +} + +static void assert_mode_seems_okay( + const char *base, const char *path, + git_filemode_t expect_mode, bool expect_setgid, bool core_filemode) +{ + git_str full = GIT_STR_INIT; + struct stat st; + + cl_git_pass(git_str_joinpath(&full, base, path)); + cl_git_pass(git_fs_path_lstat(full.ptr, &st)); + git_str_dispose(&full); + + if (!core_filemode) { + CLEAR_FOR_CORE_FILEMODE(expect_mode); + CLEAR_FOR_CORE_FILEMODE(st.st_mode); + expect_setgid = false; + } + + if (S_ISGID != 0) + cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0); + + cl_assert_equal_b( + GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode)); + + cl_assert_equal_i_fmt( + GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o"); +} + +static void setup_repo(const char *name, git_repository_init_options *opts) +{ + cl_git_pass(git_repository_init_ext(&_repo, name, opts)); + fixture_repo = name; +} + +static void setup_templates(const char *name, bool setup_globally) +{ + git_str path = GIT_STR_INIT; + + cl_fixture_sandbox("template"); + if (strcmp(name, "template")) + cl_must_pass(p_rename("template", name)); + + fixture_templates = name; + + /* + * Create a symlink from link.sample to update.sample if the filesystem + * supports it. + */ + cl_git_pass(git_str_join3(&path, '/', name, "hooks", "link.sample")); +#ifdef GIT_WIN32 + cl_git_mkfile(path.ptr, "#!/bin/sh\necho hello, world\n"); +#else + cl_must_pass(p_symlink("update.sample", path.ptr)); +#endif + + git_str_clear(&path); + + /* Create a file starting with a dot */ + cl_git_pass(git_str_join3(&path, '/', name, "hooks", ".dotfile")); + cl_git_mkfile(path.ptr, "something\n"); + + git_str_clear(&path); + + if (setup_globally) { + cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), name)); + create_tmp_global_config("tmp_global_path", "init.templatedir", path.ptr); + } + + git_str_dispose(&path); +} + +static void validate_templates(git_repository *repo, const char *template_path) +{ + git_str path = GIT_STR_INIT, expected = GIT_STR_INIT, actual = GIT_STR_INIT; + int filemode; + + cl_git_pass(git_str_joinpath(&path, template_path, "description")); + cl_git_pass(git_futils_readbuffer(&expected, path.ptr)); + + git_str_clear(&path); + + cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "description")); + cl_git_pass(git_futils_readbuffer(&actual, path.ptr)); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + filemode = cl_repo_get_bool(repo, "core.filemode"); + + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/update.sample", filemode); + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/link.sample", filemode); + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/.dotfile", filemode); + + git_str_dispose(&expected); + git_str_dispose(&actual); + git_str_dispose(&path); +} + +void test_repo_template__external_templates_specified_in_options(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + opts.template_path = "template"; + + setup_templates("template", false); + setup_repo("templated.git", &opts); + + validate_templates(_repo, "template"); +} + +void test_repo_template__external_templates_specified_in_config(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + + setup_templates("template", true); + setup_repo("templated.git", &opts); + + validate_templates(_repo, "template"); +} + +void test_repo_template__external_templates_with_leading_dot(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + + setup_templates(".template_with_leading_dot", true); + setup_repo("templated.git", &opts); + + validate_templates(_repo, ".template_with_leading_dot"); +} + +void test_repo_template__extended_with_template_and_shared_mode(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + const char *repo_path; + int filemode; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + opts.template_path = "template"; + opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP; + + setup_templates("template", false); + setup_repo("init_shared_from_tpl", &opts); + + filemode = cl_repo_get_bool(_repo, "core.filemode"); + + repo_path = git_repository_path(_repo); + assert_mode_seems_okay(repo_path, "hooks", + GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode); + assert_mode_seems_okay(repo_path, "info", + GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode); + assert_mode_seems_okay(repo_path, "description", + GIT_FILEMODE_BLOB, false, filemode); + + validate_templates(_repo, "template"); +} + +void test_repo_template__templated_head_is_used(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_str head = GIT_STR_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + + setup_templates("template", true); + cl_git_mkfile("template/HEAD", "foobar\n"); + setup_repo("repo", &opts); + + cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD")); + cl_assert_equal_s("foobar\n", head.ptr); + + git_str_dispose(&head); +} + +void test_repo_template__initial_head_option_overrides_template_head(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_str head = GIT_STR_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + opts.initial_head = "manual"; + + setup_templates("template", true); + cl_git_mkfile("template/HEAD", "foobar\n"); + setup_repo("repo", &opts); + + cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD")); + cl_assert_equal_s("ref: refs/heads/manual\n", head.ptr); + + git_str_dispose(&head); +} + +void test_repo_template__empty_template_path(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + opts.template_path = ""; + + setup_repo("foo", &opts); +} + +void test_repo_template__nonexistent_template_path(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + opts.template_path = "/tmp/path/that/does/not/exist/for/libgit2/test"; + + setup_repo("bar", &opts); +} |
