Commit 475c6eba by Edward Thomson

win32: improve impl & tests for system path / g4w interop

We look for a Git for Windows installation to use its git config,
so that clients built on libgit2 can interoperate with the Git for
Windows CLI (and clients that are built on top of _it_).

Look for `git` both in the `PATH` and in the registry.  Use the _first_
git install in the path, and the first git install in the registry.

Look in both the `etc` dir and the architecture-specific `etc` dirs
(`mingw64/etc` and `mingw32/etc`) beneath the installation root.

Prefer the git in the `PATH` to the git location in the registry so that
users can override that.

Include more tests for this behavior.
parent 925abee9
...@@ -31,7 +31,7 @@ static int git_sysdir_guess_programdata_dirs(git_str *out) ...@@ -31,7 +31,7 @@ static int git_sysdir_guess_programdata_dirs(git_str *out)
static int git_sysdir_guess_system_dirs(git_str *out) static int git_sysdir_guess_system_dirs(git_str *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
return git_win32__find_system_dirs(out, L"etc\\"); return git_win32__find_system_dirs(out, "etc");
#else #else
return git_str_sets(out, "/etc"); return git_str_sets(out, "/etc");
#endif #endif
...@@ -154,7 +154,7 @@ static int git_sysdir_guess_xdg_dirs(git_str *out) ...@@ -154,7 +154,7 @@ static int git_sysdir_guess_xdg_dirs(git_str *out)
static int git_sysdir_guess_template_dirs(git_str *out) static int git_sysdir_guess_template_dirs(git_str *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); return git_win32__find_system_dirs(out, "share/git-core/templates");
#else #else
return git_str_sets(out, "/usr/share/git-core/templates"); return git_str_sets(out, "/usr/share/git-core/templates");
#endif #endif
...@@ -190,22 +190,22 @@ int git_sysdir_global_init(void) ...@@ -190,22 +190,22 @@ int git_sysdir_global_init(void)
error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
if (error) if (error)
return error; return error;
return git_runtime_shutdown_register(git_sysdir_global_shutdown); return git_runtime_shutdown_register(git_sysdir_global_shutdown);
} }
int git_sysdir_reset(void) int git_sysdir_reset(void)
{ {
size_t i; size_t i;
int error = 0; int error = 0;
for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) { for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) {
git_str_dispose(&git_sysdir__dirs[i].buf); git_str_dispose(&git_sysdir__dirs[i].buf);
error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
} }
return error; return error;
} }
static int git_sysdir_check_selector(git_sysdir_t which) static int git_sysdir_check_selector(git_sysdir_t which)
......
...@@ -10,8 +10,10 @@ ...@@ -10,8 +10,10 @@
#include "common.h" #include "common.h"
extern int git_win32__find_system_dir_in_path(git_str* out, const wchar_t* subdir); /** Sets the mock registry root for Git for Windows for testing. */
extern int git_win32__find_system_dirs(git_str *out, const wchar_t *subpath); extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
extern int git_win32__find_system_dirs(git_str *out, const char *subpath);
extern int git_win32__find_global_dirs(git_str *out); extern int git_win32__find_global_dirs(git_str *out);
extern int git_win32__find_xdg_dirs(git_str *out); extern int git_win32__find_xdg_dirs(git_str *out);
extern int git_win32__find_programdata_dirs(git_str *out); extern int git_win32__find_programdata_dirs(git_str *out);
......
#include "clar_libgit2.h"
#include "futils.h"
#include "sysdir.h"
#include "win32/findfile.h"
#ifdef GIT_WIN32
static char *path_save;
static git_str gfw_path_root = GIT_STR_INIT;
static git_str gfw_registry_root = GIT_STR_INIT;
#endif
void test_win32_systemdir__initialize(void)
{
#ifdef GIT_WIN32
git_str path_env = GIT_STR_INIT;
path_save = cl_getenv("PATH");
git_win32__set_registry_system_dir(L"");
cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";C:\\fakefakedoesnotexist"));
cl_setenv("PATH", path_env.ptr);
cl_git_pass(git_str_puts(&gfw_path_root, clar_sandbox_path()));
cl_git_pass(git_str_puts(&gfw_path_root, "/fake_gfw_path_install"));
cl_git_pass(git_str_puts(&gfw_registry_root, clar_sandbox_path()));
cl_git_pass(git_str_puts(&gfw_registry_root, "/fake_gfw_registry_install"));
git_str_dispose(&path_env);
#endif
}
void test_win32_systemdir__cleanup(void)
{
#ifdef GIT_WIN32
cl_fixture_cleanup("fake_gfw_path_install");
cl_fixture_cleanup("fake_gfw_registry_install");
git_str_dispose(&gfw_path_root);
git_str_dispose(&gfw_registry_root);
cl_setenv("PATH", path_save);
git__free(path_save);
path_save = NULL;
git_win32__set_registry_system_dir(NULL);
cl_sandbox_set_search_path_defaults();
#endif
}
#ifdef GIT_WIN32
static void fix_path(git_str *s)
{
char *c;
for (c = s->ptr; *c; c++) {
if (*c == '/')
*c = '\\';
}
}
static void populate_fake_gfw(
git_str *expected_etc_dir,
const char *root,
const char *token,
bool create_gitconfig,
bool create_mingw64_gitconfig,
bool add_to_path,
bool add_to_registry)
{
git_str bin_path = GIT_STR_INIT, exe_path = GIT_STR_INIT,
etc_path = GIT_STR_INIT, mingw64_path = GIT_STR_INIT,
config_path = GIT_STR_INIT, path_env = GIT_STR_INIT,
config_data = GIT_STR_INIT;
cl_git_pass(git_str_puts(&bin_path, root));
cl_git_pass(git_str_puts(&bin_path, "/cmd"));
cl_git_pass(git_futils_mkdir_r(bin_path.ptr, 0755));
cl_git_pass(git_str_puts(&exe_path, bin_path.ptr));
cl_git_pass(git_str_puts(&exe_path, "/git.cmd"));
cl_git_mkfile(exe_path.ptr, "This is a fake executable.");
cl_git_pass(git_str_puts(&etc_path, root));
cl_git_pass(git_str_puts(&etc_path, "/etc"));
cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
cl_git_pass(git_str_puts(&mingw64_path, root));
cl_git_pass(git_str_puts(&mingw64_path, "/mingw64/etc"));
cl_git_pass(git_futils_mkdir_r(mingw64_path.ptr, 0755));
if (create_gitconfig) {
git_str_clear(&config_data);
git_str_printf(&config_data, "[gfw]\n\ttest = etc %s\n", token);
cl_git_pass(git_str_puts(&config_path, etc_path.ptr));
cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
cl_git_mkfile(config_path.ptr, config_data.ptr);
}
if (create_mingw64_gitconfig) {
git_str_clear(&config_data);
git_str_printf(&config_data, "[gfw]\n\ttest = mingw64 %s\n", token);
git_str_clear(&config_path);
cl_git_pass(git_str_puts(&config_path, mingw64_path.ptr));
cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
cl_git_mkfile(config_path.ptr, config_data.ptr);
}
if (add_to_path) {
fix_path(&bin_path);
cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";"));
cl_git_pass(git_str_puts(&path_env, bin_path.ptr));
cl_git_pass(git_str_puts(&path_env, ";C:\\fakefakedoesnotexist"));
cl_setenv("PATH", path_env.ptr);
}
if (add_to_registry) {
git_win32_path registry_path;
size_t offset = 0;
cl_assert(git_win32_path_from_utf8(registry_path, root) >= 0);
if (wcsncmp(registry_path, L"\\\\?\\", CONST_STRLEN("\\\\?\\")) == 0)
offset = CONST_STRLEN("\\\\?\\");
git_win32__set_registry_system_dir(registry_path + offset);
}
cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, etc_path.ptr));
cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, mingw64_path.ptr));
git_str_dispose(&bin_path);
git_str_dispose(&exe_path);
git_str_dispose(&etc_path);
git_str_dispose(&mingw64_path);
git_str_dispose(&config_path);
git_str_dispose(&path_env);
git_str_dispose(&config_data);
}
static void populate_fake_ecosystem(
git_str *expected_etc_dir,
bool create_gitconfig,
bool create_mingw64_gitconfig,
bool path,
bool registry)
{
if (path)
populate_fake_gfw(expected_etc_dir, gfw_path_root.ptr, "path", create_gitconfig, create_mingw64_gitconfig, true, false);
if (registry)
populate_fake_gfw(expected_etc_dir, gfw_registry_root.ptr, "registry", create_gitconfig, create_mingw64_gitconfig, false, true);
}
#endif
void test_win32_systemdir__finds_etc_in_path(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config *cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, true, false, true, false);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("etc path", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__finds_mingw64_etc_in_path(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, false, true, true, false);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("mingw64 path", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__prefers_etc_to_mingw64_in_path(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, true, true, true, false);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("etc path", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__finds_etc_in_registry(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, true, false, false, true);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("etc registry", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__finds_mingw64_etc_in_registry(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, false, true, false, true);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("mingw64 registry", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__prefers_etc_to_mingw64_in_registry(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, true, true, false, true);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("etc registry", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
void test_win32_systemdir__prefers_path_to_registry(void)
{
#ifdef GIT_WIN32
git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
git_config* cfg;
git_buf value = GIT_BUF_INIT;
populate_fake_ecosystem(&expected, true, true, true, true);
cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
cl_assert_equal_s(out.ptr, expected.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
cl_assert_equal_s("etc path", value.ptr);
git_buf_dispose(&value);
git_str_dispose(&expected);
git_str_dispose(&out);
git_config_free(cfg);
#endif
}
#include "clar_libgit2.h"
#include "futils.h"
#include "sysdir.h"
#include "win32/findfile.h"
static char *path_save;
static git_str gfw_root = GIT_STR_INIT;
void test_win32_systempath__initialize(void)
{
path_save = cl_getenv("PATH");
cl_git_pass(git_str_puts(&gfw_root, clar_sandbox_path()));
cl_git_pass(git_str_puts(&gfw_root, "/fake_gfw_install"));
}
void test_win32_systempath__cleanup(void)
{
cl_fixture_cleanup("fake_gfw_install");
git_str_dispose(&gfw_root);
cl_setenv("PATH", path_save);
git__free(path_save);
path_save = NULL;
git_sysdir_reset();
}
static void fix_path(git_str *s)
{
char *c;
for (c = s->ptr; *c; c++) {
if (*c == '/')
*c = '\\';
}
}
void test_win32_systempath__etc_gitconfig(void)
{
git_str bin_path = GIT_STR_INIT, exe_path = GIT_STR_INIT,
etc_path = GIT_STR_INIT, config_path = GIT_STR_INIT,
path_env = GIT_STR_INIT, out = GIT_STR_INIT;
git_config *cfg;
int value;
cl_git_pass(git_str_puts(&bin_path, gfw_root.ptr));
cl_git_pass(git_str_puts(&bin_path, "/cmd"));
cl_git_pass(git_futils_mkdir_r(bin_path.ptr, 0755));
cl_git_pass(git_str_puts(&exe_path, bin_path.ptr));
cl_git_pass(git_str_puts(&exe_path, "/git.cmd"));
cl_git_mkfile(exe_path.ptr, "This is a fake executable.");
cl_git_pass(git_str_puts(&etc_path, gfw_root.ptr));
cl_git_pass(git_str_puts(&etc_path, "/etc"));
cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
git_str_clear(&etc_path);
cl_git_pass(git_str_puts(&etc_path, gfw_root.ptr));
cl_git_pass(git_str_puts(&etc_path, "/etc"));
cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
cl_git_pass(git_str_puts(&config_path, etc_path.ptr));
cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
cl_git_mkfile(config_path.ptr, "[gfw]\n\ttest = 1337\n");
fix_path(&bin_path);
cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";"));
cl_git_pass(git_str_puts(&path_env, bin_path.ptr));
cl_git_pass(git_str_puts(&path_env, ";C:\\fakefakedoesnotexist"));
cl_setenv("PATH", path_env.ptr);
cl_git_pass(git_win32__find_system_dir_in_path(&out, L"etc"));
cl_assert_equal_s(out.ptr, etc_path.ptr);
git_sysdir_reset();
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_int32(&value, cfg, "gfw.test"));
cl_assert_equal_i(1337, value);
git_str_dispose(&exe_path);
git_str_dispose(&etc_path);
git_str_dispose(&config_path);
git_str_dispose(&path_env);
git_str_dispose(&out);
git_config_free(cfg);
}
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