]> git.proxmox.com Git - libgit2.git/blobdiff - tests/libgit2/repo/template.c
Merge https://salsa.debian.org/debian/libgit2 into proxmox/bullseye
[libgit2.git] / tests / libgit2 / repo / template.c
diff --git a/tests/libgit2/repo/template.c b/tests/libgit2/repo/template.c
new file mode 100644 (file)
index 0000000..e8fe266
--- /dev/null
@@ -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);
+}