Commit da500cc6 by Edward Thomson

symlink tests: test symbolic links on windows

Test updated symbolic link creation on Windows.  Ensure that we emulate
Git for Windows behavior.  Ensure that when `core.symlinks=true` is set
in a global configuration that new repositories are created without a
`core.symlinks` setting, and that when `core.symlinks` is unset that
`core.symlinks=false` in set in the repository.  Further ensure that
checkout honors the expected `core.symlinks` defaults on Windows.
parent 3f0caa15
......@@ -5,8 +5,10 @@
#include "fileops.h"
#include "repository.h"
#include "remote.h"
#include "repo/repo_helpers.h"
static git_repository *g_repo;
static git_buf g_global_path = GIT_BUF_INIT;
void test_checkout_index__initialize(void)
{
......@@ -22,21 +24,29 @@ void test_checkout_index__initialize(void)
cl_git_rewritefile(
"./testrepo/.gitattributes",
"* text eol=lf\n");
git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
&g_global_path);
}
void test_checkout_index__cleanup(void)
{
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
g_global_path.ptr);
git_buf_dispose(&g_global_path);
cl_git_sandbox_cleanup();
/* try to remove alternative dir */
if (git_path_isdir("alternative"))
git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
/* try to remove directories created by tests */
cl_fixture_cleanup("alternative");
cl_fixture_cleanup("symlink");
cl_fixture_cleanup("symlink.git");
cl_fixture_cleanup("tmp_global_path");
}
void test_checkout_index__cannot_checkout_a_bare_repository(void)
{
test_checkout_index__cleanup();
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("testrepo.git");
cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
......@@ -136,23 +146,20 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
#endif
}
void test_checkout_index__honor_coresymlinks_default(void)
static void populate_symlink_workdir(void)
{
git_repository *repo;
git_remote *origin;
git_object *target;
char cwd[GIT_PATH_MAX];
const char *url = git_repository_path(g_repo);
cl_assert(getcwd(cwd, sizeof(cwd)) != NULL);
cl_assert_equal_i(0, p_mkdir("readonly", 0555)); /* Read-only directory */
cl_assert_equal_i(0, chdir("readonly"));
cl_git_pass(git_repository_init(&repo, "../symlink.git", true));
cl_assert_equal_i(0, chdir(cwd));
cl_assert_equal_i(0, p_mkdir("symlink", 0777));
cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));
/* Delete the `origin` repo (if it exists) so we can recreate it. */
git_remote_delete(repo, GIT_REMOTE_ORIGIN);
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
git_remote_free(origin);
......@@ -161,23 +168,54 @@ void test_checkout_index__honor_coresymlinks_default(void)
cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
git_object_free(target);
git_repository_free(repo);
}
if (!filesystem_supports_symlinks("symlink/test")) {
check_file_contents("./symlink/link_to_new.txt", "new.txt");
} else {
char link_data[1024];
int link_size = 1024;
void test_checkout_index__honor_coresymlinks_default_true(void)
{
char link_data[GIT_PATH_MAX];
int link_size = GIT_PATH_MAX;
link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
cl_assert(link_size >= 0);
cl_must_pass(p_mkdir("symlink", 0777));
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./symlink/link_to_new.txt", "my new file\n");
}
if (!filesystem_supports_symlinks("symlink/test"))
cl_skip();
cl_fixture_cleanup("symlink");
#ifdef GIT_WIN32
/*
* Windows explicitly requires the global configuration to have
* core.symlinks=true in addition to actual filesystem support.
*/
create_tmp_global_config("tmp_global_path", "core.symlinks", "true");
#endif
populate_symlink_workdir();
link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
cl_assert(link_size >= 0);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./symlink/link_to_new.txt", "my new file\n");
}
void test_checkout_index__honor_coresymlinks_default_false(void)
{
cl_must_pass(p_mkdir("symlink", 0777));
#ifndef GIT_WIN32
/*
* This test is largely for Windows platforms to ensure that
* we respect an unset core.symlinks even when the platform
* supports symlinks. Bail entirely on POSIX platforms that
* do support symlinks.
*/
if (filesystem_supports_symlinks("symlink/test"))
cl_skip();
#endif
populate_symlink_workdir();
check_file_contents("./symlink/link_to_new.txt", "new.txt");
}
void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
......@@ -558,9 +596,9 @@ void test_checkout_index__can_update_prefixed_files(void)
void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
{
test_checkout_index__cleanup();
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
......@@ -570,8 +608,7 @@ void test_checkout_index__issue_1397(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
test_checkout_index__cleanup();
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
......@@ -624,8 +661,7 @@ void test_checkout_index__target_directory_from_bare(void)
checkout_counts cts;
memset(&cts, 0, sizeof(cts));
test_checkout_index__cleanup();
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert(git_repository_is_bare(g_repo));
......
......@@ -4,6 +4,7 @@
#include "config.h"
#include "path.h"
#include "config/config_helpers.h"
#include "repo/repo_helpers.h"
enum repo_mode {
STANDARD_REPOSITORY = 0,
......@@ -12,7 +13,6 @@ enum repo_mode {
static git_repository *_repo = NULL;
static git_buf _global_path = GIT_BUF_INIT;
static git_buf _tmp_path = GIT_BUF_INIT;
static mode_t g_umask = 0;
void test_repo_init__initialize(void)
......@@ -35,9 +35,7 @@ void test_repo_init__cleanup(void)
_global_path.ptr);
git_buf_dispose(&_global_path);
if (_tmp_path.size > 0 && git_path_isdir(_tmp_path.ptr))
git_futils_rmdir_r(_tmp_path.ptr, NULL, GIT_RMDIR_REMOVE_FILES);
git_buf_dispose(&_tmp_path);
cl_fixture_cleanup("tmp_global_path");
}
static void cleanup_repository(void *path)
......@@ -48,19 +46,6 @@ static void cleanup_repository(void *path)
cl_fixture_cleanup((const char *)path);
}
static void configure_tmp_global_path(git_buf *out)
{
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH,
GIT_CONFIG_LEVEL_GLOBAL, &_tmp_path));
cl_git_pass(git_buf_puts(&_tmp_path, ".tmp"));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH,
GIT_CONFIG_LEVEL_GLOBAL, _tmp_path.ptr));
cl_must_pass(p_mkdir(_tmp_path.ptr, 0777));
cl_git_pass(git_buf_joinpath(out, _tmp_path.ptr, ".gitconfig"));
}
static void ensure_repository_init(
const char *working_directory,
int is_bare,
......@@ -260,10 +245,66 @@ void test_repo_init__detect_ignorecase(void)
"core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
}
void test_repo_init__detect_symlinks(void)
/*
* 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 (!filesystem_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(&_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, _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);
#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", filesystem_supports_symlinks("link") ? GIT_ENOTFOUND : false);
#endif
}
void test_repo_init__detect_precompose_unicode_required(void)
......@@ -582,18 +623,7 @@ static const char *template_sandbox(const char *name)
static void configure_templatedir(const char *template_path)
{
git_buf config_path = GIT_BUF_INIT;
git_buf config_data = GIT_BUF_INIT;
configure_tmp_global_path(&config_path);
cl_git_pass(git_buf_printf(&config_data,
"[init]\n\ttemplatedir = \"%s\"\n", template_path));
cl_git_mkfile(config_path.ptr, config_data.ptr);
git_buf_dispose(&config_path);
git_buf_dispose(&config_data);
create_tmp_global_config("tmp_global_path", "init.templatedir", template_path);
}
static void validate_templates(git_repository *repo, const char *template_path)
......
......@@ -24,11 +24,29 @@ void delete_head(git_repository* repo)
int filesystem_supports_symlinks(const char *path)
{
struct stat st;
bool support = 0;
if (p_symlink("target", path) < 0 ||
p_lstat(path, &st) < 0 ||
!(S_ISLNK(st.st_mode)))
return 0;
if (p_symlink("target", path) == 0) {
if (p_lstat(path, &st) == 0 && S_ISLNK(st.st_mode))
support = 1;
return 1;
p_unlink(path);
}
return support;
}
void create_tmp_global_config(const char *dirname, const char *key, const char *val)
{
git_buf path = GIT_BUF_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_buf_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_buf_dispose(&path);
}
......@@ -5,3 +5,4 @@
extern void make_head_unborn(git_repository* repo, const char *target);
extern void delete_head(git_repository* repo);
extern int filesystem_supports_symlinks(const char *path);
extern void create_tmp_global_config(const char *path, const char *key, const char *val);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment