diff options
author | Vicent Marti <vicent@github.com> | 2014-04-23 07:13:49 -0700 |
---|---|---|
committer | Vicent Marti <vicent@github.com> | 2014-04-23 07:13:49 -0700 |
commit | 5ca410b9a9bc30a65ed0125646b3702d5943100b (patch) | |
tree | 7c352c4382def6eaee007745f3f932659b10d827 /tests | |
parent | 5b58d6f78cf8346445374eaf5cde0f54be5c5796 (diff) | |
parent | 7110000dd5b82c86863633ee37f72ac876a44476 (diff) | |
download | libgit2-5ca410b9a9bc30a65ed0125646b3702d5943100b.tar.gz |
Merge pull request #2283 from phkelley/win32_fs
Win32: UTF-8 <-> WCHAR conversion overhaul
Diffstat (limited to 'tests')
-rw-r--r-- | tests/clar_libgit2.c | 53 | ||||
-rw-r--r-- | tests/clar_libgit2.h | 11 | ||||
-rw-r--r-- | tests/core/env.c | 2 | ||||
-rw-r--r-- | tests/core/link.c | 602 |
4 files changed, 637 insertions, 31 deletions
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 90e53c5e6..6f6143dad 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -59,47 +59,40 @@ void cl_git_rewritefile(const char *path, const char *content) char *cl_getenv(const char *name) { - git_win32_path name_utf16; - DWORD alloc_len; - wchar_t *value_utf16; - char *value_utf8; + wchar_t *wide_name, *wide_value; + char *utf8_value = NULL; + DWORD value_len; - git_win32_path_from_c(name_utf16, name); - alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0); - if (alloc_len <= 0) - return NULL; + cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0); - cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t))); + value_len = GetEnvironmentVariableW(wide_name, NULL, 0); - GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - - alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */ - cl_assert(value_utf8 = git__calloc(alloc_len, 1)); - - git__utf16_to_8(value_utf8, alloc_len, value_utf16); - - git__free(value_utf16); + if (value_len) { + cl_assert(wide_value = git__malloc(value_len * sizeof(wchar_t))); + cl_assert(GetEnvironmentVariableW(wide_name, wide_value, value_len)); + cl_assert(git__utf16_to_8_alloc(&utf8_value, wide_value) >= 0); + git__free(wide_value); + } - return value_utf8; + git__free(wide_name); + return utf8_value; } int cl_setenv(const char *name, const char *value) { - git_win32_path name_utf16; - git_win32_path value_utf16; + wchar_t *wide_name, *wide_value; - git_win32_path_from_c(name_utf16, name); + cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0); if (value) { - git_win32_path_from_c(value_utf16, value); - cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16)); + cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0); + cl_assert(SetEnvironmentVariableW(wide_name, wide_value)); } else { /* Windows XP returns 0 (failed) when passing NULL for lpValue when - * lpName does not exist in the environment block. This behavior - * seems to have changed in later versions. Don't check return value - * of SetEnvironmentVariable when passing NULL for lpValue. - */ - SetEnvironmentVariableW(name_utf16, NULL); + * lpName does not exist in the environment block. This behavior + * seems to have changed in later versions. Don't check the return value + * of SetEnvironmentVariable when passing NULL for lpValue. */ + SetEnvironmentVariableW(wide_name, NULL); } return 0; @@ -115,8 +108,8 @@ int cl_rename(const char *source, const char *dest) git_win32_path dest_utf16; unsigned retries = 1; - git_win32_path_from_c(source_utf16, source); - git_win32_path_from_c(dest_utf16, dest); + cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0); + cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0); while (!MoveFileW(source_utf16, dest_utf16)) { /* Only retry if the error is ERROR_ACCESS_DENIED; diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h index d395bd66f..082fa9f4a 100644 --- a/tests/clar_libgit2.h +++ b/tests/clar_libgit2.h @@ -29,6 +29,17 @@ #define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr) +/** + * Like cl_git_pass, only for Win32 error code conventions + */ +#define cl_win32_pass(expr) do { \ + int _win32_res; \ + if ((_win32_res = (expr)) == 0) { \ + giterr_set(GITERR_OS, "Returned: %d, system error code: %d", _win32_res, GetLastError()); \ + cl_git_report_failure(_win32_res, __FILE__, __LINE__, "System call failed: " #expr); \ + } \ + } while(0) + void cl_git_report_failure(int, const char *, int, const char *); #define cl_assert_at_line(expr,file,line) \ diff --git a/tests/core/env.c b/tests/core/env.c index b01ad1c24..df1d92a02 100644 --- a/tests/core/env.c +++ b/tests/core/env.c @@ -21,7 +21,7 @@ static char *home_values[] = { "f\xc4\x80ke_\xc4\xa4ome", /* latin extended */ "f\xce\xb1\xce\xba\xce\xb5_h\xce\xbfm\xce\xad", /* having fun with greek */ "fa\xe0" "\xb8" "\x87" "e_\xe0" "\xb8" "\x99" "ome", /* thai characters */ - "f\xe1\x9cx80ke_\xe1\x9c\x91ome", /* tagalog characters */ + "f\xe1\x9c\x80ke_\xe1\x9c\x91ome", /* tagalog characters */ "\xe1\xb8\x9f\xe1\xba\xa2" "ke_ho" "\xe1" "\xb9" "\x81" "e", /* latin extended additional */ "\xf0\x9f\x98\x98\xf0\x9f\x98\x82", /* emoticons */ NULL diff --git a/tests/core/link.c b/tests/core/link.c new file mode 100644 index 000000000..1794a3893 --- /dev/null +++ b/tests/core/link.c @@ -0,0 +1,602 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "buffer.h" +#include "path.h" + +#ifdef GIT_WIN32 +# include "win32/reparse.h" +#endif + +void test_core_link__cleanup(void) +{ +#ifdef GIT_WIN32 + RemoveDirectory("lstat_junction"); + RemoveDirectory("lstat_dangling"); + RemoveDirectory("lstat_dangling_dir"); + RemoveDirectory("lstat_dangling_junction"); + + RemoveDirectory("stat_junction"); + RemoveDirectory("stat_dangling"); + RemoveDirectory("stat_dangling_dir"); + RemoveDirectory("stat_dangling_junction"); +#endif +} + +#ifdef GIT_WIN32 +static bool is_administrator(void) +{ + static SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; + PSID admin_sid; + BOOL is_admin; + + cl_win32_pass(AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_sid)); + cl_win32_pass(CheckTokenMembership(NULL, admin_sid, &is_admin)); + FreeSid(admin_sid); + + return is_admin ? true : false; +} +#endif + +static void do_symlink(const char *old, const char *new, int is_dir) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(is_dir); + + cl_must_pass(symlink(old, new)); +#else + typedef DWORD (WINAPI *create_symlink_func)(LPCTSTR, LPCTSTR, DWORD); + HMODULE module; + create_symlink_func pCreateSymbolicLink; + + if (!is_administrator()) + clar__skip(); + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateSymbolicLink = (create_symlink_func)GetProcAddress(module, "CreateSymbolicLinkA")); + + cl_win32_pass(pCreateSymbolicLink(new, old, is_dir)); +#endif +} + +static void do_hardlink(const char *old, const char *new) +{ +#ifndef GIT_WIN32 + cl_must_pass(link(old, new)); +#else + typedef DWORD (WINAPI *create_hardlink_func)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES); + HMODULE module; + create_hardlink_func pCreateHardLink; + + if (!is_administrator()) + clar__skip(); + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateHardLink = (create_hardlink_func)GetProcAddress(module, "CreateHardLinkA")); + + cl_win32_pass(pCreateHardLink(new, old, 0)); +#endif +} + +#ifdef GIT_WIN32 + +static void do_junction(const char *old, const char *new) +{ + GIT_REPARSE_DATA_BUFFER *reparse_buf; + HANDLE handle; + git_buf unparsed_buf = GIT_BUF_INIT; + wchar_t *subst_utf16, *print_utf16; + DWORD ioctl_ret; + int subst_utf16_len, subst_byte_len, print_utf16_len, print_byte_len, ret; + USHORT reparse_buflen; + size_t i; + + /* Junction targets must be the unparsed name, starting with \??\, using + * backslashes instead of forward, and end in a trailing backslash. + * eg: \??\C:\Foo\ + */ + git_buf_puts(&unparsed_buf, "\\??\\"); + + for (i = 0; i < strlen(old); i++) + git_buf_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]); + + git_buf_putc(&unparsed_buf, '\\'); + + subst_utf16_len = git__utf8_to_16(NULL, 0, git_buf_cstr(&unparsed_buf)); + subst_byte_len = subst_utf16_len * sizeof(WCHAR); + + print_utf16_len = subst_utf16_len - 4; + print_byte_len = subst_byte_len - (4 * sizeof(WCHAR)); + + /* The junction must be an empty directory before the junction attribute + * can be added. + */ + cl_win32_pass(CreateDirectoryA(new, NULL)); + + handle = CreateFileA(new, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + cl_win32_pass(handle != INVALID_HANDLE_VALUE); + + reparse_buflen = (USHORT)(REPARSE_DATA_HEADER_SIZE + + REPARSE_DATA_MOUNTPOINT_HEADER_SIZE + + subst_byte_len + sizeof(WCHAR) + + print_byte_len + sizeof(WCHAR)); + + reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); + cl_assert(reparse_buf); + + subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer; + print_utf16 = subst_utf16 + subst_utf16_len + 1; + + ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1, + git_buf_cstr(&unparsed_buf)); + cl_assert_equal_i(subst_utf16_len, ret); + + ret = git__utf8_to_16(print_utf16, + print_utf16_len + 1, git_buf_cstr(&unparsed_buf) + 4); + cl_assert_equal_i(print_utf16_len, ret); + + reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0; + reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len; + reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR)); + reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len; + reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE; + + cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, + reparse_buf, reparse_buflen, NULL, 0, &ioctl_ret, NULL)); + + CloseHandle(handle); + LocalFree(reparse_buf); +} + +static void do_custom_reparse(const char *path) +{ + REPARSE_GUID_DATA_BUFFER *reparse_buf; + HANDLE handle; + DWORD ioctl_ret; + + const char *reparse_data = "Reparse points are silly."; + size_t reparse_buflen = REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + + strlen(reparse_data) + 1; + + reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); + cl_assert(reparse_buf); + + reparse_buf->ReparseTag = 42; + reparse_buf->ReparseDataLength = (WORD)(strlen(reparse_data) + 1); + + reparse_buf->ReparseGuid.Data1 = 0xdeadbeef; + reparse_buf->ReparseGuid.Data2 = 0xdead; + reparse_buf->ReparseGuid.Data3 = 0xbeef; + reparse_buf->ReparseGuid.Data4[0] = 42; + reparse_buf->ReparseGuid.Data4[1] = 42; + reparse_buf->ReparseGuid.Data4[2] = 42; + reparse_buf->ReparseGuid.Data4[3] = 42; + reparse_buf->ReparseGuid.Data4[4] = 42; + reparse_buf->ReparseGuid.Data4[5] = 42; + reparse_buf->ReparseGuid.Data4[6] = 42; + reparse_buf->ReparseGuid.Data4[7] = 42; + reparse_buf->ReparseGuid.Data4[8] = 42; + + memcpy(reparse_buf->GenericReparseBuffer.DataBuffer, + reparse_data, strlen(reparse_data) + 1); + + handle = CreateFileA(path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + cl_win32_pass(handle != INVALID_HANDLE_VALUE); + + cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, + reparse_buf, + reparse_buf->ReparseDataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, + NULL, 0, &ioctl_ret, NULL)); + + CloseHandle(handle); + LocalFree(reparse_buf); +} + +#endif + +git_buf *unslashify(git_buf *buf) +{ +#ifdef GIT_WIN32 + size_t i; + + for (i = 0; i < buf->size; i++) + if (buf->ptr[i] == '/') + buf->ptr[i] = '\\'; +#endif + + return buf; +} + +void test_core_link__stat_regular_file(void) +{ + struct stat st; + + cl_git_rewritefile("stat_regfile", "This is a regular file!\n"); + + cl_must_pass(p_stat("stat_regfile", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(24, st.st_size); +} + +void test_core_link__lstat_regular_file(void) +{ + struct stat st; + + cl_git_rewritefile("lstat_regfile", "This is a regular file!\n"); + + cl_must_pass(p_stat("lstat_regfile", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(24, st.st_size); +} + +void test_core_link__stat_symlink(void) +{ + struct stat st; + + cl_git_rewritefile("stat_target", "This is the target of a symbolic link.\n"); + do_symlink("stat_target", "stat_symlink", 0); + + cl_must_pass(p_stat("stat_target", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); + + cl_must_pass(p_stat("stat_symlink", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); +} + +void test_core_link__stat_symlink_directory(void) +{ + struct stat st; + + p_mkdir("stat_dirtarget", 0777); + do_symlink("stat_dirtarget", "stat_dirlink", 1); + + cl_must_pass(p_stat("stat_dirtarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_stat("stat_dirlink", &st)); + cl_assert(S_ISDIR(st.st_mode)); +} + +void test_core_link__stat_symlink_chain(void) +{ + struct stat st; + + cl_git_rewritefile("stat_final_target", "Final target of some symbolic links...\n"); + do_symlink("stat_final_target", "stat_chain_3", 0); + do_symlink("stat_chain_3", "stat_chain_2", 0); + do_symlink("stat_chain_2", "stat_chain_1", 0); + + cl_must_pass(p_stat("stat_chain_1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); +} + +void test_core_link__stat_dangling_symlink(void) +{ + struct stat st; + + do_symlink("stat_nonexistent", "stat_dangling", 0); + + cl_must_fail(p_stat("stat_nonexistent", &st)); + cl_must_fail(p_stat("stat_dangling", &st)); +} + +void test_core_link__stat_dangling_symlink_directory(void) +{ + struct stat st; + + do_symlink("stat_nonexistent", "stat_dangling_dir", 1); + + cl_must_fail(p_stat("stat_nonexistent_dir", &st)); + cl_must_fail(p_stat("stat_dangling", &st)); +} + +void test_core_link__lstat_symlink(void) +{ + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + /* Windows always writes the canonical path as the link target, so + * write the full path on all platforms. + */ + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_target"); + + cl_git_rewritefile("lstat_target", "This is the target of a symbolic link.\n"); + do_symlink(git_buf_cstr(&target_path), "lstat_symlink", 0); + + cl_must_pass(p_lstat("lstat_target", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); + + cl_must_pass(p_lstat("lstat_symlink", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +} + +void test_core_link__lstat_symlink_directory(void) +{ + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget"); + + p_mkdir("lstat_dirtarget", 0777); + do_symlink(git_buf_cstr(&target_path), "lstat_dirlink", 1); + + cl_must_pass(p_lstat("lstat_dirtarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_lstat("lstat_dirlink", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +} + +void test_core_link__lstat_dangling_symlink(void) +{ + struct stat st; + + do_symlink("lstat_nonexistent", "lstat_dangling", 0); + + cl_must_fail(p_lstat("lstat_nonexistent", &st)); + + cl_must_pass(p_lstat("lstat_dangling", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); +} + +void test_core_link__lstat_dangling_symlink_directory(void) +{ + struct stat st; + + do_symlink("lstat_nonexistent", "lstat_dangling_dir", 1); + + cl_must_fail(p_lstat("lstat_nonexistent", &st)); + + cl_must_pass(p_lstat("lstat_dangling_dir", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); +} + +void test_core_link__stat_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget"); + + p_mkdir("stat_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "stat_junction"); + + cl_must_pass(p_stat("stat_junctarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_stat("stat_junction", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__stat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget"); + + p_mkdir("stat_nonexistent_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "stat_dangling_junction"); + + RemoveDirectory("stat_nonexistent_junctarget"); + + cl_must_fail(p_stat("stat_nonexistent_junctarget", &st)); + cl_must_fail(p_stat("stat_dangling_junction", &st)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__lstat_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget"); + + p_mkdir("lstat_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "lstat_junction"); + + cl_must_pass(p_lstat("lstat_junctarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_lstat("lstat_junction", &st)); + cl_assert(S_ISLNK(st.st_mode)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__lstat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget"); + + p_mkdir("lstat_nonexistent_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "lstat_dangling_junction"); + + RemoveDirectory("lstat_nonexistent_junctarget"); + + cl_must_fail(p_lstat("lstat_nonexistent_junctarget", &st)); + + cl_must_pass(p_lstat("lstat_dangling_junction", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__stat_hardlink(void) +{ + struct stat st; + + cl_git_rewritefile("stat_hardlink1", "This file has many names!\n"); + do_hardlink("stat_hardlink1", "stat_hardlink2"); + + cl_must_pass(p_stat("stat_hardlink1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); + + cl_must_pass(p_stat("stat_hardlink2", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); +} + +void test_core_link__lstat_hardlink(void) +{ + struct stat st; + + cl_git_rewritefile("lstat_hardlink1", "This file has many names!\n"); + do_hardlink("lstat_hardlink1", "lstat_hardlink2"); + + cl_must_pass(p_lstat("lstat_hardlink1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); + + cl_must_pass(p_lstat("lstat_hardlink2", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); +} + +void test_core_link__stat_reparse_point(void) +{ +#ifdef GIT_WIN32 + struct stat st; + + /* Generic reparse points should be treated as regular files, only + * symlinks and junctions should be treated as links. + */ + + cl_git_rewritefile("stat_reparse", "This is a reparse point!\n"); + do_custom_reparse("stat_reparse"); + + cl_must_pass(p_lstat("stat_reparse", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(25, st.st_size); +#endif +} + +void test_core_link__lstat_reparse_point(void) +{ +#ifdef GIT_WIN32 + struct stat st; + + cl_git_rewritefile("lstat_reparse", "This is a reparse point!\n"); + do_custom_reparse("lstat_reparse"); + + cl_must_pass(p_lstat("lstat_reparse", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(25, st.st_size); +#endif +} + +void test_core_link__readlink_nonexistent_file(void) +{ + char buf[2048]; + + cl_must_fail(p_readlink("readlink_nonexistent", buf, 2048)); + cl_assert_equal_i(ENOENT, errno); +} + +void test_core_link__readlink_normal_file(void) +{ + char buf[2048]; + + cl_git_rewritefile("readlink_regfile", "This is a regular file!\n"); + cl_must_fail(p_readlink("readlink_regfile", buf, 2048)); + cl_assert_equal_i(EINVAL, errno); +} + +void test_core_link__readlink_symlink(void) +{ + git_buf target_path = GIT_BUF_INIT; + int len; + char buf[2048]; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_target"); + + cl_git_rewritefile("readlink_target", "This is the target of a symlink\n"); + do_symlink(git_buf_cstr(&target_path), "readlink_link", 0); + + len = p_readlink("readlink_link", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf); + + git_buf_free(&target_path); +} + +void test_core_link__readlink_dangling(void) +{ + git_buf target_path = GIT_BUF_INIT; + int len; + char buf[2048]; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent"); + + do_symlink(git_buf_cstr(&target_path), "readlink_dangling", 0); + + len = p_readlink("readlink_dangling", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf); + + git_buf_free(&target_path); +} + +void test_core_link__readlink_multiple(void) +{ + git_buf target_path = GIT_BUF_INIT, + path3 = GIT_BUF_INIT, path2 = GIT_BUF_INIT, path1 = GIT_BUF_INIT; + int len; + char buf[2048]; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_final"); + git_buf_join(&path3, '/', clar_sandbox_path(), "readlink_3"); + git_buf_join(&path2, '/', clar_sandbox_path(), "readlink_2"); + git_buf_join(&path1, '/', clar_sandbox_path(), "readlink_1"); + + do_symlink(git_buf_cstr(&target_path), git_buf_cstr(&path3), 0); + do_symlink(git_buf_cstr(&path3), git_buf_cstr(&path2), 0); + do_symlink(git_buf_cstr(&path2), git_buf_cstr(&path1), 0); + + len = p_readlink("readlink_1", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(unslashify(&path2)), buf); + + git_buf_free(&path1); + git_buf_free(&path2); + git_buf_free(&path3); + git_buf_free(&target_path); +} |