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/core | |
| 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/core')
43 files changed, 8378 insertions, 0 deletions
diff --git a/tests/libgit2/core/array.c b/tests/libgit2/core/array.c new file mode 100644 index 000000000..8e626a506 --- /dev/null +++ b/tests/libgit2/core/array.c @@ -0,0 +1,57 @@ +#include "clar_libgit2.h" +#include "array.h" + +static int int_lookup(const void *k, const void *a) +{ + const int *one = (const int *)k; + int *two = (int *)a; + + return *one - *two; +} + +#define expect_pos(k, n, ret) \ + key = (k); \ + cl_assert_equal_i((ret), \ + git_array_search(&p, integers, int_lookup, &key)); \ + cl_assert_equal_i((n), p); + +void test_core_array__bsearch2(void) +{ + git_array_t(int) integers = GIT_ARRAY_INIT; + int *i, key; + size_t p; + + i = git_array_alloc(integers); *i = 2; + i = git_array_alloc(integers); *i = 3; + i = git_array_alloc(integers); *i = 5; + i = git_array_alloc(integers); *i = 7; + i = git_array_alloc(integers); *i = 7; + i = git_array_alloc(integers); *i = 8; + i = git_array_alloc(integers); *i = 13; + i = git_array_alloc(integers); *i = 21; + i = git_array_alloc(integers); *i = 25; + i = git_array_alloc(integers); *i = 42; + i = git_array_alloc(integers); *i = 69; + i = git_array_alloc(integers); *i = 121; + i = git_array_alloc(integers); *i = 256; + i = git_array_alloc(integers); *i = 512; + i = git_array_alloc(integers); *i = 513; + i = git_array_alloc(integers); *i = 514; + i = git_array_alloc(integers); *i = 516; + i = git_array_alloc(integers); *i = 516; + i = git_array_alloc(integers); *i = 517; + + /* value to search for, expected position, return code */ + expect_pos(3, 1, GIT_OK); + expect_pos(2, 0, GIT_OK); + expect_pos(1, 0, GIT_ENOTFOUND); + expect_pos(25, 8, GIT_OK); + expect_pos(26, 9, GIT_ENOTFOUND); + expect_pos(42, 9, GIT_OK); + expect_pos(50, 10, GIT_ENOTFOUND); + expect_pos(68, 10, GIT_ENOTFOUND); + expect_pos(256, 12, GIT_OK); + + git_array_clear(integers); +} + diff --git a/tests/libgit2/core/assert.c b/tests/libgit2/core/assert.c new file mode 100644 index 000000000..ef75624b9 --- /dev/null +++ b/tests/libgit2/core/assert.c @@ -0,0 +1,94 @@ +#ifdef GIT_ASSERT_HARD +# undef GIT_ASSERT_HARD +#endif + +#define GIT_ASSERT_HARD 0 + +#include "clar_libgit2.h" + +static const char *hello_world = "hello, world"; +static const char *fail = "FAIL"; + +static int dummy_fn(const char *myarg) +{ + GIT_ASSERT_ARG(myarg); + GIT_ASSERT_ARG(myarg != hello_world); + return 0; +} + +static const char *fn_returns_string(const char *myarg) +{ + GIT_ASSERT_ARG_WITH_RETVAL(myarg, fail); + GIT_ASSERT_ARG_WITH_RETVAL(myarg != hello_world, fail); + + return myarg; +} + +static int bad_math(void) +{ + GIT_ASSERT(1 + 1 == 3); + return 42; +} + +static const char *bad_returns_string(void) +{ + GIT_ASSERT_WITH_RETVAL(1 + 1 == 3, NULL); + return hello_world; +} + +void test_core_assert__argument(void) +{ + cl_git_fail(dummy_fn(NULL)); + cl_assert(git_error_last()); + cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass); + cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message); + + cl_git_fail(dummy_fn(hello_world)); + cl_assert(git_error_last()); + cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass); + cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message); + + cl_git_pass(dummy_fn("foo")); +} + +void test_core_assert__argument_with_non_int_return_type(void) +{ + const char *foo = "foo"; + + cl_assert_equal_p(fail, fn_returns_string(NULL)); + cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass); + cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message); + + cl_assert_equal_p(fail, fn_returns_string(hello_world)); + cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass); + cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message); + + cl_assert_equal_p(foo, fn_returns_string(foo)); +} + +void test_core_assert__argument_with_void_return_type(void) +{ + const char *foo = "foo"; + + git_error_clear(); + fn_returns_string(hello_world); + cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass); + cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message); + + git_error_clear(); + cl_assert_equal_p(foo, fn_returns_string(foo)); + cl_assert_equal_p(NULL, git_error_last()); +} + +void test_core_assert__internal(void) +{ + cl_git_fail(bad_math()); + cl_assert(git_error_last()); + cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass); + cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message); + + cl_assert_equal_p(NULL, bad_returns_string()); + cl_assert(git_error_last()); + cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass); + cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message); +} diff --git a/tests/libgit2/core/bitvec.c b/tests/libgit2/core/bitvec.c new file mode 100644 index 000000000..48d7b99f0 --- /dev/null +++ b/tests/libgit2/core/bitvec.c @@ -0,0 +1,64 @@ +#include "clar_libgit2.h" +#include "bitvec.h" + +#if 0 +static void print_bitvec(git_bitvec *bv) +{ + int b; + + if (!bv->length) { + for (b = 63; b >= 0; --b) + fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0); + } else { + for (b = bv->length * 8; b >= 0; --b) + fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0); + } + fprintf(stderr, "\n"); +} +#endif + +static void set_some_bits(git_bitvec *bv, size_t length) +{ + size_t i; + + for (i = 0; i < length; ++i) { + if (i % 3 == 0 || i % 7 == 0) + git_bitvec_set(bv, i, true); + } +} + +static void check_some_bits(git_bitvec *bv, size_t length) +{ + size_t i; + + for (i = 0; i < length; ++i) + cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i)); +} + +void test_core_bitvec__0(void) +{ + git_bitvec bv; + + cl_git_pass(git_bitvec_init(&bv, 32)); + set_some_bits(&bv, 16); + check_some_bits(&bv, 16); + git_bitvec_clear(&bv); + set_some_bits(&bv, 32); + check_some_bits(&bv, 32); + git_bitvec_clear(&bv); + set_some_bits(&bv, 64); + check_some_bits(&bv, 64); + git_bitvec_free(&bv); + + cl_git_pass(git_bitvec_init(&bv, 128)); + set_some_bits(&bv, 32); + check_some_bits(&bv, 32); + set_some_bits(&bv, 128); + check_some_bits(&bv, 128); + git_bitvec_free(&bv); + + cl_git_pass(git_bitvec_init(&bv, 4000)); + set_some_bits(&bv, 4000); + check_some_bits(&bv, 4000); + git_bitvec_free(&bv); +} diff --git a/tests/libgit2/core/buf.c b/tests/libgit2/core/buf.c new file mode 100644 index 000000000..3959fa883 --- /dev/null +++ b/tests/libgit2/core/buf.c @@ -0,0 +1,54 @@ +#include "clar_libgit2.h" +#include "buf.h" + +void test_core_buf__sanitize(void) +{ + git_buf buf = { (char *)0x42, 0, 16 }; + + cl_git_pass(git_buf_sanitize(&buf)); + cl_assert_equal_s(buf.ptr, ""); + cl_assert_equal_i(buf.reserved, 0); + cl_assert_equal_i(buf.size, 0); + + git_buf_dispose(&buf); +} + +void test_core_buf__tostr(void) +{ + git_str str = GIT_STR_INIT; + git_buf buf = { (char *)0x42, 0, 16 }; + + cl_git_pass(git_buf_tostr(&str, &buf)); + + cl_assert_equal_s(buf.ptr, ""); + cl_assert_equal_i(buf.reserved, 0); + cl_assert_equal_i(buf.size, 0); + + cl_assert_equal_s(str.ptr, ""); + cl_assert_equal_i(str.asize, 0); + cl_assert_equal_i(str.size, 0); + + git_buf_dispose(&buf); + git_str_dispose(&str); +} + +void test_core_buf__fromstr(void) +{ + git_str str = GIT_STR_INIT; + git_buf buf = { (char *)0x42, 0, 16 }; + + cl_git_pass(git_buf_tostr(&str, &buf)); + cl_git_pass(git_str_puts(&str, "Hello, world.")); + cl_git_pass(git_buf_fromstr(&buf, &str)); + + cl_assert(buf.reserved > 14); + cl_assert_equal_i(buf.size, 13); + cl_assert_equal_s(buf.ptr, "Hello, world."); + + cl_assert_equal_s(str.ptr, ""); + cl_assert_equal_i(str.asize, 0); + cl_assert_equal_i(str.size, 0); + + git_buf_dispose(&buf); + git_str_dispose(&str); +} diff --git a/tests/libgit2/core/copy.c b/tests/libgit2/core/copy.c new file mode 100644 index 000000000..6d22b503d --- /dev/null +++ b/tests/libgit2/core/copy.c @@ -0,0 +1,153 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "posix.h" + +void test_core_copy__file(void) +{ + struct stat st; + const char *content = "This is some stuff to copy\n"; + + cl_git_mkfile("copy_me", content); + + cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664)); + + cl_git_pass(git_fs_path_lstat("copy_me_two", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert(strlen(content) == (size_t)st.st_size); + + cl_git_pass(p_unlink("copy_me_two")); + cl_git_pass(p_unlink("copy_me")); +} + +void test_core_copy__file_in_dir(void) +{ + struct stat st; + const char *content = "This is some other stuff to copy\n"; + + cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", 0775, GIT_MKDIR_PATH)); + cl_git_mkfile("an_dir/in_a_dir/copy_me", content); + cl_assert(git_fs_path_isdir("an_dir")); + + cl_git_pass(git_futils_mkpath2file + ("an_dir/second_dir/and_more/copy_me_two", 0775)); + + cl_git_pass(git_futils_cp + ("an_dir/in_a_dir/copy_me", + "an_dir/second_dir/and_more/copy_me_two", + 0664)); + + cl_git_pass(git_fs_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert(strlen(content) == (size_t)st.st_size); + + cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_assert(!git_fs_path_isdir("an_dir")); +} + +#ifndef GIT_WIN32 +static void assert_hard_link(const char *path) +{ + /* we assert this by checking that there's more than one link to the file */ + struct stat st; + + cl_assert(git_fs_path_isfile(path)); + cl_git_pass(p_stat(path, &st)); + cl_assert(st.st_nlink > 1); +} +#endif + +void test_core_copy__tree(void) +{ + struct stat st; + const char *content = "File content\n"; + + cl_git_pass(git_futils_mkdir("src/b", 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("src/c/d", 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("src/c/e", 0775, GIT_MKDIR_PATH)); + + cl_git_mkfile("src/f1", content); + cl_git_mkfile("src/b/f2", content); + cl_git_mkfile("src/c/f3", content); + cl_git_mkfile("src/c/d/f4", content); + cl_git_mkfile("src/c/d/.f5", content); + +#ifndef GIT_WIN32 + cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0); +#endif + + cl_assert(git_fs_path_isdir("src")); + cl_assert(git_fs_path_isdir("src/b")); + cl_assert(git_fs_path_isdir("src/c/d")); + cl_assert(git_fs_path_isfile("src/c/d/f4")); + + /* copy with no empty dirs, yes links, no dotfiles, no overwrite */ + + cl_git_pass( + git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) ); + + cl_assert(git_fs_path_isdir("t1")); + cl_assert(git_fs_path_isdir("t1/b")); + cl_assert(git_fs_path_isdir("t1/c")); + cl_assert(git_fs_path_isdir("t1/c/d")); + cl_assert(!git_fs_path_isdir("t1/c/e")); + + cl_assert(git_fs_path_isfile("t1/f1")); + cl_assert(git_fs_path_isfile("t1/b/f2")); + cl_assert(git_fs_path_isfile("t1/c/f3")); + cl_assert(git_fs_path_isfile("t1/c/d/f4")); + cl_assert(!git_fs_path_isfile("t1/c/d/.f5")); + + cl_git_pass(git_fs_path_lstat("t1/c/f3", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert(strlen(content) == (size_t)st.st_size); + +#ifndef GIT_WIN32 + cl_git_pass(git_fs_path_lstat("t1/c/d/l1", &st)); + cl_assert(S_ISLNK(st.st_mode)); +#endif + + cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_assert(!git_fs_path_isdir("t1")); + + /* copy with empty dirs, no links, yes dotfiles, no overwrite */ + + cl_git_pass( + git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) ); + + cl_assert(git_fs_path_isdir("t2")); + cl_assert(git_fs_path_isdir("t2/b")); + cl_assert(git_fs_path_isdir("t2/c")); + cl_assert(git_fs_path_isdir("t2/c/d")); + cl_assert(git_fs_path_isdir("t2/c/e")); + + cl_assert(git_fs_path_isfile("t2/f1")); + cl_assert(git_fs_path_isfile("t2/b/f2")); + cl_assert(git_fs_path_isfile("t2/c/f3")); + cl_assert(git_fs_path_isfile("t2/c/d/f4")); + cl_assert(git_fs_path_isfile("t2/c/d/.f5")); + +#ifndef GIT_WIN32 + cl_git_fail(git_fs_path_lstat("t2/c/d/l1", &st)); +#endif + + cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_assert(!git_fs_path_isdir("t2")); + +#ifndef GIT_WIN32 + cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0)); + cl_assert(git_fs_path_isdir("t3")); + + cl_assert(git_fs_path_isdir("t3")); + cl_assert(git_fs_path_isdir("t3/b")); + cl_assert(git_fs_path_isdir("t3/c")); + cl_assert(git_fs_path_isdir("t3/c/d")); + cl_assert(git_fs_path_isdir("t3/c/e")); + + assert_hard_link("t3/f1"); + assert_hard_link("t3/b/f2"); + assert_hard_link("t3/c/f3"); + assert_hard_link("t3/c/d/f4"); +#endif + + cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES)); +} diff --git a/tests/libgit2/core/dirent.c b/tests/libgit2/core/dirent.c new file mode 100644 index 000000000..2419ec7ab --- /dev/null +++ b/tests/libgit2/core/dirent.c @@ -0,0 +1,306 @@ +#include "clar_libgit2.h" +#include "futils.h" + +typedef struct name_data { + int count; /* return count */ + char *name; /* filename */ +} name_data; + +typedef struct walk_data { + char *sub; /* sub-directory name */ + name_data *names; /* name state data */ + git_str path; +} walk_data; + + +static char *top_dir = "dir-walk"; +static walk_data *state_loc; + +static void setup(walk_data *d) +{ + name_data *n; + + cl_must_pass(p_mkdir(top_dir, 0777)); + + cl_must_pass(p_chdir(top_dir)); + + if (strcmp(d->sub, ".") != 0) + cl_must_pass(p_mkdir(d->sub, 0777)); + + cl_git_pass(git_str_sets(&d->path, d->sub)); + + state_loc = d; + + for (n = d->names; n->name; n++) { + git_file fd = p_creat(n->name, 0666); + cl_assert(fd >= 0); + p_close(fd); + n->count = 0; + } +} + +static void dirent_cleanup__cb(void *_d) +{ + walk_data *d = _d; + name_data *n; + + for (n = d->names; n->name; n++) { + cl_must_pass(p_unlink(n->name)); + } + + if (strcmp(d->sub, ".") != 0) + cl_must_pass(p_rmdir(d->sub)); + + cl_must_pass(p_chdir("..")); + + cl_must_pass(p_rmdir(top_dir)); + + git_str_dispose(&d->path); +} + +static void check_counts(walk_data *d) +{ + name_data *n; + + for (n = d->names; n->name; n++) { + cl_assert(n->count == 1); + } +} + +static int update_count(name_data *data, const char *name) +{ + name_data *n; + + for (n = data; n->name; n++) { + if (!strcmp(n->name, name)) { + n->count++; + return 0; + } + } + + return GIT_ERROR; +} + +static int one_entry(void *state, git_str *path) +{ + walk_data *d = (walk_data *) state; + + if (state != state_loc) + return GIT_ERROR; + + if (path != &d->path) + return GIT_ERROR; + + return update_count(d->names, path->ptr); +} + + +static name_data dot_names[] = { + { 0, "./a" }, + { 0, "./asdf" }, + { 0, "./pack-foo.pack" }, + { 0, NULL } +}; +static walk_data dot = { + ".", + dot_names, + GIT_STR_INIT +}; + +/* make sure that the '.' folder is not traversed */ +void test_core_dirent__dont_traverse_dot(void) +{ + cl_set_cleanup(&dirent_cleanup__cb, &dot); + setup(&dot); + + cl_git_pass(git_fs_path_direach(&dot.path, 0, one_entry, &dot)); + + check_counts(&dot); +} + + +static name_data sub_names[] = { + { 0, "sub/a" }, + { 0, "sub/asdf" }, + { 0, "sub/pack-foo.pack" }, + { 0, NULL } +}; +static walk_data sub = { + "sub", + sub_names, + GIT_STR_INIT +}; + +/* traverse a subfolder */ +void test_core_dirent__traverse_subfolder(void) +{ + cl_set_cleanup(&dirent_cleanup__cb, &sub); + setup(&sub); + + cl_git_pass(git_fs_path_direach(&sub.path, 0, one_entry, &sub)); + + check_counts(&sub); +} + + +static walk_data sub_slash = { + "sub/", + sub_names, + GIT_STR_INIT +}; + +/* traverse a slash-terminated subfolder */ +void test_core_dirent__traverse_slash_terminated_folder(void) +{ + cl_set_cleanup(&dirent_cleanup__cb, &sub_slash); + setup(&sub_slash); + + cl_git_pass(git_fs_path_direach(&sub_slash.path, 0, one_entry, &sub_slash)); + + check_counts(&sub_slash); +} + + +static name_data empty_names[] = { + { 0, NULL } +}; +static walk_data empty = { + "empty", + empty_names, + GIT_STR_INIT +}; + +/* make sure that empty folders are not traversed */ +void test_core_dirent__dont_traverse_empty_folders(void) +{ + cl_set_cleanup(&dirent_cleanup__cb, &empty); + setup(&empty); + + cl_git_pass(git_fs_path_direach(&empty.path, 0, one_entry, &empty)); + + check_counts(&empty); + + /* make sure callback not called */ + cl_assert(git_fs_path_is_empty_dir(empty.path.ptr)); +} + +static name_data odd_names[] = { + { 0, "odd/.a" }, + { 0, "odd/..c" }, + /* the following don't work on cygwin/win32 */ + /* { 0, "odd/.b." }, */ + /* { 0, "odd/..d.." }, */ + { 0, NULL } +}; +static walk_data odd = { + "odd", + odd_names, + GIT_STR_INIT +}; + +/* make sure that strange looking filenames ('..c') are traversed */ +void test_core_dirent__traverse_weird_filenames(void) +{ + cl_set_cleanup(&dirent_cleanup__cb, &odd); + setup(&odd); + + cl_git_pass(git_fs_path_direach(&odd.path, 0, one_entry, &odd)); + + check_counts(&odd); +} + +/* test filename length limits */ +void test_core_dirent__length_limits(void) +{ + char *big_filename = (char *)git__malloc(FILENAME_MAX + 1); + memset(big_filename, 'a', FILENAME_MAX + 1); + big_filename[FILENAME_MAX] = 0; + + cl_must_fail(p_creat(big_filename, 0666)); + + git__free(big_filename); +} + +void test_core_dirent__empty_dir(void) +{ + cl_must_pass(p_mkdir("empty_dir", 0777)); + cl_assert(git_fs_path_is_empty_dir("empty_dir")); + + cl_git_mkfile("empty_dir/content", "whatever\n"); + cl_assert(!git_fs_path_is_empty_dir("empty_dir")); + cl_assert(!git_fs_path_is_empty_dir("empty_dir/content")); + + cl_must_pass(p_unlink("empty_dir/content")); + + cl_must_pass(p_mkdir("empty_dir/content", 0777)); + cl_assert(!git_fs_path_is_empty_dir("empty_dir")); + cl_assert(git_fs_path_is_empty_dir("empty_dir/content")); + + cl_must_pass(p_rmdir("empty_dir/content")); + + cl_must_pass(p_rmdir("empty_dir")); +} + +static void handle_next(git_fs_path_diriter *diriter, walk_data *walk) +{ + const char *fullpath, *filename; + size_t fullpath_len, filename_len; + + cl_git_pass(git_fs_path_diriter_fullpath(&fullpath, &fullpath_len, diriter)); + cl_git_pass(git_fs_path_diriter_filename(&filename, &filename_len, diriter)); + + cl_assert_equal_strn(fullpath, "sub/", 4); + cl_assert_equal_s(fullpath+4, filename); + + update_count(walk->names, fullpath); +} + +/* test directory iterator */ +void test_core_dirent__diriter_with_fullname(void) +{ + git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT; + int error; + + cl_set_cleanup(&dirent_cleanup__cb, &sub); + setup(&sub); + + cl_git_pass(git_fs_path_diriter_init(&diriter, sub.path.ptr, 0)); + + while ((error = git_fs_path_diriter_next(&diriter)) == 0) + handle_next(&diriter, &sub); + + cl_assert_equal_i(error, GIT_ITEROVER); + + git_fs_path_diriter_free(&diriter); + + check_counts(&sub); +} + +void test_core_dirent__diriter_at_directory_root(void) +{ + git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT; + const char *sandbox_path, *path; + char *root_path; + size_t path_len; + int root_offset, error; + + sandbox_path = clar_sandbox_path(); + cl_assert((root_offset = git_fs_path_root(sandbox_path)) >= 0); + + cl_assert(root_path = git__calloc(1, root_offset + 2)); + strncpy(root_path, sandbox_path, root_offset + 1); + + cl_git_pass(git_fs_path_diriter_init(&diriter, root_path, 0)); + + while ((error = git_fs_path_diriter_next(&diriter)) == 0) { + cl_git_pass(git_fs_path_diriter_fullpath(&path, &path_len, &diriter)); + + cl_assert(path_len > (size_t)(root_offset + 1)); + cl_assert(path[root_offset+1] != '/'); + } + + cl_assert_equal_i(error, GIT_ITEROVER); + + git_fs_path_diriter_free(&diriter); + git__free(root_path); +} diff --git a/tests/libgit2/core/encoding.c b/tests/libgit2/core/encoding.c new file mode 100644 index 000000000..6cec24679 --- /dev/null +++ b/tests/libgit2/core/encoding.c @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" +#include "varint.h" + +void test_core_encoding__decode(void) +{ + const unsigned char *buf = (unsigned char *)"AB"; + size_t size; + + cl_assert(git_decode_varint(buf, &size) == 65); + cl_assert(size == 1); + + buf = (unsigned char *)"\xfe\xdc\xbaXY"; + cl_assert(git_decode_varint(buf, &size) == 267869656); + cl_assert(size == 4); + + buf = (unsigned char *)"\xaa\xaa\xfe\xdc\xbaXY"; + cl_assert(git_decode_varint(buf, &size) == UINT64_C(1489279344088)); + cl_assert(size == 6); + + buf = (unsigned char *)"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xfe\xdc\xbaXY"; + cl_assert(git_decode_varint(buf, &size) == 0); + cl_assert(size == 0); + +} + +void test_core_encoding__encode(void) +{ + unsigned char buf[100]; + cl_assert(git_encode_varint(buf, 100, 65) == 1); + cl_assert(buf[0] == 'A'); + + cl_assert(git_encode_varint(buf, 1, 1) == 1); + cl_assert(!memcmp(buf, "\x01", 1)); + + cl_assert(git_encode_varint(buf, 100, 267869656) == 4); + cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4)); + + cl_assert(git_encode_varint(buf, 100, UINT64_C(1489279344088)) == 6); + cl_assert(!memcmp(buf, "\xaa\xaa\xfe\xdc\xbaX", 6)); + + cl_assert(git_encode_varint(buf, 1, UINT64_C(1489279344088)) == -1); +} diff --git a/tests/libgit2/core/env.c b/tests/libgit2/core/env.c new file mode 100644 index 000000000..8ba9b9124 --- /dev/null +++ b/tests/libgit2/core/env.c @@ -0,0 +1,320 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "sysdir.h" + +#ifdef GIT_WIN32 +#define NUM_VARS 5 +static const char *env_vars[NUM_VARS] = { + "HOME", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "PROGRAMFILES" +}; +#else +#define NUM_VARS 1 +static const char *env_vars[NUM_VARS] = { "HOME" }; +#endif + +static char *env_save[NUM_VARS]; + +static char *home_values[] = { + "fake_home", + "f\xc3\xa1ke_h\xc3\xb5me", /* all in latin-1 supplement */ + "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\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 +}; + +void test_core_env__initialize(void) +{ + int i; + for (i = 0; i < NUM_VARS; ++i) + env_save[i] = cl_getenv(env_vars[i]); +} + +static void set_global_search_path_from_env(void) +{ + cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL)); +} + +static void set_system_search_path_from_env(void) +{ + cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL)); +} + +void test_core_env__cleanup(void) +{ + int i; + char **val; + + for (i = 0; i < NUM_VARS; ++i) { + cl_setenv(env_vars[i], env_save[i]); + git__free(env_save[i]); + env_save[i] = NULL; + } + + /* these will probably have already been cleaned up, but if a test + * fails, then it's probably good to try and clear out these dirs + */ + for (val = home_values; *val != NULL; val++) { + if (**val != '\0') + (void)p_rmdir(*val); + } + + cl_sandbox_set_search_path_defaults(); +} + +static void setenv_and_check(const char *name, const char *value) +{ + char *check; + + cl_git_pass(cl_setenv(name, value)); + check = cl_getenv(name); + + if (value) + cl_assert_equal_s(value, check); + else + cl_assert(check == NULL); + + git__free(check); +} + +void test_core_env__0(void) +{ + git_str path = GIT_STR_INIT, found = GIT_STR_INIT; + char testfile[16], tidx = '0'; + char **val; + const char *testname = "testfile"; + size_t testlen = strlen(testname); + + strncpy(testfile, testname, sizeof(testfile)); + cl_assert_equal_s(testname, testfile); + + for (val = home_values; *val != NULL; val++) { + + /* if we can't make the directory, let's just assume + * we are on a filesystem that doesn't support the + * characters in question and skip this test... + */ + if (p_mkdir(*val, 0777) != 0) { + *val = ""; /* mark as not created */ + continue; + } + + cl_git_pass(git_fs_path_prettify(&path, *val, NULL)); + + /* vary testfile name in each directory so accidentally leaving + * an environment variable set from a previous iteration won't + * accidentally make this test pass... + */ + testfile[testlen] = tidx++; + cl_git_pass(git_str_joinpath(&path, path.ptr, testfile)); + cl_git_mkfile(path.ptr, "find me"); + git_str_rtruncate_at_char(&path, '/'); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); + + setenv_and_check("HOME", path.ptr); + set_global_search_path_from_env(); + + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); + + cl_setenv("HOME", env_save[0]); + set_global_search_path_from_env(); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); + +#ifdef GIT_WIN32 + setenv_and_check("HOMEDRIVE", NULL); + setenv_and_check("HOMEPATH", NULL); + setenv_and_check("USERPROFILE", path.ptr); + set_global_search_path_from_env(); + + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); + + { + int root = git_fs_path_root(path.ptr); + char old; + + if (root >= 0) { + setenv_and_check("USERPROFILE", NULL); + set_global_search_path_from_env(); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); + + old = path.ptr[root]; + path.ptr[root] = '\0'; + setenv_and_check("HOMEDRIVE", path.ptr); + path.ptr[root] = old; + setenv_and_check("HOMEPATH", &path.ptr[root]); + set_global_search_path_from_env(); + + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); + } + } +#endif + + (void)p_rmdir(*val); + } + + git_str_dispose(&path); + git_str_dispose(&found); +} + + +void test_core_env__1(void) +{ + git_str path = GIT_STR_INIT; + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); + + cl_git_pass(cl_setenv("HOME", "doesnotexist")); +#ifdef GIT_WIN32 + cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist")); + cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist")); +#endif + set_global_search_path_from_env(); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); + + cl_git_pass(cl_setenv("HOME", NULL)); +#ifdef GIT_WIN32 + cl_git_pass(cl_setenv("HOMEPATH", NULL)); + cl_git_pass(cl_setenv("USERPROFILE", NULL)); +#endif + set_global_search_path_from_env(); + set_system_search_path_from_env(); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile")); + +#ifdef GIT_WIN32 + cl_git_pass(cl_setenv("PROGRAMFILES", NULL)); + set_system_search_path_from_env(); + + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile")); +#endif + + git_str_dispose(&path); +} + +static void check_global_searchpath( + const char *path, int position, const char *file, git_str *temp) +{ + git_str out = GIT_STR_INIT; + + /* build and set new path */ + if (position < 0) + cl_git_pass(git_str_join(temp, GIT_PATH_LIST_SEPARATOR, path, "$PATH")); + else if (position > 0) + cl_git_pass(git_str_join(temp, GIT_PATH_LIST_SEPARATOR, "$PATH", path)); + else + cl_git_pass(git_str_sets(temp, path)); + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, temp->ptr)); + + /* get path and make sure $PATH expansion worked */ + cl_git_pass(git_libgit2_opts( + GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &out)); + + if (position < 0) + cl_assert(git__prefixcmp(out.ptr, path) == 0); + else if (position > 0) + cl_assert(git__suffixcmp(out.ptr, path) == 0); + else + cl_assert_equal_s(out.ptr, path); + + /* find file using new path */ + cl_git_pass(git_sysdir_find_global_file(temp, file)); + + /* reset path and confirm file not found */ + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(temp, file)); + + git_str_dispose(&out); +} + +void test_core_env__2(void) +{ + git_str path = GIT_STR_INIT, found = GIT_STR_INIT; + char testfile[16], tidx = '0'; + char **val; + const char *testname = "alternate"; + size_t testlen = strlen(testname); + + strncpy(testfile, testname, sizeof(testfile)); + cl_assert_equal_s(testname, testfile); + + for (val = home_values; *val != NULL; val++) { + + /* if we can't make the directory, let's just assume + * we are on a filesystem that doesn't support the + * characters in question and skip this test... + */ + if (p_mkdir(*val, 0777) != 0 && errno != EEXIST) { + *val = ""; /* mark as not created */ + continue; + } + + cl_git_pass(git_fs_path_prettify(&path, *val, NULL)); + + /* vary testfile name so any sloppiness is resetting variables or + * deleting files won't accidentally make a test pass. + */ + testfile[testlen] = tidx++; + cl_git_pass(git_str_joinpath(&path, path.ptr, testfile)); + cl_git_mkfile(path.ptr, "find me"); + git_str_rtruncate_at_char(&path, '/'); + + /* default should be NOTFOUND */ + cl_assert_equal_i( + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); + + /* try plain, append $PATH, and prepend $PATH */ + check_global_searchpath(path.ptr, 0, testfile, &found); + check_global_searchpath(path.ptr, -1, testfile, &found); + check_global_searchpath(path.ptr, 1, testfile, &found); + + /* cleanup */ + cl_git_pass(git_str_joinpath(&path, path.ptr, testfile)); + (void)p_unlink(path.ptr); + (void)p_rmdir(*val); + } + + git_str_dispose(&path); + git_str_dispose(&found); +} + +void test_core_env__substitution(void) +{ + git_str buf = GIT_STR_INIT, expected = GIT_STR_INIT; + + /* Set it to something non-default so we have controllable values */ + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, "/tmp/a")); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf)); + cl_assert_equal_s("/tmp/a", buf.ptr); + + git_str_clear(&buf); + cl_git_pass(git_str_join(&buf, GIT_PATH_LIST_SEPARATOR, "$PATH", "/tmp/b")); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, buf.ptr)); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf)); + + cl_git_pass(git_str_join(&expected, GIT_PATH_LIST_SEPARATOR, "/tmp/a", "/tmp/b")); + cl_assert_equal_s(expected.ptr, buf.ptr); + + git_str_dispose(&expected); + git_str_dispose(&buf); +} diff --git a/tests/libgit2/core/errors.c b/tests/libgit2/core/errors.c new file mode 100644 index 000000000..386ecdc5f --- /dev/null +++ b/tests/libgit2/core/errors.c @@ -0,0 +1,222 @@ +#include "clar_libgit2.h" + +void test_core_errors__public_api(void) +{ + char *str_in_error; + + git_error_clear(); + cl_assert(git_error_last() == NULL); + + git_error_set_oom(); + + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY); + str_in_error = strstr(git_error_last()->message, "memory"); + cl_assert(str_in_error != NULL); + + git_error_clear(); + + git_error_set_str(GIT_ERROR_REPOSITORY, "This is a test"); + + cl_assert(git_error_last() != NULL); + str_in_error = strstr(git_error_last()->message, "This is a test"); + cl_assert(str_in_error != NULL); + + git_error_clear(); + cl_assert(git_error_last() == NULL); +} + +#include "common.h" +#include "util.h" +#include "posix.h" + +void test_core_errors__new_school(void) +{ + char *str_in_error; + + git_error_clear(); + cl_assert(git_error_last() == NULL); + + git_error_set_oom(); /* internal fn */ + + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY); + str_in_error = strstr(git_error_last()->message, "memory"); + cl_assert(str_in_error != NULL); + + git_error_clear(); + + git_error_set(GIT_ERROR_REPOSITORY, "This is a test"); /* internal fn */ + + cl_assert(git_error_last() != NULL); + str_in_error = strstr(git_error_last()->message, "This is a test"); + cl_assert(str_in_error != NULL); + + git_error_clear(); + cl_assert(git_error_last() == NULL); + + do { + struct stat st; + memset(&st, 0, sizeof(st)); + cl_assert(p_lstat("this_file_does_not_exist", &st) < 0); + GIT_UNUSED(st); + } while (false); + git_error_set(GIT_ERROR_OS, "stat failed"); /* internal fn */ + + cl_assert(git_error_last() != NULL); + str_in_error = strstr(git_error_last()->message, "stat failed"); + cl_assert(str_in_error != NULL); + cl_assert(git__prefixcmp(str_in_error, "stat failed: ") == 0); + cl_assert(strlen(str_in_error) > strlen("stat failed: ")); + +#ifdef GIT_WIN32 + git_error_clear(); + + /* The MSDN docs use this to generate a sample error */ + cl_assert(GetProcessId(NULL) == 0); + git_error_set(GIT_ERROR_OS, "GetProcessId failed"); /* internal fn */ + + cl_assert(git_error_last() != NULL); + str_in_error = strstr(git_error_last()->message, "GetProcessId failed"); + cl_assert(str_in_error != NULL); + cl_assert(git__prefixcmp(str_in_error, "GetProcessId failed: ") == 0); + cl_assert(strlen(str_in_error) > strlen("GetProcessId failed: ")); +#endif + + git_error_clear(); +} + +void test_core_errors__restore(void) +{ + git_error_state err_state = {0}; + + git_error_clear(); + cl_assert(git_error_last() == NULL); + + cl_assert_equal_i(0, git_error_state_capture(&err_state, 0)); + + memset(&err_state, 0x0, sizeof(git_error_state)); + + git_error_set(42, "Foo: %s", "bar"); + cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); + + cl_assert(git_error_last() == NULL); + + git_error_set(99, "Bar: %s", "foo"); + + git_error_state_restore(&err_state); + + cl_assert_equal_i(42, git_error_last()->klass); + cl_assert_equal_s("Foo: bar", git_error_last()->message); +} + +void test_core_errors__free_state(void) +{ + git_error_state err_state = {0}; + + git_error_clear(); + + git_error_set(42, "Foo: %s", "bar"); + cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); + + git_error_set(99, "Bar: %s", "foo"); + + git_error_state_free(&err_state); + + cl_assert_equal_i(99, git_error_last()->klass); + cl_assert_equal_s("Bar: foo", git_error_last()->message); + + git_error_state_restore(&err_state); + + cl_assert(git_error_last() == NULL); +} + +void test_core_errors__restore_oom(void) +{ + git_error_state err_state = {0}; + const git_error *oom_error = NULL; + + git_error_clear(); + + git_error_set_oom(); /* internal fn */ + oom_error = git_error_last(); + cl_assert(oom_error); + + cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); + + cl_assert(git_error_last() == NULL); + cl_assert_equal_i(GIT_ERROR_NOMEMORY, err_state.error_msg.klass); + cl_assert_equal_s("Out of memory", err_state.error_msg.message); + + git_error_state_restore(&err_state); + + cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY); + cl_assert_(git_error_last() == oom_error, "static oom error not restored"); + + git_error_clear(); +} + +static int test_arraysize_multiply(size_t nelem, size_t size) +{ + size_t out; + GIT_ERROR_CHECK_ALLOC_MULTIPLY(&out, nelem, size); + return 0; +} + +void test_core_errors__integer_overflow_alloc_multiply(void) +{ + cl_git_pass(test_arraysize_multiply(10, 10)); + cl_git_pass(test_arraysize_multiply(1000, 1000)); + cl_git_pass(test_arraysize_multiply(SIZE_MAX/sizeof(void *), sizeof(void *))); + cl_git_pass(test_arraysize_multiply(0, 10)); + cl_git_pass(test_arraysize_multiply(10, 0)); + + cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, sizeof(void *))); + cl_git_fail(test_arraysize_multiply((SIZE_MAX/sizeof(void *))+1, sizeof(void *))); + + cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass); + cl_assert_equal_s("Out of memory", git_error_last()->message); +} + +static int test_arraysize_add(size_t one, size_t two) +{ + size_t out; + GIT_ERROR_CHECK_ALLOC_ADD(&out, one, two); + return 0; +} + +void test_core_errors__integer_overflow_alloc_add(void) +{ + cl_git_pass(test_arraysize_add(10, 10)); + cl_git_pass(test_arraysize_add(1000, 1000)); + cl_git_pass(test_arraysize_add(SIZE_MAX-10, 10)); + + cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, 2)); + cl_git_fail(test_arraysize_multiply(SIZE_MAX, SIZE_MAX)); + + cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass); + cl_assert_equal_s("Out of memory", git_error_last()->message); +} + +void test_core_errors__integer_overflow_sets_oom(void) +{ + size_t out; + + git_error_clear(); + cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX-1, 1)); + cl_assert_equal_p(NULL, git_error_last()); + + git_error_clear(); + cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, 42, 69)); + cl_assert_equal_p(NULL, git_error_last()); + + git_error_clear(); + cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX)); + cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass); + cl_assert_equal_s("Out of memory", git_error_last()->message); + + git_error_clear(); + cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX)); + cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass); + cl_assert_equal_s("Out of memory", git_error_last()->message); +} diff --git a/tests/libgit2/core/features.c b/tests/libgit2/core/features.c new file mode 100644 index 000000000..7b28cc0cb --- /dev/null +++ b/tests/libgit2/core/features.c @@ -0,0 +1,35 @@ +#include "clar_libgit2.h" + +void test_core_features__0(void) +{ + int major, minor, rev, caps; + + git_libgit2_version(&major, &minor, &rev); + cl_assert_equal_i(LIBGIT2_VER_MAJOR, major); + cl_assert_equal_i(LIBGIT2_VER_MINOR, minor); + cl_assert_equal_i(LIBGIT2_VER_REVISION, rev); + + caps = git_libgit2_features(); + +#ifdef GIT_THREADS + cl_assert((caps & GIT_FEATURE_THREADS) != 0); +#else + cl_assert((caps & GIT_FEATURE_THREADS) == 0); +#endif + +#ifdef GIT_HTTPS + cl_assert((caps & GIT_FEATURE_HTTPS) != 0); +#endif + +#if defined(GIT_SSH) + cl_assert((caps & GIT_FEATURE_SSH) != 0); +#else + cl_assert((caps & GIT_FEATURE_SSH) == 0); +#endif + +#if defined(GIT_USE_NSEC) + cl_assert((caps & GIT_FEATURE_NSEC) != 0); +#else + cl_assert((caps & GIT_FEATURE_NSEC) == 0); +#endif +} diff --git a/tests/libgit2/core/filebuf.c b/tests/libgit2/core/filebuf.c new file mode 100644 index 000000000..6f40c2456 --- /dev/null +++ b/tests/libgit2/core/filebuf.c @@ -0,0 +1,267 @@ +#include "clar_libgit2.h" +#include "filebuf.h" + +/* make sure git_filebuf_open doesn't delete an existing lock */ +void test_core_filebuf__0(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + int fd; + char test[] = "test", testlock[] = "test.lock"; + + fd = p_creat(testlock, 0744); /* -V536 */ + + cl_must_pass(fd); + cl_must_pass(p_close(fd)); + + cl_git_fail(git_filebuf_open(&file, test, 0, 0666)); + cl_assert(git_fs_path_exists(testlock)); + + cl_must_pass(p_unlink(testlock)); +} + + +/* make sure GIT_FILEBUF_APPEND works as expected */ +void test_core_filebuf__1(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_git_mkfile(test, "libgit2 rocks\n"); + + cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + cl_git_pass(git_filebuf_commit(&file)); + + cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test); + + cl_must_pass(p_unlink(test)); +} + + +/* make sure git_filebuf_write writes large buffer correctly */ +void test_core_filebuf__2(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */ + + memset(buf, 0xfe, sizeof(buf)); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf))); + cl_git_pass(git_filebuf_commit(&file)); + + cl_assert_equal_file((char *)buf, sizeof(buf), test); + + cl_must_pass(p_unlink(test)); +} + +/* make sure git_filebuf_cleanup clears the buffer */ +void test_core_filebuf__4(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_assert(file.buffer == NULL); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + cl_assert(file.buffer != NULL); + + git_filebuf_cleanup(&file); + cl_assert(file.buffer == NULL); +} + + +/* make sure git_filebuf_commit clears the buffer */ +void test_core_filebuf__5(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_assert(file.buffer == NULL); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + cl_assert(file.buffer != NULL); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + cl_assert(file.buffer != NULL); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert(file.buffer == NULL); + + cl_must_pass(p_unlink(test)); +} + + +/* make sure git_filebuf_commit takes umask into account */ +void test_core_filebuf__umask(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + struct stat statbuf; + mode_t mask, os_mask; + +#ifdef GIT_WIN32 + os_mask = 0600; +#else + os_mask = 0777; +#endif + + p_umask(mask = p_umask(0)); + + cl_assert(file.buffer == NULL); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + cl_assert(file.buffer != NULL); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + cl_assert(file.buffer != NULL); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert(file.buffer == NULL); + + cl_must_pass(p_stat("test", &statbuf)); + cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask); + + cl_must_pass(p_unlink(test)); +} + +void test_core_filebuf__rename_error(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char *dir = "subdir", *test = "subdir/test", *test_lock = "subdir/test.lock"; + int fd; + +#ifndef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0666)); + cl_git_mkfile(test, "dummy content"); + fd = p_open(test, O_RDONLY); + cl_assert(fd > 0); + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_fs_path_exists(test_lock)); + + cl_git_fail(git_filebuf_commit(&file)); + p_close(fd); + + git_filebuf_cleanup(&file); + + cl_assert_equal_i(false, git_fs_path_exists(test_lock)); +} + +void test_core_filebuf__symlink_follow(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + cl_skip(); + + cl_git_pass(p_mkdir(dir, 0777)); + cl_git_pass(p_symlink("target", source)); + + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + + /* The second time around, the target file does exist */ + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_core_filebuf__symlink_follow_absolute_paths(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_str source = GIT_STR_INIT, target = GIT_STR_INIT; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + cl_skip(); + + cl_git_pass(git_str_joinpath(&source, clar_sandbox_path(), "linkdir/link")); + cl_git_pass(git_str_joinpath(&target, clar_sandbox_path(), "linkdir/target")); + cl_git_pass(p_mkdir("linkdir", 0777)); + cl_git_pass(p_symlink(target.ptr, source.ptr)); + + cl_git_pass(git_filebuf_open(&file, source.ptr, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_fs_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + git_str_dispose(&source); + git_str_dispose(&target); + + cl_git_pass(git_futils_rmdir_r("linkdir", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_core_filebuf__symlink_depth(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + cl_skip(); + + cl_git_pass(p_mkdir(dir, 0777)); + /* Endless loop */ + cl_git_pass(p_symlink("link", source)); + + cl_git_fail(git_filebuf_open(&file, source, 0, 0666)); + + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_core_filebuf__hidden_file(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + git_filebuf file = GIT_FILEBUF_INIT; + char *dir = "hidden", *test = "hidden/test"; + bool hidden; + + cl_git_pass(p_mkdir(dir, 0666)); + cl_git_mkfile(test, "dummy content"); + + cl_git_pass(git_win32__set_hidden(test, true)); + cl_git_pass(git_win32__hidden(&hidden, test)); + cl_assert(hidden); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_git_pass(git_filebuf_commit(&file)); + + git_filebuf_cleanup(&file); +#endif +} + +void test_core_filebuf__detects_directory(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + + cl_must_pass(p_mkdir("foo", 0777)); + cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666)); + cl_must_pass(p_rmdir("foo")); +} diff --git a/tests/libgit2/core/ftruncate.c b/tests/libgit2/core/ftruncate.c new file mode 100644 index 000000000..0c731cb1e --- /dev/null +++ b/tests/libgit2/core/ftruncate.c @@ -0,0 +1,48 @@ +/** + * Some tests for p_ftruncate() to ensure that + * properly handles large (2Gb+) files. + */ + +#include "clar_libgit2.h" + +static const char *filename = "core_ftruncate.txt"; +static int fd = -1; + +void test_core_ftruncate__initialize(void) +{ + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE")) + cl_skip(); + + cl_must_pass((fd = p_open(filename, O_CREAT | O_RDWR, 0644))); +} + +void test_core_ftruncate__cleanup(void) +{ + if (fd < 0) + return; + + p_close(fd); + fd = 0; + + p_unlink(filename); +} + +static void _extend(off64_t i64len) +{ + struct stat st; + int error; + + cl_assert((error = p_ftruncate(fd, i64len)) == 0); + cl_assert((error = p_fstat(fd, &st)) == 0); + cl_assert(st.st_size == i64len); +} + +void test_core_ftruncate__2gb(void) +{ + _extend(0x80000001); +} + +void test_core_ftruncate__4gb(void) +{ + _extend(0x100000001); +} diff --git a/tests/libgit2/core/futils.c b/tests/libgit2/core/futils.c new file mode 100644 index 000000000..3501765f6 --- /dev/null +++ b/tests/libgit2/core/futils.c @@ -0,0 +1,115 @@ +#include "clar_libgit2.h" +#include "futils.h" + +/* Fixture setup and teardown */ +void test_core_futils__initialize(void) +{ + cl_must_pass(p_mkdir("futils", 0777)); +} + +void test_core_futils__cleanup(void) +{ + cl_fixture_cleanup("futils"); +} + +void test_core_futils__writebuffer(void) +{ + git_str out = GIT_STR_INIT, + append = GIT_STR_INIT; + + /* create a new file */ + git_str_puts(&out, "hello!\n"); + git_str_printf(&out, "this is a %s\n", "test"); + + cl_git_pass(git_futils_writebuffer(&out, "futils/test-file", O_RDWR|O_CREAT, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + /* append some more data */ + git_str_puts(&append, "And some more!\n"); + git_str_put(&out, append.ptr, append.size); + + cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR|O_APPEND, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + git_str_dispose(&out); + git_str_dispose(&append); +} + +void test_core_futils__write_hidden_file(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + git_str out = GIT_STR_INIT, append = GIT_STR_INIT; + bool hidden; + + git_str_puts(&out, "hidden file.\n"); + git_futils_writebuffer(&out, "futils/test-file", O_RDWR | O_CREAT, 0666); + + cl_git_pass(git_win32__set_hidden("futils/test-file", true)); + + /* append some more data */ + git_str_puts(&append, "And some more!\n"); + git_str_put(&out, append.ptr, append.size); + + cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR | O_APPEND, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + cl_git_pass(git_win32__hidden(&hidden, "futils/test-file")); + cl_assert(hidden); + + git_str_dispose(&out); + git_str_dispose(&append); +#endif +} + +void test_core_futils__recursive_rmdir_keeps_symlink_targets(void) +{ + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + cl_skip(); + + cl_git_pass(git_futils_mkdir_r("a/b", 0777)); + cl_git_pass(git_futils_mkdir_r("dir-target", 0777)); + cl_git_mkfile("dir-target/file", "Contents"); + cl_git_mkfile("file-target", "Contents"); + cl_must_pass(p_symlink("dir-target", "a/symlink")); + cl_must_pass(p_symlink("file-target", "a/b/symlink")); + + cl_git_pass(git_futils_rmdir_r("a", NULL, GIT_RMDIR_REMOVE_FILES)); + + cl_assert(git_fs_path_exists("dir-target")); + cl_assert(git_fs_path_exists("file-target")); + + cl_must_pass(p_unlink("dir-target/file")); + cl_must_pass(p_rmdir("dir-target")); + cl_must_pass(p_unlink("file-target")); +} + +void test_core_futils__mktmp_umask(void) +{ +#ifdef GIT_WIN32 + cl_skip(); +#else + git_str path = GIT_STR_INIT; + struct stat st; + int fd; + + umask(0); + cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0); + cl_must_pass(p_fstat(fd, &st)); + cl_assert_equal_i(st.st_mode & 0777, 0777); + cl_must_pass(p_unlink(path.ptr)); + close(fd); + + umask(077); + cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0); + cl_must_pass(p_fstat(fd, &st)); + cl_assert_equal_i(st.st_mode & 0777, 0700); + cl_must_pass(p_unlink(path.ptr)); + close(fd); + git_str_dispose(&path); +#endif +} diff --git a/tests/libgit2/core/gitstr.c b/tests/libgit2/core/gitstr.c new file mode 100644 index 000000000..0a624e28c --- /dev/null +++ b/tests/libgit2/core/gitstr.c @@ -0,0 +1,1225 @@ +#include "clar_libgit2.h" +#include "git2/sys/hashsig.h" +#include "futils.h" + +#define TESTSTR "Have you seen that? Have you seeeen that??" +const char *test_string = TESTSTR; +const char *test_string_x2 = TESTSTR TESTSTR; + +#define TESTSTR_4096 REP1024("1234") +#define TESTSTR_8192 REP1024("12341234") +const char *test_4096 = TESTSTR_4096; +const char *test_8192 = TESTSTR_8192; + +/* test basic data concatenation */ +void test_core_gitstr__0(void) +{ + git_str buf = GIT_STR_INIT; + + cl_assert(buf.size == 0); + + git_str_puts(&buf, test_string); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_string, git_str_cstr(&buf)); + + git_str_puts(&buf, test_string); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_string_x2, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +/* test git_str_printf */ +void test_core_gitstr__1(void) +{ + git_str buf = GIT_STR_INIT; + + git_str_printf(&buf, "%s %s %d ", "shoop", "da", 23); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("shoop da 23 ", git_str_cstr(&buf)); + + git_str_printf(&buf, "%s %d", "woop", 42); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("shoop da 23 woop 42", git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +/* more thorough test of concatenation options */ +void test_core_gitstr__2(void) +{ + git_str buf = GIT_STR_INIT; + int i; + char data[128]; + + cl_assert(buf.size == 0); + + /* this must be safe to do */ + git_str_dispose(&buf); + cl_assert(buf.size == 0); + cl_assert(buf.asize == 0); + + /* empty buffer should be empty string */ + cl_assert_equal_s("", git_str_cstr(&buf)); + cl_assert(buf.size == 0); + /* cl_assert(buf.asize == 0); -- should not assume what git_str does */ + + /* free should set us back to the beginning */ + git_str_dispose(&buf); + cl_assert(buf.size == 0); + cl_assert(buf.asize == 0); + + /* add letter */ + git_str_putc(&buf, '+'); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("+", git_str_cstr(&buf)); + + /* add letter again */ + git_str_putc(&buf, '+'); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("++", git_str_cstr(&buf)); + + /* let's try that a few times */ + for (i = 0; i < 16; ++i) { + git_str_putc(&buf, '+'); + cl_assert(git_str_oom(&buf) == 0); + } + cl_assert_equal_s("++++++++++++++++++", git_str_cstr(&buf)); + + git_str_dispose(&buf); + + /* add data */ + git_str_put(&buf, "xo", 2); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("xo", git_str_cstr(&buf)); + + /* add letter again */ + git_str_put(&buf, "xo", 2); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s("xoxo", git_str_cstr(&buf)); + + /* let's try that a few times */ + for (i = 0; i < 16; ++i) { + git_str_put(&buf, "xo", 2); + cl_assert(git_str_oom(&buf) == 0); + } + cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo", + git_str_cstr(&buf)); + + git_str_dispose(&buf); + + /* set to string */ + git_str_sets(&buf, test_string); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_string, git_str_cstr(&buf)); + + /* append string */ + git_str_puts(&buf, test_string); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_string_x2, git_str_cstr(&buf)); + + /* set to string again (should overwrite - not append) */ + git_str_sets(&buf, test_string); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_string, git_str_cstr(&buf)); + + /* test clear */ + git_str_clear(&buf); + cl_assert_equal_s("", git_str_cstr(&buf)); + + git_str_dispose(&buf); + + /* test extracting data into buffer */ + git_str_puts(&buf, REP4("0123456789")); + cl_assert(git_str_oom(&buf) == 0); + + git_str_copy_cstr(data, sizeof(data), &buf); + cl_assert_equal_s(REP4("0123456789"), data); + git_str_copy_cstr(data, 11, &buf); + cl_assert_equal_s("0123456789", data); + git_str_copy_cstr(data, 3, &buf); + cl_assert_equal_s("01", data); + git_str_copy_cstr(data, 1, &buf); + cl_assert_equal_s("", data); + + git_str_copy_cstr(data, sizeof(data), &buf); + cl_assert_equal_s(REP4("0123456789"), data); + + git_str_sets(&buf, REP256("x")); + git_str_copy_cstr(data, sizeof(data), &buf); + /* since sizeof(data) == 128, only 127 bytes should be copied */ + cl_assert_equal_s(REP4(REP16("x")) REP16("x") REP16("x") + REP16("x") "xxxxxxxxxxxxxxx", data); + + git_str_dispose(&buf); + + git_str_copy_cstr(data, sizeof(data), &buf); + cl_assert_equal_s("", data); +} + +/* let's do some tests with larger buffers to push our limits */ +void test_core_gitstr__3(void) +{ + git_str buf = GIT_STR_INIT; + + /* set to string */ + git_str_set(&buf, test_4096, 4096); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_4096, git_str_cstr(&buf)); + + /* append string */ + git_str_puts(&buf, test_4096); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_8192, git_str_cstr(&buf)); + + /* set to string again (should overwrite - not append) */ + git_str_set(&buf, test_4096, 4096); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(test_4096, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +/* let's try some producer/consumer tests */ +void test_core_gitstr__4(void) +{ + git_str buf = GIT_STR_INIT; + int i; + + for (i = 0; i < 10; ++i) { + git_str_puts(&buf, "1234"); /* add 4 */ + cl_assert(git_str_oom(&buf) == 0); + git_str_consume(&buf, buf.ptr + 2); /* eat the first two */ + cl_assert(strlen(git_str_cstr(&buf)) == (size_t)((i + 1) * 2)); + } + /* we have appended 1234 10x and removed the first 20 letters */ + cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf)); + + git_str_consume(&buf, NULL); + cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf)); + + git_str_consume(&buf, "invalid pointer"); + cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf)); + + git_str_consume(&buf, buf.ptr); + cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf)); + + git_str_consume(&buf, buf.ptr + 1); + cl_assert_equal_s("2341234123412341234", git_str_cstr(&buf)); + + git_str_consume(&buf, buf.ptr + buf.size); + cl_assert_equal_s("", git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + + +static void +check_buf_append( + const char* data_a, + const char* data_b, + const char* expected_data, + size_t expected_size, + size_t expected_asize) +{ + git_str tgt = GIT_STR_INIT; + + git_str_sets(&tgt, data_a); + cl_assert(git_str_oom(&tgt) == 0); + git_str_puts(&tgt, data_b); + cl_assert(git_str_oom(&tgt) == 0); + cl_assert_equal_s(expected_data, git_str_cstr(&tgt)); + cl_assert_equal_i(tgt.size, expected_size); + if (expected_asize > 0) + cl_assert_equal_i(tgt.asize, expected_asize); + + git_str_dispose(&tgt); +} + +static void +check_buf_append_abc( + const char* buf_a, + const char* buf_b, + const char* buf_c, + const char* expected_ab, + const char* expected_abc, + const char* expected_abca, + const char* expected_abcab, + const char* expected_abcabc) +{ + git_str buf = GIT_STR_INIT; + + git_str_sets(&buf, buf_a); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(buf_a, git_str_cstr(&buf)); + + git_str_puts(&buf, buf_b); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected_ab, git_str_cstr(&buf)); + + git_str_puts(&buf, buf_c); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected_abc, git_str_cstr(&buf)); + + git_str_puts(&buf, buf_a); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected_abca, git_str_cstr(&buf)); + + git_str_puts(&buf, buf_b); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected_abcab, git_str_cstr(&buf)); + + git_str_puts(&buf, buf_c); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected_abcabc, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +/* more variations on append tests */ +void test_core_gitstr__5(void) +{ + check_buf_append("", "", "", 0, 0); + check_buf_append("a", "", "a", 1, 0); + check_buf_append("", "a", "a", 1, 8); + check_buf_append("", "a", "a", 1, 8); + check_buf_append("a", "b", "ab", 2, 8); + check_buf_append("", "abcdefgh", "abcdefgh", 8, 16); + check_buf_append("abcdefgh", "", "abcdefgh", 8, 16); + + /* buffer with starting asize will grow to: + * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9, + * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18, + * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27, + * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36, + * ... + * follow sequence until value > target size, + * then round up to nearest multiple of 8. + */ + + check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16); + check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16); + check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24); + check_buf_append("0123456789", "0123456789", + "01234567890123456789", 20, 24); + check_buf_append(REP16("x"), REP16("o"), + REP16("x") REP16("o"), 32, 40); + + check_buf_append(test_4096, "", test_4096, 4096, 4104); + check_buf_append(test_4096, test_4096, test_8192, 8192, 8200); + + /* check sequences of appends */ + check_buf_append_abc("a", "b", "c", + "ab", "abc", "abca", "abcab", "abcabc"); + check_buf_append_abc("a1", "b2", "c3", + "a1b2", "a1b2c3", "a1b2c3a1", + "a1b2c3a1b2", "a1b2c3a1b2c3"); + check_buf_append_abc("a1/", "b2/", "c3/", + "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/", + "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/"); +} + +/* test swap */ +void test_core_gitstr__6(void) +{ + git_str a = GIT_STR_INIT; + git_str b = GIT_STR_INIT; + + git_str_sets(&a, "foo"); + cl_assert(git_str_oom(&a) == 0); + git_str_sets(&b, "bar"); + cl_assert(git_str_oom(&b) == 0); + + cl_assert_equal_s("foo", git_str_cstr(&a)); + cl_assert_equal_s("bar", git_str_cstr(&b)); + + git_str_swap(&a, &b); + + cl_assert_equal_s("bar", git_str_cstr(&a)); + cl_assert_equal_s("foo", git_str_cstr(&b)); + + git_str_dispose(&a); + git_str_dispose(&b); +} + + +/* test detach/attach data */ +void test_core_gitstr__7(void) +{ + const char *fun = "This is fun"; + git_str a = GIT_STR_INIT; + char *b = NULL; + + git_str_sets(&a, "foo"); + cl_assert(git_str_oom(&a) == 0); + cl_assert_equal_s("foo", git_str_cstr(&a)); + + b = git_str_detach(&a); + + cl_assert_equal_s("foo", b); + cl_assert_equal_s("", a.ptr); + git__free(b); + + b = git_str_detach(&a); + + cl_assert_equal_s(NULL, b); + cl_assert_equal_s("", a.ptr); + + git_str_dispose(&a); + + b = git__strdup(fun); + git_str_attach(&a, b, 0); + + cl_assert_equal_s(fun, a.ptr); + cl_assert(a.size == strlen(fun)); + cl_assert(a.asize == strlen(fun) + 1); + + git_str_dispose(&a); + + b = git__strdup(fun); + git_str_attach(&a, b, strlen(fun) + 1); + + cl_assert_equal_s(fun, a.ptr); + cl_assert(a.size == strlen(fun)); + cl_assert(a.asize == strlen(fun) + 1); + + git_str_dispose(&a); +} + + +static void +check_joinbuf_2( + const char *a, + const char *b, + const char *expected) +{ + char sep = '/'; + git_str buf = GIT_STR_INIT; + + git_str_join(&buf, sep, a, b); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected, git_str_cstr(&buf)); + git_str_dispose(&buf); +} + +static void +check_joinbuf_overlapped( + const char *oldval, + int ofs_a, + const char *b, + const char *expected) +{ + char sep = '/'; + git_str buf = GIT_STR_INIT; + + git_str_sets(&buf, oldval); + git_str_join(&buf, sep, buf.ptr + ofs_a, b); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected, git_str_cstr(&buf)); + git_str_dispose(&buf); +} + +static void +check_joinbuf_n_2( + const char *a, + const char *b, + const char *expected) +{ + char sep = '/'; + git_str buf = GIT_STR_INIT; + + git_str_sets(&buf, a); + cl_assert(git_str_oom(&buf) == 0); + + git_str_join_n(&buf, sep, 1, b); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +static void +check_joinbuf_n_4( + const char *a, + const char *b, + const char *c, + const char *d, + const char *expected) +{ + char sep = ';'; + git_str buf = GIT_STR_INIT; + git_str_join_n(&buf, sep, 4, a, b, c, d); + cl_assert(git_str_oom(&buf) == 0); + cl_assert_equal_s(expected, git_str_cstr(&buf)); + git_str_dispose(&buf); +} + +/* test join */ +void test_core_gitstr__8(void) +{ + git_str a = GIT_STR_INIT; + + git_str_join_n(&a, '/', 1, "foo"); + cl_assert(git_str_oom(&a) == 0); + cl_assert_equal_s("foo", git_str_cstr(&a)); + + git_str_join_n(&a, '/', 1, "bar"); + cl_assert(git_str_oom(&a) == 0); + cl_assert_equal_s("foo/bar", git_str_cstr(&a)); + + git_str_join_n(&a, '/', 1, "baz"); + cl_assert(git_str_oom(&a) == 0); + cl_assert_equal_s("foo/bar/baz", git_str_cstr(&a)); + + git_str_dispose(&a); + + check_joinbuf_2(NULL, "", ""); + check_joinbuf_2(NULL, "a", "a"); + check_joinbuf_2(NULL, "/a", "/a"); + check_joinbuf_2("", "", ""); + check_joinbuf_2("", "a", "a"); + check_joinbuf_2("", "/a", "/a"); + check_joinbuf_2("a", "", "a/"); + check_joinbuf_2("a", "/", "a/"); + check_joinbuf_2("a", "b", "a/b"); + check_joinbuf_2("/", "a", "/a"); + check_joinbuf_2("/", "", "/"); + check_joinbuf_2("/a", "/b", "/a/b"); + check_joinbuf_2("/a", "/b/", "/a/b/"); + check_joinbuf_2("/a/", "b/", "/a/b/"); + check_joinbuf_2("/a/", "/b/", "/a/b/"); + check_joinbuf_2("/a/", "//b/", "/a/b/"); + check_joinbuf_2("/abcd", "/defg", "/abcd/defg"); + check_joinbuf_2("/abcd", "/defg/", "/abcd/defg/"); + check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/"); + check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/"); + + check_joinbuf_overlapped("abcd", 0, "efg", "abcd/efg"); + check_joinbuf_overlapped("abcd", 1, "efg", "bcd/efg"); + check_joinbuf_overlapped("abcd", 2, "efg", "cd/efg"); + check_joinbuf_overlapped("abcd", 3, "efg", "d/efg"); + check_joinbuf_overlapped("abcd", 4, "efg", "efg"); + check_joinbuf_overlapped("abc/", 2, "efg", "c/efg"); + check_joinbuf_overlapped("abc/", 3, "efg", "/efg"); + check_joinbuf_overlapped("abc/", 4, "efg", "efg"); + check_joinbuf_overlapped("abcd", 3, "", "d/"); + check_joinbuf_overlapped("abcd", 4, "", ""); + check_joinbuf_overlapped("abc/", 2, "", "c/"); + check_joinbuf_overlapped("abc/", 3, "", "/"); + check_joinbuf_overlapped("abc/", 4, "", ""); + + check_joinbuf_n_2("", "", ""); + check_joinbuf_n_2("", "a", "a"); + check_joinbuf_n_2("", "/a", "/a"); + check_joinbuf_n_2("a", "", "a/"); + check_joinbuf_n_2("a", "/", "a/"); + check_joinbuf_n_2("a", "b", "a/b"); + check_joinbuf_n_2("/", "a", "/a"); + check_joinbuf_n_2("/", "", "/"); + check_joinbuf_n_2("/a", "/b", "/a/b"); + check_joinbuf_n_2("/a", "/b/", "/a/b/"); + check_joinbuf_n_2("/a/", "b/", "/a/b/"); + check_joinbuf_n_2("/a/", "/b/", "/a/b/"); + check_joinbuf_n_2("/abcd", "/defg", "/abcd/defg"); + check_joinbuf_n_2("/abcd", "/defg/", "/abcd/defg/"); + check_joinbuf_n_2("/abcd/", "defg/", "/abcd/defg/"); + check_joinbuf_n_2("/abcd/", "/defg/", "/abcd/defg/"); + + check_joinbuf_n_4("", "", "", "", ""); + check_joinbuf_n_4("", "a", "", "", "a;"); + check_joinbuf_n_4("a", "", "", "", "a;"); + check_joinbuf_n_4("", "", "", "a", "a"); + check_joinbuf_n_4("a", "b", "", ";c;d;", "a;b;c;d;"); + check_joinbuf_n_4("a", "b", "", ";c;d", "a;b;c;d"); + check_joinbuf_n_4("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop"); + check_joinbuf_n_4("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;"); + check_joinbuf_n_4(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;"); +} + +void test_core_gitstr__9(void) +{ + git_str buf = GIT_STR_INIT; + + /* just some exhaustive tests of various separator placement */ + char *a[] = { "", "-", "a-", "-a", "-a-" }; + char *b[] = { "", "-", "b-", "-b", "-b-" }; + char sep[] = { 0, '-', '/' }; + char *expect_null[] = { "", "-", "a-", "-a", "-a-", + "-", "--", "a--", "-a-", "-a--", + "b-", "-b-", "a-b-", "-ab-", "-a-b-", + "-b", "--b", "a--b", "-a-b", "-a--b", + "-b-", "--b-", "a--b-", "-a-b-", "-a--b-" }; + char *expect_dash[] = { "", "-", "a-", "-a-", "-a-", + "-", "-", "a-", "-a-", "-a-", + "b-", "-b-", "a-b-", "-a-b-", "-a-b-", + "-b", "-b", "a-b", "-a-b", "-a-b", + "-b-", "-b-", "a-b-", "-a-b-", "-a-b-" }; + char *expect_slas[] = { "", "-/", "a-/", "-a/", "-a-/", + "-", "-/-", "a-/-", "-a/-", "-a-/-", + "b-", "-/b-", "a-/b-", "-a/b-", "-a-/b-", + "-b", "-/-b", "a-/-b", "-a/-b", "-a-/-b", + "-b-", "-/-b-", "a-/-b-", "-a/-b-", "-a-/-b-" }; + char **expect_values[] = { expect_null, expect_dash, expect_slas }; + char separator, **expect; + unsigned int s, i, j; + + for (s = 0; s < sizeof(sep) / sizeof(char); ++s) { + separator = sep[s]; + expect = expect_values[s]; + + for (j = 0; j < sizeof(b) / sizeof(char*); ++j) { + for (i = 0; i < sizeof(a) / sizeof(char*); ++i) { + git_str_join(&buf, separator, a[i], b[j]); + cl_assert_equal_s(*expect, buf.ptr); + expect++; + } + } + } + + git_str_dispose(&buf); +} + +void test_core_gitstr__10(void) +{ + git_str a = GIT_STR_INIT; + + cl_git_pass(git_str_join_n(&a, '/', 1, "test")); + cl_assert_equal_s(a.ptr, "test"); + cl_git_pass(git_str_join_n(&a, '/', 1, "string")); + cl_assert_equal_s(a.ptr, "test/string"); + git_str_clear(&a); + cl_git_pass(git_str_join_n(&a, '/', 3, "test", "string", "join")); + cl_assert_equal_s(a.ptr, "test/string/join"); + cl_git_pass(git_str_join_n(&a, '/', 2, a.ptr, "more")); + cl_assert_equal_s(a.ptr, "test/string/join/test/string/join/more"); + + git_str_dispose(&a); +} + +void test_core_gitstr__join3(void) +{ + git_str a = GIT_STR_INIT; + + cl_git_pass(git_str_join3(&a, '/', "test", "string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "test/", "string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "test/", "/string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "test/", "/string/", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "test/", "/string/", "/join")); + cl_assert_equal_s("test/string/join", a.ptr); + + cl_git_pass(git_str_join3(&a, '/', "", "string", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "", "string/", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "", "string/", "/join")); + cl_assert_equal_s("string/join", a.ptr); + + cl_git_pass(git_str_join3(&a, '/', "string", "", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "string/", "", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_str_join3(&a, '/', "string/", "", "/join")); + cl_assert_equal_s("string/join", a.ptr); + + git_str_dispose(&a); +} + +void test_core_gitstr__11(void) +{ + git_str a = GIT_STR_INIT; + char *t1[] = { "nothing", "in", "common" }; + char *t2[] = { "something", "something else", "some other" }; + char *t3[] = { "something", "some fun", "no fun" }; + char *t4[] = { "happy", "happier", "happiest" }; + char *t5[] = { "happiest", "happier", "happy" }; + char *t6[] = { "no", "nope", "" }; + char *t7[] = { "", "doesn't matter" }; + + cl_git_pass(git_str_common_prefix(&a, t1, 3)); + cl_assert_equal_s(a.ptr, ""); + + cl_git_pass(git_str_common_prefix(&a, t2, 3)); + cl_assert_equal_s(a.ptr, "some"); + + cl_git_pass(git_str_common_prefix(&a, t3, 3)); + cl_assert_equal_s(a.ptr, ""); + + cl_git_pass(git_str_common_prefix(&a, t4, 3)); + cl_assert_equal_s(a.ptr, "happ"); + + cl_git_pass(git_str_common_prefix(&a, t5, 3)); + cl_assert_equal_s(a.ptr, "happ"); + + cl_git_pass(git_str_common_prefix(&a, t6, 3)); + cl_assert_equal_s(a.ptr, ""); + + cl_git_pass(git_str_common_prefix(&a, t7, 3)); + cl_assert_equal_s(a.ptr, ""); + + git_str_dispose(&a); +} + +void test_core_gitstr__rfind_variants(void) +{ + git_str a = GIT_STR_INIT; + ssize_t len; + + cl_git_pass(git_str_sets(&a, "/this/is/it/")); + + len = (ssize_t)git_str_len(&a); + + cl_assert(git_str_rfind(&a, '/') == len - 1); + cl_assert(git_str_rfind_next(&a, '/') == len - 4); + + cl_assert(git_str_rfind(&a, 'i') == len - 3); + cl_assert(git_str_rfind_next(&a, 'i') == len - 3); + + cl_assert(git_str_rfind(&a, 'h') == 2); + cl_assert(git_str_rfind_next(&a, 'h') == 2); + + cl_assert(git_str_rfind(&a, 'q') == -1); + cl_assert(git_str_rfind_next(&a, 'q') == -1); + + git_str_dispose(&a); +} + +void test_core_gitstr__puts_escaped(void) +{ + git_str a = GIT_STR_INIT; + + git_str_clear(&a); + cl_git_pass(git_str_puts_escaped(&a, "this is a test", "", "")); + cl_assert_equal_s("this is a test", a.ptr); + + git_str_clear(&a); + cl_git_pass(git_str_puts_escaped(&a, "this is a test", "t", "\\")); + cl_assert_equal_s("\\this is a \\tes\\t", a.ptr); + + git_str_clear(&a); + cl_git_pass(git_str_puts_escaped(&a, "this is a test", "i ", "__")); + cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr); + + git_str_clear(&a); + cl_git_pass(git_str_puts_escape_regex(&a, "^match\\s*[A-Z]+.*")); + cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr); + + git_str_dispose(&a); +} + +static void assert_unescape(char *expected, char *to_unescape) { + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_sets(&buf, to_unescape)); + git_str_unescape(&buf); + cl_assert_equal_s(expected, buf.ptr); + cl_assert_equal_sz(strlen(expected), buf.size); + + git_str_dispose(&buf); +} + +void test_core_gitstr__unescape(void) +{ + assert_unescape("Escaped\\", "Es\\ca\\ped\\"); + assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\"); + assert_unescape("\\", "\\"); + assert_unescape("\\", "\\\\"); + assert_unescape("", ""); +} + +void test_core_gitstr__encode_base64(void) +{ + git_str buf = GIT_STR_INIT; + + /* t h i s + * 0x 74 68 69 73 + * 0b 01110100 01101000 01101001 01110011 + * 0b 011101 000110 100001 101001 011100 110000 + * 0x 1d 06 21 29 1c 30 + * d G h p c w + */ + cl_git_pass(git_str_encode_base64(&buf, "this", 4)); + cl_assert_equal_s("dGhpcw==", buf.ptr); + + git_str_clear(&buf); + cl_git_pass(git_str_encode_base64(&buf, "this!", 5)); + cl_assert_equal_s("dGhpcyE=", buf.ptr); + + git_str_clear(&buf); + cl_git_pass(git_str_encode_base64(&buf, "this!\n", 6)); + cl_assert_equal_s("dGhpcyEK", buf.ptr); + + git_str_dispose(&buf); +} + +void test_core_gitstr__decode_base64(void) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_decode_base64(&buf, "dGhpcw==", 8)); + cl_assert_equal_s("this", buf.ptr); + + git_str_clear(&buf); + cl_git_pass(git_str_decode_base64(&buf, "dGhpcyE=", 8)); + cl_assert_equal_s("this!", buf.ptr); + + git_str_clear(&buf); + cl_git_pass(git_str_decode_base64(&buf, "dGhpcyEK", 8)); + cl_assert_equal_s("this!\n", buf.ptr); + + cl_git_fail(git_str_decode_base64(&buf, "This is not a valid base64 string!!!", 36)); + cl_assert_equal_s("this!\n", buf.ptr); + + git_str_dispose(&buf); +} + +void test_core_gitstr__encode_base85(void) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_encode_base85(&buf, "this", 4)); + cl_assert_equal_s("bZBXF", buf.ptr); + git_str_clear(&buf); + + cl_git_pass(git_str_encode_base85(&buf, "two rnds", 8)); + cl_assert_equal_s("ba!tca&BaE", buf.ptr); + git_str_clear(&buf); + + cl_git_pass(git_str_encode_base85(&buf, "this is base 85 encoded", + strlen("this is base 85 encoded"))); + cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr); + git_str_clear(&buf); + + git_str_dispose(&buf); +} + +void test_core_gitstr__decode_base85(void) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_decode_base85(&buf, "bZBXF", 5, 4)); + cl_assert_equal_sz(4, buf.size); + cl_assert_equal_s("this", buf.ptr); + git_str_clear(&buf); + + cl_git_pass(git_str_decode_base85(&buf, "ba!tca&BaE", 10, 8)); + cl_assert_equal_sz(8, buf.size); + cl_assert_equal_s("two rnds", buf.ptr); + git_str_clear(&buf); + + cl_git_pass(git_str_decode_base85(&buf, "bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", 30, 23)); + cl_assert_equal_sz(23, buf.size); + cl_assert_equal_s("this is base 85 encoded", buf.ptr); + git_str_clear(&buf); + + git_str_dispose(&buf); +} + +void test_core_gitstr__decode_base85_fails_gracefully(void) +{ + git_str buf = GIT_STR_INIT; + + git_str_puts(&buf, "foobar"); + + cl_git_fail(git_str_decode_base85(&buf, "invalid charsZZ", 15, 42)); + cl_git_fail(git_str_decode_base85(&buf, "invalidchars__ ", 15, 42)); + cl_git_fail(git_str_decode_base85(&buf, "overflowZZ~~~~~", 15, 42)); + cl_git_fail(git_str_decode_base85(&buf, "truncated", 9, 42)); + cl_assert_equal_sz(6, buf.size); + cl_assert_equal_s("foobar", buf.ptr); + + git_str_dispose(&buf); +} + +void test_core_gitstr__classify_with_utf8(void) +{ + char *data0 = "Simple text\n"; + size_t data0len = 12; + char *data1 = "Is that UTF-8 data I see…\nYep!\n"; + size_t data1len = 31; + char *data2 = "Internal NUL!!!\000\n\nI see you!\n"; + size_t data2len = 29; + char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n"; + size_t data3len = 20; + git_str b; + + b.ptr = data0; b.size = b.asize = data0len; + cl_assert(!git_str_is_binary(&b)); + cl_assert(!git_str_contains_nul(&b)); + + b.ptr = data1; b.size = b.asize = data1len; + cl_assert(!git_str_is_binary(&b)); + cl_assert(!git_str_contains_nul(&b)); + + b.ptr = data2; b.size = b.asize = data2len; + cl_assert(git_str_is_binary(&b)); + cl_assert(git_str_contains_nul(&b)); + + b.ptr = data3; b.size = b.asize = data3len; + cl_assert(!git_str_is_binary(&b)); + cl_assert(!git_str_contains_nul(&b)); +} + +#define SIMILARITY_TEST_DATA_1 \ + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \ + "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \ + "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n" + +void test_core_gitstr__similarity_metric(void) +{ + git_hashsig *a, *b; + git_str buf = GIT_STR_INIT; + int sim; + + /* in the first case, we compare data to itself and expect 100% match */ + + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1)); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + + cl_assert_equal_i(100, git_hashsig_compare(a, b)); + + git_hashsig_free(a); + git_hashsig_free(b); + + /* if we change just a single byte, how much does that change magnify? */ + + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1)); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + cl_git_pass(git_str_sets(&buf, + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \ + "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \ + "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n" + )); + cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + + sim = git_hashsig_compare(a, b); + + cl_assert_in_range(95, sim, 100); /* expect >95% similarity */ + + git_hashsig_free(a); + git_hashsig_free(b); + + /* let's try comparing data to a superset of itself */ + + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1)); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1 + "050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n")); + cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + + sim = git_hashsig_compare(a, b); + /* 20% lines added ~= 10% lines changed */ + + cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */ + + git_hashsig_free(a); + git_hashsig_free(b); + + /* what if we keep about half the original data and add half new */ + + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1)); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + cl_git_pass(git_str_sets(&buf, + "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \ + "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \ + "020x\n021\n022\n023\n024\n" \ + "x25\nx26\nx27\nx28\nx29\n" \ + "x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \ + "x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n" + )); + cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + + sim = git_hashsig_compare(a, b); + /* 50% lines changed */ + + cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */ + + git_hashsig_free(a); + git_hashsig_free(b); + + /* lastly, let's check that we can hash file content as well */ + + cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1)); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); + + cl_git_pass(git_futils_mkdir("scratch", 0755, GIT_MKDIR_PATH)); + cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1); + cl_git_pass(git_hashsig_create_fromfile( + &b, "scratch/testdata", GIT_HASHSIG_NORMAL)); + + cl_assert_equal_i(100, git_hashsig_compare(a, b)); + + git_hashsig_free(a); + git_hashsig_free(b); + + git_str_dispose(&buf); + git_futils_rmdir_r("scratch", NULL, GIT_RMDIR_REMOVE_FILES); +} + + +void test_core_gitstr__similarity_metric_whitespace(void) +{ + git_hashsig *a, *b; + git_str buf = GIT_STR_INIT; + int sim, i, j; + git_hashsig_option_t opt; + const char *tabbed = + " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n" + " separator = sep[s];\n" + " expect = expect_values[s];\n" + "\n" + " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n" + " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n" + " git_str_join(&buf, separator, a[i], b[j]);\n" + " cl_assert_equal_s(*expect, buf.ptr);\n" + " expect++;\n" + " }\n" + " }\n" + " }\n"; + const char *spaced = + " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n" + " separator = sep[s];\n" + " expect = expect_values[s];\n" + "\n" + " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n" + " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n" + " git_str_join(&buf, separator, a[i], b[j]);\n" + " cl_assert_equal_s(*expect, buf.ptr);\n" + " expect++;\n" + " }\n" + " }\n" + " }\n"; + const char *crlf_spaced2 = + " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\r\n" + " separator = sep[s];\r\n" + " expect = expect_values[s];\r\n" + "\r\n" + " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\r\n" + " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\r\n" + " git_str_join(&buf, separator, a[i], b[j]);\r\n" + " cl_assert_equal_s(*expect, buf.ptr);\r\n" + " expect++;\r\n" + " }\r\n" + " }\r\n" + " }\r\n"; + const char *text[3] = { tabbed, spaced, crlf_spaced2 }; + + /* let's try variations of our own code with whitespace changes */ + + for (opt = GIT_HASHSIG_NORMAL; opt <= GIT_HASHSIG_SMART_WHITESPACE; ++opt) { + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + cl_git_pass(git_str_sets(&buf, text[i])); + cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, opt)); + + cl_git_pass(git_str_sets(&buf, text[j])); + cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, opt)); + + sim = git_hashsig_compare(a, b); + + if (opt == GIT_HASHSIG_NORMAL) { + if (i == j) + cl_assert_equal_i(100, sim); + else + cl_assert_in_range(0, sim, 30); /* pretty different */ + } else { + cl_assert_equal_i(100, sim); + } + + git_hashsig_free(a); + git_hashsig_free(b); + } + } + } + + git_str_dispose(&buf); +} + +#include "../filter/crlf.h" + +#define check_buf(expected,buf) do { \ + cl_assert_equal_s(expected, buf.ptr); \ + cl_assert_equal_sz(strlen(expected), buf.size); } while (0) + +void test_core_gitstr__lf_and_crlf_conversions(void) +{ + git_str src = GIT_STR_INIT, tgt = GIT_STR_INIT; + + /* LF source */ + + git_str_sets(&src, "lf\nlf\nlf\nlf\n"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(src.ptr, tgt); + + git_str_sets(&src, "\nlf\nlf\nlf\nlf\nlf"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(src.ptr, tgt); + + /* CRLF source */ + + git_str_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt); + + git_str_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt); + + git_str_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt); + + git_str_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf"); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt); + + /* CRLF in LF text */ + + git_str_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt); + + git_str_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n"); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt); + + /* LF in CRLF text */ + + git_str_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt); + + /* bare CR test */ + + git_str_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r"); + + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt); + + git_str_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r"); + + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt); + + git_str_sets(&src, "\rcr\r"); + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf(src.ptr, tgt); + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf("\rcr\r", tgt); + + git_str_dispose(&src); + git_str_dispose(&tgt); + + /* blob correspondence tests */ + + git_str_sets(&src, ALL_CRLF_TEXT_RAW); + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt); + git_str_sets(&src, ALL_CRLF_TEXT_RAW); + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(ALL_CRLF_TEXT_AS_LF, tgt); + git_str_dispose(&src); + git_str_dispose(&tgt); + + git_str_sets(&src, ALL_LF_TEXT_RAW); + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf(ALL_LF_TEXT_AS_CRLF, tgt); + git_str_sets(&src, ALL_LF_TEXT_RAW); + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(ALL_LF_TEXT_AS_LF, tgt); + git_str_dispose(&src); + git_str_dispose(&tgt); + + git_str_sets(&src, MORE_CRLF_TEXT_RAW); + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt); + git_str_sets(&src, MORE_CRLF_TEXT_RAW); + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(MORE_CRLF_TEXT_AS_LF, tgt); + git_str_dispose(&src); + git_str_dispose(&tgt); + + git_str_sets(&src, MORE_LF_TEXT_RAW); + cl_git_pass(git_str_lf_to_crlf(&tgt, &src)); + check_buf(MORE_LF_TEXT_AS_CRLF, tgt); + git_str_sets(&src, MORE_LF_TEXT_RAW); + cl_git_pass(git_str_crlf_to_lf(&tgt, &src)); + check_buf(MORE_LF_TEXT_AS_LF, tgt); + git_str_dispose(&src); + git_str_dispose(&tgt); +} + +void test_core_gitstr__dont_grow_borrowed(void) +{ + const char *somestring = "blah blah"; + git_str buf = GIT_STR_INIT; + + git_str_attach_notowned(&buf, somestring, strlen(somestring) + 1); + cl_assert_equal_p(somestring, buf.ptr); + cl_assert_equal_i(0, buf.asize); + cl_assert_equal_i(strlen(somestring) + 1, buf.size); + + cl_git_fail_with(GIT_EINVALID, git_str_grow(&buf, 1024)); +} + +void test_core_gitstr__dont_hit_infinite_loop_when_resizing(void) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_puts(&buf, "foobar")); + /* + * We do not care whether this succeeds or fails, which + * would depend on platform-specific allocation + * semantics. We only want to know that the function + * actually returns. + */ + (void)git_str_try_grow(&buf, SIZE_MAX, true); + + git_str_dispose(&buf); +} + +void test_core_gitstr__avoid_printing_into_oom_buffer(void) +{ + git_str buf = GIT_STR_INIT; + + /* Emulate OOM situation with a previous allocation */ + buf.asize = 8; + buf.ptr = git_str__oom; + + /* + * Print the same string again. As the buffer still has + * an `asize` of 8 due to the previous print, + * `ENSURE_SIZE` would not try to reallocate the array at + * all. As it didn't explicitly check for `git_str__oom` + * in earlier versions, this would've resulted in it + * returning successfully and thus `git_str_puts` would + * just print into the `git_str__oom` array. + */ + cl_git_fail(git_str_puts(&buf, "foobar")); +} diff --git a/tests/libgit2/core/hex.c b/tests/libgit2/core/hex.c new file mode 100644 index 000000000..930af1670 --- /dev/null +++ b/tests/libgit2/core/hex.c @@ -0,0 +1,22 @@ +#include "clar_libgit2.h" +#include "util.h" + +void test_core_hex__fromhex(void) +{ + /* Passing cases */ + cl_assert(git__fromhex('0') == 0x0); + cl_assert(git__fromhex('1') == 0x1); + cl_assert(git__fromhex('3') == 0x3); + cl_assert(git__fromhex('9') == 0x9); + cl_assert(git__fromhex('A') == 0xa); + cl_assert(git__fromhex('C') == 0xc); + cl_assert(git__fromhex('F') == 0xf); + cl_assert(git__fromhex('a') == 0xa); + cl_assert(git__fromhex('c') == 0xc); + cl_assert(git__fromhex('f') == 0xf); + + /* Failing cases */ + cl_assert(git__fromhex('g') == -1); + cl_assert(git__fromhex('z') == -1); + cl_assert(git__fromhex('X') == -1); +} diff --git a/tests/libgit2/core/iconv.c b/tests/libgit2/core/iconv.c new file mode 100644 index 000000000..af1b4eabf --- /dev/null +++ b/tests/libgit2/core/iconv.c @@ -0,0 +1,78 @@ +#include "clar_libgit2.h" +#include "fs_path.h" + +#ifdef GIT_USE_ICONV +static git_fs_path_iconv_t ic; +static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D"; +static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; +#endif + +void test_core_iconv__initialize(void) +{ +#ifdef GIT_USE_ICONV + cl_git_pass(git_fs_path_iconv_init_precompose(&ic)); +#endif +} + +void test_core_iconv__cleanup(void) +{ +#ifdef GIT_USE_ICONV + git_fs_path_iconv_clear(&ic); +#endif +} + +void test_core_iconv__unchanged(void) +{ +#ifdef GIT_USE_ICONV + const char *data = "Ascii data", *original = data; + size_t datalen = strlen(data); + + cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen)); + GIT_UNUSED(datalen); + + /* There are no high bits set, so this should leave data untouched */ + cl_assert(data == original); +#endif +} + +void test_core_iconv__decomposed_to_precomposed(void) +{ +#ifdef GIT_USE_ICONV + const char *data = nfd; + size_t datalen, nfdlen = strlen(nfd); + + datalen = nfdlen; + cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen)); + GIT_UNUSED(datalen); + + /* The decomposed nfd string should be transformed to the nfc form + * (on platforms where iconv is enabled, of course). + */ + cl_assert_equal_s(nfc, data); + + /* should be able to do it multiple times with the same git_fs_path_iconv_t */ + data = nfd; datalen = nfdlen; + cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen)); + cl_assert_equal_s(nfc, data); + + data = nfd; datalen = nfdlen; + cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen)); + cl_assert_equal_s(nfc, data); +#endif +} + +void test_core_iconv__precomposed_is_unmodified(void) +{ +#ifdef GIT_USE_ICONV + const char *data = nfc; + size_t datalen = strlen(nfc); + + cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen)); + GIT_UNUSED(datalen); + + /* data is already in precomposed form, so even though some bytes have + * the high-bit set, the iconv transform should result in no change. + */ + cl_assert_equal_s(nfc, data); +#endif +} diff --git a/tests/libgit2/core/init.c b/tests/libgit2/core/init.c new file mode 100644 index 000000000..eba77ef52 --- /dev/null +++ b/tests/libgit2/core/init.c @@ -0,0 +1,54 @@ +#include "clar_libgit2.h" + +void test_core_init__returns_count(void) +{ + /* libgit2_tests initializes us first, so we have an existing + * initialization. + */ + cl_assert_equal_i(2, git_libgit2_init()); + cl_assert_equal_i(3, git_libgit2_init()); + + cl_assert_equal_i(2, git_libgit2_shutdown()); + cl_assert_equal_i(1, git_libgit2_shutdown()); +} + +void test_core_init__reinit_succeeds(void) +{ + cl_assert_equal_i(0, git_libgit2_shutdown()); + cl_assert_equal_i(1, git_libgit2_init()); + cl_sandbox_set_search_path_defaults(); +} + +#ifdef GIT_THREADS +static void *reinit(void *unused) +{ + unsigned i; + + for (i = 0; i < 20; i++) { + cl_assert(git_libgit2_init() > 0); + cl_assert(git_libgit2_shutdown() >= 0); + } + + return unused; +} +#endif + +void test_core_init__concurrent_init_succeeds(void) +{ +#ifdef GIT_THREADS + git_thread threads[10]; + unsigned i; + + cl_assert_equal_i(2, git_libgit2_init()); + + for (i = 0; i < ARRAY_SIZE(threads); i++) + git_thread_create(&threads[i], reinit, NULL); + for (i = 0; i < ARRAY_SIZE(threads); i++) + git_thread_join(&threads[i], NULL); + + cl_assert_equal_i(1, git_libgit2_shutdown()); + cl_sandbox_set_search_path_defaults(); +#else + cl_skip(); +#endif +} diff --git a/tests/libgit2/core/integer.c b/tests/libgit2/core/integer.c new file mode 100644 index 000000000..18364ba62 --- /dev/null +++ b/tests/libgit2/core/integer.c @@ -0,0 +1,253 @@ +#include "clar_libgit2.h" + +void test_core_integer__multiply_int64_no_overflow(void) +{ +#if !defined(git__multiply_int64_overflow) + int64_t result = 0; + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x8000000000000000))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x1)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x1)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(-0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x800000000000000))); + cl_assert_equal_i(result, INT64_C(-0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(-0x1)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x1)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(-0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x800000000000000))); + cl_assert_equal_i(result, INT64_C(-0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7fffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(0x4)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(-0x4)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x800000000000000))); + cl_assert_equal_i(result, INT64_C(-0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(-0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x2)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(-0x4)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(0x4)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7ffffffffffffff))); + cl_assert_equal_i(result, INT64_C(0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x800000000000000))); + cl_assert_equal_i(result, INT64_C(-0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x800000000000000))); + cl_assert_equal_i(result, INT64_C(0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x4000000000000000))); + cl_assert_equal_i(result, INT64_C(-0x8000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(0xffffffffffffffe)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(-0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(-0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x800000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(-0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x2))); + cl_assert_equal_i(result, INT64_C(0x1000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x4000000000000000), INT64_C(0x2))); + cl_assert_equal_i(result, INT64_C(-0x8000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x1))); + cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x0))); + cl_assert_equal_i(result, INT64_C(0x0)); +#endif +} + +void test_core_integer__multiply_int64_overflow(void) +{ +#if !defined(git__multiply_int64_overflow) + int64_t result = 0; + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x4000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x4000000000000000), INT64_C(0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x2))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7ffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x800000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7fffffffffffffff))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x8000000000000000))); +#endif +} + +void test_core_integer__multiply_int64_edge_cases(void) +{ +#if !defined(git__multiply_int64_overflow) + int64_t result = 0; + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x1))); + cl_assert_equal_i(result, INT64_C(-0x8000000000000000)); + cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x8000000000000000))); + cl_assert_equal_i(result, INT64_C(-0x8000000000000000)); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x8000000000000000))); + cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x1))); +#endif +} diff --git a/tests/libgit2/core/link.c b/tests/libgit2/core/link.c new file mode 100644 index 000000000..a1e2706b2 --- /dev/null +++ b/tests/libgit2/core/link.c @@ -0,0 +1,630 @@ +#include "clar_libgit2.h" +#include "posix.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 should_run(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; +} +#else +static bool should_run(void) +{ + return true; +} +#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; + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateSymbolicLink = (create_symlink_func)(void *)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; + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateHardLink = (create_hardlink_func)(void *)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_str unparsed_buf = GIT_STR_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_str_puts(&unparsed_buf, "\\??\\"); + + for (i = 0; i < strlen(old); i++) + git_str_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]); + + git_str_putc(&unparsed_buf, '\\'); + + subst_utf16_len = git__utf8_to_16(NULL, 0, git_str_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->ReparseBuffer.MountPoint.PathBuffer; + print_utf16 = subst_utf16 + subst_utf16_len + 1; + + ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1, + git_str_cstr(&unparsed_buf)); + cl_assert_equal_i(subst_utf16_len, ret); + + ret = git__utf8_to_16(print_utf16, + print_utf16_len + 1, git_str_cstr(&unparsed_buf) + 4); + cl_assert_equal_i(print_utf16_len, ret); + + reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset = 0; + reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength = subst_byte_len; + reparse_buf->ReparseBuffer.MountPoint.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR)); + reparse_buf->ReparseBuffer.MountPoint.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); + + git_str_dispose(&unparsed_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 + +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; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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_str target_path = GIT_STR_INIT; + struct stat st; + + if (!should_run()) + clar__skip(); + + /* Windows always writes the canonical path as the link target, so + * write the full path on all platforms. + */ + git_str_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_str_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_str_len(&target_path), st.st_size); + + git_str_dispose(&target_path); +} + +void test_core_link__lstat_symlink_directory(void) +{ + git_str target_path = GIT_STR_INIT; + struct stat st; + + if (!should_run()) + clar__skip(); + + git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget"); + + p_mkdir("lstat_dirtarget", 0777); + do_symlink(git_str_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_str_len(&target_path), st.st_size); + + git_str_dispose(&target_path); +} + +void test_core_link__lstat_dangling_symlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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_str target_path = GIT_STR_INIT; + struct stat st; + + git_str_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget"); + + p_mkdir("stat_junctarget", 0777); + do_junction(git_str_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_str_dispose(&target_path); +#endif +} + +void test_core_link__stat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_str target_path = GIT_STR_INIT; + struct stat st; + + git_str_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget"); + + p_mkdir("stat_nonexistent_junctarget", 0777); + do_junction(git_str_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_str_dispose(&target_path); +#endif +} + +void test_core_link__lstat_junction(void) +{ +#ifdef GIT_WIN32 + git_str target_path = GIT_STR_INIT; + struct stat st; + + git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget"); + + p_mkdir("lstat_junctarget", 0777); + do_junction(git_str_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_str_dispose(&target_path); +#endif +} + +void test_core_link__lstat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_str target_path = GIT_STR_INIT; + struct stat st; + + git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget"); + + p_mkdir("lstat_nonexistent_junctarget", 0777); + do_junction(git_str_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_str_len(&target_path), st.st_size); + + git_str_dispose(&target_path); +#endif +} + +void test_core_link__stat_hardlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + 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; + + if (!should_run()) + clar__skip(); + + 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_str target_path = GIT_STR_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_target"); + + cl_git_rewritefile("readlink_target", "This is the target of a symlink\n"); + do_symlink(git_str_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_str_cstr(&target_path), buf); + + git_str_dispose(&target_path); +} + +void test_core_link__readlink_dangling(void) +{ + git_str target_path = GIT_STR_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent"); + + do_symlink(git_str_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_str_cstr(&target_path), buf); + + git_str_dispose(&target_path); +} + +void test_core_link__readlink_multiple(void) +{ + git_str target_path = GIT_STR_INIT, + path3 = GIT_STR_INIT, path2 = GIT_STR_INIT, path1 = GIT_STR_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_final"); + git_str_join(&path3, '/', clar_sandbox_path(), "readlink_3"); + git_str_join(&path2, '/', clar_sandbox_path(), "readlink_2"); + git_str_join(&path1, '/', clar_sandbox_path(), "readlink_1"); + + do_symlink(git_str_cstr(&target_path), git_str_cstr(&path3), 0); + do_symlink(git_str_cstr(&path3), git_str_cstr(&path2), 0); + do_symlink(git_str_cstr(&path2), git_str_cstr(&path1), 0); + + len = p_readlink("readlink_1", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_str_cstr(&path2), buf); + + git_str_dispose(&path1); + git_str_dispose(&path2); + git_str_dispose(&path3); + git_str_dispose(&target_path); +} diff --git a/tests/libgit2/core/memmem.c b/tests/libgit2/core/memmem.c new file mode 100644 index 000000000..fd9986d01 --- /dev/null +++ b/tests/libgit2/core/memmem.c @@ -0,0 +1,46 @@ +#include "clar_libgit2.h" + +static void assert_found(const char *haystack, const char *needle, size_t expected_pos) +{ + cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0, + needle, needle ? strlen(needle) : 0), + haystack + expected_pos); +} + +static void assert_absent(const char *haystack, const char *needle) +{ + cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0, + needle, needle ? strlen(needle) : 0), + NULL); +} + +void test_core_memmem__found(void) +{ + assert_found("a", "a", 0); + assert_found("ab", "a", 0); + assert_found("ba", "a", 1); + assert_found("aa", "a", 0); + assert_found("aab", "aa", 0); + assert_found("baa", "aa", 1); + assert_found("dabc", "abc", 1); + assert_found("abababc", "abc", 4); +} + +void test_core_memmem__absent(void) +{ + assert_absent("a", "b"); + assert_absent("a", "aa"); + assert_absent("ba", "ab"); + assert_absent("ba", "ab"); + assert_absent("abc", "abcd"); + assert_absent("abcabcabc", "bcac"); +} + +void test_core_memmem__edgecases(void) +{ + assert_absent(NULL, NULL); + assert_absent("a", NULL); + assert_absent(NULL, "a"); + assert_absent("", "a"); + assert_absent("a", ""); +} diff --git a/tests/libgit2/core/mkdir.c b/tests/libgit2/core/mkdir.c new file mode 100644 index 000000000..58a4cfcdb --- /dev/null +++ b/tests/libgit2/core/mkdir.c @@ -0,0 +1,291 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "posix.h" + +static void cleanup_basic_dirs(void *ref) +{ + GIT_UNUSED(ref); + git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY); + git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY); + git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY); + git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY); + git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY); +} + +void test_core_mkdir__absolute(void) +{ + git_str path = GIT_STR_INIT; + + cl_set_cleanup(cleanup_basic_dirs, NULL); + + git_str_joinpath(&path, clar_sandbox_path(), "d0"); + + /* make a directory */ + cl_assert(!git_fs_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(git_fs_path_isdir(path.ptr)); + + git_str_joinpath(&path, path.ptr, "subdir"); + cl_assert(!git_fs_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(git_fs_path_isdir(path.ptr)); + + /* ensure mkdir_r works for a single subdir */ + git_str_joinpath(&path, path.ptr, "another"); + cl_assert(!git_fs_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); + cl_assert(git_fs_path_isdir(path.ptr)); + + /* ensure mkdir_r works */ + git_str_joinpath(&path, clar_sandbox_path(), "d1/foo/bar/asdf"); + cl_assert(!git_fs_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); + cl_assert(git_fs_path_isdir(path.ptr)); + + /* ensure we don't imply recursive */ + git_str_joinpath(&path, clar_sandbox_path(), "d2/foo/bar/asdf"); + cl_assert(!git_fs_path_isdir(path.ptr)); + cl_git_fail(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(!git_fs_path_isdir(path.ptr)); + + git_str_dispose(&path); +} + +void test_core_mkdir__basic(void) +{ + cl_set_cleanup(cleanup_basic_dirs, NULL); + + /* make a directory */ + cl_assert(!git_fs_path_isdir("d0")); + cl_git_pass(git_futils_mkdir("d0", 0755, 0)); + cl_assert(git_fs_path_isdir("d0")); + + /* make a path */ + cl_assert(!git_fs_path_isdir("d1")); + cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", 0755, GIT_MKDIR_PATH)); + cl_assert(git_fs_path_isdir("d1")); + cl_assert(git_fs_path_isdir("d1/d1.1")); + cl_assert(git_fs_path_isdir("d1/d1.1/d1.2")); + + /* make a dir exclusively */ + cl_assert(!git_fs_path_isdir("d2")); + cl_git_pass(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL)); + cl_assert(git_fs_path_isdir("d2")); + + /* make exclusive failure */ + cl_git_fail(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL)); + + /* make a path exclusively */ + cl_assert(!git_fs_path_isdir("d3")); + cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_assert(git_fs_path_isdir("d3")); + cl_assert(git_fs_path_isdir("d3/d3.1/d3.2")); + + /* make exclusive path failure */ + cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + /* ??? Should EXCL only apply to the last item in the path? */ + + /* path with trailing slash? */ + cl_assert(!git_fs_path_isdir("d4")); + cl_git_pass(git_futils_mkdir("d4/d4.1/", 0755, GIT_MKDIR_PATH)); + cl_assert(git_fs_path_isdir("d4/d4.1")); +} + +static void cleanup_basedir(void *ref) +{ + GIT_UNUSED(ref); + git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY); +} + +void test_core_mkdir__with_base(void) +{ +#define BASEDIR "base/dir/here" + + cl_set_cleanup(cleanup_basedir, NULL); + + cl_git_pass(git_futils_mkdir(BASEDIR, 0755, GIT_MKDIR_PATH)); + + cl_git_pass(git_futils_mkdir_relative("a", BASEDIR, 0755, 0, NULL)); + cl_assert(git_fs_path_isdir(BASEDIR "/a")); + + cl_git_pass(git_futils_mkdir_relative("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH, NULL)); + cl_assert(git_fs_path_isdir(BASEDIR "/b/b1/b2")); + + /* exclusive with existing base */ + cl_git_pass(git_futils_mkdir_relative("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); + + /* fail: exclusive with duplicated suffix */ + cl_git_fail(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); + + /* fail: exclusive with any duplicated component */ + cl_git_fail(git_futils_mkdir_relative("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); + + /* success: exclusive without path */ + cl_git_pass(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL, NULL)); + + /* path with shorter base and existing dirs */ + cl_git_pass(git_futils_mkdir_relative("dir/here/d/", "base", 0755, GIT_MKDIR_PATH, NULL)); + cl_assert(git_fs_path_isdir("base/dir/here/d")); + + /* fail: path with shorter base and existing dirs */ + cl_git_fail(git_futils_mkdir_relative("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); + + /* fail: base with missing components */ + cl_git_fail(git_futils_mkdir_relative("f/", "base/missing", 0755, GIT_MKDIR_PATH, NULL)); + + /* success: shift missing component to path */ + cl_git_pass(git_futils_mkdir_relative("missing/f/", "base/", 0755, GIT_MKDIR_PATH, NULL)); +} + +static void cleanup_chmod_root(void *ref) +{ + mode_t *mode = ref; + + if (mode != NULL) { + (void)p_umask(*mode); + git__free(mode); + } + + git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY); +} + +#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __func__, __LINE__) + +static void check_mode_at_line( + mode_t expected, mode_t actual, + const char *file, const char *func, int line) +{ + /* FAT filesystems don't support exec bit, nor group/world bits */ + if (!cl_is_chmod_supported()) { + expected &= 0600; + actual &= 0600; + } + + clar__assert_equal( + file, func, line, "expected_mode != actual_mode", 1, + "%07o", (int)expected, (int)(actual & 0777)); +} + +void test_core_mkdir__chmods(void) +{ + struct stat st; + mode_t *old = git__malloc(sizeof(mode_t)); + *old = p_umask(022); + + cl_set_cleanup(cleanup_chmod_root, old); + + cl_git_pass(git_futils_mkdir("r", 0777, 0)); + + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL)); + + cl_git_pass(git_fs_path_lstat("r/mode", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode/is", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode/is/important", &st)); + check_mode(0755, st.st_mode); + + cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL)); + + cl_git_pass(git_fs_path_lstat("r/mode2", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode2/is2", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode2/is2/important2", &st)); + check_mode(0777, st.st_mode); + + cl_git_pass(git_futils_mkdir_relative("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL)); + + cl_git_pass(git_fs_path_lstat("r/mode3", &st)); + check_mode(0777, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode3/is3", &st)); + check_mode(0777, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode3/is3/important3", &st)); + check_mode(0777, st.st_mode); + + /* test that we chmod existing dir */ + + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL)); + + cl_git_pass(git_fs_path_lstat("r/mode", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode/is", &st)); + check_mode(0755, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode/is/important", &st)); + check_mode(0777, st.st_mode); + + /* test that we chmod even existing dirs if CHMOD_PATH is set */ + + cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL)); + + cl_git_pass(git_fs_path_lstat("r/mode2", &st)); + check_mode(0777, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode2/is2", &st)); + check_mode(0777, st.st_mode); + cl_git_pass(git_fs_path_lstat("r/mode2/is2/important2.1", &st)); + check_mode(0777, st.st_mode); +} + +void test_core_mkdir__keeps_parent_symlinks(void) +{ +#ifndef GIT_WIN32 + git_str path = GIT_STR_INIT; + + cl_set_cleanup(cleanup_basic_dirs, NULL); + + /* make a directory */ + cl_assert(!git_fs_path_isdir("d0")); + cl_git_pass(git_futils_mkdir("d0", 0755, 0)); + cl_assert(git_fs_path_isdir("d0")); + + cl_must_pass(symlink("d0", "d1")); + cl_assert(git_fs_path_islink("d1")); + + cl_git_pass(git_futils_mkdir("d1/foo/bar", 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS)); + cl_assert(git_fs_path_islink("d1")); + cl_assert(git_fs_path_isdir("d1/foo/bar")); + cl_assert(git_fs_path_isdir("d0/foo/bar")); + + cl_must_pass(symlink("d0", "d2")); + cl_assert(git_fs_path_islink("d2")); + + git_str_joinpath(&path, clar_sandbox_path(), "d2/other/dir"); + + cl_git_pass(git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS)); + cl_assert(git_fs_path_islink("d2")); + cl_assert(git_fs_path_isdir("d2/other/dir")); + cl_assert(git_fs_path_isdir("d0/other/dir")); + + git_str_dispose(&path); +#endif +} + +void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) +{ + struct stat st; + mode_t *old; + + /* FAT filesystems don't support exec bit, nor group/world bits */ + if (!cl_is_chmod_supported()) + return; + + cl_assert((old = git__malloc(sizeof(mode_t))) != NULL); + *old = p_umask(022); + cl_set_cleanup(cleanup_chmod_root, old); + + cl_git_pass(git_futils_mkdir("r", 0777, 0)); + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL)); + cl_git_pass(git_fs_path_lstat("r/mode", &st)); + check_mode(0755, st.st_mode); + + cl_must_pass(p_chmod("r/mode", 0111)); + cl_git_pass(git_fs_path_lstat("r/mode", &st)); + check_mode(0111, st.st_mode); + + cl_git_pass( + git_futils_mkdir_relative("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH, NULL)); + cl_git_pass(git_fs_path_lstat("r/mode/is/okay/inside", &st)); + check_mode(0755, st.st_mode); + + cl_must_pass(p_chmod("r/mode", 0777)); +} diff --git a/tests/libgit2/core/oid.c b/tests/libgit2/core/oid.c new file mode 100644 index 000000000..894feadf6 --- /dev/null +++ b/tests/libgit2/core/oid.c @@ -0,0 +1,79 @@ +#include "clar_libgit2.h" +#include "oid.h" + +static git_oid id; +static git_oid idp; +static git_oid idm; +const char *str_oid = "ae90f12eea699729ed24555e40b9fd669da12a12"; +const char *str_oid_p = "ae90f12eea699729ed"; +const char *str_oid_m = "ae90f12eea699729ed24555e40b9fd669da12a12THIS IS EXTRA TEXT THAT SHOULD GET IGNORED"; + +void test_core_oid__initialize(void) +{ + cl_git_pass(git_oid_fromstr(&id, str_oid)); + cl_git_pass(git_oid_fromstrp(&idp, str_oid_p)); + cl_git_fail(git_oid_fromstrp(&idm, str_oid_m)); +} + +void test_core_oid__streq(void) +{ + cl_assert_equal_i(0, git_oid_streq(&id, str_oid)); + cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + + cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeef")); + cl_assert_equal_i(-1, git_oid_streq(&id, "I'm not an oid.... :)")); + + cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed0000000000000000000000")); + cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ed1")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ec")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + + cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeef")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "I'm not an oid.... :)")); +} + +void test_core_oid__strcmp(void) +{ + cl_assert_equal_i(0, git_oid_strcmp(&id, str_oid)); + cl_assert(git_oid_strcmp(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0); + + cl_assert(git_oid_strcmp(&id, "deadbeef") < 0); + cl_assert_equal_i(-1, git_oid_strcmp(&id, "I'm not an oid.... :)")); + + cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed0000000000000000000000")); + cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed")); + cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ed1") < 0); + cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ec") > 0); + cl_assert(git_oid_strcmp(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0); + + cl_assert(git_oid_strcmp(&idp, "deadbeef") < 0); + cl_assert_equal_i(-1, git_oid_strcmp(&idp, "I'm not an oid.... :)")); +} + +void test_core_oid__ncmp(void) +{ + cl_assert(!git_oid_ncmp(&id, &idp, 0)); + cl_assert(!git_oid_ncmp(&id, &idp, 1)); + cl_assert(!git_oid_ncmp(&id, &idp, 2)); + cl_assert(!git_oid_ncmp(&id, &idp, 17)); + cl_assert(!git_oid_ncmp(&id, &idp, 18)); + cl_assert(git_oid_ncmp(&id, &idp, 19)); + cl_assert(git_oid_ncmp(&id, &idp, 40)); + cl_assert(git_oid_ncmp(&id, &idp, 41)); + cl_assert(git_oid_ncmp(&id, &idp, 42)); + + cl_assert(!git_oid_ncmp(&id, &id, 0)); + cl_assert(!git_oid_ncmp(&id, &id, 1)); + cl_assert(!git_oid_ncmp(&id, &id, 39)); + cl_assert(!git_oid_ncmp(&id, &id, 40)); + cl_assert(!git_oid_ncmp(&id, &id, 41)); +} + +void test_core_oid__is_hexstr(void) +{ + cl_assert(git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + cl_assert(!git_oid__is_hexstr("deadbeefdeadbeef")); + cl_assert(!git_oid__is_hexstr("zeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + cl_assert(!git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef1")); +} diff --git a/tests/libgit2/core/oidmap.c b/tests/libgit2/core/oidmap.c new file mode 100644 index 000000000..7f98287a6 --- /dev/null +++ b/tests/libgit2/core/oidmap.c @@ -0,0 +1,127 @@ +#include "clar_libgit2.h" +#include "oidmap.h" + +static struct { + git_oid oid; + size_t extra; +} test_oids[0x0FFF]; + +static git_oidmap *g_map; + +void test_core_oidmap__initialize(void) +{ + uint32_t i, j; + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) { + uint32_t segment = i / 8; + int modi = i - (segment * 8); + + test_oids[i].extra = i; + + for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) { + test_oids[i].oid.id[j * 4 ] = (unsigned char)modi; + test_oids[i].oid.id[j * 4 + 1] = (unsigned char)(modi >> 8); + test_oids[i].oid.id[j * 4 + 2] = (unsigned char)(modi >> 16); + test_oids[i].oid.id[j * 4 + 3] = (unsigned char)(modi >> 24); + } + + test_oids[i].oid.id[ 8] = (unsigned char)i; + test_oids[i].oid.id[ 9] = (unsigned char)(i >> 8); + test_oids[i].oid.id[10] = (unsigned char)(i >> 16); + test_oids[i].oid.id[11] = (unsigned char)(i >> 24); + } + + cl_git_pass(git_oidmap_new(&g_map)); +} + +void test_core_oidmap__cleanup(void) +{ + git_oidmap_free(g_map); +} + +void test_core_oidmap__basic(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) { + cl_assert(!git_oidmap_exists(g_map, &test_oids[i].oid)); + cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i])); + } + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) { + cl_assert(git_oidmap_exists(g_map, &test_oids[i].oid)); + cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]); + } +} + +void test_core_oidmap__hash_collision(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) { + cl_assert(!git_oidmap_exists(g_map, &test_oids[i].oid)); + cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i])); + } + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) { + cl_assert(git_oidmap_exists(g_map, &test_oids[i].oid)); + cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]); + } +} + +void test_core_oidmap__get_succeeds_with_existing_keys(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) + cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i])); + + for (i = 0; i < ARRAY_SIZE(test_oids); ++i) + cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]); +} + +void test_core_oidmap__get_fails_with_nonexisting_key(void) +{ + size_t i; + + /* Do _not_ add last OID to verify that we cannot look it up */ + for (i = 0; i < ARRAY_SIZE(test_oids) - 1; ++i) + cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i])); + + cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[ARRAY_SIZE(test_oids) - 1].oid), NULL); +} + +void test_core_oidmap__setting_oid_persists(void) +{ + git_oid oids[] = { + {{ 0x01 }}, + {{ 0x02 }}, + {{ 0x03 }} + }; + + cl_git_pass(git_oidmap_set(g_map, &oids[0], "one")); + cl_git_pass(git_oidmap_set(g_map, &oids[1], "two")); + cl_git_pass(git_oidmap_set(g_map, &oids[2], "three")); + + cl_assert_equal_s(git_oidmap_get(g_map, &oids[0]), "one"); + cl_assert_equal_s(git_oidmap_get(g_map, &oids[1]), "two"); + cl_assert_equal_s(git_oidmap_get(g_map, &oids[2]), "three"); +} + +void test_core_oidmap__setting_existing_key_updates(void) +{ + git_oid oids[] = { + {{ 0x01 }}, + {{ 0x02 }}, + {{ 0x03 }} + }; + + cl_git_pass(git_oidmap_set(g_map, &oids[0], "one")); + cl_git_pass(git_oidmap_set(g_map, &oids[1], "two")); + cl_git_pass(git_oidmap_set(g_map, &oids[2], "three")); + cl_assert_equal_i(git_oidmap_size(g_map), 3); + + cl_git_pass(git_oidmap_set(g_map, &oids[1], "other")); + cl_assert_equal_i(git_oidmap_size(g_map), 3); + + cl_assert_equal_s(git_oidmap_get(g_map, &oids[1]), "other"); +} diff --git a/tests/libgit2/core/opts.c b/tests/libgit2/core/opts.c new file mode 100644 index 000000000..e8f65d510 --- /dev/null +++ b/tests/libgit2/core/opts.c @@ -0,0 +1,71 @@ +#include "clar_libgit2.h" +#include "cache.h" + +void test_core_opts__cleanup(void) +{ + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0)); +} + +void test_core_opts__readwrite(void) +{ + size_t old_val = 0; + size_t new_val = 0; + + git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &old_val); + git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, (size_t)1234); + git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val); + + cl_assert(new_val == 1234); + + git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, old_val); + git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val); + + cl_assert(new_val == old_val); +} + +void test_core_opts__invalid_option(void) +{ + cl_git_fail(git_libgit2_opts(-1, "foobar")); +} + +void test_core_opts__extensions_query(void) +{ + git_strarray out = { 0 }; + + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out)); + + cl_assert_equal_sz(out.count, 1); + cl_assert_equal_s("noop", out.strings[0]); + + git_strarray_dispose(&out); +} + +void test_core_opts__extensions_add(void) +{ + const char *in[] = { "foo" }; + git_strarray out = { 0 }; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in))); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out)); + + cl_assert_equal_sz(out.count, 2); + cl_assert_equal_s("noop", out.strings[0]); + cl_assert_equal_s("foo", out.strings[1]); + + git_strarray_dispose(&out); +} + +void test_core_opts__extensions_remove(void) +{ + const char *in[] = { "bar", "!negate", "!noop", "baz" }; + git_strarray out = { 0 }; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in))); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out)); + + cl_assert_equal_sz(out.count, 2); + cl_assert_equal_s("bar", out.strings[0]); + cl_assert_equal_s("baz", out.strings[1]); + + git_strarray_dispose(&out); +} diff --git a/tests/libgit2/core/path.c b/tests/libgit2/core/path.c new file mode 100644 index 000000000..a0ae77f1c --- /dev/null +++ b/tests/libgit2/core/path.c @@ -0,0 +1,739 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "fs_path.h" + +static char *path_save; + +void test_core_path__initialize(void) +{ + path_save = cl_getenv("PATH"); +} + +void test_core_path__cleanup(void) +{ + cl_setenv("PATH", path_save); + git__free(path_save); + path_save = NULL; +} + +static void +check_dirname(const char *A, const char *B) +{ + git_str dir = GIT_STR_INIT; + char *dir2; + + cl_assert(git_fs_path_dirname_r(&dir, A) >= 0); + cl_assert_equal_s(B, dir.ptr); + git_str_dispose(&dir); + + cl_assert((dir2 = git_fs_path_dirname(A)) != NULL); + cl_assert_equal_s(B, dir2); + git__free(dir2); +} + +static void +check_basename(const char *A, const char *B) +{ + git_str base = GIT_STR_INIT; + char *base2; + + cl_assert(git_fs_path_basename_r(&base, A) >= 0); + cl_assert_equal_s(B, base.ptr); + git_str_dispose(&base); + + cl_assert((base2 = git_fs_path_basename(A)) != NULL); + cl_assert_equal_s(B, base2); + git__free(base2); +} + +static void +check_joinpath(const char *path_a, const char *path_b, const char *expected_path) +{ + git_str joined_path = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b)); + cl_assert_equal_s(expected_path, joined_path.ptr); + + git_str_dispose(&joined_path); +} + +static void +check_joinpath_n( + const char *path_a, + const char *path_b, + const char *path_c, + const char *path_d, + const char *expected_path) +{ + git_str joined_path = GIT_STR_INIT; + + cl_git_pass(git_str_join_n(&joined_path, '/', 4, + path_a, path_b, path_c, path_d)); + cl_assert_equal_s(expected_path, joined_path.ptr); + + git_str_dispose(&joined_path); +} + +static void check_setenv(const char* name, const char* value) +{ + char* check; + + cl_git_pass(cl_setenv(name, value)); + check = cl_getenv(name); + + if (value) + cl_assert_equal_s(value, check); + else + cl_assert(check == NULL); + + git__free(check); +} + +/* get the dirname of a path */ +void test_core_path__00_dirname(void) +{ + check_dirname(NULL, "."); + check_dirname("", "."); + check_dirname("a", "."); + check_dirname("/", "/"); + check_dirname("/usr", "/"); + check_dirname("/usr/", "/"); + check_dirname("/usr/lib", "/usr"); + check_dirname("/usr/lib/", "/usr"); + check_dirname("/usr/lib//", "/usr"); + check_dirname("usr/lib", "usr"); + check_dirname("usr/lib/", "usr"); + check_dirname("usr/lib//", "usr"); + check_dirname(".git/", "."); + + check_dirname(REP16("/abc"), REP15("/abc")); + +#ifdef GIT_WIN32 + check_dirname("C:/", "C:/"); + check_dirname("C:", "C:/"); + check_dirname("C:/path/", "C:/"); + check_dirname("C:/path", "C:/"); + check_dirname("//computername/", "//computername/"); + check_dirname("//computername", "//computername/"); + check_dirname("//computername/path/", "//computername/"); + check_dirname("//computername/path", "//computername/"); + check_dirname("//computername/sub/path/", "//computername/sub"); + check_dirname("//computername/sub/path", "//computername/sub"); +#endif +} + +/* get the base name of a path */ +void test_core_path__01_basename(void) +{ + check_basename(NULL, "."); + check_basename("", "."); + check_basename("a", "a"); + check_basename("/", "/"); + check_basename("/usr", "usr"); + check_basename("/usr/", "usr"); + check_basename("/usr/lib", "lib"); + check_basename("/usr/lib//", "lib"); + check_basename("usr/lib", "lib"); + + check_basename(REP16("/abc"), "abc"); + check_basename(REP1024("/abc"), "abc"); +} + +/* properly join path components */ +void test_core_path__05_joins(void) +{ + check_joinpath("", "", ""); + check_joinpath("", "a", "a"); + check_joinpath("", "/a", "/a"); + check_joinpath("a", "", "a/"); + check_joinpath("a", "/", "a/"); + check_joinpath("a", "b", "a/b"); + check_joinpath("/", "a", "/a"); + check_joinpath("/", "", "/"); + check_joinpath("/a", "/b", "/a/b"); + check_joinpath("/a", "/b/", "/a/b/"); + check_joinpath("/a/", "b/", "/a/b/"); + check_joinpath("/a/", "/b/", "/a/b/"); + + check_joinpath("/abcd", "/defg", "/abcd/defg"); + check_joinpath("/abcd", "/defg/", "/abcd/defg/"); + check_joinpath("/abcd/", "defg/", "/abcd/defg/"); + check_joinpath("/abcd/", "/defg/", "/abcd/defg/"); + + check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678"); + check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/"); + check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/"); + + check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/"); + check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/")); + check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/"); + + check_joinpath(REP1024("aaaa"), REP1024("bbbb"), + REP1024("aaaa") "/" REP1024("bbbb")); + check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"), + REP1024("/aaaa") REP1024("/bbbb")); +} + +/* properly join path components for more than one path */ +void test_core_path__06_long_joins(void) +{ + check_joinpath_n("", "", "", "", ""); + check_joinpath_n("", "a", "", "", "a/"); + check_joinpath_n("a", "", "", "", "a/"); + check_joinpath_n("", "", "", "a", "a"); + check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/"); + check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d"); + check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop"); + check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/"); + check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/"); + + check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"), + REP1024("a") "/" REP1024("b") "/" + REP1024("c") "/" REP1024("d")); + check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"), + REP1024("/a") REP1024("/b") + REP1024("/c") REP1024("/d")); +} + + +static void +check_path_to_dir( + const char* path, + const char* expected) +{ + git_str tgt = GIT_STR_INIT; + + git_str_sets(&tgt, path); + cl_git_pass(git_fs_path_to_dir(&tgt)); + cl_assert_equal_s(expected, tgt.ptr); + + git_str_dispose(&tgt); +} + +static void +check_string_to_dir( + const char* path, + size_t maxlen, + const char* expected) +{ + size_t len = strlen(path); + char *buf = git__malloc(len + 2); + cl_assert(buf); + + strncpy(buf, path, len + 2); + + git_fs_path_string_to_dir(buf, maxlen); + + cl_assert_equal_s(expected, buf); + + git__free(buf); +} + +/* convert paths to dirs */ +void test_core_path__07_path_to_dir(void) +{ + check_path_to_dir("", ""); + check_path_to_dir(".", "./"); + check_path_to_dir("./", "./"); + check_path_to_dir("a/", "a/"); + check_path_to_dir("ab", "ab/"); + /* make sure we try just under and just over an expansion that will + * require a realloc + */ + check_path_to_dir("abcdef", "abcdef/"); + check_path_to_dir("abcdefg", "abcdefg/"); + check_path_to_dir("abcdefgh", "abcdefgh/"); + check_path_to_dir("abcdefghi", "abcdefghi/"); + check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/"); + check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/"); + + check_string_to_dir("", 1, ""); + check_string_to_dir(".", 1, "."); + check_string_to_dir(".", 2, "./"); + check_string_to_dir(".", 3, "./"); + check_string_to_dir("abcd", 3, "abcd"); + check_string_to_dir("abcd", 4, "abcd"); + check_string_to_dir("abcd", 5, "abcd/"); + check_string_to_dir("abcd", 6, "abcd/"); +} + +/* join path to itself */ +void test_core_path__08_self_join(void) +{ + git_str path = GIT_STR_INIT; + size_t asize = 0; + + asize = path.asize; + cl_git_pass(git_str_sets(&path, "/foo")); + cl_assert_equal_s(path.ptr, "/foo"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string")); + cl_assert_equal_s(path.ptr, "/foo/this is a new string"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer")); + cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer"); + cl_assert(asize < path.asize); + + git_str_dispose(&path); + cl_git_pass(git_str_sets(&path, "/foo/bar")); + + cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz")); + cl_assert_equal_s(path.ptr, "/bar/baz"); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc")); + cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc"); + cl_assert(asize < path.asize); + + git_str_dispose(&path); +} + +static void check_percent_decoding(const char *expected_result, const char *input) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git__percent_decode(&buf, input)); + cl_assert_equal_s(expected_result, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +void test_core_path__09_percent_decode(void) +{ + check_percent_decoding("abcd", "abcd"); + check_percent_decoding("a2%", "a2%"); + check_percent_decoding("a2%3", "a2%3"); + check_percent_decoding("a2%%3", "a2%%3"); + check_percent_decoding("a2%3z", "a2%3z"); + check_percent_decoding("a,", "a%2c"); + check_percent_decoding("a21", "a2%31"); + check_percent_decoding("a2%1", "a2%%31"); + check_percent_decoding("a bc ", "a%20bc%20"); + check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED"); +} + +static void check_fromurl(const char *expected_result, const char *input, int should_fail) +{ + git_str buf = GIT_STR_INIT; + + assert(should_fail || expected_result); + + if (!should_fail) { + cl_git_pass(git_fs_path_fromurl(&buf, input)); + cl_assert_equal_s(expected_result, git_str_cstr(&buf)); + } else + cl_git_fail(git_fs_path_fromurl(&buf, input)); + + git_str_dispose(&buf); +} + +#ifdef GIT_WIN32 +#define ABS_PATH_MARKER "" +#else +#define ABS_PATH_MARKER "/" +#endif + +void test_core_path__10_fromurl(void) +{ + /* Failing cases */ + check_fromurl(NULL, "a", 1); + check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file:///", 1); + check_fromurl(NULL, "file:////", 1); + check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1); + + /* Passing cases */ + check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0); +} + +typedef struct { + int expect_idx; + int cancel_after; + char **expect; +} check_walkup_info; + +#define CANCEL_VALUE 1234 + +static int check_one_walkup_step(void *ref, const char *path) +{ + check_walkup_info *info = (check_walkup_info *)ref; + + if (!info->cancel_after) { + cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); + return CANCEL_VALUE; + } + info->cancel_after--; + + cl_assert(info->expect[info->expect_idx] != NULL); + cl_assert_equal_s(info->expect[info->expect_idx], path); + info->expect_idx++; + + return 0; +} + +void test_core_path__11_walkup(void) +{ + git_str p = GIT_STR_INIT; + + char *expect[] = { + /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 7 */ "this_is_a_path", "", NULL, + /* 8 */ "this_is_a_path/", "", NULL, + /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL, + /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL, + /* 11 */ "a/b/c", "a/b/", "a/", "", NULL, + /* 12 */ "a/b/c/", "a/b/", "a/", NULL, + /* 13 */ "", NULL, + /* 14 */ "/", NULL, + /* 15 */ NULL + }; + + char *root[] = { + /* 1 */ NULL, + /* 2 */ NULL, + /* 3 */ "/", + /* 4 */ "", + /* 5 */ "/a/b", + /* 6 */ "/a/b/", + /* 7 */ NULL, + /* 8 */ NULL, + /* 9 */ NULL, + /* 10 */ NULL, + /* 11 */ NULL, + /* 12 */ "a/", + /* 13 */ NULL, + /* 14 */ NULL, + }; + + int i, j; + check_walkup_info info; + + info.expect = expect; + info.cancel_after = -1; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_str_sets(&p, expect[i]); + + info.expect_idx = i; + cl_git_pass( + git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + cl_assert_equal_s(p.ptr, expect[i]); + cl_assert(expect[info.expect_idx] == NULL); + i = info.expect_idx; + } + + git_str_dispose(&p); +} + +void test_core_path__11a_walkup_cancel(void) +{ + git_str p = GIT_STR_INIT; + int cancel[] = { 3, 2, 1, 0 }; + char *expect[] = { + "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, + "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, + "/a/b/c/d/e", "[CANCEL]", NULL, + "[CANCEL]", NULL, + NULL + }; + char *root[] = { NULL, NULL, "/", "", NULL }; + int i, j; + check_walkup_info info; + + info.expect = expect; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_str_sets(&p, expect[i]); + + info.cancel_after = cancel[j]; + info.expect_idx = i; + + cl_assert_equal_i( + CANCEL_VALUE, + git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + /* skip to next run of expectations */ + while (expect[i] != NULL) i++; + } + + git_str_dispose(&p); +} + +void test_core_path__12_offset_to_path_root(void) +{ + cl_assert(git_fs_path_root("non/rooted/path") == -1); + cl_assert(git_fs_path_root("/rooted/path") == 0); + +#ifdef GIT_WIN32 + /* Windows specific tests */ + cl_assert(git_fs_path_root("C:non/rooted/path") == -1); + cl_assert(git_fs_path_root("C:/rooted/path") == 2); + cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14); + cl_assert(git_fs_path_root("//computername/sharefolder") == 14); + cl_assert(git_fs_path_root("//computername") == -1); +#endif +} + +#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist" + +void test_core_path__13_cannot_prettify_a_non_existing_file(void) +{ + git_str p = GIT_STR_INIT; + + cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false); + cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL)); + cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL)); + + git_str_dispose(&p); +} + +void test_core_path__14_apply_relative(void) +{ + git_str p = GIT_STR_INIT; + + cl_git_pass(git_str_sets(&p, "/this/is/a/base")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../test")); + cl_assert_equal_s("/this/is/a/test", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end")); + cl_assert_equal_s("/this/is/the/end", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string")); + cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../..")); + cl_assert_equal_s("/this/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../")); + cl_assert_equal_s("/", p.ptr); + + cl_git_fail(git_fs_path_apply_relative(&p, "../../..")); + + + cl_git_pass(git_str_sets(&p, "d:/another/test")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../..")); + cl_assert_equal_s("d:/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/.")); + cl_assert_equal_s("d:/from/here/and/back/", p.ptr); + + + cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../another.git")); + cl_assert_equal_s("https://my.url.com/another.git", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch")); + cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "..")); + cl_assert_equal_s("https://my.url.com/full/path/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../")); + cl_assert_equal_s("https://", p.ptr); + + + cl_git_pass(git_str_sets(&p, "../../this/is/relative")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix")); + cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that")); + cl_assert_equal_s("../../that", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../there")); + cl_assert_equal_s("../../there", p.ptr); + git_str_dispose(&p); +} + +static void assert_resolve_relative( + git_str *buf, const char *expected, const char *path) +{ + cl_git_pass(git_str_sets(buf, path)); + cl_git_pass(git_fs_path_resolve_relative(buf, 0)); + cl_assert_equal_s(expected, buf->ptr); +} + +void test_core_path__15_resolve_relative(void) +{ + git_str buf = GIT_STR_INIT; + + assert_resolve_relative(&buf, "", ""); + assert_resolve_relative(&buf, "", "."); + assert_resolve_relative(&buf, "", "./"); + assert_resolve_relative(&buf, "..", ".."); + assert_resolve_relative(&buf, "../", "../"); + assert_resolve_relative(&buf, "..", "./.."); + assert_resolve_relative(&buf, "../", "./../"); + assert_resolve_relative(&buf, "../", "../."); + assert_resolve_relative(&buf, "../", ".././"); + assert_resolve_relative(&buf, "../..", "../.."); + assert_resolve_relative(&buf, "../../", "../../"); + + assert_resolve_relative(&buf, "/", "/"); + assert_resolve_relative(&buf, "/", "/."); + + assert_resolve_relative(&buf, "", "a/.."); + assert_resolve_relative(&buf, "", "a/../"); + assert_resolve_relative(&buf, "", "a/../."); + + assert_resolve_relative(&buf, "/a", "/a"); + assert_resolve_relative(&buf, "/a/", "/a/."); + assert_resolve_relative(&buf, "/", "/a/../"); + assert_resolve_relative(&buf, "/", "/a/../."); + assert_resolve_relative(&buf, "/", "/a/.././"); + + assert_resolve_relative(&buf, "a", "a"); + assert_resolve_relative(&buf, "a/", "a/"); + assert_resolve_relative(&buf, "a/", "a/."); + assert_resolve_relative(&buf, "a/", "a/./"); + + assert_resolve_relative(&buf, "a/b", "a//b"); + assert_resolve_relative(&buf, "a/b/c", "a/b/c"); + assert_resolve_relative(&buf, "b/c", "./b/c"); + assert_resolve_relative(&buf, "a/c", "a/./c"); + assert_resolve_relative(&buf, "a/b/", "a/b/."); + + assert_resolve_relative(&buf, "/a/b/c", "///a/b/c"); + assert_resolve_relative(&buf, "/", "////"); + assert_resolve_relative(&buf, "/a", "///a"); + assert_resolve_relative(&buf, "/", "///."); + assert_resolve_relative(&buf, "/", "///a/.."); + + assert_resolve_relative(&buf, "../../path", "../../test//../././path"); + assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d"); + + cl_git_pass(git_str_sets(&buf, "/..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/./..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/.//..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/../.")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/../.././../a")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "////..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + /* things that start with Windows network paths */ +#ifdef GIT_WIN32 + assert_resolve_relative(&buf, "//a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "//a/", "//a/b/.."); + assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c"); + + cl_git_pass(git_str_sets(&buf, "//a/b/../..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); +#else + assert_resolve_relative(&buf, "/a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "/a/", "//a/b/.."); + assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c"); + assert_resolve_relative(&buf, "/", "//a/b/../.."); +#endif + + git_str_dispose(&buf); +} + +#define assert_common_dirlen(i, p, q) \ + cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q))); + +void test_core_path__16_resolve_relative(void) +{ + assert_common_dirlen(0, "", ""); + assert_common_dirlen(0, "", "bar.txt"); + assert_common_dirlen(0, "foo.txt", "bar.txt"); + assert_common_dirlen(0, "foo.txt", ""); + assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt"); + assert_common_dirlen(0, "foo/bar.txt", "../foo.txt"); + + assert_common_dirlen(1, "/one.txt", "/two.txt"); + assert_common_dirlen(4, "foo/one.txt", "foo/two.txt"); + assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt"); + + assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt"); + assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt"); +} + +static void fix_path(git_str *s) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(s); +#else + char* c; + + for (c = s->ptr; *c; c++) { + if (*c == '/') + *c = '\\'; + } +#endif +} + +void test_core_path__find_exe_in_path(void) +{ + char *orig_path; + git_str sandbox_path = GIT_STR_INIT; + git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT, + dummy_path = GIT_STR_INIT; + +#ifdef GIT_WIN32 + static const char *bogus_path_1 = "c:\\does\\not\\exist\\"; + static const char *bogus_path_2 = "e:\\non\\existent"; +#else + static const char *bogus_path_1 = "/this/path/does/not/exist/"; + static const char *bogus_path_2 = "/non/existent"; +#endif + + orig_path = cl_getenv("PATH"); + + git_str_puts(&sandbox_path, clar_sandbox_path()); + git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file"); + cl_git_rewritefile(dummy_path.ptr, "this is a dummy file"); + + fix_path(&sandbox_path); + fix_path(&dummy_path); + + cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s", + bogus_path_1, GIT_PATH_LIST_SEPARATOR, + orig_path, GIT_PATH_LIST_SEPARATOR, + sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR, + bogus_path_2)); + + check_setenv("PATH", new_path.ptr); + + cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist")); + cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file")); + + cl_assert_equal_s(full_path.ptr, dummy_path.ptr); + + git_str_dispose(&full_path); + git_str_dispose(&new_path); + git_str_dispose(&dummy_path); + git_str_dispose(&sandbox_path); + git__free(orig_path); +} diff --git a/tests/libgit2/core/pool.c b/tests/libgit2/core/pool.c new file mode 100644 index 000000000..b07da0abd --- /dev/null +++ b/tests/libgit2/core/pool.c @@ -0,0 +1,92 @@ +#include "clar_libgit2.h" +#include "pool.h" +#include "git2/oid.h" + +void test_core_pool__0(void) +{ + int i; + git_pool p; + void *ptr; + + git_pool_init(&p, 1); + + for (i = 1; i < 10000; i *= 2) { + ptr = git_pool_malloc(&p, i); + cl_assert(ptr != NULL); + cl_assert(git_pool__ptr_in_pool(&p, ptr)); + cl_assert(!git_pool__ptr_in_pool(&p, &i)); + } + + git_pool_clear(&p); +} + +void test_core_pool__1(void) +{ + int i; + git_pool p; + + git_pool_init(&p, 1); + p.page_size = 4000; + + for (i = 2010; i > 0; i--) + cl_assert(git_pool_malloc(&p, i) != NULL); + +#ifndef GIT_DEBUG_POOL + /* with fixed page size, allocation must end up with these values */ + cl_assert_equal_i(591, git_pool__open_pages(&p)); +#endif + git_pool_clear(&p); + + git_pool_init(&p, 1); + p.page_size = 4120; + + for (i = 2010; i > 0; i--) + cl_assert(git_pool_malloc(&p, i) != NULL); + +#ifndef GIT_DEBUG_POOL + /* with fixed page size, allocation must end up with these values */ + cl_assert_equal_i(sizeof(void *) == 8 ? 575 : 573, git_pool__open_pages(&p)); +#endif + git_pool_clear(&p); +} + +static char to_hex[] = "0123456789abcdef"; + +void test_core_pool__2(void) +{ + git_pool p; + char oid_hex[GIT_OID_HEXSZ]; + git_oid *oid; + int i, j; + + memset(oid_hex, '0', sizeof(oid_hex)); + + git_pool_init(&p, sizeof(git_oid)); + p.page_size = 4000; + + for (i = 1000; i < 10000; i++) { + oid = git_pool_malloc(&p, 1); + cl_assert(oid != NULL); + + for (j = 0; j < 8; j++) + oid_hex[j] = to_hex[(i >> (4 * j)) & 0x0f]; + cl_git_pass(git_oid_fromstr(oid, oid_hex)); + } + +#ifndef GIT_DEBUG_POOL + /* with fixed page size, allocation must end up with these values */ + cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p)); +#endif + git_pool_clear(&p); +} + +void test_core_pool__strndup_limit(void) +{ + git_pool p; + + git_pool_init(&p, 1); + /* ensure 64 bit doesn't overflow */ + cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); + git_pool_clear(&p); +} + diff --git a/tests/libgit2/core/posix.c b/tests/libgit2/core/posix.c new file mode 100644 index 000000000..cba312913 --- /dev/null +++ b/tests/libgit2/core/posix.c @@ -0,0 +1,238 @@ +#ifndef _WIN32 +# include <arpa/inet.h> +# include <sys/socket.h> +# include <netinet/in.h> +#else +# include <ws2tcpip.h> +# ifdef _MSC_VER +# pragma comment(lib, "ws2_32") +# endif +#endif + +#include "clar_libgit2.h" +#include "futils.h" +#include "posix.h" + +void test_core_posix__initialize(void) +{ +#ifdef GIT_WIN32 + /* on win32, the WSA context needs to be initialized + * before any socket calls can be performed */ + WSADATA wsd; + + cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd)); + cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2); +#endif +} + +static bool supports_ipv6(void) +{ +#ifdef GIT_WIN32 + /* IPv6 is supported on Vista and newer */ + return git_has_win32_version(6, 0, 0); +#else + return 1; +#endif +} + +void test_core_posix__inet_pton(void) +{ + struct in_addr addr; + struct in6_addr addr6; + size_t i; + + struct in_addr_data { + const char *p; + const uint8_t n[4]; + }; + + struct in6_addr_data { + const char *p; + const uint8_t n[16]; + }; + + static struct in_addr_data in_addr_data[] = { + { "0.0.0.0", { 0, 0, 0, 0 } }, + { "10.42.101.8", { 10, 42, 101, 8 } }, + { "127.0.0.1", { 127, 0, 0, 1 } }, + { "140.177.10.12", { 140, 177, 10, 12 } }, + { "204.232.175.90", { 204, 232, 175, 90 } }, + { "255.255.255.255", { 255, 255, 255, 255 } }, + }; + + static struct in6_addr_data in6_addr_data[] = { + { "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }, + { "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }, + { "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } }, + { "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } }, + { "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } }, + }; + + /* Test some ipv4 addresses */ + for (i = 0; i < 6; i++) { + cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1); + cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0); + } + + /* Test some ipv6 addresses */ + if (supports_ipv6()) + { + for (i = 0; i < 6; i++) { + cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1); + cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0); + } + } + + /* Test some invalid strings */ + cl_assert(p_inet_pton(AF_INET, "", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0); + cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0); + + /* Test unsupported address families */ + cl_git_fail(p_inet_pton(INT_MAX-1, "52.472", &addr)); + cl_assert_equal_i(EAFNOSUPPORT, errno); +} + +void test_core_posix__utimes(void) +{ + struct p_timeval times[2]; + struct stat st; + time_t curtime; + int fd; + + /* test p_utimes */ + times[0].tv_sec = 1234567890; + times[0].tv_usec = 0; + times[1].tv_sec = 1234567890; + times[1].tv_usec = 0; + + cl_git_mkfile("foo", "Dummy file."); + cl_must_pass(p_utimes("foo", times)); + + cl_must_pass(p_stat("foo", &st)); + cl_assert_equal_i(1234567890, st.st_atime); + cl_assert_equal_i(1234567890, st.st_mtime); + + + /* test p_futimes */ + times[0].tv_sec = 1414141414; + times[0].tv_usec = 0; + times[1].tv_sec = 1414141414; + times[1].tv_usec = 0; + + cl_must_pass(fd = p_open("foo", O_RDWR)); + cl_must_pass(p_futimes(fd, times)); + cl_must_pass(p_close(fd)); + + cl_must_pass(p_stat("foo", &st)); + cl_assert_equal_i(1414141414, st.st_atime); + cl_assert_equal_i(1414141414, st.st_mtime); + + + /* test p_utimes with current time, assume that + * it takes < 5 seconds to get the time...! + */ + cl_must_pass(p_utimes("foo", NULL)); + + curtime = time(NULL); + cl_must_pass(p_stat("foo", &st)); + cl_assert((st.st_atime - curtime) < 5); + cl_assert((st.st_mtime - curtime) < 5); + + cl_must_pass(p_unlink("foo")); +} + +void test_core_posix__unlink_removes_symlink(void) +{ + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + clar__skip(); + + cl_git_mkfile("file", "Dummy file."); + cl_git_pass(git_futils_mkdir("dir", 0777, 0)); + + cl_must_pass(p_symlink("file", "file-symlink")); + cl_must_pass(p_symlink("dir", "dir-symlink")); + + cl_must_pass(p_unlink("file-symlink")); + cl_must_pass(p_unlink("dir-symlink")); + + cl_assert(git_fs_path_exists("file")); + cl_assert(git_fs_path_exists("dir")); + + cl_must_pass(p_unlink("file")); + cl_must_pass(p_rmdir("dir")); +} + +void test_core_posix__symlink_resolves_to_correct_type(void) +{ + git_str contents = GIT_STR_INIT; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + clar__skip(); + + cl_must_pass(git_futils_mkdir("dir", 0777, 0)); + cl_must_pass(git_futils_mkdir("file", 0777, 0)); + cl_git_mkfile("dir/file", "symlink target"); + + cl_git_pass(p_symlink("file", "dir/link")); + + cl_git_pass(git_futils_readbuffer(&contents, "dir/file")); + cl_assert_equal_s(contents.ptr, "symlink target"); + + cl_must_pass(p_unlink("dir/link")); + cl_must_pass(p_unlink("dir/file")); + cl_must_pass(p_rmdir("dir")); + cl_must_pass(p_rmdir("file")); + + git_str_dispose(&contents); +} + +void test_core_posix__relative_symlink(void) +{ + git_str contents = GIT_STR_INIT; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + clar__skip(); + + cl_must_pass(git_futils_mkdir("dir", 0777, 0)); + cl_git_mkfile("file", "contents"); + cl_git_pass(p_symlink("../file", "dir/link")); + cl_git_pass(git_futils_readbuffer(&contents, "dir/link")); + cl_assert_equal_s(contents.ptr, "contents"); + + cl_must_pass(p_unlink("file")); + cl_must_pass(p_unlink("dir/link")); + cl_must_pass(p_rmdir("dir")); + + git_str_dispose(&contents); +} + +void test_core_posix__symlink_to_file_across_dirs(void) +{ + git_str contents = GIT_STR_INIT; + + if (!git_fs_path_supports_symlinks(clar_sandbox_path())) + clar__skip(); + + /* + * Create a relative symlink that points into another + * directory. This used to not work on Win32, where we + * forgot to convert directory separators to + * Windows-style ones. + */ + cl_must_pass(git_futils_mkdir("dir", 0777, 0)); + cl_git_mkfile("dir/target", "symlink target"); + cl_git_pass(p_symlink("dir/target", "link")); + + cl_git_pass(git_futils_readbuffer(&contents, "dir/target")); + cl_assert_equal_s(contents.ptr, "symlink target"); + + cl_must_pass(p_unlink("dir/target")); + cl_must_pass(p_unlink("link")); + cl_must_pass(p_rmdir("dir")); + + git_str_dispose(&contents); +} diff --git a/tests/libgit2/core/pqueue.c b/tests/libgit2/core/pqueue.c new file mode 100644 index 000000000..2b90f4172 --- /dev/null +++ b/tests/libgit2/core/pqueue.c @@ -0,0 +1,150 @@ +#include "clar_libgit2.h" +#include "pqueue.h" + +static int cmp_ints(const void *v1, const void *v2) +{ + int i1 = *(int *)v1, i2 = *(int *)v2; + return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; +} + +void test_core_pqueue__items_are_put_in_order(void) +{ + git_pqueue pq; + int i, vals[20]; + + cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints)); + + for (i = 0; i < 20; ++i) { + if (i < 10) + vals[i] = 10 - i; /* 10 down to 1 */ + else + vals[i] = i + 1; /* 11 up to 20 */ + + cl_git_pass(git_pqueue_insert(&pq, &vals[i])); + } + + cl_assert_equal_i(20, git_pqueue_size(&pq)); + + for (i = 1; i <= 20; ++i) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(i, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +void test_core_pqueue__interleave_inserts_and_pops(void) +{ + git_pqueue pq; + int chunk, v, i, vals[200]; + + cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints)); + + for (v = 0, chunk = 20; chunk <= 200; chunk += 20) { + /* push the next 20 */ + for (; v < chunk; ++v) { + vals[v] = (v & 1) ? 200 - v : v; + cl_git_pass(git_pqueue_insert(&pq, &vals[v])); + } + + /* pop the lowest 10 */ + for (i = 0; i < 10; ++i) + (void)git_pqueue_pop(&pq); + } + + cl_assert_equal_i(100, git_pqueue_size(&pq)); + + /* at this point, we've popped 0-99 */ + + for (v = 100; v < 200; ++v) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(v, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +void test_core_pqueue__max_heap_size(void) +{ + git_pqueue pq; + int i, vals[100]; + + cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, cmp_ints)); + + for (i = 0; i < 100; ++i) { + vals[i] = (i & 1) ? 100 - i : i; + cl_git_pass(git_pqueue_insert(&pq, &vals[i])); + } + + cl_assert_equal_i(50, git_pqueue_size(&pq)); + + for (i = 50; i < 100; ++i) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(i, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +void test_core_pqueue__max_heap_size_without_comparison(void) +{ + git_pqueue pq; + int i, vals[100] = { 0 }; + + cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, NULL)); + + for (i = 0; i < 100; ++i) + cl_git_pass(git_pqueue_insert(&pq, &vals[i])); + + cl_assert_equal_i(50, git_pqueue_size(&pq)); + + /* As we have no comparison function, we cannot make any + * actual assumptions about which entries are part of the + * pqueue */ + for (i = 0; i < 50; ++i) + cl_assert(git_pqueue_pop(&pq)); + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +static int cmp_ints_like_commit_time(const void *a, const void *b) +{ + return *((const int *)a) < *((const int *)b); +} + +void test_core_pqueue__interleaved_pushes_and_pops(void) +{ + git_pqueue pq; + int i, j, *val; + static int commands[] = + { 6, 9, 8, 0, 5, 0, 7, 0, 4, 3, 0, 0, 0, 4, 0, 2, 0, 1, 0, 0, -1 }; + static int expected[] = + { 9, 8, 7, 6, 5, 4, 4, 3, 2, 1, -1 }; + + cl_git_pass(git_pqueue_init(&pq, 0, 10, cmp_ints_like_commit_time)); + + for (i = 0, j = 0; commands[i] >= 0; ++i) { + if (!commands[i]) { + cl_assert((val = git_pqueue_pop(&pq)) != NULL); + cl_assert_equal_i(expected[j], *val); + ++j; + } else { + cl_git_pass(git_pqueue_insert(&pq, &commands[i])); + } + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + git_pqueue_free(&pq); +} + diff --git a/tests/libgit2/core/qsort.c b/tests/libgit2/core/qsort.c new file mode 100644 index 000000000..efb79e6e9 --- /dev/null +++ b/tests/libgit2/core/qsort.c @@ -0,0 +1,90 @@ +#include "clar_libgit2.h" + +#define assert_sorted(a, cmp) \ + _assert_sorted(a, ARRAY_SIZE(a), sizeof(*a), cmp) + +struct big_entries { + char c[311]; +}; + +static void _assert_sorted(void *els, size_t n, size_t elsize, git__sort_r_cmp cmp) +{ + int8_t *p = els; + + git__qsort_r(p, n, elsize, cmp, NULL); + while (n-- > 1) { + cl_assert(cmp(p, p + elsize, NULL) <= 0); + p += elsize; + } +} + +static int cmp_big(const void *_a, const void *_b, void *payload) +{ + const struct big_entries *a = (const struct big_entries *)_a, *b = (const struct big_entries *)_b; + GIT_UNUSED(payload); + return (a->c[0] < b->c[0]) ? -1 : (a->c[0] > b->c[0]) ? +1 : 0; +} + +static int cmp_int(const void *_a, const void *_b, void *payload) +{ + int a = *(const int *)_a, b = *(const int *)_b; + GIT_UNUSED(payload); + return (a < b) ? -1 : (a > b) ? +1 : 0; +} + +static int cmp_str(const void *_a, const void *_b, void *payload) +{ + GIT_UNUSED(payload); + return strcmp((const char *) _a, (const char *) _b); +} + +void test_core_qsort__array_with_single_entry(void) +{ + int a[] = { 10 }; + assert_sorted(a, cmp_int); +} + +void test_core_qsort__array_with_equal_entries(void) +{ + int a[] = { 4, 4, 4, 4 }; + assert_sorted(a, cmp_int); +} + +void test_core_qsort__sorted_array(void) +{ + int a[] = { 1, 10 }; + assert_sorted(a, cmp_int); +} + +void test_core_qsort__unsorted_array(void) +{ + int a[] = { 123, 9, 412938, 10, 234, 89 }; + assert_sorted(a, cmp_int); +} + +void test_core_qsort__sorting_strings(void) +{ + char *a[] = { "foo", "bar", "baz" }; + assert_sorted(a, cmp_str); +} + +void test_core_qsort__sorting_big_entries(void) +{ + struct big_entries a[5]; + + memset(&a, 0, sizeof(a)); + + memset(a[0].c, 'w', sizeof(a[0].c) - 1); + memset(a[1].c, 'c', sizeof(a[1].c) - 1); + memset(a[2].c, 'w', sizeof(a[2].c) - 1); + memset(a[3].c, 'h', sizeof(a[3].c) - 1); + memset(a[4].c, 'a', sizeof(a[4].c) - 1); + + assert_sorted(a, cmp_big); + + cl_assert_equal_i(strspn(a[0].c, "a"), sizeof(a[0].c) - 1); + cl_assert_equal_i(strspn(a[1].c, "c"), sizeof(a[1].c) - 1); + cl_assert_equal_i(strspn(a[2].c, "h"), sizeof(a[2].c) - 1); + cl_assert_equal_i(strspn(a[3].c, "w"), sizeof(a[3].c) - 1); + cl_assert_equal_i(strspn(a[4].c, "w"), sizeof(a[4].c) - 1); +} diff --git a/tests/libgit2/core/regexp.c b/tests/libgit2/core/regexp.c new file mode 100644 index 000000000..8db5641e5 --- /dev/null +++ b/tests/libgit2/core/regexp.c @@ -0,0 +1,213 @@ +#include "clar_libgit2.h" + +#include <locale.h> + +#include "regexp.h" +#include "userdiff.h" + +#if LC_ALL > 0 +static const char *old_locales[LC_ALL]; +#endif + +static git_regexp regex; + +void test_core_regexp__initialize(void) +{ +#if LC_ALL > 0 + memset(&old_locales, 0, sizeof(old_locales)); +#endif +} + +void test_core_regexp__cleanup(void) +{ + git_regexp_dispose(®ex); +} + +static void try_set_locale(int category) +{ +#if LC_ALL > 0 + old_locales[category] = setlocale(category, NULL); +#endif + + if (!setlocale(category, "UTF-8") && + !setlocale(category, "c.utf8") && + !setlocale(category, "en_US.UTF-8")) + cl_skip(); + + if (MB_CUR_MAX == 1) + cl_fail("Expected locale to be switched to multibyte"); +} + + +void test_core_regexp__compile_ignores_global_locale_ctype(void) +{ + try_set_locale(LC_CTYPE); + cl_git_pass(git_regexp_compile(®ex, "[\xc0-\xff][\x80-\xbf]", 0)); +} + +void test_core_regexp__compile_ignores_global_locale_collate(void) +{ +#ifdef GIT_WIN32 + cl_skip(); +#endif + + try_set_locale(LC_COLLATE); + cl_git_pass(git_regexp_compile(®ex, "[\xc0-\xff][\x80-\xbf]", 0)); +} + +void test_core_regexp__regex_matches_digits_with_locale(void) +{ + char c, str[2]; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + try_set_locale(LC_COLLATE); + try_set_locale(LC_CTYPE); + + cl_git_pass(git_regexp_compile(®ex, "[[:digit:]]", 0)); + + str[1] = '\0'; + for (c = '0'; c <= '9'; c++) { + str[0] = c; + cl_git_pass(git_regexp_match(®ex, str)); + } +} + +void test_core_regexp__regex_matches_alphabet_with_locale(void) +{ + char c, str[2]; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + try_set_locale(LC_COLLATE); + try_set_locale(LC_CTYPE); + + cl_git_pass(git_regexp_compile(®ex, "[[:alpha:]]", 0)); + + str[1] = '\0'; + for (c = 'a'; c <= 'z'; c++) { + str[0] = c; + cl_git_pass(git_regexp_match(®ex, str)); + } + for (c = 'A'; c <= 'Z'; c++) { + str[0] = c; + cl_git_pass(git_regexp_match(®ex, str)); + } +} + +void test_core_regexp__compile_userdiff_regexps(void) +{ + size_t idx; + + for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) { + git_diff_driver_definition ddef = builtin_defs[idx]; + + cl_git_pass(git_regexp_compile(®ex, ddef.fns, ddef.flags)); + git_regexp_dispose(®ex); + + cl_git_pass(git_regexp_compile(®ex, ddef.words, 0)); + git_regexp_dispose(®ex); + } +} + +void test_core_regexp__simple_search_matches(void) +{ + cl_git_pass(git_regexp_compile(®ex, "a", 0)); + cl_git_pass(git_regexp_search(®ex, "a", 0, NULL)); +} + +void test_core_regexp__case_insensitive_search_matches(void) +{ + cl_git_pass(git_regexp_compile(®ex, "a", GIT_REGEXP_ICASE)); + cl_git_pass(git_regexp_search(®ex, "A", 0, NULL)); +} + +void test_core_regexp__nonmatching_search_returns_error(void) +{ + cl_git_pass(git_regexp_compile(®ex, "a", 0)); + cl_git_fail(git_regexp_search(®ex, "b", 0, NULL)); +} + +void test_core_regexp__search_finds_complete_match(void) +{ + git_regmatch matches[1]; + + cl_git_pass(git_regexp_compile(®ex, "abc", 0)); + cl_git_pass(git_regexp_search(®ex, "abc", 1, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 3); +} + +void test_core_regexp__search_finds_correct_offsets(void) +{ + git_regmatch matches[3]; + + cl_git_pass(git_regexp_compile(®ex, "(a*)(b*)", 0)); + cl_git_pass(git_regexp_search(®ex, "ab", 3, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 2); + cl_assert_equal_i(matches[1].start, 0); + cl_assert_equal_i(matches[1].end, 1); + cl_assert_equal_i(matches[2].start, 1); + cl_assert_equal_i(matches[2].end, 2); +} + +void test_core_regexp__search_finds_empty_group(void) +{ + git_regmatch matches[3]; + + cl_git_pass(git_regexp_compile(®ex, "(a*)(b*)c", 0)); + cl_git_pass(git_regexp_search(®ex, "ac", 3, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 2); + cl_assert_equal_i(matches[1].start, 0); + cl_assert_equal_i(matches[1].end, 1); + cl_assert_equal_i(matches[2].start, 1); + cl_assert_equal_i(matches[2].end, 1); +} + +void test_core_regexp__search_fills_matches_with_first_matching_groups(void) +{ + git_regmatch matches[2]; + + cl_git_pass(git_regexp_compile(®ex, "(a)(b)(c)", 0)); + cl_git_pass(git_regexp_search(®ex, "abc", 2, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 3); + cl_assert_equal_i(matches[1].start, 0); + cl_assert_equal_i(matches[1].end, 1); +} + +void test_core_regexp__search_skips_nonmatching_group(void) +{ + git_regmatch matches[4]; + + cl_git_pass(git_regexp_compile(®ex, "(a)(b)?(c)", 0)); + cl_git_pass(git_regexp_search(®ex, "ac", 4, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 2); + cl_assert_equal_i(matches[1].start, 0); + cl_assert_equal_i(matches[1].end, 1); + cl_assert_equal_i(matches[2].start, -1); + cl_assert_equal_i(matches[2].end, -1); + cl_assert_equal_i(matches[3].start, 1); + cl_assert_equal_i(matches[3].end, 2); +} + +void test_core_regexp__search_initializes_trailing_nonmatching_groups(void) +{ + git_regmatch matches[3]; + + cl_git_pass(git_regexp_compile(®ex, "(a)bc", 0)); + cl_git_pass(git_regexp_search(®ex, "abc", 3, matches)); + cl_assert_equal_i(matches[0].start, 0); + cl_assert_equal_i(matches[0].end, 3); + cl_assert_equal_i(matches[1].start, 0); + cl_assert_equal_i(matches[1].end, 1); + cl_assert_equal_i(matches[2].start, -1); + cl_assert_equal_i(matches[2].end, -1); +} diff --git a/tests/libgit2/core/rmdir.c b/tests/libgit2/core/rmdir.c new file mode 100644 index 000000000..f6c66b3a4 --- /dev/null +++ b/tests/libgit2/core/rmdir.c @@ -0,0 +1,120 @@ +#include "clar_libgit2.h" +#include "futils.h" + +static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test"; + +void test_core_rmdir__initialize(void) +{ + git_str path = GIT_STR_INIT; + + cl_must_pass(p_mkdir(empty_tmp_dir, 0777)); + + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one")); + cl_must_pass(p_mkdir(path.ptr, 0777)); + + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_one")); + cl_must_pass(p_mkdir(path.ptr, 0777)); + + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_two")); + cl_must_pass(p_mkdir(path.ptr, 0777)); + + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_two/three")); + cl_must_pass(p_mkdir(path.ptr, 0777)); + + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/two")); + cl_must_pass(p_mkdir(path.ptr, 0777)); + + git_str_dispose(&path); +} + +void test_core_rmdir__cleanup(void) +{ + if (git_fs_path_exists(empty_tmp_dir)) + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +/* make sure empty dir can be deleted recursively */ +void test_core_rmdir__delete_recursive(void) +{ + git_str path = GIT_STR_INIT; + cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one")); + cl_assert(git_fs_path_exists(git_str_cstr(&path))); + + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY)); + + cl_assert(!git_fs_path_exists(git_str_cstr(&path))); + + git_str_dispose(&path); +} + +/* make sure non-empty dir cannot be deleted recursively */ +void test_core_rmdir__fail_to_delete_non_empty_dir(void) +{ + git_str file = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&file, empty_tmp_dir, "/two/file.txt")); + + cl_git_mkfile(git_str_cstr(&file), "dummy"); + + cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY)); + + cl_must_pass(p_unlink(file.ptr)); + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY)); + + cl_assert(!git_fs_path_exists(empty_tmp_dir)); + + git_str_dispose(&file); +} + +void test_core_rmdir__keep_base(void) +{ + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_ROOT)); + cl_assert(git_fs_path_exists(empty_tmp_dir)); +} + +void test_core_rmdir__can_skip_non_empty_dir(void) +{ + git_str file = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&file, empty_tmp_dir, "/two/file.txt")); + + cl_git_mkfile(git_str_cstr(&file), "dummy"); + + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_NONEMPTY)); + cl_assert(git_fs_path_exists(git_str_cstr(&file)) == true); + + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES)); + cl_assert(git_fs_path_exists(empty_tmp_dir) == false); + + git_str_dispose(&file); +} + +void test_core_rmdir__can_remove_empty_parents(void) +{ + git_str file = GIT_STR_INIT; + + cl_git_pass( + git_str_joinpath(&file, empty_tmp_dir, "/one/two_two/three/file.txt")); + cl_git_mkfile(git_str_cstr(&file), "dummy"); + cl_assert(git_fs_path_isfile(git_str_cstr(&file))); + + cl_git_pass(git_futils_rmdir_r("one/two_two/three/file.txt", empty_tmp_dir, + GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS)); + + cl_assert(!git_fs_path_exists(git_str_cstr(&file))); + + git_str_rtruncate_at_char(&file, '/'); /* three (only contained file.txt) */ + cl_assert(!git_fs_path_exists(git_str_cstr(&file))); + + git_str_rtruncate_at_char(&file, '/'); /* two_two (only contained three) */ + cl_assert(!git_fs_path_exists(git_str_cstr(&file))); + + git_str_rtruncate_at_char(&file, '/'); /* one (contained two_one also) */ + cl_assert(git_fs_path_exists(git_str_cstr(&file))); + + cl_assert(git_fs_path_exists(empty_tmp_dir) == true); + + git_str_dispose(&file); + + cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY)); +} diff --git a/tests/libgit2/core/sha1.c b/tests/libgit2/core/sha1.c new file mode 100644 index 000000000..9ccdaab3c --- /dev/null +++ b/tests/libgit2/core/sha1.c @@ -0,0 +1,70 @@ +#include "clar_libgit2.h" +#include "hash.h" + +#define FIXTURE_DIR "sha1" + +void test_core_sha1__initialize(void) +{ + cl_fixture_sandbox(FIXTURE_DIR); +} + +void test_core_sha1__cleanup(void) +{ + cl_fixture_cleanup(FIXTURE_DIR); +} + +static int sha1_file(unsigned char *out, const char *filename) +{ + git_hash_ctx ctx; + char buf[2048]; + int fd, ret; + ssize_t read_len; + + fd = p_open(filename, O_RDONLY); + cl_assert(fd >= 0); + + cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1)); + + while ((read_len = p_read(fd, buf, 2048)) > 0) + cl_git_pass(git_hash_update(&ctx, buf, (size_t)read_len)); + + cl_assert_equal_i(0, read_len); + p_close(fd); + + ret = git_hash_final(out, &ctx); + git_hash_ctx_cleanup(&ctx); + + return ret; +} + +void test_core_sha1__sum(void) +{ + unsigned char expected[GIT_HASH_SHA1_SIZE] = { + 0x4e, 0x72, 0x67, 0x9e, 0x3e, 0xa4, 0xd0, 0x4e, 0x0c, 0x64, + 0x2f, 0x02, 0x9e, 0x61, 0xeb, 0x80, 0x56, 0xc7, 0xed, 0x94 + }; + unsigned char actual[GIT_HASH_SHA1_SIZE]; + + cl_git_pass(sha1_file(actual, FIXTURE_DIR "/hello_c")); + cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE)); +} + +/* test that sha1 collision detection works when enabled */ +void test_core_sha1__detect_collision_attack(void) +{ + unsigned char actual[GIT_HASH_SHA1_SIZE]; + unsigned char expected[GIT_HASH_SHA1_SIZE] = { + 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, + 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a + }; + +#ifdef GIT_SHA1_COLLISIONDETECT + GIT_UNUSED(&expected); + cl_git_fail(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf")); + cl_assert_equal_s("SHA1 collision attack detected", git_error_last()->message); +#else + cl_git_pass(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf")); + cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE)); +#endif +} + diff --git a/tests/libgit2/core/sortedcache.c b/tests/libgit2/core/sortedcache.c new file mode 100644 index 000000000..cb4e34efa --- /dev/null +++ b/tests/libgit2/core/sortedcache.c @@ -0,0 +1,363 @@ +#include "clar_libgit2.h" +#include "sortedcache.h" + +static int name_only_cmp(const void *a, const void *b) +{ + return strcmp(a, b); +} + +void test_core_sortedcache__name_only(void) +{ + git_sortedcache *sc; + void *item; + size_t pos; + + cl_git_pass(git_sortedcache_new( + &sc, 0, NULL, NULL, name_only_cmp, NULL)); + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm")); + cl_git_pass(git_sortedcache_upsert(&item, sc, "iii")); + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(5, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL); + cl_assert_equal_s("aaa", item); + cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL); + cl_assert_equal_s("mmm", item); + cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL); + cl_assert_equal_s("zzz", item); + cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bbb", item); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("iii", item); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("mmm", item); + cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL); + cl_assert_equal_s("zzz", item); + cl_assert(git_sortedcache_entry(sc, 5) == NULL); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii")); + cl_assert_equal_sz(2, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz")); + cl_assert_equal_sz(4, pos); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc")); + + cl_git_pass(git_sortedcache_clear(sc, true)); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + + git_sortedcache_free(sc); +} + +typedef struct { + int value; + char smaller_value; + char path[GIT_FLEX_ARRAY]; +} sortedcache_test_struct; + +static int sortedcache_test_struct_cmp(const void *a_, const void *b_) +{ + const sortedcache_test_struct *a = a_, *b = b_; + return strcmp(a->path, b->path); +} + +static void sortedcache_test_struct_free(void *payload, void *item_) +{ + sortedcache_test_struct *item = item_; + int *count = payload; + (*count)++; + item->smaller_value = 0; +} + +void test_core_sortedcache__in_memory(void) +{ + git_sortedcache *sc; + sortedcache_test_struct *item; + int free_count = 0; + + cl_git_pass(git_sortedcache_new( + &sc, offsetof(sortedcache_test_struct, path), + sortedcache_test_struct_free, &free_count, + sortedcache_test_struct_cmp, NULL)); + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa")); + item->value = 10; + item->smaller_value = 1; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb")); + item->value = 20; + item->smaller_value = 2; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz")); + item->value = 30; + item->smaller_value = 26; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm")); + item->value = 40; + item->smaller_value = 14; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii")); + item->value = 50; + item->smaller_value = 9; + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(5, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_rlock(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL); + cl_assert_equal_s("mmm", item->path); + cl_assert_equal_i(40, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "abc") == NULL); + + /* not on Windows: + * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one + */ + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bbb", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("iii", item->path); + cl_assert_equal_i(50, item->value); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("mmm", item->path); + cl_assert_equal_i(40, item->value); + cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_entry(sc, 5) == NULL); + + git_sortedcache_runlock(sc); + /* git_sortedcache_runlock(sc); */ + + cl_assert_equal_i(0, free_count); + + cl_git_pass(git_sortedcache_clear(sc, true)); + + cl_assert_equal_i(5, free_count); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + cl_assert(git_sortedcache_entry(sc, 0) == NULL); + + free_count = 0; + + cl_git_pass(git_sortedcache_wlock(sc)); + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing")); + item->value = 10; + item->smaller_value = 3; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again")); + item->value = 20; + item->smaller_value = 1; + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final")); + item->value = 30; + item->smaller_value = 2; + git_sortedcache_wunlock(sc); + + cl_assert_equal_sz(3, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL); + cl_assert_equal_s("testing", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL); + cl_assert_equal_s("again", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("again", item->path); + cl_assert_equal_i(20, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(30, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("testing", item->path); + cl_assert_equal_i(10, item->value); + cl_assert(git_sortedcache_entry(sc, 3) == NULL); + + { + size_t pos; + + cl_git_pass(git_sortedcache_wlock(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again")); + + cl_assert_equal_sz(2, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing")); + cl_assert_equal_sz(1, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing")); + + cl_assert_equal_sz(1, git_sortedcache_entrycount(sc)); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_remove(sc, pos)); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final")); + + cl_assert_equal_sz(0, git_sortedcache_entrycount(sc)); + + git_sortedcache_wunlock(sc); + } + + git_sortedcache_free(sc); + + cl_assert_equal_i(3, free_count); +} + +static void sortedcache_test_reload(git_sortedcache *sc) +{ + int count = 0; + git_str buf = GIT_STR_INIT; + char *scan, *after; + sortedcache_test_struct *item; + + cl_assert(git_sortedcache_lockandload(sc, &buf) > 0); + + cl_git_pass(git_sortedcache_clear(sc, false)); /* clear once we already have lock */ + + for (scan = buf.ptr; *scan; scan = after + 1) { + int val = strtol(scan, &after, 0); + cl_assert(after > scan); + scan = after; + + for (scan = after; git__isspace(*scan); ++scan) /* find start */; + for (after = scan; *after && *after != '\n'; ++after) /* find eol */; + *after = '\0'; + + cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan)); + + item->value = val; + item->smaller_value = (char)(count++); + } + + git_sortedcache_wunlock(sc); + + git_str_dispose(&buf); +} + +void test_core_sortedcache__on_disk(void) +{ + git_sortedcache *sc; + sortedcache_test_struct *item; + int free_count = 0; + size_t pos; + + cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n"); + + cl_git_pass(git_sortedcache_new( + &sc, offsetof(sortedcache_test_struct, path), + sortedcache_test_struct_free, &free_count, + sortedcache_test_struct_cmp, "cacheitems.txt")); + + /* should need to reload the first time */ + + sortedcache_test_reload(sc); + + /* test what we loaded */ + + cl_assert_equal_sz(3, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL); + cl_assert_equal_s("cde", item->path); + cl_assert_equal_i(30, item->value); + cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL); + cl_assert_equal_s("bcd", item->path); + cl_assert_equal_i(20, item->value); + cl_assert(git_sortedcache_entry(sc, 3) == NULL); + + /* should not need to reload this time */ + + cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL)); + + /* rewrite ondisk file and reload */ + + cl_assert_equal_i(0, free_count); + + cl_git_rewritefile( + "cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n"); + sortedcache_test_reload(sc); + + cl_assert_equal_i(3, free_count); + + /* test what we loaded */ + + cl_assert_equal_sz(4, git_sortedcache_entrycount(sc)); + + cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL); + cl_assert_equal_s("abc", item->path); + cl_assert_equal_i(100, item->value); + cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(10, item->value); + cl_assert(git_sortedcache_lookup(sc, "cde") == NULL); + + cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL); + cl_assert_equal_s("aaa", item->path); + cl_assert_equal_i(500, item->value); + cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL); + cl_assert_equal_s("final", item->path); + cl_assert_equal_i(10, item->value); + cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL); + cl_assert_equal_s("zzz", item->path); + cl_assert_equal_i(200, item->value); + + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa")); + cl_assert_equal_sz(0, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc")); + cl_assert_equal_sz(1, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final")); + cl_assert_equal_sz(2, pos); + cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz")); + cl_assert_equal_sz(3, pos); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing")); + cl_assert_equal_i( + GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde")); + + git_sortedcache_free(sc); + + cl_assert_equal_i(7, free_count); +} + diff --git a/tests/libgit2/core/stat.c b/tests/libgit2/core/stat.c new file mode 100644 index 000000000..210072fe3 --- /dev/null +++ b/tests/libgit2/core/stat.c @@ -0,0 +1,113 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "posix.h" + +void test_core_stat__initialize(void) +{ + cl_git_pass(git_futils_mkdir("root/d1/d2", 0755, GIT_MKDIR_PATH)); + cl_git_mkfile("root/file", "whatever\n"); + cl_git_mkfile("root/d1/file", "whatever\n"); +} + +void test_core_stat__cleanup(void) +{ + git_futils_rmdir_r("root", NULL, GIT_RMDIR_REMOVE_FILES); +} + +#define cl_assert_error(val) \ + do { err = errno; cl_assert_equal_i((val), err); } while (0) + +void test_core_stat__0(void) +{ + struct stat st; + int err; + + cl_assert_equal_i(0, p_lstat("root", &st)); + cl_assert(S_ISDIR(st.st_mode)); + cl_assert_error(0); + + cl_assert_equal_i(0, p_lstat("root/", &st)); + cl_assert(S_ISDIR(st.st_mode)); + cl_assert_error(0); + + cl_assert_equal_i(0, p_lstat("root/file", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_error(0); + + cl_assert_equal_i(0, p_lstat("root/d1", &st)); + cl_assert(S_ISDIR(st.st_mode)); + cl_assert_error(0); + + cl_assert_equal_i(0, p_lstat("root/d1/", &st)); + cl_assert(S_ISDIR(st.st_mode)); + cl_assert_error(0); + + cl_assert_equal_i(0, p_lstat("root/d1/file", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_error(0); + + cl_assert(p_lstat("root/missing", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat("root/d1/missing", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0); + cl_assert_error(ENOENT); + + cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0); + cl_assert_error(ENOTDIR); + + cl_assert(p_lstat("root/file/invalid", &st) < 0); +#ifdef GIT_WIN32 + cl_assert_error(ENOENT); +#else + cl_assert_error(ENOTDIR); +#endif + + cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0); + cl_assert_error(ENOTDIR); + + cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0); +#ifdef GIT_WIN32 + cl_assert_error(ENOENT); +#else + cl_assert_error(ENOTDIR); +#endif + + cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0); + cl_assert_error(ENOTDIR); + + cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0); + cl_assert_error(ENOTDIR); + + cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0); + cl_assert_error(ENOTDIR); +} + +void test_core_stat__root(void) +{ + const char *sandbox = clar_sandbox_path(); + git_str root = GIT_STR_INIT; + int root_len; + struct stat st; + + root_len = git_fs_path_root(sandbox); + cl_assert(root_len >= 0); + + git_str_set(&root, sandbox, root_len+1); + + cl_must_pass(p_stat(root.ptr, &st)); + cl_assert(S_ISDIR(st.st_mode)); + + git_str_dispose(&root); +} diff --git a/tests/libgit2/core/string.c b/tests/libgit2/core/string.c new file mode 100644 index 000000000..928dfbcc1 --- /dev/null +++ b/tests/libgit2/core/string.c @@ -0,0 +1,136 @@ +#include "clar_libgit2.h" + +/* compare prefixes */ +void test_core_string__0(void) +{ + cl_assert(git__prefixcmp("", "") == 0); + cl_assert(git__prefixcmp("a", "") == 0); + cl_assert(git__prefixcmp("", "a") < 0); + cl_assert(git__prefixcmp("a", "b") < 0); + cl_assert(git__prefixcmp("b", "a") > 0); + cl_assert(git__prefixcmp("ab", "a") == 0); + cl_assert(git__prefixcmp("ab", "ac") < 0); + cl_assert(git__prefixcmp("ab", "aa") > 0); +} + +/* compare suffixes */ +void test_core_string__1(void) +{ + cl_assert(git__suffixcmp("", "") == 0); + cl_assert(git__suffixcmp("a", "") == 0); + cl_assert(git__suffixcmp("", "a") < 0); + cl_assert(git__suffixcmp("a", "b") < 0); + cl_assert(git__suffixcmp("b", "a") > 0); + cl_assert(git__suffixcmp("ba", "a") == 0); + cl_assert(git__suffixcmp("zaa", "ac") < 0); + cl_assert(git__suffixcmp("zaz", "ac") > 0); +} + +/* compare icase sorting with case equality */ +void test_core_string__2(void) +{ + cl_assert(git__strcasesort_cmp("", "") == 0); + cl_assert(git__strcasesort_cmp("foo", "foo") == 0); + cl_assert(git__strcasesort_cmp("foo", "bar") > 0); + cl_assert(git__strcasesort_cmp("bar", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "FOO") > 0); + cl_assert(git__strcasesort_cmp("FOO", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "BAR") > 0); + cl_assert(git__strcasesort_cmp("BAR", "foo") < 0); + cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); +} + +/* compare prefixes with len */ +void test_core_string__prefixncmp(void) +{ + cl_assert(git__prefixncmp("", 0, "") == 0); + cl_assert(git__prefixncmp("a", 1, "") == 0); + cl_assert(git__prefixncmp("", 0, "a") < 0); + cl_assert(git__prefixncmp("a", 1, "b") < 0); + cl_assert(git__prefixncmp("b", 1, "a") > 0); + cl_assert(git__prefixncmp("ab", 2, "a") == 0); + cl_assert(git__prefixncmp("ab", 1, "a") == 0); + cl_assert(git__prefixncmp("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp("a", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp("ab", 1, "aa") < 0); +} + +/* compare prefixes with len */ +void test_core_string__prefixncmp_icase(void) +{ + cl_assert(git__prefixncmp_icase("", 0, "") == 0); + cl_assert(git__prefixncmp_icase("a", 1, "") == 0); + cl_assert(git__prefixncmp_icase("", 0, "a") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("A", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "B") < 0); + cl_assert(git__prefixncmp_icase("b", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("B", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("b", 1, "A") > 0); + cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0); + cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0); +} + +void test_core_string__strcmp(void) +{ + cl_assert(git__strcmp("", "") == 0); + cl_assert(git__strcmp("foo", "foo") == 0); + cl_assert(git__strcmp("Foo", "foo") < 0); + cl_assert(git__strcmp("foo", "FOO") > 0); + cl_assert(git__strcmp("foo", "fOO") > 0); + + cl_assert(strcmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(strcmp("e\342\202\254ghi=", "et") > 0); + cl_assert(strcmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(strcmp("et", "e\342\202\254ghi=") < 0); + cl_assert(strcmp("\303\215", "\303\255") < 0); + + cl_assert(git__strcmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(git__strcmp("e\342\202\254ghi=", "et") > 0); + cl_assert(git__strcmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(git__strcmp("et", "e\342\202\254ghi=") < 0); + cl_assert(git__strcmp("\303\215", "\303\255") < 0); +} + +void test_core_string__strcasecmp(void) +{ + cl_assert(git__strcasecmp("", "") == 0); + cl_assert(git__strcasecmp("foo", "foo") == 0); + cl_assert(git__strcasecmp("foo", "Foo") == 0); + cl_assert(git__strcasecmp("foo", "FOO") == 0); + cl_assert(git__strcasecmp("foo", "fOO") == 0); + + cl_assert(strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(strcasecmp("e\342\202\254ghi=", "et") > 0); + cl_assert(strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(strcasecmp("et", "e\342\202\254ghi=") < 0); + cl_assert(strcasecmp("\303\215", "\303\255") < 0); + + cl_assert(git__strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(git__strcasecmp("e\342\202\254ghi=", "et") > 0); + cl_assert(git__strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0); + cl_assert(git__strcasecmp("\303\215", "\303\255") < 0); +} + +void test_core_string__strlcmp(void) +{ + const char foo[3] = { 'f', 'o', 'o' }; + + cl_assert(git__strlcmp("foo", "foo", 3) == 0); + cl_assert(git__strlcmp("foo", foo, 3) == 0); + cl_assert(git__strlcmp("foo", "foobar", 3) == 0); + cl_assert(git__strlcmp("foobar", "foo", 3) > 0); + cl_assert(git__strlcmp("foo", "foobar", 6) < 0); +} diff --git a/tests/libgit2/core/strmap.c b/tests/libgit2/core/strmap.c new file mode 100644 index 000000000..ba118ae1e --- /dev/null +++ b/tests/libgit2/core/strmap.c @@ -0,0 +1,190 @@ +#include "clar_libgit2.h" +#include "strmap.h" + +static git_strmap *g_table; + +void test_core_strmap__initialize(void) +{ + cl_git_pass(git_strmap_new(&g_table)); + cl_assert(g_table != NULL); +} + +void test_core_strmap__cleanup(void) +{ + git_strmap_free(g_table); +} + +void test_core_strmap__0(void) +{ + cl_assert(git_strmap_size(g_table) == 0); +} + +static void insert_strings(git_strmap *table, size_t count) +{ + size_t i, j, over; + char *str; + + for (i = 0; i < count; ++i) { + str = malloc(10); + for (j = 0; j < 10; ++j) + str[j] = 'a' + (i % 26); + str[9] = '\0'; + + /* if > 26, then encode larger value in first letters */ + for (j = 0, over = i / 26; over > 0; j++, over = over / 26) + str[j] = 'A' + (over % 26); + + cl_git_pass(git_strmap_set(table, str, str)); + } + + cl_assert_equal_i(git_strmap_size(table), count); +} + +void test_core_strmap__inserted_strings_can_be_retrieved(void) +{ + char *str; + int i; + + insert_strings(g_table, 20); + + cl_assert(git_strmap_exists(g_table, "aaaaaaaaa")); + cl_assert(git_strmap_exists(g_table, "ggggggggg")); + cl_assert(!git_strmap_exists(g_table, "aaaaaaaab")); + cl_assert(!git_strmap_exists(g_table, "abcdefghi")); + + i = 0; + git_strmap_foreach_value(g_table, str, { i++; free(str); }); + cl_assert(i == 20); +} + +void test_core_strmap__deleted_entry_cannot_be_retrieved(void) +{ + char *str; + int i; + + insert_strings(g_table, 20); + + cl_assert(git_strmap_exists(g_table, "bbbbbbbbb")); + str = git_strmap_get(g_table, "bbbbbbbbb"); + cl_assert_equal_s(str, "bbbbbbbbb"); + cl_git_pass(git_strmap_delete(g_table, "bbbbbbbbb")); + free(str); + + cl_assert(!git_strmap_exists(g_table, "bbbbbbbbb")); + + i = 0; + git_strmap_foreach_value(g_table, str, { i++; free(str); }); + cl_assert_equal_i(i, 19); +} + +void test_core_strmap__inserting_many_keys_succeeds(void) +{ + char *str; + int i; + + insert_strings(g_table, 10000); + + i = 0; + git_strmap_foreach_value(g_table, str, { i++; free(str); }); + cl_assert_equal_i(i, 10000); +} + +void test_core_strmap__get_succeeds_with_existing_entries(void) +{ + const char *keys[] = { "foo", "bar", "gobble" }; + char *values[] = { "oof", "rab", "elbbog" }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(keys); i++) + cl_git_pass(git_strmap_set(g_table, keys[i], values[i])); + + cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof"); + cl_assert_equal_s(git_strmap_get(g_table, "bar"), "rab"); + cl_assert_equal_s(git_strmap_get(g_table, "gobble"), "elbbog"); +} + +void test_core_strmap__get_returns_null_on_nonexisting_key(void) +{ + const char *keys[] = { "foo", "bar", "gobble" }; + char *values[] = { "oof", "rab", "elbbog" }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(keys); i++) + cl_git_pass(git_strmap_set(g_table, keys[i], values[i])); + + cl_assert_equal_p(git_strmap_get(g_table, "other"), NULL); +} + +void test_core_strmap__set_persists_key(void) +{ + cl_git_pass(git_strmap_set(g_table, "foo", "oof")); + cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof"); +} + +void test_core_strmap__set_persists_multpile_keys(void) +{ + cl_git_pass(git_strmap_set(g_table, "foo", "oof")); + cl_git_pass(git_strmap_set(g_table, "bar", "rab")); + cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof"); + cl_assert_equal_s(git_strmap_get(g_table, "bar"), "rab"); +} + +void test_core_strmap__set_updates_existing_key(void) +{ + cl_git_pass(git_strmap_set(g_table, "foo", "oof")); + cl_git_pass(git_strmap_set(g_table, "bar", "rab")); + cl_git_pass(git_strmap_set(g_table, "gobble", "elbbog")); + cl_assert_equal_i(git_strmap_size(g_table), 3); + + cl_git_pass(git_strmap_set(g_table, "foo", "other")); + cl_assert_equal_i(git_strmap_size(g_table), 3); + + cl_assert_equal_s(git_strmap_get(g_table, "foo"), "other"); +} + +void test_core_strmap__iteration(void) +{ + struct { + char *key; + char *value; + int seen; + } entries[] = { + { "foo", "oof" }, + { "bar", "rab" }, + { "gobble", "elbbog" }, + }; + const char *key, *value; + size_t i, n; + + for (i = 0; i < ARRAY_SIZE(entries); i++) + cl_git_pass(git_strmap_set(g_table, entries[i].key, entries[i].value)); + + i = 0, n = 0; + while (git_strmap_iterate((void **) &value, g_table, &i, &key) == 0) { + size_t j; + + for (j = 0; j < ARRAY_SIZE(entries); j++) { + if (strcmp(entries[j].key, key)) + continue; + + cl_assert_equal_i(entries[j].seen, 0); + cl_assert_equal_s(entries[j].value, value); + entries[j].seen++; + break; + } + + n++; + } + + for (i = 0; i < ARRAY_SIZE(entries); i++) + cl_assert_equal_i(entries[i].seen, 1); + + cl_assert_equal_i(n, ARRAY_SIZE(entries)); +} + +void test_core_strmap__iterating_empty_map_stops_immediately(void) +{ + size_t i = 0; + + cl_git_fail_with(git_strmap_iterate(NULL, g_table, &i, NULL), GIT_ITEROVER); +} diff --git a/tests/libgit2/core/strtol.c b/tests/libgit2/core/strtol.c new file mode 100644 index 000000000..851b91b0a --- /dev/null +++ b/tests/libgit2/core/strtol.c @@ -0,0 +1,128 @@ +#include "clar_libgit2.h" + +static void assert_l32_parses(const char *string, int32_t expected, int base) +{ + int32_t i; + cl_git_pass(git__strntol32(&i, string, strlen(string), NULL, base)); + cl_assert_equal_i(i, expected); +} + +static void assert_l32_fails(const char *string, int base) +{ + int32_t i; + cl_git_fail(git__strntol32(&i, string, strlen(string), NULL, base)); +} + +static void assert_l64_parses(const char *string, int64_t expected, int base) +{ + int64_t i; + cl_git_pass(git__strntol64(&i, string, strlen(string), NULL, base)); + cl_assert_equal_i(i, expected); +} + +static void assert_l64_fails(const char *string, int base) +{ + int64_t i; + cl_git_fail(git__strntol64(&i, string, strlen(string), NULL, base)); +} + +void test_core_strtol__int32(void) +{ + assert_l32_parses("123", 123, 10); + assert_l32_parses(" +123 ", 123, 10); + assert_l32_parses(" -123 ", -123, 10); + assert_l32_parses(" +2147483647 ", 2147483647, 10); + assert_l32_parses(" -2147483648 ", INT64_C(-2147483648), 10); + assert_l32_parses("A", 10, 16); + assert_l32_parses("1x1", 1, 10); + + assert_l32_fails("", 10); + assert_l32_fails("a", 10); + assert_l32_fails("x10x", 10); + assert_l32_fails(" 2147483657 ", 10); + assert_l32_fails(" -2147483657 ", 10); +} + +void test_core_strtol__int64(void) +{ + assert_l64_parses("123", 123, 10); + assert_l64_parses(" +123 ", 123, 10); + assert_l64_parses(" -123 ", -123, 10); + assert_l64_parses(" +2147483647 ", 2147483647, 10); + assert_l64_parses(" -2147483648 ", INT64_C(-2147483648), 10); + assert_l64_parses(" 2147483657 ", INT64_C(2147483657), 10); + assert_l64_parses(" -2147483657 ", INT64_C(-2147483657), 10); + assert_l64_parses(" 9223372036854775807 ", INT64_MAX, 10); + assert_l64_parses(" -9223372036854775808 ", INT64_MIN, 10); + assert_l64_parses(" 0x7fffffffffffffff ", INT64_MAX, 16); + assert_l64_parses(" -0x8000000000000000 ", INT64_MIN, 16); + assert_l64_parses("1a", 26, 16); + assert_l64_parses("1A", 26, 16); + + assert_l64_fails("", 10); + assert_l64_fails("a", 10); + assert_l64_fails("x10x", 10); + assert_l64_fails("0x8000000000000000", 16); + assert_l64_fails("-0x8000000000000001", 16); +} + +void test_core_strtol__base_autodetection(void) +{ + assert_l64_parses("0", 0, 0); + assert_l64_parses("00", 0, 0); + assert_l64_parses("0x", 0, 0); + assert_l64_parses("0foobar", 0, 0); + assert_l64_parses("07", 7, 0); + assert_l64_parses("017", 15, 0); + assert_l64_parses("0x8", 8, 0); + assert_l64_parses("0x18", 24, 0); +} + +void test_core_strtol__buffer_length_with_autodetection_truncates(void) +{ + int64_t i64; + + cl_git_pass(git__strntol64(&i64, "011", 2, NULL, 0)); + cl_assert_equal_i(i64, 1); + cl_git_pass(git__strntol64(&i64, "0x11", 3, NULL, 0)); + cl_assert_equal_i(i64, 1); +} + +void test_core_strtol__buffer_length_truncates(void) +{ + int32_t i32; + int64_t i64; + + cl_git_pass(git__strntol32(&i32, "11", 1, NULL, 10)); + cl_assert_equal_i(i32, 1); + + cl_git_pass(git__strntol64(&i64, "11", 1, NULL, 10)); + cl_assert_equal_i(i64, 1); +} + +void test_core_strtol__buffer_length_with_leading_ws_truncates(void) +{ + int64_t i64; + + cl_git_fail(git__strntol64(&i64, " 1", 1, NULL, 10)); + + cl_git_pass(git__strntol64(&i64, " 11", 2, NULL, 10)); + cl_assert_equal_i(i64, 1); +} + +void test_core_strtol__buffer_length_with_leading_sign_truncates(void) +{ + int64_t i64; + + cl_git_fail(git__strntol64(&i64, "-1", 1, NULL, 10)); + + cl_git_pass(git__strntol64(&i64, "-11", 2, NULL, 10)); + cl_assert_equal_i(i64, -1); +} + +void test_core_strtol__error_message_cuts_off(void) +{ + assert_l32_fails("2147483657foobar", 10); + cl_assert(strstr(git_error_last()->message, "2147483657") != NULL); + cl_assert(strstr(git_error_last()->message, "foobar") == NULL); +} diff --git a/tests/libgit2/core/structinit.c b/tests/libgit2/core/structinit.c new file mode 100644 index 000000000..160e2f612 --- /dev/null +++ b/tests/libgit2/core/structinit.c @@ -0,0 +1,201 @@ +#include "clar_libgit2.h" +#include <git2/sys/commit_graph.h> +#include <git2/sys/config.h> +#include <git2/sys/filter.h> +#include <git2/sys/odb_backend.h> +#include <git2/sys/refdb_backend.h> +#include <git2/sys/transport.h> + +#define STRINGIFY(s) #s + +/* Checks two conditions for the specified structure: + * 1. That the initializers for the latest version produces the same + * in-memory representation. + * 2. That the function-based initializer supports all versions from 1...n, + * where n is the latest version (often represented by GIT_*_VERSION). + * + * Parameters: + * structname: The name of the structure to test, e.g. git_blame_options. + * structver: The latest version of the specified structure. + * macroinit: The macro that initializes the latest version of the structure. + * funcinitname: The function that initializes the structure. Must have the + * signature "int (structname* instance, int version)". + */ +#define CHECK_MACRO_FUNC_INIT_EQUAL(structname, structver, macroinit, funcinitname) \ +do { \ + structname structname##_macro_latest = macroinit; \ + structname structname##_func_latest; \ + int structname##_curr_ver = structver - 1; \ + memset(&structname##_func_latest, 0, sizeof(structname##_func_latest)); \ + cl_git_pass(funcinitname(&structname##_func_latest, structver)); \ + options_cmp(&structname##_macro_latest, &structname##_func_latest, \ + sizeof(structname), STRINGIFY(structname)); \ + \ + while (structname##_curr_ver > 0) \ + { \ + structname macro; \ + cl_git_pass(funcinitname(¯o, structname##_curr_ver)); \ + structname##_curr_ver--; \ + }\ +} while(0) + +static void options_cmp(void *one, void *two, size_t size, const char *name) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (((char *)one)[i] != ((char *)two)[i]) { + char desc[1024]; + + p_snprintf(desc, 1024, "Difference in %s at byte %" PRIuZ ": macro=%u / func=%u", + name, i, ((char *)one)[i], ((char *)two)[i]); + clar__fail(__FILE__, __func__, __LINE__, + "Difference between macro and function options initializer", + desc, 0); + return; + } + } +} + +void test_core_structinit__compare(void) +{ + /* These tests assume that they can memcmp() two structures that were + * initialized with the same static initializer. Eg, + * git_blame_options = GIT_BLAME_OPTIONS_INIT; + * + * This assumption fails when there is padding between structure members, + * which is not guaranteed to be initialized to anything sane at all. + * + * Assume most compilers, in a debug build, will clear that memory for + * us or set it to sentinel markers. Etc. + */ +#if !defined(DEBUG) && !defined(_DEBUG) + clar__skip(); +#endif + + /* apply */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_apply_options, GIT_APPLY_OPTIONS_VERSION, \ + GIT_APPLY_OPTIONS_INIT, git_apply_options_init); + + /* blame */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_blame_options, GIT_BLAME_OPTIONS_VERSION, \ + GIT_BLAME_OPTIONS_INIT, git_blame_options_init); + + /* blob_filter_options */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_VERSION, \ + GIT_BLOB_FILTER_OPTIONS_INIT, git_blob_filter_options_init); + + /* checkout */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \ + GIT_CHECKOUT_OPTIONS_INIT, git_checkout_options_init); + + /* clone */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_clone_options, GIT_CLONE_OPTIONS_VERSION, \ + GIT_CLONE_OPTIONS_INIT, git_clone_options_init); + + /* commit_graph_writer */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_commit_graph_writer_options, \ + GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION, \ + GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT, \ + git_commit_graph_writer_options_init); + + /* diff */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_diff_options, GIT_DIFF_OPTIONS_VERSION, \ + GIT_DIFF_OPTIONS_INIT, git_diff_options_init); + + /* diff_find */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_diff_find_options, GIT_DIFF_FIND_OPTIONS_VERSION, \ + GIT_DIFF_FIND_OPTIONS_INIT, git_diff_find_options_init); + + /* filter */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_filter, GIT_FILTER_VERSION, \ + GIT_FILTER_INIT, git_filter_init); + + /* merge_file_input */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_file_input, GIT_MERGE_FILE_INPUT_VERSION, \ + GIT_MERGE_FILE_INPUT_INIT, git_merge_file_input_init); + + /* merge_file */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_file_options, GIT_MERGE_FILE_OPTIONS_VERSION, \ + GIT_MERGE_FILE_OPTIONS_INIT, git_merge_file_options_init); + + /* merge_tree */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_options, GIT_MERGE_OPTIONS_VERSION, \ + GIT_MERGE_OPTIONS_INIT, git_merge_options_init); + + /* push */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_push_options, GIT_PUSH_OPTIONS_VERSION, \ + GIT_PUSH_OPTIONS_INIT, git_push_options_init); + + /* remote */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_remote_callbacks, GIT_REMOTE_CALLBACKS_VERSION, \ + GIT_REMOTE_CALLBACKS_INIT, git_remote_init_callbacks); + + /* repository_init */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_repository_init_options, GIT_REPOSITORY_INIT_OPTIONS_VERSION, \ + GIT_REPOSITORY_INIT_OPTIONS_INIT, git_repository_init_options_init); + + /* revert */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_revert_options, GIT_REVERT_OPTIONS_VERSION, \ + GIT_REVERT_OPTIONS_INIT, git_revert_options_init); + + /* stash apply */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_VERSION, \ + GIT_STASH_APPLY_OPTIONS_INIT, git_stash_apply_options_init); + + /* status */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_status_options, GIT_STATUS_OPTIONS_VERSION, \ + GIT_STATUS_OPTIONS_INIT, git_status_options_init); + + /* transport */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_transport, GIT_TRANSPORT_VERSION, \ + GIT_TRANSPORT_INIT, git_transport_init); + + /* config_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_config_backend, GIT_CONFIG_BACKEND_VERSION, \ + GIT_CONFIG_BACKEND_INIT, git_config_init_backend); + + /* odb_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_odb_backend, GIT_ODB_BACKEND_VERSION, \ + GIT_ODB_BACKEND_INIT, git_odb_init_backend); + + /* refdb_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_refdb_backend, GIT_REFDB_BACKEND_VERSION, \ + GIT_REFDB_BACKEND_INIT, git_refdb_init_backend); + + /* submodule update */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \ + GIT_SUBMODULE_UPDATE_OPTIONS_INIT, git_submodule_update_options_init); + + /* submodule update */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_proxy_options, GIT_PROXY_OPTIONS_VERSION, \ + GIT_PROXY_OPTIONS_INIT, git_proxy_options_init); + + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_VERSION, \ + GIT_DIFF_PATCHID_OPTIONS_INIT, git_diff_patchid_options_init); +} diff --git a/tests/libgit2/core/useragent.c b/tests/libgit2/core/useragent.c new file mode 100644 index 000000000..a4ece902f --- /dev/null +++ b/tests/libgit2/core/useragent.c @@ -0,0 +1,17 @@ +#include "clar_libgit2.h" +#include "settings.h" + +void test_core_useragent__get(void) +{ + const char *custom_name = "super duper git"; + git_str buf = GIT_STR_INIT; + + cl_assert_equal_p(NULL, git_libgit2__user_agent()); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name)); + cl_assert_equal_s(custom_name, git_libgit2__user_agent()); + + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf)); + cl_assert_equal_s(custom_name, buf.ptr); + + git_str_dispose(&buf); +} diff --git a/tests/libgit2/core/utf8.c b/tests/libgit2/core/utf8.c new file mode 100644 index 000000000..e1987b8d6 --- /dev/null +++ b/tests/libgit2/core/utf8.c @@ -0,0 +1,20 @@ +#include "clar_libgit2.h" +#include "utf8.h" + +void test_core_utf8__char_length(void) +{ + cl_assert_equal_i(0, git_utf8_char_length("", 0)); + cl_assert_equal_i(1, git_utf8_char_length("$", 1)); + cl_assert_equal_i(5, git_utf8_char_length("abcde", 5)); + cl_assert_equal_i(1, git_utf8_char_length("\xc2\xa2", 2)); + cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2\xa2", 3)); + cl_assert_equal_i(1, git_utf8_char_length("\xf0\x90\x8d\x88", 4)); + + /* uncontinued character counted as single characters */ + cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2", 2)); + cl_assert_equal_i(3, git_utf8_char_length("\x24\xc2\xc2\xa2", 4)); + + /* invalid characters are counted as single characters */ + cl_assert_equal_i(4, git_utf8_char_length("\x24\xc0\xc0\x34", 4)); + cl_assert_equal_i(4, git_utf8_char_length("\x24\xf5\xfd\xc2", 4)); +} diff --git a/tests/libgit2/core/vector.c b/tests/libgit2/core/vector.c new file mode 100644 index 000000000..08cd2c19b --- /dev/null +++ b/tests/libgit2/core/vector.c @@ -0,0 +1,430 @@ +#include <stdint.h> + +#include "clar_libgit2.h" +#include "vector.h" + +/* initial size of 1 would cause writing past array bounds */ +void test_core_vector__0(void) +{ + git_vector x; + int i; + cl_git_pass(git_vector_init(&x, 1, NULL)); + for (i = 0; i < 10; ++i) { + git_vector_insert(&x, (void*) 0xabc); + } + git_vector_free(&x); +} + + +/* don't read past array bounds on remove() */ +void test_core_vector__1(void) +{ + git_vector x; + /* make initial capacity exact for our insertions. */ + cl_git_pass(git_vector_init(&x, 3, NULL)); + git_vector_insert(&x, (void*) 0xabc); + git_vector_insert(&x, (void*) 0xdef); + git_vector_insert(&x, (void*) 0x123); + + git_vector_remove(&x, 0); /* used to read past array bounds. */ + git_vector_free(&x); +} + + +static int test_cmp(const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +/* remove duplicates */ +void test_core_vector__2(void) +{ + git_vector x; + int *ptrs[2]; + + ptrs[0] = git__malloc(sizeof(int)); + ptrs[1] = git__malloc(sizeof(int)); + + *ptrs[0] = 2; + *ptrs[1] = 1; + + cl_git_pass(git_vector_init(&x, 5, test_cmp)); + cl_git_pass(git_vector_insert(&x, ptrs[0])); + cl_git_pass(git_vector_insert(&x, ptrs[1])); + cl_git_pass(git_vector_insert(&x, ptrs[1])); + cl_git_pass(git_vector_insert(&x, ptrs[0])); + cl_git_pass(git_vector_insert(&x, ptrs[1])); + cl_assert(x.length == 5); + + git_vector_uniq(&x, NULL); + cl_assert(x.length == 2); + + git_vector_free(&x); + + git__free(ptrs[0]); + git__free(ptrs[1]); +} + + +static int compare_them(const void *a, const void *b) +{ + return (int)((intptr_t)a - (intptr_t)b); +} + +/* insert_sorted */ +void test_core_vector__3(void) +{ + git_vector x; + intptr_t i; + cl_git_pass(git_vector_init(&x, 1, &compare_them)); + + for (i = 0; i < 10; i += 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + for (i = 9; i > 0; i -= 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + cl_assert(x.length == 10); + for (i = 0; i < 10; ++i) { + cl_assert(git_vector_get(&x, i) == (void*)(i + 1)); + } + + git_vector_free(&x); +} + +/* insert_sorted with duplicates */ +void test_core_vector__4(void) +{ + git_vector x; + intptr_t i; + cl_git_pass(git_vector_init(&x, 1, &compare_them)); + + for (i = 0; i < 10; i += 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + for (i = 9; i > 0; i -= 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + for (i = 0; i < 10; i += 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + for (i = 9; i > 0; i -= 2) { + git_vector_insert_sorted(&x, (void*)(i + 1), NULL); + } + + cl_assert(x.length == 20); + for (i = 0; i < 20; ++i) { + cl_assert(git_vector_get(&x, i) == (void*)(i / 2 + 1)); + } + + git_vector_free(&x); +} + +typedef struct { + int content; + int count; +} my_struct; + +static int _struct_count = 0; + +static int compare_structs(const void *a, const void *b) +{ + return ((const my_struct *)a)->content - + ((const my_struct *)b)->content; +} + +static int merge_structs(void **old_raw, void *new) +{ + my_struct *old = *(my_struct **)old_raw; + cl_assert(((my_struct *)old)->content == ((my_struct *)new)->content); + ((my_struct *)old)->count += 1; + git__free(new); + _struct_count--; + return GIT_EEXISTS; +} + +static my_struct *alloc_struct(int value) +{ + my_struct *st = git__malloc(sizeof(my_struct)); + st->content = value; + st->count = 0; + _struct_count++; + return st; +} + +/* insert_sorted with duplicates and special handling */ +void test_core_vector__5(void) +{ + git_vector x; + int i; + + cl_git_pass(git_vector_init(&x, 1, &compare_structs)); + + for (i = 0; i < 10; i += 2) + git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs); + + for (i = 9; i > 0; i -= 2) + git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs); + + cl_assert(x.length == 10); + cl_assert(_struct_count == 10); + + for (i = 0; i < 10; i += 2) + git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs); + + for (i = 9; i > 0; i -= 2) + git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs); + + cl_assert(x.length == 10); + cl_assert(_struct_count == 10); + + for (i = 0; i < 10; ++i) { + cl_assert(((my_struct *)git_vector_get(&x, i))->content == i); + git__free(git_vector_get(&x, i)); + _struct_count--; + } + + git_vector_free(&x); +} + +static int remove_ones(const git_vector *v, size_t idx, void *p) +{ + GIT_UNUSED(p); + return (git_vector_get(v, idx) == (void *)0x001); +} + +/* Test removal based on callback */ +void test_core_vector__remove_matching(void) +{ + git_vector x; + size_t i; + void *compare; + + cl_git_pass(git_vector_init(&x, 1, NULL)); + git_vector_insert(&x, (void*) 0x001); + + cl_assert(x.length == 1); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 0); + + git_vector_insert(&x, (void*) 0x001); + git_vector_insert(&x, (void*) 0x001); + git_vector_insert(&x, (void*) 0x001); + + cl_assert(x.length == 3); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 0); + + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x001); + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x001); + + cl_assert(x.length == 4); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 2); + + git_vector_foreach(&x, i, compare) { + cl_assert(compare != (void *)0x001); + } + + git_vector_clear(&x); + + git_vector_insert(&x, (void*) 0x001); + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x001); + + cl_assert(x.length == 4); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 2); + + git_vector_foreach(&x, i, compare) { + cl_assert(compare != (void *)0x001); + } + + git_vector_clear(&x); + + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x001); + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x001); + + cl_assert(x.length == 4); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 2); + + git_vector_foreach(&x, i, compare) { + cl_assert(compare != (void *)0x001); + } + + git_vector_clear(&x); + + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x003); + git_vector_insert(&x, (void*) 0x002); + git_vector_insert(&x, (void*) 0x003); + + cl_assert(x.length == 4); + git_vector_remove_matching(&x, remove_ones, NULL); + cl_assert(x.length == 4); + + git_vector_free(&x); +} + +static void assert_vector(git_vector *x, void *expected[], size_t len) +{ + size_t i; + + cl_assert_equal_i(len, x->length); + + for (i = 0; i < len; i++) + cl_assert(expected[i] == x->contents[i]); +} + +void test_core_vector__grow_and_shrink(void) +{ + git_vector x = GIT_VECTOR_INIT; + void *expected1[] = { + (void *)0x02, (void *)0x03, (void *)0x04, (void *)0x05, + (void *)0x06, (void *)0x07, (void *)0x08, (void *)0x09, + (void *)0x0a + }; + void *expected2[] = { + (void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06, + (void *)0x07, (void *)0x08, (void *)0x09, (void *)0x0a + }; + void *expected3[] = { + (void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06, + (void *)0x0a + }; + void *expected4[] = { + (void *)0x02, (void *)0x04, (void *)0x05 + }; + void *expected5[] = { + (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04, + (void *)0x05 + }; + void *expected6[] = { + (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04, + (void *)0x05, (void *)0x00 + }; + void *expected7[] = { + (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04, + (void *)0x00, (void *)0x00, (void *)0x00, (void *)0x05, + (void *)0x00 + }; + void *expected8[] = { + (void *)0x04, (void *)0x00, (void *)0x00, (void *)0x00, + (void *)0x05, (void *)0x00 + }; + void *expected9[] = { + (void *)0x04, (void *)0x00, (void *)0x05, (void *)0x00 + }; + void *expectedA[] = { (void *)0x04, (void *)0x00 }; + void *expectedB[] = { (void *)0x04 }; + + git_vector_insert(&x, (void *)0x01); + git_vector_insert(&x, (void *)0x02); + git_vector_insert(&x, (void *)0x03); + git_vector_insert(&x, (void *)0x04); + git_vector_insert(&x, (void *)0x05); + git_vector_insert(&x, (void *)0x06); + git_vector_insert(&x, (void *)0x07); + git_vector_insert(&x, (void *)0x08); + git_vector_insert(&x, (void *)0x09); + git_vector_insert(&x, (void *)0x0a); + + git_vector_remove_range(&x, 0, 1); + assert_vector(&x, expected1, ARRAY_SIZE(expected1)); + + git_vector_remove_range(&x, 1, 1); + assert_vector(&x, expected2, ARRAY_SIZE(expected2)); + + git_vector_remove_range(&x, 4, 3); + assert_vector(&x, expected3, ARRAY_SIZE(expected3)); + + git_vector_remove_range(&x, 3, 2); + assert_vector(&x, expected4, ARRAY_SIZE(expected4)); + + git_vector_insert_null(&x, 0, 2); + assert_vector(&x, expected5, ARRAY_SIZE(expected5)); + + git_vector_insert_null(&x, 5, 1); + assert_vector(&x, expected6, ARRAY_SIZE(expected6)); + + git_vector_insert_null(&x, 4, 3); + assert_vector(&x, expected7, ARRAY_SIZE(expected7)); + + git_vector_remove_range(&x, 0, 3); + assert_vector(&x, expected8, ARRAY_SIZE(expected8)); + + git_vector_remove_range(&x, 1, 2); + assert_vector(&x, expected9, ARRAY_SIZE(expected9)); + + git_vector_remove_range(&x, 2, 2); + assert_vector(&x, expectedA, ARRAY_SIZE(expectedA)); + + git_vector_remove_range(&x, 1, 1); + assert_vector(&x, expectedB, ARRAY_SIZE(expectedB)); + + git_vector_remove_range(&x, 0, 1); + assert_vector(&x, NULL, 0); + + git_vector_free(&x); +} + +void test_core_vector__reverse(void) +{ + git_vector v = GIT_VECTOR_INIT; + size_t i; + + void *in1[] = {(void *) 0x0, (void *) 0x1, (void *) 0x2, (void *) 0x3}; + void *out1[] = {(void *) 0x3, (void *) 0x2, (void *) 0x1, (void *) 0x0}; + + void *in2[] = {(void *) 0x0, (void *) 0x1, (void *) 0x2, (void *) 0x3, (void *) 0x4}; + void *out2[] = {(void *) 0x4, (void *) 0x3, (void *) 0x2, (void *) 0x1, (void *) 0x0}; + + for (i = 0; i < 4; i++) + cl_git_pass(git_vector_insert(&v, in1[i])); + + git_vector_reverse(&v); + + for (i = 0; i < 4; i++) + cl_assert_equal_p(out1[i], git_vector_get(&v, i)); + + git_vector_clear(&v); + for (i = 0; i < 5; i++) + cl_git_pass(git_vector_insert(&v, in2[i])); + + git_vector_reverse(&v); + + for (i = 0; i < 5; i++) + cl_assert_equal_p(out2[i], git_vector_get(&v, i)); + + git_vector_free(&v); +} + +void test_core_vector__dup_empty_vector(void) +{ + git_vector v = GIT_VECTOR_INIT; + git_vector dup = GIT_VECTOR_INIT; + int dummy; + + cl_assert_equal_i(0, v.length); + + cl_git_pass(git_vector_dup(&dup, &v, v._cmp)); + cl_assert_equal_i(0, dup._alloc_size); + cl_assert_equal_i(0, dup.length); + + cl_git_pass(git_vector_insert(&dup, &dummy)); + cl_assert_equal_i(8, dup._alloc_size); + cl_assert_equal_i(1, dup.length); + + git_vector_free(&dup); +} diff --git a/tests/libgit2/core/wildmatch.c b/tests/libgit2/core/wildmatch.c new file mode 100644 index 000000000..7c56ee7b8 --- /dev/null +++ b/tests/libgit2/core/wildmatch.c @@ -0,0 +1,248 @@ +#include "clar_libgit2.h" + +#include "wildmatch.h" + +#define assert_matches(string, pattern, wildmatch, iwildmatch, pathmatch, ipathmatch) \ + assert_matches_(string, pattern, wildmatch, iwildmatch, pathmatch, ipathmatch, __FILE__, __func__, __LINE__) + +static void assert_matches_(const char *string, const char *pattern, + char expected_wildmatch, char expected_iwildmatch, + char expected_pathmatch, char expected_ipathmatch, + const char *file, const char *func, size_t line) +{ + if (wildmatch(pattern, string, WM_PATHNAME) == expected_wildmatch) + clar__fail(file, func, line, "Test failed (wildmatch).", string, 1); + if (wildmatch(pattern, string, WM_PATHNAME|WM_CASEFOLD) == expected_iwildmatch) + clar__fail(file, func, line, "Test failed (iwildmatch).", string, 1); + if (wildmatch(pattern, string, 0) == expected_pathmatch) + clar__fail(file, func, line, "Test failed (pathmatch).", string, 1); + if (wildmatch(pattern, string, WM_CASEFOLD) == expected_ipathmatch) + clar__fail(file, func, line, "Test failed (ipathmatch).", string, 1); +} + +/* + * Below testcases are imported from git.git, t3070-wildmatch,sh at tag v2.22.0. + * Note that we've only imported the direct wildcard tests, but not the matching + * tests for git-ls-files. + */ + +void test_core_wildmatch__basic_wildmatch(void) +{ + assert_matches("foo", "foo", 1, 1, 1, 1); + assert_matches("foo", "bar", 0, 0, 0, 0); + assert_matches("", "", 1, 1, 1, 1); + assert_matches("foo", "???", 1, 1, 1, 1); + assert_matches("foo", "??", 0, 0, 0, 0); + assert_matches("foo", "*", 1, 1, 1, 1); + assert_matches("foo", "f*", 1, 1, 1, 1); + assert_matches("foo", "*f", 0, 0, 0, 0); + assert_matches("foo", "*foo*", 1, 1, 1, 1); + assert_matches("foobar", "*ob*a*r*", 1, 1, 1, 1); + assert_matches("aaaaaaabababab", "*ab", 1, 1, 1, 1); + assert_matches("foo*", "foo\\*", 1, 1, 1, 1); + assert_matches("foobar", "foo\\*bar", 0, 0, 0, 0); + assert_matches("f\\oo", "f\\\\oo", 1, 1, 1, 1); + assert_matches("ball", "*[al]?", 1, 1, 1, 1); + assert_matches("ten", "[ten]", 0, 0, 0, 0); + assert_matches("ten", "**[!te]", 1, 1, 1, 1); + assert_matches("ten", "**[!ten]", 0, 0, 0, 0); + assert_matches("ten", "t[a-g]n", 1, 1, 1, 1); + assert_matches("ten", "t[!a-g]n", 0, 0, 0, 0); + assert_matches("ton", "t[!a-g]n", 1, 1, 1, 1); + assert_matches("ton", "t[^a-g]n", 1, 1, 1, 1); + assert_matches("a]b", "a[]]b", 1, 1, 1, 1); + assert_matches("a-b", "a[]-]b", 1, 1, 1, 1); + assert_matches("a]b", "a[]-]b", 1, 1, 1, 1); + assert_matches("aab", "a[]-]b", 0, 0, 0, 0); + assert_matches("aab", "a[]a-]b", 1, 1, 1, 1); + assert_matches("]", "]", 1, 1, 1, 1); +} + +void test_core_wildmatch__slash_matching_features(void) +{ + assert_matches("foo/baz/bar", "foo*bar", 0, 0, 1, 1); + assert_matches("foo/baz/bar", "foo**bar", 0, 0, 1, 1); + assert_matches("foobazbar", "foo**bar", 1, 1, 1, 1); + assert_matches("foo/baz/bar", "foo/**/bar", 1, 1, 1, 1); + assert_matches("foo/baz/bar", "foo/**/**/bar", 1, 1, 0, 0); + assert_matches("foo/b/a/z/bar", "foo/**/bar", 1, 1, 1, 1); + assert_matches("foo/b/a/z/bar", "foo/**/**/bar", 1, 1, 1, 1); + assert_matches("foo/bar", "foo/**/bar", 1, 1, 0, 0); + assert_matches("foo/bar", "foo/**/**/bar", 1, 1, 0, 0); + assert_matches("foo/bar", "foo?bar", 0, 0, 1, 1); + assert_matches("foo/bar", "foo[/]bar", 0, 0, 1, 1); + assert_matches("foo/bar", "foo[^a-z]bar", 0, 0, 1, 1); + assert_matches("foo/bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r", 0, 0, 1, 1); + assert_matches("foo-bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r", 1, 1, 1, 1); + assert_matches("foo", "**/foo", 1, 1, 0, 0); + assert_matches("XXX/foo", "**/foo", 1, 1, 1, 1); + assert_matches("bar/baz/foo", "**/foo", 1, 1, 1, 1); + assert_matches("bar/baz/foo", "*/foo", 0, 0, 1, 1); + assert_matches("foo/bar/baz", "**/bar*", 0, 0, 1, 1); + assert_matches("deep/foo/bar/baz", "**/bar/*", 1, 1, 1, 1); + assert_matches("deep/foo/bar/baz/", "**/bar/*", 0, 0, 1, 1); + assert_matches("deep/foo/bar/baz/", "**/bar/**", 1, 1, 1, 1); + assert_matches("deep/foo/bar", "**/bar/*", 0, 0, 0, 0); + assert_matches("deep/foo/bar/", "**/bar/**", 1, 1, 1, 1); + assert_matches("foo/bar/baz", "**/bar**", 0, 0, 1, 1); + assert_matches("foo/bar/baz/x", "*/bar/**", 1, 1, 1, 1); + assert_matches("deep/foo/bar/baz/x", "*/bar/**", 0, 0, 1, 1); + assert_matches("deep/foo/bar/baz/x", "**/bar/*/*", 1, 1, 1, 1); +} + +void test_core_wildmatch__various_additional(void) +{ + assert_matches("acrt", "a[c-c]st", 0, 0, 0, 0); + assert_matches("acrt", "a[c-c]rt", 1, 1, 1, 1); + assert_matches("]", "[!]-]", 0, 0, 0, 0); + assert_matches("a", "[!]-]", 1, 1, 1, 1); + assert_matches("", "\\", 0, 0, 0, 0); + assert_matches("\\", "\\", 0, 0, 0, 0); + assert_matches("XXX/\\", "*/\\", 0, 0, 0, 0); + assert_matches("XXX/\\", "*/\\\\", 1, 1, 1, 1); + assert_matches("foo", "foo", 1, 1, 1, 1); + assert_matches("@foo", "@foo", 1, 1, 1, 1); + assert_matches("foo", "@foo", 0, 0, 0, 0); + assert_matches("[ab]", "\\[ab]", 1, 1, 1, 1); + assert_matches("[ab]", "[[]ab]", 1, 1, 1, 1); + assert_matches("[ab]", "[[:]ab]", 1, 1, 1, 1); + assert_matches("[ab]", "[[::]ab]", 0, 0, 0, 0); + assert_matches("[ab]", "[[:digit]ab]", 1, 1, 1, 1); + assert_matches("[ab]", "[\\[:]ab]", 1, 1, 1, 1); + assert_matches("?a?b", "\\??\\?b", 1, 1, 1, 1); + assert_matches("abc", "\\a\\b\\c", 1, 1, 1, 1); + assert_matches("foo", "", 0, 0, 0, 0); + assert_matches("foo/bar/baz/to", "**/t[o]", 1, 1, 1, 1); +} + +void test_core_wildmatch__character_classes(void) +{ + assert_matches("a1B", "[[:alpha:]][[:digit:]][[:upper:]]", 1, 1, 1, 1); + assert_matches("a", "[[:digit:][:upper:][:space:]]", 0, 1, 0, 1); + assert_matches("A", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1); + assert_matches("1", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1); + assert_matches("1", "[[:digit:][:upper:][:spaci:]]", 0, 0, 0, 0); + assert_matches(" ", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1); + assert_matches(".", "[[:digit:][:upper:][:space:]]", 0, 0, 0, 0); + assert_matches(".", "[[:digit:][:punct:][:space:]]", 1, 1, 1, 1); + assert_matches("5", "[[:xdigit:]]", 1, 1, 1, 1); + assert_matches("f", "[[:xdigit:]]", 1, 1, 1, 1); + assert_matches("D", "[[:xdigit:]]", 1, 1, 1, 1); + assert_matches("_", "[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]", 1, 1, 1, 1); + assert_matches(".", "[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]", 1, 1, 1, 1); + assert_matches("5", "[a-c[:digit:]x-z]", 1, 1, 1, 1); + assert_matches("b", "[a-c[:digit:]x-z]", 1, 1, 1, 1); + assert_matches("y", "[a-c[:digit:]x-z]", 1, 1, 1, 1); + assert_matches("q", "[a-c[:digit:]x-z]", 0, 0, 0, 0); +} + +void test_core_wildmatch__additional_with_malformed(void) +{ + assert_matches("]", "[\\\\-^]", 1, 1, 1, 1); + assert_matches("[", "[\\\\-^]", 0, 0, 0, 0); + assert_matches("-", "[\\-_]", 1, 1, 1, 1); + assert_matches("]", "[\\]]", 1, 1, 1, 1); + assert_matches("\\]", "[\\]]", 0, 0, 0, 0); + assert_matches("\\", "[\\]]", 0, 0, 0, 0); + assert_matches("ab", "a[]b", 0, 0, 0, 0); + assert_matches("a[]b", "a[]b", 0, 0, 0, 0); + assert_matches("ab[", "ab[", 0, 0, 0, 0); + assert_matches("ab", "[!", 0, 0, 0, 0); + assert_matches("ab", "[-", 0, 0, 0, 0); + assert_matches("-", "[-]", 1, 1, 1, 1); + assert_matches("-", "[a-", 0, 0, 0, 0); + assert_matches("-", "[!a-", 0, 0, 0, 0); + assert_matches("-", "[--A]", 1, 1, 1, 1); + assert_matches("5", "[--A]", 1, 1, 1, 1); + assert_matches(" ", "[ --]", 1, 1, 1, 1); + assert_matches("$", "[ --]", 1, 1, 1, 1); + assert_matches("-", "[ --]", 1, 1, 1, 1); + assert_matches("0", "[ --]", 0, 0, 0, 0); + assert_matches("-", "[---]", 1, 1, 1, 1); + assert_matches("-", "[------]", 1, 1, 1, 1); + assert_matches("j", "[a-e-n]", 0, 0, 0, 0); + assert_matches("-", "[a-e-n]", 1, 1, 1, 1); + assert_matches("a", "[!------]", 1, 1, 1, 1); + assert_matches("[", "[]-a]", 0, 0, 0, 0); + assert_matches("^", "[]-a]", 1, 1, 1, 1); + assert_matches("^", "[!]-a]", 0, 0, 0, 0); + assert_matches("[", "[!]-a]", 1, 1, 1, 1); + assert_matches("^", "[a^bc]", 1, 1, 1, 1); + assert_matches("-b]", "[a-]b]", 1, 1, 1, 1); + assert_matches("\\", "[\\]", 0, 0, 0, 0); + assert_matches("\\", "[\\\\]", 1, 1, 1, 1); + assert_matches("\\", "[!\\\\]", 0, 0, 0, 0); + assert_matches("G", "[A-\\\\]", 1, 1, 1, 1); + assert_matches("aaabbb", "b*a", 0, 0, 0, 0); + assert_matches("aabcaa", "*ba*", 0, 0, 0, 0); + assert_matches(",", "[,]", 1, 1, 1, 1); + assert_matches(",", "[\\\\,]", 1, 1, 1, 1); + assert_matches("\\", "[\\\\,]", 1, 1, 1, 1); + assert_matches("-", "[,-.]", 1, 1, 1, 1); + assert_matches("+", "[,-.]", 0, 0, 0, 0); + assert_matches("-.]", "[,-.]", 0, 0, 0, 0); + assert_matches("2", "[\\1-\\3]", 1, 1, 1, 1); + assert_matches("3", "[\\1-\\3]", 1, 1, 1, 1); + assert_matches("4", "[\\1-\\3]", 0, 0, 0, 0); + assert_matches("\\", "[[-\\]]", 1, 1, 1, 1); + assert_matches("[", "[[-\\]]", 1, 1, 1, 1); + assert_matches("]", "[[-\\]]", 1, 1, 1, 1); + assert_matches("-", "[[-\\]]", 0, 0, 0, 0); +} + +void test_core_wildmatch__recursion(void) +{ + assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 1, 1, 1, 1); + assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 0, 0, 0, 0); + assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 0, 0, 0, 0); + assert_matches("XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*", 1, 1, 1, 1); + assert_matches("XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*", 0, 0, 0, 0); + assert_matches("abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt", "**/*a*b*g*n*t", 1, 1, 1, 1); + assert_matches("abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz", "**/*a*b*g*n*t", 0, 0, 0, 0); + assert_matches("foo", "*/*/*", 0, 0, 0, 0); + assert_matches("foo/bar", "*/*/*", 0, 0, 0, 0); + assert_matches("foo/bba/arr", "*/*/*", 1, 1, 1, 1); + assert_matches("foo/bb/aa/rr", "*/*/*", 0, 0, 1, 1); + assert_matches("foo/bb/aa/rr", "**/**/**", 1, 1, 1, 1); + assert_matches("abcXdefXghi", "*X*i", 1, 1, 1, 1); + assert_matches("ab/cXd/efXg/hi", "*X*i", 0, 0, 1, 1); + assert_matches("ab/cXd/efXg/hi", "*/*X*/*/*i", 1, 1, 1, 1); + assert_matches("ab/cXd/efXg/hi", "**/*X*/**/*i", 1, 1, 1, 1); +} + +void test_core_wildmatch__pathmatch(void) +{ + assert_matches("foo", "fo", 0, 0, 0, 0); + assert_matches("foo/bar", "foo/bar", 1, 1, 1, 1); + assert_matches("foo/bar", "foo/*", 1, 1, 1, 1); + assert_matches("foo/bba/arr", "foo/*", 0, 0, 1, 1); + assert_matches("foo/bba/arr", "foo/**", 1, 1, 1, 1); + assert_matches("foo/bba/arr", "foo*", 0, 0, 1, 1); + assert_matches("foo/bba/arr", "foo**", 0, 0, 1, 1); + assert_matches("foo/bba/arr", "foo/*arr", 0, 0, 1, 1); + assert_matches("foo/bba/arr", "foo/**arr", 0, 0, 1, 1); + assert_matches("foo/bba/arr", "foo/*z", 0, 0, 0, 0); + assert_matches("foo/bba/arr", "foo/**z", 0, 0, 0, 0); + assert_matches("foo/bar", "foo?bar", 0, 0, 1, 1); + assert_matches("foo/bar", "foo[/]bar", 0, 0, 1, 1); + assert_matches("foo/bar", "foo[^a-z]bar", 0, 0, 1, 1); + assert_matches("ab/cXd/efXg/hi", "*Xg*i", 0, 0, 1, 1); +} + +void test_core_wildmatch__case_sensitivity(void) +{ + assert_matches("a", "[A-Z]", 0, 1, 0, 1); + assert_matches("A", "[A-Z]", 1, 1, 1, 1); + assert_matches("A", "[a-z]", 0, 1, 0, 1); + assert_matches("a", "[a-z]", 1, 1, 1, 1); + assert_matches("a", "[[:upper:]]", 0, 1, 0, 1); + assert_matches("A", "[[:upper:]]", 1, 1, 1, 1); + assert_matches("A", "[[:lower:]]", 0, 1, 0, 1); + assert_matches("a", "[[:lower:]]", 1, 1, 1, 1); + assert_matches("A", "[B-Za]", 0, 1, 0, 1); + assert_matches("a", "[B-Za]", 1, 1, 1, 1); + assert_matches("A", "[B-a]", 0, 1, 0, 1); + assert_matches("a", "[B-a]", 1, 1, 1, 1); + assert_matches("z", "[Z-y]", 0, 1, 0, 1); + assert_matches("Z", "[Z-y]", 1, 1, 1, 1); +} diff --git a/tests/libgit2/core/zstream.c b/tests/libgit2/core/zstream.c new file mode 100644 index 000000000..c22e81008 --- /dev/null +++ b/tests/libgit2/core/zstream.c @@ -0,0 +1,167 @@ +#include "clar_libgit2.h" +#include "zstream.h" + +static const char *data = "This is a test test test of This is a test"; + +#define INFLATE_EXTRA 2 + +static void assert_zlib_equal_( + const void *expected, size_t e_len, + const void *compressed, size_t c_len, + const char *msg, const char *file, const char *func, int line) +{ + z_stream stream; + char *expanded = git__calloc(1, e_len + INFLATE_EXTRA); + cl_assert(expanded); + + memset(&stream, 0, sizeof(stream)); + stream.next_out = (Bytef *)expanded; + stream.avail_out = (uInt)(e_len + INFLATE_EXTRA); + stream.next_in = (Bytef *)compressed; + stream.avail_in = (uInt)c_len; + + cl_assert(inflateInit(&stream) == Z_OK); + cl_assert(inflate(&stream, Z_FINISH)); + inflateEnd(&stream); + + clar__assert_equal( + file, func, line, msg, 1, + "%d", (int)stream.total_out, (int)e_len); + clar__assert_equal( + file, func, line, "Buffer len was not exact match", 1, + "%d", (int)stream.avail_out, (int)INFLATE_EXTRA); + + clar__assert( + memcmp(expanded, expected, e_len) == 0, + file, func, line, "uncompressed data did not match", NULL, 1); + + git__free(expanded); +} + +#define assert_zlib_equal(E,EL,C,CL) \ + assert_zlib_equal_(E, EL, C, CL, #EL " != " #CL, __FILE__, __func__, (int)__LINE__) + +void test_core_zstream__basic(void) +{ + git_zstream z = GIT_ZSTREAM_INIT; + char out[128]; + size_t outlen = sizeof(out); + + cl_git_pass(git_zstream_init(&z, GIT_ZSTREAM_DEFLATE)); + cl_git_pass(git_zstream_set_input(&z, data, strlen(data) + 1)); + cl_git_pass(git_zstream_get_output(out, &outlen, &z)); + cl_assert(git_zstream_done(&z)); + cl_assert(outlen > 0); + git_zstream_free(&z); + + assert_zlib_equal(data, strlen(data) + 1, out, outlen); +} + +void test_core_zstream__fails_on_trailing_garbage(void) +{ + git_str deflated = GIT_STR_INIT, inflated = GIT_STR_INIT; + char i = 0; + + /* compress a simple string */ + git_zstream_deflatebuf(&deflated, "foobar!!", 8); + + /* append some garbage */ + for (i = 0; i < 10; i++) { + git_str_putc(&deflated, i); + } + + cl_git_fail(git_zstream_inflatebuf(&inflated, deflated.ptr, deflated.size)); + + git_str_dispose(&deflated); + git_str_dispose(&inflated); +} + +void test_core_zstream__buffer(void) +{ + git_str out = GIT_STR_INIT; + cl_git_pass(git_zstream_deflatebuf(&out, data, strlen(data) + 1)); + assert_zlib_equal(data, strlen(data) + 1, out.ptr, out.size); + git_str_dispose(&out); +} + +#define BIG_STRING_PART "Big Data IS Big - Long Data IS Long - We need a buffer larger than 1024 x 1024 to make sure we trigger chunked compression - Big Big Data IS Bigger than Big - Long Long Data IS Longer than Long" + +static void compress_and_decompress_input_various_ways(git_str *input) +{ + git_str out1 = GIT_STR_INIT, out2 = GIT_STR_INIT; + git_str inflated = GIT_STR_INIT; + size_t i, fixed_size = max(input->size / 2, 256); + char *fixed = git__malloc(fixed_size); + cl_assert(fixed); + + /* compress with deflatebuf */ + + cl_git_pass(git_zstream_deflatebuf(&out1, input->ptr, input->size)); + assert_zlib_equal(input->ptr, input->size, out1.ptr, out1.size); + + /* compress with various fixed size buffer (accumulating the output) */ + + for (i = 0; i < 3; ++i) { + git_zstream zs = GIT_ZSTREAM_INIT; + size_t use_fixed_size; + + switch (i) { + case 0: use_fixed_size = 256; break; + case 1: use_fixed_size = fixed_size / 2; break; + case 2: use_fixed_size = fixed_size; break; + } + cl_assert(use_fixed_size <= fixed_size); + + cl_git_pass(git_zstream_init(&zs, GIT_ZSTREAM_DEFLATE)); + cl_git_pass(git_zstream_set_input(&zs, input->ptr, input->size)); + + while (!git_zstream_done(&zs)) { + size_t written = use_fixed_size; + cl_git_pass(git_zstream_get_output(fixed, &written, &zs)); + cl_git_pass(git_str_put(&out2, fixed, written)); + } + + git_zstream_free(&zs); + assert_zlib_equal(input->ptr, input->size, out2.ptr, out2.size); + + /* did both approaches give the same data? */ + cl_assert_equal_sz(out1.size, out2.size); + cl_assert(!memcmp(out1.ptr, out2.ptr, out1.size)); + + git_str_dispose(&out2); + } + + cl_git_pass(git_zstream_inflatebuf(&inflated, out1.ptr, out1.size)); + cl_assert_equal_i(input->size, inflated.size); + cl_assert(memcmp(input->ptr, inflated.ptr, inflated.size) == 0); + + git_str_dispose(&out1); + git_str_dispose(&inflated); + git__free(fixed); +} + +void test_core_zstream__big_data(void) +{ + git_str in = GIT_STR_INIT; + size_t scan, target; + + for (target = 1024; target <= 1024 * 1024 * 4; target *= 8) { + + /* make a big string that's easy to compress */ + git_str_clear(&in); + while (in.size < target) + cl_git_pass( + git_str_put(&in, BIG_STRING_PART, strlen(BIG_STRING_PART))); + + compress_and_decompress_input_various_ways(&in); + + /* make a big string that's hard to compress */ + srand(0xabad1dea); + for (scan = 0; scan < in.size; ++scan) + in.ptr[scan] = (char)rand(); + + compress_and_decompress_input_various_ways(&in); + } + + git_str_dispose(&in); +} |
