Initial commit - full huishou project

This commit is contained in:
jiapengyu
2026-07-27 14:07:26 +08:00
commit 60790ad1b3
39127 changed files with 5989265 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
FIND_PACKAGE(PythonInterp)
IF(NOT PYTHONINTERP_FOUND)
MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
"Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests")
ENDIF()
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/resources/")
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
IF(TEST_LONGPATHS)
ADD_DEFINITIONS(-DGIT_TEST_LONGPATHS=1)
ENDIF()
# Ensure that we do not use deprecated functions internally
ADD_DEFINITIONS(-DGIT_DEPRECATE_HARD)
INCLUDE_DIRECTORIES(${CLAR_PATH} ${libgit2_BINARY_DIR}/src)
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
SET(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c")
IF(MSVC_IDE)
LIST(APPEND SRC_CLAR "precompiled.c")
ENDIF()
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite
COMMAND ${PYTHON_EXECUTABLE} generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf .
DEPENDS ${SRC_TEST}
WORKING_DIRECTORY ${CLAR_PATH}
)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
SET_SOURCE_FILES_PROPERTIES(
${CLAR_PATH}/clar.c
PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
INCLUDE_DIRECTORIES(SYSTEM ${LIBGIT2_SYSTEM_INCLUDES})
ADD_EXECUTABLE(libgit2_clar ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES C_STANDARD 90)
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
TARGET_INCLUDE_DIRECTORIES(libgit2_clar PRIVATE ../src PUBLIC ../include)
TARGET_LINK_LIBRARIES(libgit2_clar ${LIBGIT2_LIBS})
IDE_SPLIT_SOURCES(libgit2_clar)
IF (MSVC_IDE)
# Precompiled headers
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
SET_SOURCE_FILES_PROPERTIES("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
ENDIF ()
FUNCTION(ADD_CLAR_TEST name)
IF (NOT USE_LEAK_CHECKER STREQUAL "OFF")
ADD_TEST(${name} "${libgit2_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${libgit2_BINARY_DIR}/libgit2_clar" ${ARGN})
ELSE()
ADD_TEST(${name} "${libgit2_BINARY_DIR}/libgit2_clar" ${ARGN})
ENDIF()
ENDFUNCTION(ADD_CLAR_TEST)
ADD_CLAR_TEST(offline -v -xonline)
ADD_CLAR_TEST(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
ADD_CLAR_TEST(online -v -sonline)
ADD_CLAR_TEST(gitdaemon -v -sonline::push)
ADD_CLAR_TEST(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh)
ADD_CLAR_TEST(proxy -v -sonline::clone::proxy)
+48
View File
@@ -0,0 +1,48 @@
Writing Clar tests for libgit2
==============================
For information on the Clar testing framework and a detailed introduction
please visit:
https://github.com/vmg/clar
* Write your modules and tests. Use good, meaningful names.
* Make sure you actually build the tests by setting:
cmake -DBUILD_CLAR=ON build/
* Test:
./build/libgit2_clar
* Make sure everything is fine.
* Send your pull request. That's it.
Memory leak checks
------------------
These are automatically run as part of CI, but if you want to check locally:
#### Linux
Uses [`valgrind`](http://www.valgrind.org/):
```console
$ cmake -DBUILD_CLAR=ON -DVALGRIND=ON ..
$ cmake --build .
$ valgrind --leak-check=full --show-reachable=yes --num-callers=50 --suppressions=../libgit2_clar.supp \
./libgit2_clar
```
#### macOS
Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html), which requires XCode installed:
```console
$ MallocStackLogging=1 MallocScribble=1 MallocLogFile=/dev/null CLAR_AT_EXIT="leaks -quiet \$PPID" \
./libgit2_clar
```
+135
View File
@@ -0,0 +1,135 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
struct iterator_compare_data {
struct merge_index_entry *expected;
size_t cnt;
size_t idx;
};
static int iterator_compare(const git_index_entry *entry, void *_data)
{
git_oid expected_id;
struct iterator_compare_data *data = (struct iterator_compare_data *)_data;
cl_assert_equal_i(GIT_INDEX_ENTRY_STAGE(entry), data->expected[data->idx].stage);
cl_git_pass(git_oid_fromstr(&expected_id, data->expected[data->idx].oid_str));
cl_assert_equal_oid(&entry->id, &expected_id);
cl_assert_equal_i(entry->mode, data->expected[data->idx].mode);
cl_assert_equal_s(entry->path, data->expected[data->idx].path);
if (data->idx >= data->cnt)
return -1;
data->idx++;
return 0;
}
void validate_apply_workdir(
git_repository *repo,
struct merge_index_entry *workdir_entries,
size_t workdir_cnt)
{
git_index *index;
git_iterator *iterator;
git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
struct iterator_compare_data data = { workdir_entries, workdir_cnt };
opts.flags |= GIT_ITERATOR_INCLUDE_HASH;
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_workdir(&iterator, repo, index, NULL, &opts));
cl_git_pass(git_iterator_foreach(iterator, iterator_compare, &data));
cl_assert_equal_i(data.idx, data.cnt);
git_iterator_free(iterator);
git_index_free(index);
}
void validate_apply_index(
git_repository *repo,
struct merge_index_entry *index_entries,
size_t index_cnt)
{
git_index *index;
git_iterator *iterator;
struct iterator_compare_data data = { index_entries, index_cnt };
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_index(&iterator, repo, index, NULL));
cl_git_pass(git_iterator_foreach(iterator, iterator_compare, &data));
cl_assert_equal_i(data.idx, data.cnt);
git_iterator_free(iterator);
git_index_free(index);
}
static int iterator_eq(const git_index_entry **entry, void *_data)
{
GIT_UNUSED(_data);
if (!entry[0] || !entry[1])
return -1;
cl_assert_equal_i(GIT_INDEX_ENTRY_STAGE(entry[0]), GIT_INDEX_ENTRY_STAGE(entry[1]));
cl_assert_equal_oid(&entry[0]->id, &entry[1]->id);
cl_assert_equal_i(entry[0]->mode, entry[1]->mode);
cl_assert_equal_s(entry[0]->path, entry[1]->path);
return 0;
}
void validate_index_unchanged(git_repository *repo)
{
git_tree *head;
git_index *index;
git_iterator *head_iterator, *index_iterator, *iterators[2];
cl_git_pass(git_repository_head_tree(&head, repo));
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_tree(&head_iterator, head, NULL));
cl_git_pass(git_iterator_for_index(&index_iterator, repo, index, NULL));
iterators[0] = head_iterator;
iterators[1] = index_iterator;
cl_git_pass(git_iterator_walk(iterators, 2, iterator_eq, NULL));
git_iterator_free(head_iterator);
git_iterator_free(index_iterator);
git_tree_free(head);
git_index_free(index);
}
void validate_workdir_unchanged(git_repository *repo)
{
git_tree *head;
git_index *index;
git_iterator *head_iterator, *workdir_iterator, *iterators[2];
git_iterator_options workdir_opts = GIT_ITERATOR_OPTIONS_INIT;
cl_git_pass(git_repository_head_tree(&head, repo));
cl_git_pass(git_repository_index(&index, repo));
workdir_opts.flags |= GIT_ITERATOR_INCLUDE_HASH;
cl_git_pass(git_iterator_for_tree(&head_iterator, head, NULL));
cl_git_pass(git_iterator_for_workdir(&workdir_iterator, repo, index, NULL, &workdir_opts));
iterators[0] = head_iterator;
iterators[1] = workdir_iterator;
cl_git_pass(git_iterator_walk(iterators, 2, iterator_eq, NULL));
git_iterator_free(head_iterator);
git_iterator_free(workdir_iterator);
git_tree_free(head);
git_index_free(index);
}
+488
View File
@@ -0,0 +1,488 @@
#include "../merge/merge_helpers.h"
#define TEST_REPO_PATH "merge-recursive"
#define DIFF_MODIFY_TWO_FILES \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"index f516580..ffb36e5 100644\n" \
"--- a/asparagus.txt\n" \
"+++ b/asparagus.txt\n" \
"@@ -1 +1 @@\n" \
"-ASPARAGUS SOUP!\n" \
"+ASPARAGUS SOUP.\n" \
"diff --git a/veal.txt b/veal.txt\n" \
"index 94d2c01..a7b0665 100644\n" \
"--- a/veal.txt\n" \
"+++ b/veal.txt\n" \
"@@ -1 +1 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP.\n" \
"@@ -7 +7 @@ occasionally, then put into it a shin of veal, let it boil two hours\n" \
"-longer. take out the slices of ham, and skim off the grease if any\n" \
"+longer; take out the slices of ham, and skim off the grease if any\n"
/* This is the binary equivalent of DIFF_MODIFY_TWO_FILES */
#define DIFF_MODIFY_TWO_FILES_BINARY \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"index f51658077d85f2264fa179b4d0848268cb3475c3..ffb36e513f5fdf8a6ba850a20142676a2ac4807d 100644\n" \
"GIT binary patch\n" \
"delta 24\n" \
"fcmX@ja+-zTF*v|6$k9DCSRvRyG(c}7zYP-rT_OhP\n" \
"\n" \
"delta 24\n" \
"fcmX@ja+-zTF*v|6$k9DCSRvRyG(d49zYP-rT;T@W\n" \
"\n" \
"diff --git a/veal.txt b/veal.txt\n" \
"index 94d2c01087f48213bd157222d54edfefd77c9bba..a7b066537e6be7109abfe4ff97b675d4e077da20 100644\n" \
"GIT binary patch\n" \
"delta 26\n" \
"hcmX@kah!uI%+=9HA=p1OKyM?L03)OIW@$zpW&mXg25bNT\n" \
"\n" \
"delta 26\n" \
"hcmX@kah!uI%+=9HA=p1OKyf3N03)N`W@$zpW&mU#22ub3\n" \
"\n"
#define DIFF_DELETE_FILE \
"diff --git a/gravy.txt b/gravy.txt\n" \
"deleted file mode 100644\n" \
"index c4e6cca..0000000\n" \
"--- a/gravy.txt\n" \
"+++ /dev/null\n" \
"@@ -1,8 +0,0 @@\n" \
"-GRAVY SOUP.\n" \
"-\n" \
"-Get eight pounds of coarse lean beef--wash it clean and lay it in your\n" \
"-pot, put in the same ingredients as for the shin soup, with the same\n" \
"-quantity of water, and follow the process directed for that. Strain the\n" \
"-soup through a sieve, and serve it up clear, with nothing more than\n" \
"-toasted bread in it; two table-spoonsful of mushroom catsup will add a\n" \
"-fine flavour to the soup.\n"
#define DIFF_ADD_FILE \
"diff --git a/newfile.txt b/newfile.txt\n" \
"new file mode 100644\n" \
"index 0000000..6370543\n" \
"--- /dev/null\n" \
"+++ b/newfile.txt\n" \
"@@ -0,0 +1,2 @@\n" \
"+This is a new file!\n" \
"+Added by a patch.\n"
#define DIFF_EXECUTABLE_FILE \
"diff --git a/beef.txt b/beef.txt\n" \
"old mode 100644\n" \
"new mode 100755\n"
#define DIFF_MANY_CHANGES_ONE \
"diff --git a/veal.txt b/veal.txt\n" \
"index 94d2c01..c9d7d5d 100644\n" \
"--- a/veal.txt\n" \
"+++ b/veal.txt\n" \
"@@ -1,2 +1,2 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP\n" \
" \n" \
"@@ -4,3 +4,2 @@\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n" \
"-slices of lean ham; let it boil steadily two hours; skim it\n" \
" occasionally, then put into it a shin of veal, let it boil two hours\n" \
"@@ -8,3 +7,3 @@\n" \
" should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
"-of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
"+OF FLOUR very nicely, and the yelks of two eggs beaten well, strain this\n" \
" mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
"@@ -12,2 +11,3 @@\n" \
" boiled two or three minutes to take off the raw taste of the eggs. If\n" \
"+Inserted line.\n" \
" the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
"@@ -15,3 +15,3 @@\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
"-hot water, when they may be easily peeled. When made in this way you\n" \
"+Changed line.\n" \
" must thicken it with the flour only. Any part of the veal may be used,\n"
#define DIFF_MANY_CHANGES_TWO \
"diff --git a/veal.txt b/veal.txt\n" \
"index 94d2c01..6b943d6 100644\n" \
"--- a/veal.txt\n" \
"+++ b/veal.txt\n" \
"@@ -1,2 +1,2 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP!!!\n" \
" \n" \
"@@ -4,3 +4,2 @@\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n" \
"-slices of lean ham; let it boil steadily two hours; skim it\n" \
" occasionally, then put into it a shin of veal, let it boil two hours\n" \
"@@ -8,3 +7,3 @@\n" \
" should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
"-of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
"+of flour very nicely, AND the yelks of two eggs beaten well, strain this\n" \
" mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
"@@ -12,2 +11,3 @@\n" \
" boiled two or three minutes to take off the raw taste of the eggs. If\n" \
"+New line.\n" \
" the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
"@@ -15,4 +15,5 @@\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
"-hot water, when they may be easily peeled. When made in this way you\n" \
"-must thicken it with the flour only. Any part of the veal may be used,\n" \
"-but the shin or knuckle is the nicest.\n" \
"+HOT water, when they may be easily peeled. When made in this way you\n" \
"+must THICKEN it with the flour only. Any part of the veal may be used,\n" \
"+but the shin OR knuckle is the nicest.\n" \
"+Another new line.\n" \
#define DIFF_RENAME_FILE \
"diff --git a/beef.txt b/notbeef.txt\n" \
"similarity index 100%\n" \
"rename from beef.txt\n" \
"rename to notbeef.txt\n"
#define DIFF_RENAME_AND_MODIFY_FILE \
"diff --git a/beef.txt b/notbeef.txt\n" \
"similarity index 97%\n" \
"rename from beef.txt\n" \
"rename to notbeef.txt\n" \
"index 68f6182..6fa1014 100644\n" \
"--- a/beef.txt\n" \
"+++ b/notbeef.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-BEEF SOUP.\n" \
"+THIS IS NOT BEEF SOUP, IT HAS A NEW NAME.\n" \
"\n" \
" Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
" which must be taken away entirely, or the soup will be greasy. Wash the\n"
#define DIFF_RENAME_A_TO_B_TO_C \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"deleted file mode 100644\n" \
"index f516580..0000000\n" \
"--- a/asparagus.txt\n" \
"+++ /dev/null\n" \
"@@ -1,10 +0,0 @@\n" \
"-ASPARAGUS SOUP!\n" \
"-\n" \
"-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
"-of the tops, and lay them in water, chop the stalks and put them on the\n" \
"-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
"-add two quarts of water, boil them till the stalks are quite soft, then\n" \
"-pulp them through a sieve, and strain the water to it, which must be put\n" \
"-back in the pot; put into it a chicken cut up, with the tops of\n" \
"-asparagus which had been laid by, boil it until these last articles are\n" \
"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
"diff --git a/beef.txt b/beef.txt\n" \
"index 68f6182..f516580 100644\n" \
"--- a/beef.txt\n" \
"+++ b/beef.txt\n" \
"@@ -1,22 +1,10 @@\n" \
"-BEEF SOUP.\n" \
"+ASPARAGUS SOUP!\n" \
"\n" \
"-Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
"-which must be taken away entirely, or the soup will be greasy. Wash the\n" \
"-meat clean and lay it in a pot, sprinkle over it one small\n" \
"-table-spoonful of pounded black pepper, and two of salt; three onions\n" \
"-the size of a hen's egg, cut small, six small carrots scraped and cut\n" \
"-up, two small turnips pared and cut into dice; pour on three quarts of\n" \
"-water, cover the pot close, and keep it gently and steadily boiling five\n" \
"-hours, which will leave about three pints of clear soup; do not let the\n" \
"-pot boil over, but take off the scum carefully, as it rises. When it has\n" \
"-boiled four hours, put in a small bundle of thyme and parsley, and a\n" \
"-pint of celery cut small, or a tea-spoonful of celery seed pounded.\n" \
"-These latter ingredients would lose their delicate flavour if boiled too\n" \
"-much. Just before you take it up, brown it in the following manner: put\n" \
"-a small table-spoonful of nice brown sugar into an iron skillet, set it\n" \
"-on the fire and stir it till it melts and looks very dark, pour into it\n" \
"-a ladle full of the soup, a little at a time; stirring it all the while.\n" \
"-Strain this browning and mix it well with the soup; take out the bundle\n" \
"-of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
"-pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
"-and serve it up.\n" \
"+Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
"+of the tops, and lay them in water, chop the stalks and put them on the\n" \
"+fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
"+add two quarts of water, boil them till the stalks are quite soft, then\n" \
"+pulp them through a sieve, and strain the water to it, which must be put\n" \
"+back in the pot; put into it a chicken cut up, with the tops of\n" \
"+asparagus which had been laid by, boil it until these last articles are\n" \
"+sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
"diff --git a/notbeef.txt b/notbeef.txt\n" \
"new file mode 100644\n" \
"index 0000000..68f6182\n" \
"--- /dev/null\n" \
"+++ b/notbeef.txt\n" \
"@@ -0,0 +1,22 @@\n" \
"+BEEF SOUP.\n" \
"+\n" \
"+Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
"+which must be taken away entirely, or the soup will be greasy. Wash the\n" \
"+meat clean and lay it in a pot, sprinkle over it one small\n" \
"+table-spoonful of pounded black pepper, and two of salt; three onions\n" \
"+the size of a hen's egg, cut small, six small carrots scraped and cut\n" \
"+up, two small turnips pared and cut into dice; pour on three quarts of\n" \
"+water, cover the pot close, and keep it gently and steadily boiling five\n" \
"+hours, which will leave about three pints of clear soup; do not let the\n" \
"+pot boil over, but take off the scum carefully, as it rises. When it has\n" \
"+boiled four hours, put in a small bundle of thyme and parsley, and a\n" \
"+pint of celery cut small, or a tea-spoonful of celery seed pounded.\n" \
"+These latter ingredients would lose their delicate flavour if boiled too\n" \
"+much. Just before you take it up, brown it in the following manner: put\n" \
"+a small table-spoonful of nice brown sugar into an iron skillet, set it\n" \
"+on the fire and stir it till it melts and looks very dark, pour into it\n" \
"+a ladle full of the soup, a little at a time; stirring it all the while.\n" \
"+Strain this browning and mix it well with the soup; take out the bundle\n" \
"+of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
"+pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
"+and serve it up.\n"
#define DIFF_RENAME_A_TO_B_TO_C_EXACT \
"diff --git a/asparagus.txt b/beef.txt\n" \
"similarity index 100%\n" \
"rename from asparagus.txt\n" \
"rename to beef.txt\n" \
"diff --git a/beef.txt b/notbeef.txt\n" \
"similarity index 100%\n" \
"rename from beef.txt\n" \
"rename to notbeef.txt\n"
#define DIFF_RENAME_CIRCULAR \
"diff --git a/asparagus.txt b/beef.txt\n" \
"similarity index 100%\n" \
"rename from asparagus.txt\n" \
"rename to beef.txt\n" \
"diff --git a/beef.txt b/notbeef.txt\n" \
"similarity index 100%\n" \
"rename from beef.txt\n" \
"rename to asparagus.txt\n"
#define DIFF_RENAME_2_TO_1 \
"diff --git a/asparagus.txt b/2.txt\n" \
"similarity index 100%\n" \
"rename from asparagus.txt\n" \
"rename to 2.txt\n" \
"diff --git a/beef.txt b/2.txt\n" \
"similarity index 100%\n" \
"rename from beef.txt\n" \
"rename to 2.txt\n"
#define DIFF_RENAME_1_TO_2 \
"diff --git a/asparagus.txt b/2.txt\n" \
"similarity index 100%\n" \
"rename from asparagus.txt\n" \
"rename to 1.txt\n" \
"diff --git a/asparagus.txt b/2.txt\n" \
"similarity index 100%\n" \
"rename from asparagus.txt\n" \
"rename to 2.txt\n"
#define DIFF_TWO_DELTAS_ONE_FILE \
"diff --git a/beef.txt b/beef.txt\n" \
"index 68f6182..235069d 100644\n" \
"--- a/beef.txt\n" \
"+++ b/beef.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-BEEF SOUP.\n" \
"+BEEF SOUP!\n" \
"\n" \
" Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
" which must be taken away entirely, or the soup will be greasy. Wash the\n" \
"diff --git a/beef.txt b/beef.txt\n" \
"index 68f6182..e059eb5 100644\n" \
"--- a/beef.txt\n" \
"+++ b/beef.txt\n" \
"@@ -19,4 +19,4 @@ a ladle full of the soup, a little at a time; stirring it all the while.\n" \
" Strain this browning and mix it well with the soup; take out the bundle\n" \
" of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
" pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
"-and serve it up.\n" \
"+and serve it up!\n"
#define DIFF_TWO_DELTAS_ONE_NEW_FILE \
"diff --git a/newfile.txt b/newfile.txt\n" \
"new file mode 100644\n" \
"index 0000000..6434b13\n" \
"--- /dev/null\n" \
"+++ b/newfile.txt\n" \
"@@ -0,0 +1 @@\n" \
"+This is a new file.\n" \
"diff --git a/newfile.txt b/newfile.txt\n" \
"index 6434b13..08d4c44 100644\n" \
"--- a/newfile.txt\n" \
"+++ b/newfile.txt\n" \
"@@ -1 +1,3 @@\n" \
" This is a new file.\n" \
"+\n" \
"+This is another change to a new file.\n"
#define DIFF_RENAME_AND_MODIFY_DELTAS \
"diff --git a/veal.txt b/asdf.txt\n" \
"similarity index 96%\n" \
"rename from veal.txt\n" \
"rename to asdf.txt\n" \
"index 94d2c01..292cb60 100644\n" \
"--- a/veal.txt\n" \
"+++ b/asdf.txt\n" \
"@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
" hot water, when they may be easily peeled. When made in this way you\n" \
" must thicken it with the flour only. Any part of the veal may be used,\n" \
"-but the shin or knuckle is the nicest.\n" \
"+but the shin or knuckle is the nicest!\n" \
"diff --git a/asdf.txt b/asdf.txt\n" \
"index 292cb60..61c686b 100644\n" \
"--- a/asdf.txt\n" \
"+++ b/asdf.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP\n" \
"\n" \
" Put into a pot three quarts of water, three onions cut small, one\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n"
#define DIFF_RENAME_AFTER_MODIFY \
"diff --git a/veal.txt b/veal.txt\n" \
"index 292cb60..61c686b 100644\n" \
"--- a/veal.txt\n" \
"+++ b/veal.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP\n" \
"\n" \
" Put into a pot three quarts of water, three onions cut small, one\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n" \
"diff --git a/veal.txt b/other.txt\n" \
"similarity index 96%\n" \
"rename from veal.txt\n" \
"rename to other.txt\n" \
"index 94d2c01..292cb60 100644\n" \
"--- a/veal.txt\n" \
"+++ b/other.txt\n" \
"@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
" hot water, when they may be easily peeled. When made in this way you\n" \
" must thicken it with the flour only. Any part of the veal may be used,\n" \
"-but the shin or knuckle is the nicest.\n" \
"+but the shin or knuckle is the nicest!\n"
#define DIFF_RENAME_AFTER_MODIFY_TARGET_PATH \
"diff --git a/beef.txt b/beef.txt\n" \
"index 292cb60..61c686b 100644\n" \
"--- a/beef.txt\n" \
"+++ b/beef.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP\n" \
"\n" \
" Put into a pot three quarts of water, three onions cut small, one\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n" \
"diff --git a/veal.txt b/beef.txt\n" \
"similarity index 96%\n" \
"rename from veal.txt\n" \
"rename to beef.txt\n" \
"index 94d2c01..292cb60 100644\n" \
"--- a/veal.txt\n" \
"+++ b/beef.txt\n" \
"@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
" hot water, when they may be easily peeled. When made in this way you\n" \
" must thicken it with the flour only. Any part of the veal may be used,\n" \
"-but the shin or knuckle is the nicest.\n" \
"+but the shin or knuckle is the nicest!\n"
#define DIFF_RENAME_AND_MODIFY_SOURCE_PATH \
"diff --git a/veal.txt b/asdf.txt\n" \
"similarity index 96%\n" \
"rename from veal.txt\n" \
"rename to asdf.txt\n" \
"index 94d2c01..292cb60 100644\n" \
"--- a/veal.txt\n" \
"+++ b/asdf.txt\n" \
"@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
" in, first taking off their skins, by letting them stand a few minutes in\n" \
" hot water, when they may be easily peeled. When made in this way you\n" \
" must thicken it with the flour only. Any part of the veal may be used,\n" \
"-but the shin or knuckle is the nicest.\n" \
"+but the shin or knuckle is the nicest!\n" \
"diff --git a/veal.txt b/veal.txt\n" \
"index 292cb60..61c686b 100644\n" \
"--- a/veal.txt\n" \
"+++ b/veal.txt\n" \
"@@ -1,4 +1,4 @@\n" \
"-VEAL SOUP!\n" \
"+VEAL SOUP\n" \
"\n" \
" Put into a pot three quarts of water, three onions cut small, one\n" \
" spoonful of black pepper pounded, and two of salt, with two or three\n"
#define DIFF_DELETE_AND_READD_FILE \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"deleted file mode 100644\n" \
"index f516580..0000000\n" \
"--- a/asparagus.txt\n" \
"+++ /dev/null\n" \
"@@ -1,10 +0,0 @@\n" \
"-ASPARAGUS SOUP!\n" \
"-\n" \
"-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
"-of the tops, and lay them in water, chop the stalks and put them on the\n" \
"-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
"-add two quarts of water, boil them till the stalks are quite soft, then\n" \
"-pulp them through a sieve, and strain the water to it, which must be put\n" \
"-back in the pot; put into it a chicken cut up, with the tops of\n" \
"-asparagus which had been laid by, boil it until these last articles are\n" \
"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"new file mode 100644\n" \
"index 0000000..2dc7f8b\n" \
"--- /dev/null\n" \
"+++ b/asparagus.txt\n" \
"@@ -0,0 +1 @@\n" \
"+New file.\n" \
#define DIFF_REMOVE_FILE_TWICE \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"deleted file mode 100644\n" \
"index f516580..0000000\n" \
"--- a/asparagus.txt\n" \
"+++ /dev/null\n" \
"@@ -1,10 +0,0 @@\n" \
"-ASPARAGUS SOUP!\n" \
"-\n" \
"-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
"-of the tops, and lay them in water, chop the stalks and put them on the\n" \
"-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
"-add two quarts of water, boil them till the stalks are quite soft, then\n" \
"-pulp them through a sieve, and strain the water to it, which must be put\n" \
"-back in the pot; put into it a chicken cut up, with the tops of\n" \
"-asparagus which had been laid by, boil it until these last articles are\n" \
"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
"diff --git a/asparagus.txt b/asparagus.txt\n" \
"deleted file mode 100644\n" \
"index f516580..0000000\n" \
"--- a/asparagus.txt\n" \
"+++ /dev/null\n" \
"@@ -1,10 +0,0 @@\n" \
"-ASPARAGUS SOUP!\n" \
"-\n" \
"-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
"-of the tops, and lay them in water, chop the stalks and put them on the\n" \
"-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
"-add two quarts of water, boil them till the stalks are quite soft, then\n" \
"-pulp them through a sieve, and strain the water to it, which must be put\n" \
"-back in the pot; put into it a chicken cut up, with the tops of\n" \
"-asparagus which had been laid by, boil it until these last articles are\n" \
"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n"
void validate_apply_workdir(
git_repository *repo,
struct merge_index_entry *workdir_entries,
size_t workdir_cnt);
void validate_apply_index(
git_repository *repo,
struct merge_index_entry *index_entries,
size_t index_cnt);
void validate_index_unchanged(git_repository *repo);
void validate_workdir_unchanged(git_repository *repo);
+736
View File
@@ -0,0 +1,736 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_both__initialize(void)
{
git_oid oid;
git_commit *commit;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
}
void test_apply_both__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_apply_both__generated_diff(void)
{
git_oid a_oid, b_oid;
git_commit *a_commit, *b_commit;
git_tree *a_tree, *b_tree;
git_diff *diff;
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
struct merge_index_entry both_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707");
git_oid_fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f");
cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_commit_tree(&b_tree, b_commit));
cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
git_tree_free(a_tree);
git_tree_free(b_tree);
git_commit_free(a_commit);
git_commit_free(b_commit);
}
void test_apply_both__parsed_diff(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__removes_file(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__adds_file(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__application_failure_leaves_index_unmodified(void)
{
git_diff *diff;
git_index *index;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
/* mutate the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_remove(index, "veal.txt", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_both__index_must_match_workdir(void)
{
git_diff *diff;
git_index *index;
git_index_entry idx_entry;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
/*
* Append a line to the end of the file in both the index and the
* working directory. Although the appended line would allow for
* patch application in each, the line appended is different in
* each, so the application should not be allowed.
*/
cl_git_append2file("merge-recursive/asparagus.txt",
"This is a modification.\n");
cl_git_pass(git_repository_index(&index, repo));
memset(&idx_entry, 0, sizeof(git_index_entry));
idx_entry.mode = 0100644;
idx_entry.path = "asparagus.txt";
cl_git_pass(git_oid_fromstr(&idx_entry.id, "06d3fefb8726ab1099acc76e02dfb85e034b2538"));
cl_git_pass(git_index_add(index, &idx_entry));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
}
void test_apply_both__index_mode_must_match_workdir(void)
{
git_diff *diff;
if (!cl_is_chmod_supported())
clar__skip();
/* Set a file in the working directory executable. */
cl_must_pass(p_chmod("merge-recursive/asparagus.txt", 0755));
cl_git_pass(git_diff_from_buffer(&diff, DIFF_MODIFY_TWO_FILES,
strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
}
void test_apply_both__application_failure_leaves_workdir_unmodified(void)
{
git_diff *diff;
git_index *index;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "8684724651336001c5dbce74bed6736d2443958d", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/* mutate the workdir */
cl_git_rewritefile("merge-recursive/veal.txt",
"This is a modification.\n");
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, "veal.txt"));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__keeps_nonconflicting_changes(void)
{
git_diff *diff;
git_index *index;
git_index_entry idx_entry;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "beef.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
struct merge_index_entry workdir_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "gravy.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/* mutate the index */
cl_git_pass(git_repository_index(&index, repo));
memset(&idx_entry, 0, sizeof(git_index_entry));
idx_entry.mode = 0100644;
idx_entry.path = "beef.txt";
cl_git_pass(git_oid_fromstr(&idx_entry.id, "898d12687fb35be271c27c795a6b32c8b51da79e"));
cl_git_pass(git_index_add(index, &idx_entry));
cl_git_pass(git_index_remove(index, "bouilli.txt", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
/* and mutate the working directory */
cl_git_rmfile("merge-recursive/oyster.txt");
cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__can_apply_nonconflicting_file_changes(void)
{
git_diff *diff;
git_index *index;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry both_expected[] = {
{ 0100644, "f8a701c8a1a22c1729ee50faff1111f2d64f96fc", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
/*
* Replace the workdir file with a version that is different than
* HEAD but such that the patch still applies cleanly. This item
* has a new line appended.
*/
cl_git_append2file("merge-recursive/asparagus.txt",
"This line is added in the index and the workdir.\n");
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, "asparagus.txt"));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__honors_crlf_attributes(void)
{
git_diff *diff;
git_oid oid;
git_commit *commit;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
struct merge_index_entry workdir_expected[] = {
{ 0100644, "176a458f94e0ea5272ce67c36bf30b6be9caf623", 0, ".gitattributes" },
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_mkfile("merge-recursive/.gitattributes", "* text=auto\n");
cl_git_rmfile("merge-recursive/asparagus.txt");
cl_git_rmfile("merge-recursive/veal.txt");
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_FILE,
strlen(DIFF_RENAME_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_and_modify(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "6fa10147f00fe1fab1d5e835529a9dad53db8552", 0, "notbeef.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_FILE,
strlen(DIFF_RENAME_AND_MODIFY_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_a_to_b_to_c(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C,
strlen(DIFF_RENAME_A_TO_B_TO_C)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_a_to_b_to_c_exact(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C_EXACT,
strlen(DIFF_RENAME_A_TO_B_TO_C_EXACT)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_circular(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "asparagus.txt" },
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_CIRCULAR,
strlen(DIFF_RENAME_CIRCULAR)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_2_to_1(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "2.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_2_TO_1,
strlen(DIFF_RENAME_2_TO_1)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_1_to_2(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "1.txt" },
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "2.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_1_TO_2,
strlen(DIFF_RENAME_1_TO_2)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__two_deltas_one_file(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "0a9fd4415635e72573f0f6b5e68084cfe18f5075", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_FILE,
strlen(DIFF_TWO_DELTAS_ONE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__two_deltas_one_new_file(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "08d4c445cf0078f3d9b604b82f32f4d87e083325", 0, "newfile.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_NEW_FILE,
strlen(DIFF_TWO_DELTAS_ONE_NEW_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_and_modify_deltas(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "61c686bed39684eee8a2757ceb1291004a21333f", 0, "asdf.txt" },
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_DELTAS,
strlen(DIFF_RENAME_AND_MODIFY_DELTAS)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__rename_delta_after_modify_delta(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "292cb60ce5e25c337c5b6e12957bbbfe1be4bf49", 0, "other.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "c8c120f466591bbe3b8867361d5ec3cdd9fda756", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY,
strlen(DIFF_RENAME_AFTER_MODIFY)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__cant_rename_after_modify_nonexistent_target_path(void)
{
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY_TARGET_PATH,
strlen(DIFF_RENAME_AFTER_MODIFY_TARGET_PATH)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
}
void test_apply_both__cant_modify_source_path_after_rename(void)
{
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_SOURCE_PATH,
strlen(DIFF_RENAME_AND_MODIFY_SOURCE_PATH)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
}
void test_apply_both__readd_deleted_file(void)
{
git_diff *diff;
struct merge_index_entry both_expected[] = {
{ 0100644, "2dc7f8b24ba27f3888368bd180df03ff4c6c6fab", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
};
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_AND_READD_FILE,
strlen(DIFF_DELETE_AND_READD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
validate_apply_workdir(repo, both_expected, both_expected_cnt);
git_diff_free(diff);
}
void test_apply_both__cant_remove_file_twice(void)
{
git_diff *diff;
cl_git_pass(git_diff_from_buffer(&diff, DIFF_REMOVE_FILE_TWICE,
strlen(DIFF_REMOVE_FILE_TWICE)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
}
+128
View File
@@ -0,0 +1,128 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_callbacks__initialize(void)
{
git_oid oid;
git_commit *commit;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
}
void test_apply_callbacks__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int delta_abort_cb(const git_diff_delta *delta, void *payload)
{
GIT_UNUSED(payload);
if (!strcmp(delta->old_file.path, "veal.txt"))
return -99;
return 0;
}
void test_apply_callbacks__delta_aborts(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
opts.delta_cb = delta_abort_cb;
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_fail_with(-99,
git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
validate_index_unchanged(repo);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
static int delta_skip_cb(const git_diff_delta *delta, void *payload)
{
GIT_UNUSED(payload);
if (!strcmp(delta->old_file.path, "asparagus.txt"))
return 1;
return 0;
}
void test_apply_callbacks__delta_can_skip(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
opts.delta_cb = delta_skip_cb;
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
static int hunk_skip_odds_cb(const git_diff_hunk *hunk, void *payload)
{
int *count = (int *)payload;
GIT_UNUSED(hunk);
return ((*count)++ % 2 == 1);
}
void test_apply_callbacks__hunk_can_skip(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
int count = 0;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "06f751b6ba4f017ddbf4248015768300268e092a", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
opts.hunk_cb = hunk_skip_odds_cb;
opts.payload = &count;
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
+121
View File
@@ -0,0 +1,121 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_check__initialize(void)
{
git_oid oid;
git_commit *commit;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
}
void test_apply_check__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_apply_check__generate_diff(void)
{
git_oid a_oid, b_oid;
git_commit *a_commit, *b_commit;
git_tree *a_tree, *b_tree;
git_diff *diff;
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
cl_git_pass(git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707"));
cl_git_pass(git_oid_fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f"));
cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_commit_tree(&b_tree, b_commit));
opts.flags |= GIT_APPLY_CHECK;
cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, &opts));
validate_index_unchanged(repo);
validate_workdir_unchanged(repo);
git_diff_free(diff);
git_tree_free(a_tree);
git_tree_free(b_tree);
git_commit_free(a_commit);
git_commit_free(b_commit);
}
void test_apply_check__parsed_diff(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
opts.flags |= GIT_APPLY_CHECK;
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
validate_index_unchanged(repo);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_check__binary(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
opts.flags |= GIT_APPLY_CHECK;
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES_BINARY,
strlen(DIFF_MODIFY_TWO_FILES_BINARY)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
validate_index_unchanged(repo);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_check__does_not_apply(void)
{
git_diff *diff;
git_index *index;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
/* mutate the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_remove(index, "veal.txt", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
opts.flags |= GIT_APPLY_CHECK;
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
validate_apply_index(repo, index_expected, index_expected_cnt);
git_diff_free(diff);
}
+379
View File
@@ -0,0 +1,379 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "apply.h"
#include "repository.h"
#include "buf_text.h"
#include "../patch/patch_common.h"
static git_repository *repo = NULL;
static git_diff_options binary_opts = GIT_DIFF_OPTIONS_INIT;
void test_apply_fromdiff__initialize(void)
{
repo = cl_git_sandbox_init("renames");
binary_opts.flags |= GIT_DIFF_SHOW_BINARY;
}
void test_apply_fromdiff__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int apply_gitbuf(
const git_buf *old,
const char *oldname,
const git_buf *new,
const char *newname,
const char *patch_expected,
const git_diff_options *diff_opts)
{
git_patch *patch;
git_buf result = GIT_BUF_INIT;
git_buf patchbuf = GIT_BUF_INIT;
char *filename;
unsigned int mode;
int error;
cl_git_pass(git_patch_from_buffers(&patch,
old ? old->ptr : NULL, old ? old->size : 0,
oldname,
new ? new->ptr : NULL, new ? new->size : 0,
newname,
diff_opts));
if (patch_expected) {
cl_git_pass(git_patch_to_buf(&patchbuf, patch));
cl_assert_equal_s(patch_expected, patchbuf.ptr);
}
error = git_apply__patch(&result, &filename, &mode, old ? old->ptr : NULL, old ? old->size : 0, patch, NULL);
if (error == 0 && new == NULL) {
cl_assert_equal_i(0, result.size);
cl_assert_equal_p(NULL, filename);
cl_assert_equal_i(0, mode);
}
else if (error == 0) {
cl_assert_equal_s(new->ptr, result.ptr);
cl_assert_equal_s(newname ? newname : oldname, filename);
cl_assert_equal_i(0100644, mode);
}
git__free(filename);
git_buf_dispose(&result);
git_buf_dispose(&patchbuf);
git_patch_free(patch);
return error;
}
static int apply_buf(
const char *old,
const char *oldname,
const char *new,
const char *newname,
const char *patch_expected,
const git_diff_options *diff_opts)
{
git_buf o = GIT_BUF_INIT, n = GIT_BUF_INIT,
*optr = NULL, *nptr = NULL;
if (old) {
o.ptr = (char *)old;
o.size = strlen(old);
optr = &o;
}
if (new) {
n.ptr = (char *)new;
n.size = strlen(new);
nptr = &n;
}
return apply_gitbuf(optr, oldname, nptr, newname, patch_expected, diff_opts);
}
void test_apply_fromdiff__change_middle(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_CHANGE_MIDDLE, "file.txt",
PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL));
}
void test_apply_fromdiff__change_middle_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_CHANGE_MIDDLE, "file.txt",
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__change_firstline(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_CHANGE_FIRSTLINE, "file.txt",
PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL));
}
void test_apply_fromdiff__lastline(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_CHANGE_LASTLINE, "file.txt",
PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL));
}
void test_apply_fromdiff__change_middle_and_lastline_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_CHANGE_MIDDLE_AND_LASTLINE, "file.txt",
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_AND_LASTLINE_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__prepend(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND, "file.txt",
PATCH_ORIGINAL_TO_PREPEND, NULL));
}
void test_apply_fromdiff__prepend_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND, "file.txt",
PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__prepend_and_change(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE, NULL));
}
void test_apply_fromdiff__prepend_and_change_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__delete_and_change(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
PATCH_ORIGINAL_TO_DELETE_AND_CHANGE, NULL));
}
void test_apply_fromdiff__delete_and_change_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
PATCH_ORIGINAL_TO_DELETE_AND_CHANGE_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__delete_firstline(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_FIRSTLINE, "file.txt",
PATCH_ORIGINAL_TO_DELETE_FIRSTLINE, NULL));
}
void test_apply_fromdiff__append(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_APPEND, "file.txt",
PATCH_ORIGINAL_TO_APPEND, NULL));
}
void test_apply_fromdiff__append_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_APPEND, "file.txt",
PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts));
}
void test_apply_fromdiff__prepend_and_append(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_APPEND, "file.txt",
PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL));
}
void test_apply_fromdiff__to_empty_file(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
"", NULL,
PATCH_ORIGINAL_TO_EMPTY_FILE, NULL));
}
void test_apply_fromdiff__from_empty_file(void)
{
cl_git_pass(apply_buf(
"", NULL,
FILE_ORIGINAL, "file.txt",
PATCH_EMPTY_FILE_TO_ORIGINAL, NULL));
}
void test_apply_fromdiff__add(void)
{
cl_git_pass(apply_buf(
NULL, NULL,
FILE_ORIGINAL, "file.txt",
PATCH_ADD_ORIGINAL, NULL));
}
void test_apply_fromdiff__delete(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
NULL, NULL,
PATCH_DELETE_ORIGINAL, NULL));
}
void test_apply_fromdiff__no_change(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_ORIGINAL, "file.txt",
"", NULL));
}
void test_apply_fromdiff__binary_add(void)
{
git_buf newfile = GIT_BUF_INIT;
newfile.ptr = FILE_BINARY_DELTA_MODIFIED;
newfile.size = FILE_BINARY_DELTA_MODIFIED_LEN;
cl_git_pass(apply_gitbuf(
NULL, NULL,
&newfile, "binary.bin",
NULL, &binary_opts));
}
void test_apply_fromdiff__binary_no_change(void)
{
git_buf original = GIT_BUF_INIT;
original.ptr = FILE_BINARY_DELTA_ORIGINAL;
original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
cl_git_pass(apply_gitbuf(
&original, "binary.bin",
&original, "binary.bin",
"", &binary_opts));
}
void test_apply_fromdiff__binary_change_delta(void)
{
git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
original.ptr = FILE_BINARY_DELTA_ORIGINAL;
original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
modified.ptr = FILE_BINARY_DELTA_MODIFIED;
modified.size = FILE_BINARY_DELTA_MODIFIED_LEN;
cl_git_pass(apply_gitbuf(
&original, "binary.bin",
&modified, "binary.bin",
NULL, &binary_opts));
}
void test_apply_fromdiff__binary_change_literal(void)
{
git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
original.ptr = FILE_BINARY_LITERAL_ORIGINAL;
original.size = FILE_BINARY_LITERAL_ORIGINAL_LEN;
modified.ptr = FILE_BINARY_LITERAL_MODIFIED;
modified.size = FILE_BINARY_LITERAL_MODIFIED_LEN;
cl_git_pass(apply_gitbuf(
&original, "binary.bin",
&modified, "binary.bin",
NULL, &binary_opts));
}
void test_apply_fromdiff__binary_delete(void)
{
git_buf original = GIT_BUF_INIT;
original.ptr = FILE_BINARY_DELTA_MODIFIED;
original.size = FILE_BINARY_DELTA_MODIFIED_LEN;
cl_git_pass(apply_gitbuf(
&original, "binary.bin",
NULL, NULL,
NULL, &binary_opts));
}
void test_apply_fromdiff__patching_correctly_truncates_source(void)
{
git_buf original = GIT_BUF_INIT, patched = GIT_BUF_INIT;
git_patch *patch;
unsigned int mode;
char *path;
cl_git_pass(git_patch_from_buffers(&patch,
"foo\nbar", 7, "file.txt",
"foo\nfoo", 7, "file.txt", NULL));
/*
* Previously, we would fail to correctly truncate the source buffer if
* the source has more than one line and ends with a non-newline
* character. In the following call, we thus truncate the source string
* in the middle of the second line. Without the bug fixed, we would
* successfully apply the patch to the source and return success. With
* the overflow being fixed, we should return an error.
*/
cl_git_fail_with(GIT_EAPPLYFAIL,
git_apply__patch(&patched, &path, &mode,
"foo\nbar\n", 5, patch, NULL));
/* Verify that the patch succeeds if we do not truncate */
cl_git_pass(git_apply__patch(&patched, &path, &mode,
"foo\nbar\n", 7, patch, NULL));
git_buf_dispose(&original);
git_buf_dispose(&patched);
git_patch_free(patch);
git__free(path);
}
+450
View File
@@ -0,0 +1,450 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "apply.h"
#include "patch.h"
#include "patch_parse.h"
#include "repository.h"
#include "buf_text.h"
#include "../patch/patch_common.h"
static git_repository *repo = NULL;
void test_apply_fromfile__initialize(void)
{
repo = cl_git_sandbox_init("renames");
}
void test_apply_fromfile__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int apply_patchfile(
const char *old,
size_t old_len,
const char *new,
size_t new_len,
const char *patchfile,
const char *filename_expected,
unsigned int mode_expected)
{
git_patch *patch;
git_buf result = GIT_BUF_INIT;
git_buf patchbuf = GIT_BUF_INIT;
char *filename;
unsigned int mode;
int error;
cl_git_pass(git_patch_from_buffer(&patch, patchfile, strlen(patchfile), NULL));
error = git_apply__patch(&result, &filename, &mode, old, old_len, patch, NULL);
if (error == 0) {
cl_assert_equal_i(new_len, result.size);
if (new_len)
cl_assert(memcmp(new, result.ptr, new_len) == 0);
cl_assert_equal_s(filename_expected, filename);
cl_assert_equal_i(mode_expected, mode);
}
git__free(filename);
git_buf_dispose(&result);
git_buf_dispose(&patchbuf);
git_patch_free(patch);
return error;
}
static int validate_and_apply_patchfile(
const char *old,
size_t old_len,
const char *new,
size_t new_len,
const char *patchfile,
const git_diff_options *diff_opts,
const char *filename_expected,
unsigned int mode_expected)
{
git_patch *patch_fromdiff;
git_buf validated = GIT_BUF_INIT;
int error;
cl_git_pass(git_patch_from_buffers(&patch_fromdiff,
old, old_len, "file.txt",
new, new_len, "file.txt",
diff_opts));
cl_git_pass(git_patch_to_buf(&validated, patch_fromdiff));
cl_assert_equal_s(patchfile, validated.ptr);
error = apply_patchfile(old, old_len, new, new_len, patchfile, filename_expected, mode_expected);
git_buf_dispose(&validated);
git_patch_free(patch_fromdiff);
return error;
}
void test_apply_fromfile__change_middle(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__change_middle_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT,
&diff_opts, "file.txt", 0100644));
}
void test_apply_fromfile__change_firstline(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_FIRSTLINE, strlen(FILE_CHANGE_FIRSTLINE),
PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__lastline(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_LASTLINE, strlen(FILE_CHANGE_LASTLINE),
PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__change_middle_shrink(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_SHRINK, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__change_middle_shrink_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
PATCH_ORIGINAL_TO_MIDDLE_SHRINK_NOCONTEXT, &diff_opts,
"file.txt", 0100644));
}
void test_apply_fromfile__change_middle_grow(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_GROW, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__change_middle_grow_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
PATCH_ORIGINAL_TO_MIDDLE_GROW_NOCONTEXT, &diff_opts,
"file.txt", 0100644));
}
void test_apply_fromfile__prepend(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_PREPEND, strlen(FILE_PREPEND),
PATCH_ORIGINAL_TO_PREPEND, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__prepend_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_PREPEND, strlen(FILE_PREPEND),
PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts,
"file.txt", 0100644));
}
void test_apply_fromfile__append(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_APPEND, strlen(FILE_APPEND),
PATCH_ORIGINAL_TO_APPEND, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__append_nocontext(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_APPEND, strlen(FILE_APPEND),
PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts,
"file.txt", 0100644));
}
void test_apply_fromfile__prepend_and_append(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_PREPEND_AND_APPEND, strlen(FILE_PREPEND_AND_APPEND),
PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL,
"file.txt", 0100644));
}
void test_apply_fromfile__to_empty_file(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
"", 0,
PATCH_ORIGINAL_TO_EMPTY_FILE, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__from_empty_file(void)
{
cl_git_pass(validate_and_apply_patchfile(
"", 0,
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
PATCH_EMPTY_FILE_TO_ORIGINAL, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__add(void)
{
cl_git_pass(validate_and_apply_patchfile(
NULL, 0,
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
PATCH_ADD_ORIGINAL, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__delete(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
NULL, 0,
PATCH_DELETE_ORIGINAL, NULL, NULL, 0));
}
void test_apply_fromfile__rename_exact(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
PATCH_RENAME_EXACT, "newfile.txt", 0100644));
}
void test_apply_fromfile__rename_similar(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_RENAME_SIMILAR, "newfile.txt", 0100644));
}
void test_apply_fromfile__rename_similar_quotedname(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_RENAME_SIMILAR_QUOTEDNAME, "foo\"bar.txt", 0100644));
}
void test_apply_fromfile__modechange(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
PATCH_MODECHANGE_UNCHANGED, "file.txt", 0100755));
}
void test_apply_fromfile__modechange_with_modification(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_MODECHANGE_MODIFIED, "file.txt", 0100755));
}
void test_apply_fromfile__noisy(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_NOISY, "file.txt", 0100644));
}
void test_apply_fromfile__noisy_nocontext(void)
{
cl_git_pass(apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
PATCH_NOISY_NOCONTEXT, "file.txt", 0100644));
}
void test_apply_fromfile__fail_truncated_1(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_1,
strlen(PATCH_TRUNCATED_1), NULL));
}
void test_apply_fromfile__fail_truncated_2(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_2,
strlen(PATCH_TRUNCATED_2), NULL));
}
void test_apply_fromfile__fail_truncated_3(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_3,
strlen(PATCH_TRUNCATED_3), NULL));
}
void test_apply_fromfile__fail_corrupt_githeader(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, PATCH_CORRUPT_GIT_HEADER,
strlen(PATCH_CORRUPT_GIT_HEADER), NULL));
}
void test_apply_fromfile__empty_context(void)
{
cl_git_pass(apply_patchfile(
FILE_EMPTY_CONTEXT_ORIGINAL, strlen(FILE_EMPTY_CONTEXT_ORIGINAL),
FILE_EMPTY_CONTEXT_MODIFIED, strlen(FILE_EMPTY_CONTEXT_MODIFIED),
PATCH_EMPTY_CONTEXT,
"file.txt", 0100644));
}
void test_apply_fromfile__append_no_nl(void)
{
cl_git_pass(validate_and_apply_patchfile(
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
FILE_APPEND_NO_NL, strlen(FILE_APPEND_NO_NL),
PATCH_APPEND_NO_NL, NULL, "file.txt", 0100644));
}
void test_apply_fromfile__fail_missing_new_file(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch,
PATCH_CORRUPT_MISSING_NEW_FILE,
strlen(PATCH_CORRUPT_MISSING_NEW_FILE), NULL));
}
void test_apply_fromfile__fail_missing_old_file(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch,
PATCH_CORRUPT_MISSING_OLD_FILE,
strlen(PATCH_CORRUPT_MISSING_OLD_FILE), NULL));
}
void test_apply_fromfile__fail_no_changes(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch,
PATCH_CORRUPT_NO_CHANGES,
strlen(PATCH_CORRUPT_NO_CHANGES), NULL));
}
void test_apply_fromfile__fail_missing_hunk_header(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch,
PATCH_CORRUPT_MISSING_HUNK_HEADER,
strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
}
void test_apply_fromfile__fail_not_a_patch(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, PATCH_NOT_A_PATCH,
strlen(PATCH_NOT_A_PATCH), NULL));
}
void test_apply_fromfile__binary_add(void)
{
cl_git_pass(apply_patchfile(
NULL, 0,
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
PATCH_BINARY_ADD, "binary.bin", 0100644));
}
void test_apply_fromfile__binary_change_delta(void)
{
cl_git_pass(apply_patchfile(
FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
PATCH_BINARY_DELTA, "binary.bin", 0100644));
}
void test_apply_fromfile__binary_change_literal(void)
{
cl_git_pass(apply_patchfile(
FILE_BINARY_LITERAL_ORIGINAL, FILE_BINARY_LITERAL_ORIGINAL_LEN,
FILE_BINARY_LITERAL_MODIFIED, FILE_BINARY_LITERAL_MODIFIED_LEN,
PATCH_BINARY_LITERAL, "binary.bin", 0100644));
}
void test_apply_fromfile__binary_delete(void)
{
cl_git_pass(apply_patchfile(
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
NULL, 0,
PATCH_BINARY_DELETE, NULL, 0));
}
void test_apply_fromfile__binary_change_does_not_apply(void)
{
/* try to apply patch backwards, ensure it does not apply */
cl_git_fail(apply_patchfile(
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
PATCH_BINARY_DELTA, "binary.bin", 0100644));
}
void test_apply_fromfile__binary_change_must_be_reversible(void)
{
cl_git_fail(apply_patchfile(
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
NULL, 0,
PATCH_BINARY_NOT_REVERSIBLE, NULL, 0));
}
void test_apply_fromfile__empty_file_not_allowed(void)
{
git_patch *patch;
cl_git_fail(git_patch_from_buffer(&patch, "", 0, NULL));
cl_git_fail(git_patch_from_buffer(&patch, NULL, 0, NULL));
}
+321
View File
@@ -0,0 +1,321 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_index__initialize(void)
{
git_oid oid;
git_commit *commit;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
}
void test_apply_index__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_apply_index__generate_diff(void)
{
git_oid a_oid, b_oid;
git_commit *a_commit, *b_commit;
git_tree *a_tree, *b_tree;
git_diff *diff;
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707");
git_oid_fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f");
cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_commit_tree(&b_tree, b_commit));
cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
git_tree_free(a_tree);
git_tree_free(b_tree);
git_commit_free(a_commit);
git_commit_free(b_commit);
}
void test_apply_index__parsed_diff(void)
{
git_diff *diff;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_index__removes_file(void)
{
git_diff *diff;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_index__adds_file(void)
{
git_diff *diff;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_index__modified_workdir_with_unmodified_index_is_ok(void)
{
git_diff *diff;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
struct merge_index_entry workdir_expected[] = {
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "veal.txt" }
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/* mutate the workdir and leave the index matching HEAD */
cl_git_rmfile("merge-recursive/asparagus.txt");
cl_git_rewritefile("merge-recursive/veal.txt", "Hello, world.\n");
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_index__application_failure_leaves_index_unmodified(void)
{
git_diff *diff;
git_index *index;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
/* mutate the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_remove(index, "veal.txt", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
git_diff_free(diff);
}
void test_apply_index__keeps_nonconflicting_changes(void)
{
git_diff *diff;
git_index *index;
git_index_entry idx_entry;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "beef.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
/* mutate the index */
cl_git_pass(git_repository_index(&index, repo));
memset(&idx_entry, 0, sizeof(git_index_entry));
idx_entry.mode = 0100644;
idx_entry.path = "beef.txt";
cl_git_pass(git_oid_fromstr(&idx_entry.id, "898d12687fb35be271c27c795a6b32c8b51da79e"));
cl_git_pass(git_index_add(index, &idx_entry));
cl_git_pass(git_index_remove(index, "bouilli.txt", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_index__can_apply_nonconflicting_file_changes(void)
{
git_diff *diff;
git_index *index;
git_index_entry idx_entry;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "4f2d1645dee99ced096877911de540c65ade2ef8", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
/*
* Replace the index entry with a version that is different than
* HEAD but such that the patch still applies cleanly. This item
* has a new line appended.
*/
cl_git_pass(git_repository_index(&index, repo));
memset(&idx_entry, 0, sizeof(git_index_entry));
idx_entry.mode = 0100644;
idx_entry.path = "asparagus.txt";
cl_git_pass(git_oid_fromstr(&idx_entry.id, "06d3fefb8726ab1099acc76e02dfb85e034b2538"));
cl_git_pass(git_index_add(index, &idx_entry));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
void test_apply_index__change_mode(void)
{
git_diff *diff;
const char *diff_file = DIFF_EXECUTABLE_FILE;
struct merge_index_entry index_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100755, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_workdir_unchanged(repo);
git_diff_free(diff);
}
+232
View File
@@ -0,0 +1,232 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "apply.h"
#include "repository.h"
#include "buf_text.h"
#include "../patch/patch_common.h"
static git_repository *repo = NULL;
void test_apply_partial__initialize(void)
{
repo = cl_git_sandbox_init("renames");
}
void test_apply_partial__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int skip_addition(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines > hunk->old_lines) ? 1 : 0;
}
static int skip_deletion(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines < hunk->old_lines) ? 1 : 0;
}
static int skip_change(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines == hunk->old_lines) ? 1 : 0;
}
static int abort_addition(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines > hunk->old_lines) ? GIT_EUSER : 0;
}
static int abort_deletion(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines < hunk->old_lines) ? GIT_EUSER : 0;
}
static int abort_change(
const git_diff_hunk *hunk,
void *payload)
{
GIT_UNUSED(payload);
return (hunk->new_lines == hunk->old_lines) ? GIT_EUSER : 0;
}
static int apply_buf(
const char *old,
const char *oldname,
const char *new,
const char *newname,
const char *expected,
const git_diff_options *diff_opts,
git_apply_hunk_cb hunk_cb,
void *payload)
{
git_patch *patch;
git_buf result = GIT_BUF_INIT;
git_buf patchbuf = GIT_BUF_INIT;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
char *filename;
unsigned int mode;
int error;
size_t oldsize = strlen(old);
size_t newsize = strlen(new);
opts.hunk_cb = hunk_cb;
opts.payload = payload;
cl_git_pass(git_patch_from_buffers(&patch, old, oldsize, oldname, new, newsize, newname, diff_opts));
if ((error = git_apply__patch(&result, &filename, &mode, old, oldsize, patch, &opts)) == 0) {
cl_assert_equal_s(expected, result.ptr);
cl_assert_equal_s(newname, filename);
cl_assert_equal_i(0100644, mode);
}
git__free(filename);
git_buf_dispose(&result);
git_buf_dispose(&patchbuf);
git_patch_free(patch);
return error;
}
void test_apply_partial__prepend_and_change_skip_addition(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_ORIGINAL, NULL, skip_addition, NULL));
}
void test_apply_partial__prepend_and_change_nocontext_skip_addition(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_CHANGE_MIDDLE, &diff_opts, skip_addition, NULL));
}
void test_apply_partial__prepend_and_change_nocontext_abort_addition(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_fail(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_ORIGINAL, &diff_opts, abort_addition, NULL));
}
void test_apply_partial__prepend_and_change_skip_change(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_PREPEND_AND_CHANGE, NULL, skip_change, NULL));
}
void test_apply_partial__prepend_and_change_nocontext_skip_change(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_PREPEND, &diff_opts, skip_change, NULL));
}
void test_apply_partial__prepend_and_change_nocontext_abort_change(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_fail(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_PREPEND_AND_CHANGE, "file.txt",
FILE_PREPEND, &diff_opts, abort_change, NULL));
}
void test_apply_partial__delete_and_change_skip_deletion(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_ORIGINAL, NULL, skip_deletion, NULL));
}
void test_apply_partial__delete_and_change_nocontext_skip_deletion(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_CHANGE_MIDDLE, &diff_opts, skip_deletion, NULL));
}
void test_apply_partial__delete_and_change_nocontext_abort_deletion(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_fail(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_ORIGINAL, &diff_opts, abort_deletion, NULL));
}
void test_apply_partial__delete_and_change_skip_change(void)
{
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_DELETE_AND_CHANGE, NULL, skip_change, NULL));
}
void test_apply_partial__delete_and_change_nocontext_skip_change(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_pass(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_DELETE_FIRSTLINE, &diff_opts, skip_change, NULL));
}
void test_apply_partial__delete_and_change_nocontext_abort_change(void)
{
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
diff_opts.context_lines = 0;
cl_git_fail(apply_buf(
FILE_ORIGINAL, "file.txt",
FILE_DELETE_AND_CHANGE, "file.txt",
FILE_DELETE_FIRSTLINE, &diff_opts, abort_change, NULL));
}
+94
View File
@@ -0,0 +1,94 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
#include "../merge/merge_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_tree__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_apply_tree__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_apply_tree__one(void)
{
git_oid a_oid, b_oid;
git_commit *a_commit, *b_commit;
git_tree *a_tree, *b_tree;
git_diff *diff;
git_index *index = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
struct merge_index_entry expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707");
git_oid_fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f");
cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_commit_tree(&b_tree, b_commit));
cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &opts));
cl_git_pass(git_apply_to_tree(&index, repo, a_tree, diff, NULL));
merge_test_index(index, expected, 6);
git_index_free(index);
git_diff_free(diff);
git_tree_free(a_tree);
git_tree_free(b_tree);
git_commit_free(a_commit);
git_commit_free(b_commit);
}
void test_apply_tree__adds_file(void)
{
git_oid a_oid;
git_commit *a_commit;
git_tree *a_tree;
git_diff *diff;
git_index *index = NULL;
struct merge_index_entry expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply_to_tree(&index, repo, a_tree, diff, NULL));
merge_test_index(index, expected, 7);
git_index_free(index);
git_diff_free(diff);
git_tree_free(a_tree);
git_commit_free(a_commit);
}
+358
View File
@@ -0,0 +1,358 @@
#include "clar_libgit2.h"
#include "apply_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_apply_workdir__initialize(void)
{
git_oid oid;
git_commit *commit;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
}
void test_apply_workdir__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_apply_workdir__generated_diff(void)
{
git_oid a_oid, b_oid;
git_commit *a_commit, *b_commit;
git_tree *a_tree, *b_tree;
git_diff *diff;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
git_oid_fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707");
git_oid_fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f"); cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
cl_git_pass(git_commit_tree(&a_tree, a_commit));
cl_git_pass(git_commit_tree(&b_tree, b_commit));
cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &opts));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
git_tree_free(a_tree);
git_tree_free(b_tree);
git_commit_free(a_commit);
git_commit_free(b_commit);
}
void test_apply_workdir__parsed_diff(void)
{
git_diff *diff;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__removes_file(void)
{
git_diff *diff;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__adds_file(void)
{
git_diff *diff;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__modified_index_with_unmodified_workdir_is_ok(void)
{
git_index *index;
git_index_entry idx_entry = {{0}};
git_diff *diff;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry index_expected[] = {
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "veal.txt" }
};
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
struct merge_index_entry workdir_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/* mutate the index and leave the workdir matching HEAD */
cl_git_pass(git_repository_index(&index, repo));
idx_entry.mode = 0100644;
idx_entry.path = "veal.txt";
cl_git_pass(git_oid_fromstr(&idx_entry.id, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d"));
cl_git_pass(git_index_add(index, &idx_entry));
cl_git_pass(git_index_remove(index, "asparagus.txt", 0));
cl_git_pass(git_index_write(index));
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_index_free(index);
git_diff_free(diff);
}
void test_apply_workdir__application_failure_leaves_workdir_unmodified(void)
{
git_diff *diff;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "8684724651336001c5dbce74bed6736d2443958d", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/* mutate the workdir */
cl_git_rewritefile("merge-recursive/veal.txt",
"This is a modification.\n");
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__keeps_nonconflicting_changes(void)
{
git_diff *diff;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "gravy.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_rmfile("merge-recursive/oyster.txt");
cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__can_apply_nonconflicting_file_changes(void)
{
git_diff *diff;
const char *diff_file = DIFF_MODIFY_TWO_FILES;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "5db1a0fef164cb66cc0c00d35cc5af979ddc1a64", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
/*
* Replace the workdir file with a version that is different than
* HEAD but such that the patch still applies cleanly. This item
* has a new line appended.
*/
cl_git_append2file("merge-recursive/asparagus.txt",
"This line is added in the workdir.\n");
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__change_mode(void)
{
#ifndef GIT_WIN32
git_diff *diff;
const char *diff_file = DIFF_EXECUTABLE_FILE;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100755, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
#endif
}
void test_apply_workdir__apply_many_changes_one(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "c9d7d5d58088bc91f6e06f17ca3a205091568d3a", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
void test_apply_workdir__apply_many_changes_two(void)
{
git_diff *diff;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
struct merge_index_entry workdir_expected[] = {
{ 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "6b943d65af6d8db74d747284fa4ca7d716ad5bbb", 0, "veal.txt" },
};
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
cl_git_pass(git_diff_from_buffer(&diff,
DIFF_MANY_CHANGES_TWO, strlen(DIFF_MANY_CHANGES_TWO)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
validate_index_unchanged(repo);
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
git_diff_free(diff);
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef __CLAR_TEST_ATTR_EXPECT__
#define __CLAR_TEST_ATTR_EXPECT__
enum attr_expect_t {
EXPECT_FALSE,
EXPECT_TRUE,
EXPECT_UNDEFINED,
EXPECT_STRING
};
struct attr_expected {
const char *path;
const char *attr;
enum attr_expect_t expected;
const char *expected_str;
};
GIT_INLINE(void) attr_check_expected(
enum attr_expect_t expected,
const char *expected_str,
const char *name,
const char *value)
{
switch (expected) {
case EXPECT_TRUE:
cl_assert_(GIT_ATTR_IS_TRUE(value), name);
break;
case EXPECT_FALSE:
cl_assert_(GIT_ATTR_IS_FALSE(value), name);
break;
case EXPECT_UNDEFINED:
cl_assert_(GIT_ATTR_IS_UNSPECIFIED(value), name);
break;
case EXPECT_STRING:
cl_assert_equal_s(expected_str, value);
break;
}
}
#endif
+243
View File
@@ -0,0 +1,243 @@
#include "clar_libgit2.h"
#include "attr_file.h"
#include "attr_expect.h"
#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
void test_attr_file__simple_read(void)
{
git_attr_file *file;
git_attr_assignment *assign;
git_attr_rule *rule;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
cl_assert(file->rules.length == 1);
rule = get_rule(0);
cl_assert(rule != NULL);
cl_assert_equal_s("*", rule->match.pattern);
cl_assert(rule->match.length == 1);
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
cl_assert(assign != NULL);
cl_assert_equal_s("binary", assign->name);
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
git_attr_file__free(file);
}
void test_attr_file__match_variants(void)
{
git_attr_file *file;
git_attr_rule *rule;
git_attr_assignment *assign;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
cl_assert(file->rules.length == 10);
/* let's do a thorough check of this rule, then just verify
* the things that are unique for the later rules
*/
rule = get_rule(0);
cl_assert(rule);
cl_assert_equal_s("pat0", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat0"));
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule,0);
cl_assert_equal_s("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
rule = get_rule(1);
cl_assert_equal_s("pat1", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat1"));
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0);
rule = get_rule(2);
cl_assert_equal_s("pat2", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat2"));
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0);
rule = get_rule(3);
cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0);
rule = get_rule(4);
cl_assert_equal_s("pat4.*", rule->match.pattern);
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
rule = get_rule(5);
cl_assert_equal_s("*.pat5", rule->match.pattern);
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
rule = get_rule(7);
cl_assert_equal_s("pat7[a-e]??[xyz]", rule->match.pattern);
cl_assert(rule->assigns.length == 1);
cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
assign = get_assign(rule,0);
cl_assert_equal_s("attr7", assign->name);
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
rule = get_rule(8);
cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat8 with spaces"));
rule = get_rule(9);
cl_assert_equal_s("pat9", rule->match.pattern);
git_attr_file__free(file);
}
static void check_one_assign(
git_attr_file *file,
int rule_idx,
int assign_idx,
const char *pattern,
const char *name,
enum attr_expect_t expected,
const char *expected_str)
{
git_attr_rule *rule = get_rule(rule_idx);
git_attr_assignment *assign = get_assign(rule, assign_idx);
cl_assert_equal_s(pattern, rule->match.pattern);
cl_assert(rule->assigns.length == 1);
cl_assert_equal_s(name, assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
attr_check_expected(expected, expected_str, assign->name, assign->value);
}
void test_attr_file__assign_variants(void)
{
git_attr_file *file;
git_attr_rule *rule;
git_attr_assignment *assign;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
cl_assert_equal_s(cl_fixture("attr/attr2"), file->entry->path);
cl_assert(file->rules.length == 11);
check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
rule = get_rule(8);
cl_assert_equal_s("pat7", rule->match.pattern);
cl_assert(rule->assigns.length == 5);
/* assignments will be sorted by hash value, so we have to do
* lookups by search instead of by position
*/
assign = git_attr_rule__lookup_assignment(rule, "multiple");
cl_assert(assign);
cl_assert_equal_s("multiple", assign->name);
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "single");
cl_assert(assign);
cl_assert_equal_s("single", assign->name);
cl_assert(GIT_ATTR_IS_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "values");
cl_assert(assign);
cl_assert_equal_s("values", assign->name);
cl_assert_equal_s("1", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "also");
cl_assert(assign);
cl_assert_equal_s("also", assign->name);
cl_assert_equal_s("a-really-long-value/*", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "happy");
cl_assert(assign);
cl_assert_equal_s("happy", assign->name);
cl_assert_equal_s("yes!", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "other");
cl_assert(!assign);
rule = get_rule(9);
cl_assert_equal_s("pat8", rule->match.pattern);
cl_assert(rule->assigns.length == 2);
assign = git_attr_rule__lookup_assignment(rule, "again");
cl_assert(assign);
cl_assert_equal_s("again", assign->name);
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "another");
cl_assert(assign);
cl_assert_equal_s("another", assign->name);
cl_assert_equal_s("12321", assign->value);
check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
git_attr_file__free(file);
}
static void assert_examples(git_attr_file *file)
{
git_attr_rule *rule;
git_attr_assignment *assign;
rule = get_rule(0);
cl_assert_equal_s("*.java", rule->match.pattern);
cl_assert(rule->assigns.length == 3);
assign = git_attr_rule__lookup_assignment(rule, "diff");
cl_assert_equal_s("diff", assign->name);
cl_assert_equal_s("java", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "crlf");
cl_assert_equal_s("crlf", assign->name);
cl_assert(GIT_ATTR_IS_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "myAttr");
cl_assert_equal_s("myAttr", assign->name);
cl_assert(GIT_ATTR_IS_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "missing");
cl_assert(assign == NULL);
rule = get_rule(1);
cl_assert_equal_s("NoMyAttr.java", rule->match.pattern);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
cl_assert_equal_s("myAttr", assign->name);
cl_assert(GIT_ATTR_IS_UNSPECIFIED(assign->value));
rule = get_rule(2);
cl_assert_equal_s("README", rule->match.pattern);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
cl_assert_equal_s("caveat", assign->name);
cl_assert_equal_s("unspecified", assign->value);
}
void test_attr_file__check_attr_examples(void)
{
git_attr_file *file;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path);
cl_assert(file->rules.length == 3);
assert_examples(file);
git_attr_file__free(file);
}
void test_attr_file__whitespace(void)
{
git_attr_file *file;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr4")));
cl_assert_equal_s(cl_fixture("attr/attr4"), file->entry->path);
cl_assert(file->rules.length == 3);
assert_examples(file);
git_attr_file__free(file);
}
+108
View File
@@ -0,0 +1,108 @@
#include "clar_libgit2.h"
#include "git2/attr.h"
void test_attr_flags__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_attr_flags__bare(void)
{
git_repository *repo = cl_git_sandbox_init("testrepo.git");
const char *value;
cl_assert(git_repository_is_bare(repo));
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM, "README.md", "diff"));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
}
void test_attr_flags__index_vs_workdir(void)
{
git_repository *repo = cl_git_sandbox_init("attr_index");
const char *value;
cl_assert(!git_repository_is_bare(repo));
/* wd then index */
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.md", "bar"));
cl_assert(GIT_ATTR_IS_FALSE(value));
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.md", "blargh"));
cl_assert_equal_s(value, "goop");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.txt", "foo"));
cl_assert(GIT_ATTR_IS_FALSE(value));
/* index then wd */
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.md", "bar"));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.md", "blargh"));
cl_assert_equal_s(value, "garble");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.txt", "foo"));
cl_assert(GIT_ATTR_IS_TRUE(value));
}
void test_attr_flags__subdir(void)
{
git_repository *repo = cl_git_sandbox_init("attr_index");
const char *value;
/* wd then index */
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.md", "bar"));
cl_assert_equal_s(value, "1234");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "another"));
cl_assert_equal_s(value, "one");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "again"));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "beep"));
cl_assert_equal_s(value, "10");
/* index then wd */
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.md", "bar"));
cl_assert_equal_s(value, "1337");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "another"));
cl_assert_equal_s(value, "one");
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "again"));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_get(
&value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "beep"));
cl_assert_equal_s(value, "5");
}
+262
View File
@@ -0,0 +1,262 @@
#include "clar_libgit2.h"
#include "attr_file.h"
#include "attr_expect.h"
void test_attr_lookup__simple(void)
{
git_attr_file *file;
git_attr_path path;
const char *value = NULL;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
cl_assert(file->rules.length == 1);
cl_git_pass(git_attr_path__init(&path, "test", NULL, GIT_DIR_FLAG_UNKNOWN));
cl_assert_equal_s("test", path.path);
cl_assert_equal_s("test", path.basename);
cl_assert(!path.is_dir);
cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value));
cl_assert(!value);
git_attr_path__free(&path);
git_attr_file__free(file);
}
static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
{
git_attr_path path;
const char *value = NULL;
struct attr_expected *c;
int error;
for (c = cases; c->path != NULL; c++) {
cl_git_pass(git_attr_path__init(&path, c->path, NULL, GIT_DIR_FLAG_UNKNOWN));
if (force_dir)
path.is_dir = 1;
error = git_attr_file__lookup_one(file,&path,c->attr,&value);
cl_git_pass(error);
attr_check_expected(c->expected, c->expected_str, c->attr, value);
git_attr_path__free(&path);
}
}
void test_attr_lookup__match_variants(void)
{
git_attr_file *file;
git_attr_path path;
struct attr_expected dir_cases[] = {
{ "pat2", "attr2", EXPECT_TRUE, NULL },
{ "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL },
{ "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
{ "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL },
{ "foo.pat5", "attr5", EXPECT_TRUE, NULL },
{ NULL, NULL, 0, NULL }
};
struct attr_expected cases[] = {
/* pat0 -> simple match */
{ "pat0", "attr0", EXPECT_TRUE, NULL },
{ "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL },
{ "relative/to/pat0", "attr0", EXPECT_TRUE, NULL },
{ "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL },
{ "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL },
{ "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL },
/* negative match */
{ "pat0", "attr1", EXPECT_TRUE, NULL },
{ "pat1", "attr1", EXPECT_UNDEFINED, NULL },
{ "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL },
{ "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL },
{ "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL },
{ "misc", "attr1", EXPECT_TRUE, NULL },
/* dir match */
{ "pat2", "attr2", EXPECT_UNDEFINED, NULL },
{ "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
{ "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
/* path match */
{ "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
{ "/pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
{ "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
/* pattern* match */
{ "pat4.txt", "attr4", EXPECT_TRUE, NULL },
{ "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL },
{ "pat4.", "attr4", EXPECT_TRUE, NULL },
{ "pat4", "attr4", EXPECT_UNDEFINED, NULL },
/* *pattern match */
{ "foo.pat5", "attr5", EXPECT_TRUE, NULL },
{ "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL },
{ "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL },
{ "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL },
/* glob match with slashes */
{ "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL },
{ "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
{ "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL },
{ "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
{ "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
{ "/pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
/* complex pattern */
{ "pat7a12z", "attr7", EXPECT_TRUE, NULL },
{ "pat7e__x", "attr7", EXPECT_TRUE, NULL },
{ "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */
{ "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL },
{ "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
{ "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL },
{ "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL },
{ "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
{ "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL },
/* pattern with spaces */
{ "pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
{ "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
{ "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL },
{ "spaces", "attr8", EXPECT_UNDEFINED, NULL },
/* pattern at eof */
{ "pat9", "attr9", EXPECT_TRUE, NULL },
{ "/eof/pat9", "attr9", EXPECT_TRUE, NULL },
{ "pat", "attr9", EXPECT_UNDEFINED, NULL },
{ "at9", "attr9", EXPECT_UNDEFINED, NULL },
{ "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
cl_assert(file->rules.length == 10);
cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL, GIT_DIR_FLAG_UNKNOWN));
cl_assert_equal_s("pat0", path.basename);
run_test_cases(file, cases, 0);
run_test_cases(file, dir_cases, 1);
git_attr_file__free(file);
git_attr_path__free(&path);
}
void test_attr_lookup__assign_variants(void)
{
git_attr_file *file;
struct attr_expected cases[] = {
/* pat0 -> simple assign */
{ "pat0", "simple", EXPECT_TRUE, NULL },
{ "/testing/pat0", "simple", EXPECT_TRUE, NULL },
{ "pat0", "fail", EXPECT_UNDEFINED, NULL },
{ "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL },
/* negative assign */
{ "pat1", "neg", EXPECT_FALSE, NULL },
{ "/testing/pat1", "neg", EXPECT_FALSE, NULL },
{ "pat1", "fail", EXPECT_UNDEFINED, NULL },
{ "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL },
/* forced undef */
{ "pat1", "notundef", EXPECT_TRUE, NULL },
{ "pat2", "notundef", EXPECT_UNDEFINED, NULL },
{ "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL },
{ "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL },
/* assign value */
{ "pat3", "assigned", EXPECT_STRING, "test-value" },
{ "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
/* assign value */
{ "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
{ "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
/* empty assignments */
{ "pat5", "empty", EXPECT_TRUE, NULL },
{ "pat6", "negempty", EXPECT_FALSE, NULL },
/* multiple assignment */
{ "pat7", "multiple", EXPECT_TRUE, NULL },
{ "pat7", "single", EXPECT_FALSE, NULL },
{ "pat7", "values", EXPECT_STRING, "1" },
{ "pat7", "also", EXPECT_STRING, "a-really-long-value/*" },
{ "pat7", "happy", EXPECT_STRING, "yes!" },
{ "pat8", "again", EXPECT_TRUE, NULL },
{ "pat8", "another", EXPECT_STRING, "12321" },
/* bad assignment */
{ "patbad0", "simple", EXPECT_UNDEFINED, NULL },
{ "patbad0", "notundef", EXPECT_TRUE, NULL },
{ "patbad1", "simple", EXPECT_UNDEFINED, NULL },
/* eof assignment */
{ "pat9", "at-eof", EXPECT_FALSE, NULL },
/* sentinel at end */
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
cl_assert(file->rules.length == 11);
run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
void test_attr_lookup__check_attr_examples(void)
{
git_attr_file *file;
struct attr_expected cases[] = {
{ "foo.java", "diff", EXPECT_STRING, "java" },
{ "foo.java", "crlf", EXPECT_FALSE, NULL },
{ "foo.java", "myAttr", EXPECT_TRUE, NULL },
{ "foo.java", "other", EXPECT_UNDEFINED, NULL },
{ "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" },
{ "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL },
{ "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL },
{ "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL },
{ "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
{ "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
{ "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
{ "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
{ "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
{ "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
{ "README", "caveat", EXPECT_STRING, "unspecified" },
{ "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" },
{ "README", "missing", EXPECT_UNDEFINED, NULL },
{ "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
cl_assert(file->rules.length == 3);
run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
struct attr_expected cases[] = {
{ "abc", "foo", EXPECT_TRUE, NULL },
{ "abc", "bar", EXPECT_TRUE, NULL },
{ "abc", "baz", EXPECT_TRUE, NULL },
{ "aaa", "foo", EXPECT_TRUE, NULL },
{ "aaa", "bar", EXPECT_UNDEFINED, NULL },
{ "aaa", "baz", EXPECT_TRUE, NULL },
{ "qqq", "foo", EXPECT_UNDEFINED, NULL },
{ "qqq", "bar", EXPECT_UNDEFINED, NULL },
{ "qqq", "baz", EXPECT_TRUE, NULL },
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file, NULL, 0));
cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true));
cl_assert(file->rules.length == 3);
run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
+197
View File
@@ -0,0 +1,197 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "attr.h"
static git_repository *g_repo = NULL;
void test_attr_macro__cleanup(void)
{
cl_git_sandbox_cleanup();
g_repo = NULL;
}
void test_attr_macro__macros(void)
{
const char *names[7] = { "rootattr", "binary", "diff", "crlf", "merge", "text", "frotz" };
const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
const char *names3[3] = { "macro2", "multi2", "multi3" };
const char *values[7];
g_repo = cl_git_sandbox_init("attr");
cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 7, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_TRUE(values[1]));
cl_assert(GIT_ATTR_IS_FALSE(values[2]));
cl_assert(GIT_ATTR_IS_FALSE(values[3]));
cl_assert(GIT_ATTR_IS_FALSE(values[4]));
cl_assert(GIT_ATTR_IS_FALSE(values[5]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[6]));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_TRUE(values[1]));
cl_assert(GIT_ATTR_IS_FALSE(values[2]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
cl_assert_equal_s("77", values[4]);
cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_FALSE(values[1]));
cl_assert_equal_s("answer", values[2]);
}
void test_attr_macro__bad_macros(void)
{
const char *names[6] = { "rootattr", "positive", "negative",
"firstmacro", "secondmacro", "thirdmacro" };
const char *values[6];
g_repo = cl_git_sandbox_init("attr");
cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
/* these three just confirm that the "mymacro" rule ran */
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[0]));
cl_assert(GIT_ATTR_IS_TRUE(values[1]));
cl_assert(GIT_ATTR_IS_FALSE(values[2]));
/* file contains:
* # let's try some malicious macro defs
* [attr]firstmacro -thirdmacro -secondmacro
* [attr]secondmacro firstmacro -firstmacro
* [attr]thirdmacro secondmacro=hahaha -firstmacro
* macro_bad firstmacro secondmacro thirdmacro
*
* firstmacro assignment list ends up with:
* -thirdmacro -secondmacro
* secondmacro assignment list expands "firstmacro" and ends up with:
* -thirdmacro -secondmacro -firstmacro
* thirdmacro assignment don't expand so list ends up with:
* secondmacro="hahaha"
*
* macro_bad assignment list ends up with:
* -thirdmacro -secondmacro firstmacro &&
* -thirdmacro -secondmacro -firstmacro secondmacro &&
* secondmacro="hahaha" thirdmacro
*
* so summary results should be:
* -firstmacro secondmacro="hahaha" thirdmacro
*/
cl_assert(GIT_ATTR_IS_FALSE(values[3]));
cl_assert_equal_s("hahaha", values[4]);
cl_assert(GIT_ATTR_IS_TRUE(values[5]));
}
void test_attr_macro__macros_in_root_wd_apply(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777));
cl_git_rewritefile("empty_standard_repo/.gitattributes", "[attr]customattr key=value\n");
cl_git_rewritefile("empty_standard_repo/dir/.gitattributes", "file customattr\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key"));
cl_assert_equal_s(value, "value");
}
void test_attr_macro__changing_macro_in_root_wd_updates_attributes(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_rewritefile("empty_standard_repo/.gitattributes",
"[attr]customattr key=first\n"
"file customattr\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key"));
cl_assert_equal_s(value, "first");
cl_git_rewritefile("empty_standard_repo/.gitattributes",
"[attr]customattr key=second\n"
"file customattr\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key"));
cl_assert_equal_s(value, "second");
}
void test_attr_macro__macros_in_subdir_do_not_apply(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777));
cl_git_rewritefile("empty_standard_repo/dir/.gitattributes",
"[attr]customattr key=value\n"
"file customattr\n");
/* This should _not_ pass, as macros in subdirectories shall be ignored */
cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key"));
cl_assert_equal_p(value, NULL);
}
void test_attr_macro__adding_macro_succeeds(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value"));
cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
cl_assert_equal_s(value, "value");
}
void test_attr_macro__adding_boolean_macros_succeeds(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_attr_add_macro(g_repo, "macro-pos", "positive"));
cl_git_pass(git_attr_add_macro(g_repo, "macro-neg", "-negative"));
cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro-pos macro-neg\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "positive"));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "negative"));
cl_assert(GIT_ATTR_IS_FALSE(value));
}
void test_attr_macro__redefining_macro_succeeds(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value1"));
cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value2"));
cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
cl_assert_equal_s(value, "value2");
}
void test_attr_macro__recursive_macro_resolves(void)
{
const char *value;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_attr_add_macro(g_repo, "expandme", "key=value"));
cl_git_pass(git_attr_add_macro(g_repo, "macro", "expandme"));
cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
cl_assert_equal_s(value, "value");
}
+405
View File
@@ -0,0 +1,405 @@
#include "clar_libgit2.h"
#include "futils.h"
#include "git2/attr.h"
#include "attr.h"
#include "sysdir.h"
#include "attr_expect.h"
#include "git2/sys/repository.h"
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
{
g_repo = cl_git_sandbox_init("attr");
}
void test_attr_repo__cleanup(void)
{
cl_git_sandbox_cleanup();
g_repo = NULL;
cl_sandbox_set_search_path_defaults();
}
static struct attr_expected get_one_test_cases[] = {
{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
{ "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
{ "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
{ "root_test2", "repoattr", EXPECT_TRUE, NULL },
{ "root_test2", "rootattr", EXPECT_FALSE, NULL },
{ "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
{ "root_test2", "multiattr", EXPECT_FALSE, NULL },
{ "root_test3", "repoattr", EXPECT_TRUE, NULL },
{ "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
{ "root_test3", "multiattr", EXPECT_STRING, "3" },
{ "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
{ "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
{ "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
{ "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
{ "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
{ "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
{ "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
{ "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
{ "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
{ "does-not-exist", "foo", EXPECT_STRING, "yes" },
{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
{ "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
{ "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
};
void test_attr_repo__get_one(void)
{
int i;
for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
struct attr_expected *scan = &get_one_test_cases[i];
const char *value;
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
attr_check_expected(
scan->expected, scan->expected_str, scan->attr, value);
}
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_one_start_deep(void)
{
int i;
for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
struct attr_expected *scan = &get_one_test_cases[i];
const char *value;
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
attr_check_expected(
scan->expected, scan->expected_str, scan->attr, value);
}
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_many(void)
{
const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
const char *values[4];
cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_TRUE(values[1]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_FALSE(values[1]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert(GIT_ATTR_IS_TRUE(values[1]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
cl_assert_equal_s("yes", values[3]);
}
void test_attr_repo__get_many_in_place(void)
{
const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
/* it should be legal to look up values into the same array that has
* the attribute names, overwriting each name as the value is found.
*/
cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
cl_assert(GIT_ATTR_IS_TRUE(vals[0]));
cl_assert(GIT_ATTR_IS_TRUE(vals[1]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(vals[2]));
cl_assert_equal_s("yes", vals[3]);
}
static int count_attrs(
const char *name,
const char *value,
void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(value);
*((int *)payload) += 1;
return 0;
}
#define CANCEL_VALUE 12345
static int cancel_iteration(
const char *name,
const char *value,
void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(value);
*((int *)payload) -= 1;
if (*((int *)payload) < 0)
return CANCEL_VALUE;
return 0;
}
void test_attr_repo__foreach(void)
{
int count;
count = 0;
cl_git_pass(git_attr_foreach(
g_repo, 0, "root_test1", &count_attrs, &count));
cl_assert(count == 2);
count = 0;
cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
&count_attrs, &count));
cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
count = 0;
cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
&count_attrs, &count));
cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
count = 2;
cl_assert_equal_i(
CANCEL_VALUE, git_attr_foreach(
g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
);
}
void test_attr_repo__manpage_example(void)
{
const char *value;
cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
cl_assert(GIT_ATTR_IS_TRUE(value));
cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
cl_assert(GIT_ATTR_IS_FALSE(value));
cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
cl_assert_equal_s("filfre", value);
cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
}
#define CONTENT "I'm going to be dynamically processed\r\n" \
"And my line endings...\r\n" \
"...are going to be\n" \
"normalized!\r\n"
#define GITATTR "* text=auto\n" \
"*.txt text\n" \
"*.data binary\n"
static void add_to_workdir(const char *filename, const char *content)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
cl_git_rewritefile(git_buf_cstr(&buf), content);
git_buf_dispose(&buf);
}
static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
{
size_t index_pos;
const git_index_entry *entry;
add_to_workdir(filename, CONTENT);
cl_git_pass(git_index_add_bypath(index, filename));
cl_assert(!git_index_find(&index_pos, index, filename));
entry = git_index_get_byindex(index, index_pos);
cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha));
}
void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
{
git_index* index;
cl_git_pass(git_repository_index(&index, g_repo));
add_to_workdir(".gitattributes", GITATTR);
assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
git_index_free(index);
}
void test_attr_repo__bare_repo_with_index(void)
{
const char *names[4] = { "test1", "test2", "test3", "test4" };
const char *values[4];
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_mkfile(
"attr/.gitattributes",
"*.txt test1 test2=foobar -test3\n"
"trial.txt -test1 test2=barfoo !test3 test4\n");
cl_git_pass(git_index_add_bypath(index, ".gitattributes"));
git_index_free(index);
cl_must_pass(p_unlink("attr/.gitattributes"));
cl_assert(!git_path_exists("attr/.gitattributes"));
cl_git_pass(git_repository_set_bare(g_repo));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert_equal_s("foobar", values[1]);
cl_assert(GIT_ATTR_IS_FALSE(values[2]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names));
cl_assert(GIT_ATTR_IS_FALSE(values[0]));
cl_assert_equal_s("barfoo", values[1]);
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_IS_TRUE(values[3]));
cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names));
cl_assert(GIT_ATTR_IS_TRUE(values[0]));
cl_assert_equal_s("foobar", values[1]);
cl_assert(GIT_ATTR_IS_FALSE(values[2]));
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
}
void test_attr_repo__sysdir(void)
{
git_buf sysdir = GIT_BUF_INIT;
const char *value;
cl_git_pass(p_mkdir("system", 0777));
cl_git_rewritefile("system/gitattributes", "file merge=foo");
cl_git_pass(git_buf_joinpath(&sysdir, clar_sandbox_path(), "system"));
cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
g_repo = cl_git_sandbox_reopen();
cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "merge"));
cl_assert_equal_s(value, "foo");
cl_git_pass(p_unlink("system/gitattributes"));
cl_git_pass(p_rmdir("system"));
git_buf_dispose(&sysdir);
}
void test_attr_repo__sysdir_with_session(void)
{
const char *values[2], *attrs[2] = { "foo", "bar" };
git_buf sysdir = GIT_BUF_INIT;
git_attr_session session;
cl_git_pass(p_mkdir("system", 0777));
cl_git_rewritefile("system/gitattributes", "file foo=1 bar=2");
cl_git_pass(git_buf_joinpath(&sysdir, clar_sandbox_path(), "system"));
cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
g_repo = cl_git_sandbox_reopen();
cl_git_pass(git_attr_session__init(&session, g_repo));
cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs));
cl_assert_equal_s(values[0], "1");
cl_assert_equal_s(values[1], "2");
cl_git_pass(p_unlink("system/gitattributes"));
cl_git_pass(p_rmdir("system"));
git_buf_dispose(&sysdir);
git_attr_session__free(&session);
}
void test_attr_repo__rewrite(void)
{
const char *value;
cl_git_rewritefile("attr/.gitattributes", "file.txt foo=first\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
cl_assert_equal_s(value, "first");
cl_git_rewritefile("attr/.gitattributes", "file.txt foo=second\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
cl_assert_equal_s(value, "second");
cl_git_rewritefile("attr/.gitattributes", "file.txt other=value\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
cl_assert_equal_p(value, NULL);
}
void test_attr_repo__rewrite_sysdir(void)
{
git_buf sysdir = GIT_BUF_INIT;
const char *value;
cl_git_pass(p_mkdir("system", 0777));
cl_git_pass(git_buf_joinpath(&sysdir, clar_sandbox_path(), "system"));
cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
g_repo = cl_git_sandbox_reopen();
cl_git_rewritefile("system/gitattributes", "file foo=first");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo"));
cl_assert_equal_s(value, "first");
cl_git_rewritefile("system/gitattributes", "file foo=second");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo"));
cl_assert_equal_s(value, "second");
git_buf_dispose(&sysdir);
}
void test_attr_repo__unlink(void)
{
const char *value;
cl_git_rewritefile("attr/.gitattributes", "file.txt foo=value1\n");
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
cl_assert_equal_s(value, "value1");
cl_git_pass(p_unlink("attr/.gitattributes"));
cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
cl_assert_equal_p(value, NULL);
}
+67
View File
@@ -0,0 +1,67 @@
#include "blame_helpers.h"
void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
{
va_list arglist;
printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx,
hunk->final_start_line_number, hunk->lines_in_hunk-1);
va_start(arglist, fmt);
vprintf(fmt, arglist);
va_end(arglist);
printf("\n");
}
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path)
{
char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0};
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
cl_assert(hunk);
if (!strncmp(commit_id, "0000", 4)) {
strcpy(expected, "0000000000000000000000000000000000000000");
} else {
git_object *obj;
cl_git_pass(git_revparse_single(&obj, repo, commit_id));
git_oid_fmt(expected, git_object_id(obj));
git_object_free(obj);
}
if (hunk->final_start_line_number != start_line) {
hunk_message(idx, hunk, "mismatched start line number: expected %d, got %d",
start_line, hunk->final_start_line_number);
}
cl_assert_equal_i(hunk->final_start_line_number, start_line);
if (hunk->lines_in_hunk != len) {
hunk_message(idx, hunk, "mismatched line count: expected %d, got %d",
len, hunk->lines_in_hunk);
}
cl_assert_equal_i(hunk->lines_in_hunk, len);
git_oid_fmt(actual, &hunk->final_commit_id);
if (strcmp(expected, actual)) {
hunk_message(idx, hunk, "has mismatched original id (got %s, expected %s)\n",
actual, expected);
}
cl_assert_equal_s(actual, expected);
cl_assert_equal_oid(&hunk->final_commit_id, &hunk->orig_commit_id);
if (strcmp(hunk->orig_path, orig_path)) {
hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n",
hunk->orig_path, orig_path);
}
cl_assert_equal_s(hunk->orig_path, orig_path);
if (hunk->boundary != boundary) {
hunk_message(idx, hunk, "doesn't match boundary flag (got %d, expected %d)\n",
hunk->boundary, boundary);
}
cl_assert_equal_i(boundary, hunk->boundary);
}
+14
View File
@@ -0,0 +1,14 @@
#include "clar_libgit2.h"
#include "blame.h"
void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...);
void check_blame_hunk_index(
git_repository *repo,
git_blame *blame,
int idx,
size_t start_line,
size_t len,
char boundary,
const char *commit_id,
const char *orig_path);
+166
View File
@@ -0,0 +1,166 @@
#include "blame_helpers.h"
static git_repository *g_repo;
static git_blame *g_fileblame, *g_bufferblame;
void test_blame_buffer__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_fileblame, g_repo, "b.txt", NULL));
g_bufferblame = NULL;
}
void test_blame_buffer__cleanup(void)
{
git_blame_free(g_fileblame);
git_blame_free(g_bufferblame);
git_repository_free(g_repo);
}
void test_blame_buffer__added_line(void)
{
const git_blame_hunk *hunk;
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
abcdefg\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt");
hunk = git_blame_get_hunk_byline(g_bufferblame, 16);
cl_assert(hunk);
cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
}
void test_blame_buffer__deleted_line(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 9, 1, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 10, 5, 0, "aa06ecca", "b.txt");
}
void test_blame_buffer__add_splits_hunk(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
abc\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 2, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 8, 1, 0, "00000000", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 9, 3, 0, "63d671eb", "b.txt");
}
void test_blame_buffer__delete_crosses_hunk_boundary(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 2, 0, "aa06ecca", "b.txt");
}
void test_blame_buffer__replace_line(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
abc\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt");
}
void test_blame_buffer__add_lines_at_end(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
\n\
abc\n\
def\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 5, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt");
}
+56
View File
@@ -0,0 +1,56 @@
#include "clar_libgit2.h"
#include "blame.h"
git_blame *g_blame;
void test_blame_getters__initialize(void)
{
size_t i;
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
git_blame_hunk hunks[] = {
{ 3, {{0}}, 1, NULL, {{0}}, "a", 0},
{ 3, {{0}}, 4, NULL, {{0}}, "b", 0},
{ 3, {{0}}, 7, NULL, {{0}}, "c", 0},
{ 3, {{0}}, 10, NULL, {{0}}, "d", 0},
{ 3, {{0}}, 13, NULL, {{0}}, "e", 0},
};
g_blame = git_blame__alloc(NULL, opts, "");
for (i=0; i<5; i++) {
git_blame_hunk *h = git__calloc(1, sizeof(git_blame_hunk));
h->final_start_line_number = hunks[i].final_start_line_number;
h->orig_path = git__strdup(hunks[i].orig_path);
h->lines_in_hunk = hunks[i].lines_in_hunk;
git_vector_insert(&g_blame->hunks, h);
}
}
void test_blame_getters__cleanup(void)
{
git_blame_free(g_blame);
}
void test_blame_getters__byindex(void)
{
const git_blame_hunk *h = git_blame_get_hunk_byindex(g_blame, 2);
cl_assert(h);
cl_assert_equal_s(h->orig_path, "c");
h = git_blame_get_hunk_byindex(g_blame, 95);
cl_assert_equal_p(h, NULL);
}
void test_blame_getters__byline(void)
{
const git_blame_hunk *h = git_blame_get_hunk_byline(g_blame, 5);
cl_assert(h);
cl_assert_equal_s(h->orig_path, "b");
h = git_blame_get_hunk_byline(g_blame, 95);
cl_assert_equal_p(h, NULL);
}
+79
View File
@@ -0,0 +1,79 @@
#include "clar_libgit2.h"
#include "blame.h"
/**
* The test repo has a history that looks like this:
*
* * (A) bc7c5ac
* |\
* | * (B) aa06ecc
* * | (C) 63d671e
* |/
* * (D) da23739
* * (E) b99f7ac
*
*/
static git_repository *g_repo = NULL;
void test_blame_harder__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
}
void test_blame_harder__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
}
void test_blame_harder__m(void)
{
/* TODO */
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
GIT_UNUSED(opts);
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE;
}
void test_blame_harder__c(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
GIT_UNUSED(opts);
/* Attribute the first hunk in b.txt to (E), since it was cut/pasted from
* a.txt in (D).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
}
void test_blame_harder__cc(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
GIT_UNUSED(opts);
/* Attribute the second hunk in b.txt to (E), since it was copy/pasted from
* a.txt in (C).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
}
void test_blame_harder__ccc(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
GIT_UNUSED(opts);
/* Attribute the third hunk in b.txt to (E). This hunk was deleted from
* a.txt in (D), but reintroduced in (B).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES;
}
+344
View File
@@ -0,0 +1,344 @@
#include "blame_helpers.h"
static git_repository *g_repo;
static git_blame *g_blame;
void test_blame_simple__initialize(void)
{
g_repo = NULL;
g_blame = NULL;
}
void test_blame_simple__cleanup(void)
{
git_blame_free(g_blame);
git_repository_free(g_repo);
}
/*
* $ git blame -s branch_file.txt
* orig line no final line no
* commit V author timestamp V
* c47800c7 1 (Scott Chacon 2010-05-25 11:58:14 -0700 1
* a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2
*/
void test_blame_simple__trivial_testrepo(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo/.gitted")));
cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", NULL));
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "c47800c7", "branch_file.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf3", "branch_file.txt");
}
/*
* $ git blame -n b.txt
* orig line no final line no
* commit V author timestamp V
* da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
* da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
* da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
* da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
* ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
* 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
* 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
* 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
* 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
* 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
* aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
* aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
* aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
* aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
* aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
*/
void test_blame_simple__trivial_blamerepo(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", NULL));
cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "aa06ecca", "b.txt");
}
/*
* $ git blame -n 359fc2d -- include/git2.h
* orig line no final line no
* commit orig path V author timestamp V
* d12299fe src/git.h 1 (Vicent Martí 2010-12-03 22:22:10 +0200 1
* 359fc2d2 include/git2.h 2 (Edward Thomson 2013-01-08 17:07:25 -0600 2
* d12299fe src/git.h 5 (Vicent Martí 2010-12-03 22:22:10 +0200 3
* bb742ede include/git2.h 4 (Vicent Martí 2011-09-19 01:54:32 +0300 4
* bb742ede include/git2.h 5 (Vicent Martí 2011-09-19 01:54:32 +0300 5
* d12299fe src/git.h 24 (Vicent Martí 2010-12-03 22:22:10 +0200 6
* d12299fe src/git.h 25 (Vicent Martí 2010-12-03 22:22:10 +0200 7
* d12299fe src/git.h 26 (Vicent Martí 2010-12-03 22:22:10 +0200 8
* d12299fe src/git.h 27 (Vicent Martí 2010-12-03 22:22:10 +0200 9
* d12299fe src/git.h 28 (Vicent Martí 2010-12-03 22:22:10 +0200 10
* 96fab093 include/git2.h 11 (Sven Strickroth 2011-10-09 18:37:41 +0200 11
* 9d1dcca2 src/git2.h 33 (Vicent Martí 2011-02-07 10:35:58 +0200 12
* 44908fe7 src/git2.h 29 (Vicent Martí 2010-12-06 23:03:16 +0200 13
* a15c550d include/git2.h 14 (Vicent Martí 2011-11-16 14:09:44 +0100 14
* 44908fe7 src/git2.h 30 (Vicent Martí 2010-12-06 23:03:16 +0200 15
* d12299fe src/git.h 32 (Vicent Martí 2010-12-03 22:22:10 +0200 16
* 44908fe7 src/git2.h 33 (Vicent Martí 2010-12-06 23:03:16 +0200 17
* d12299fe src/git.h 34 (Vicent Martí 2010-12-03 22:22:10 +0200 18
* 44908fe7 src/git2.h 35 (Vicent Martí 2010-12-06 23:03:16 +0200 19
* 638c2ca4 src/git2.h 36 (Vicent Martí 2010-12-18 02:10:25 +0200 20
* 44908fe7 src/git2.h 36 (Vicent Martí 2010-12-06 23:03:16 +0200 21
* d12299fe src/git.h 37 (Vicent Martí 2010-12-03 22:22:10 +0200 22
* 44908fe7 src/git2.h 38 (Vicent Martí 2010-12-06 23:03:16 +0200 23
* 44908fe7 src/git2.h 39 (Vicent Martí 2010-12-06 23:03:16 +0200 24
* bf787bd8 include/git2.h 25 (Carlos Martín Nieto 2012-04-08 18:56:50 +0200 25
* 0984c876 include/git2.h 26 (Scott J. Goldman 2012-11-28 18:27:43 -0800 26
* 2f8a8ab2 src/git2.h 41 (Vicent Martí 2011-01-29 01:56:25 +0200 27
* 27df4275 include/git2.h 47 (Michael Schubert 2011-06-28 14:13:12 +0200 28
* a346992f include/git2.h 28 (Ben Straub 2012-05-10 09:47:14 -0700 29
* d12299fe src/git.h 40 (Vicent Martí 2010-12-03 22:22:10 +0200 30
* 44908fe7 src/git2.h 41 (Vicent Martí 2010-12-06 23:03:16 +0200 31
* 44908fe7 src/git2.h 42 (Vicent Martí 2010-12-06 23:03:16 +0200 32
* 44908fe7 src/git2.h 43 (Vicent Martí 2010-12-06 23:03:16 +0200 33
* 44908fe7 src/git2.h 44 (Vicent Martí 2010-12-06 23:03:16 +0200 34
* 44908fe7 src/git2.h 45 (Vicent Martí 2010-12-06 23:03:16 +0200 35
* 65b09b1d include/git2.h 33 (Russell Belfer 2012-02-02 18:03:43 -0800 36
* d12299fe src/git.h 46 (Vicent Martí 2010-12-03 22:22:10 +0200 37
* 44908fe7 src/git2.h 47 (Vicent Martí 2010-12-06 23:03:16 +0200 38
* 5d4cd003 include/git2.h 55 (Carlos Martín Nieto 2011-03-28 17:02:45 +0200 39
* 41fb1ca0 include/git2.h 39 (Philip Kelley 2012-10-29 13:41:14 -0400 40
* 2dc31040 include/git2.h 56 (Carlos Martín Nieto 2011-06-20 18:58:57 +0200 41
* 764df57e include/git2.h 40 (Ben Straub 2012-06-15 13:14:43 -0700 42
* 5280f4e6 include/git2.h 41 (Ben Straub 2012-07-31 19:39:06 -0700 43
* 613d5eb9 include/git2.h 43 (Philip Kelley 2012-11-28 11:42:37 -0500 44
* d12299fe src/git.h 48 (Vicent Martí 2010-12-03 22:22:10 +0200 45
* 111ee3fe include/git2.h 41 (Vicent Martí 2012-07-11 14:37:26 +0200 46
* f004c4a8 include/git2.h 44 (Russell Belfer 2012-08-21 17:26:39 -0700 47
* 111ee3fe include/git2.h 42 (Vicent Martí 2012-07-11 14:37:26 +0200 48
* 9c82357b include/git2.h 58 (Carlos Martín Nieto 2011-06-17 18:13:14 +0200 49
* d6258deb include/git2.h 61 (Carlos Martín Nieto 2011-06-25 15:10:09 +0200 50
* b311e313 include/git2.h 63 (Julien Miotte 2011-07-27 18:31:13 +0200 51
* 3412391d include/git2.h 63 (Carlos Martín Nieto 2011-07-07 11:47:31 +0200 52
* bfc9ca59 include/git2.h 43 (Russell Belfer 2012-03-28 16:45:36 -0700 53
* bf477ed4 include/git2.h 44 (Michael Schubert 2012-02-15 00:33:38 +0100 54
* edebceff include/git2.h 46 (nulltoken 2012-05-01 13:57:45 +0200 55
* 743a4b3b include/git2.h 48 (nulltoken 2012-06-15 22:24:59 +0200 56
* 0a32dca5 include/git2.h 54 (Michael Schubert 2012-08-19 22:26:32 +0200 57
* 590fb68b include/git2.h 55 (nulltoken 2012-10-04 13:47:45 +0200 58
* bf477ed4 include/git2.h 45 (Michael Schubert 2012-02-15 00:33:38 +0100 59
* d12299fe src/git.h 49 (Vicent Martí 2010-12-03 22:22:10 +0200 60
*/
void test_blame_simple__trivial_libgit2(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
git_object *obj;
/* If we can't open the libgit2 repo or if it isn't a full repo
* with proper history, just skip this test */
if (git_repository_open(&g_repo, cl_fixture("../..")) < 0)
cl_skip();
if (git_repository_is_shallow(g_repo))
cl_skip();
if (git_revparse_single(&obj, g_repo, "359fc2d") < 0)
cl_skip();
git_oid_cpy(&opts.newest_commit, git_object_id(obj));
git_object_free(obj);
cl_git_pass(git_blame_file(&g_blame, g_repo, "include/git2.h", &opts));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "359fc2d2", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 2, 3, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 3, 4, 2, 0, "bb742ede", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 4, 6, 5, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 5, 11, 1, 0, "96fab093", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 6, 12, 1, 0, "9d1dcca2", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 7, 13, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 8, 14, 1, 0, "a15c550d", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 9, 15, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 10, 16, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 11, 17, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 12, 18, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 13, 19, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 14, 20, 1, 0, "638c2ca4", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 15, 21, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 16, 22, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 17, 23, 2, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 18, 25, 1, 0, "bf787bd8", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 19, 26, 1, 0, "0984c876", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 20, 27, 1, 0, "2f8a8ab2", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 21, 28, 1, 0, "27df4275", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 22, 29, 1, 0, "a346992f", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 23, 30, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 24, 31, 5, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 25, 36, 1, 0, "65b09b1d", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 26, 37, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 27, 38, 1, 0, "44908fe7", "src/git2.h");
check_blame_hunk_index(g_repo, g_blame, 28, 39, 1, 0, "5d4cd003", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 29, 40, 1, 0, "41fb1ca0", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 30, 41, 1, 0, "2dc31040", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 31, 42, 1, 0, "764df57e", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 32, 43, 1, 0, "5280f4e6", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 33, 44, 1, 0, "613d5eb9", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 34, 45, 1, 0, "d12299fe", "src/git.h");
check_blame_hunk_index(g_repo, g_blame, 35, 46, 1, 0, "111ee3fe", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 36, 47, 1, 0, "f004c4a8", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 37, 48, 1, 0, "111ee3fe", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 38, 49, 1, 0, "9c82357b", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 39, 50, 1, 0, "d6258deb", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 40, 51, 1, 0, "b311e313", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 41, 52, 1, 0, "3412391d", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 42, 53, 1, 0, "bfc9ca59", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 43, 54, 1, 0, "bf477ed4", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 44, 55, 1, 0, "edebceff", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 45, 56, 1, 0, "743a4b3b", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 46, 57, 1, 0, "0a32dca5", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 47, 58, 1, 0, "590fb68b", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 48, 59, 1, 0, "bf477ed4", "include/git2.h");
check_blame_hunk_index(g_repo, g_blame, 49, 60, 1, 0, "d12299fe", "src/git.h");
}
/* This was leading to segfaults on some systems during cache eviction. */
void test_blame_simple__trivial_libgit2_under_cache_pressure(void)
{
ssize_t old_max_storage = git_cache__max_storage;
git_cache__max_storage = 1024 * 1024;
test_blame_simple__trivial_libgit2();
git_cache__max_storage = old_max_storage;
}
/*
* $ git blame -n b.txt -L 8
* orig line no final line no
* commit V author timestamp V
* 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
* 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
* 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
* aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
* aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
* aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
* aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
* aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
*/
void test_blame_simple__can_restrict_lines_min(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
opts.min_line = 8;
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 8, 3, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 11, 5, 0, "aa06ecca", "b.txt");
}
/*
* $ git blame -n b.txt -L ,6
* orig line no final line no
* commit V author timestamp V
* da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
* da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
* da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
* da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
* ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
* 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
*/
void test_blame_simple__can_restrict_lines_max(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
opts.max_line = 6;
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 2, 6, 1, 0, "63d671eb", "b.txt");
}
/*
* $ git blame -n b.txt -L 2,7
* orig line no final line no
* commit V author timestamp V
* da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
* da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
* da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
* ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
* 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
* 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
*/
void test_blame_simple__can_restrict_lines_both(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
opts.min_line = 2;
opts.max_line = 7;
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 2, 3, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
}
void test_blame_simple__can_blame_huge_file(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts));
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt");
}
/*
* $ git blame -n branch_file.txt be3563a..HEAD
* orig line no final line no
* commit V author timestamp V
* ^be3563a 1 (Scott Chacon 2010-05-25 11:58:27 -0700 1) hi
* a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2) bye!
*/
void test_blame_simple__can_restrict_to_newish_commits(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
{
git_object *obj;
cl_git_pass(git_revparse_single(&obj, g_repo, "be3563a"));
git_oid_cpy(&opts.oldest_commit, git_object_id(obj));
git_object_free(obj);
}
cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", &opts));
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt");
}
void test_blame_simple__can_restrict_to_first_parent_commits(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
opts.flags |= GIT_BLAME_FIRST_PARENT;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "bc7c5ac2", "b.txt");
}
+51
View File
@@ -0,0 +1,51 @@
#include "clar_libgit2.h"
#include "buffer.h"
static const char *test_string = "Have you seen that? Have you seeeen that??";
void test_buf_basic__resize(void)
{
git_buf buf1 = GIT_BUF_INIT;
git_buf_puts(&buf1, test_string);
cl_assert(git_buf_oom(&buf1) == 0);
cl_assert_equal_s(git_buf_cstr(&buf1), test_string);
git_buf_puts(&buf1, test_string);
cl_assert(strlen(git_buf_cstr(&buf1)) == strlen(test_string) * 2);
git_buf_dispose(&buf1);
}
void test_buf_basic__resize_incremental(void)
{
git_buf buf1 = GIT_BUF_INIT;
/* Presently, asking for 6 bytes will round up to 8. */
cl_git_pass(git_buf_puts(&buf1, "Hello"));
cl_assert_equal_i(5, buf1.size);
cl_assert_equal_i(8, buf1.asize);
/* Ensure an additional byte does not realloc. */
cl_git_pass(git_buf_grow_by(&buf1, 1));
cl_assert_equal_i(5, buf1.size);
cl_assert_equal_i(8, buf1.asize);
/* But requesting many does. */
cl_git_pass(git_buf_grow_by(&buf1, 16));
cl_assert_equal_i(5, buf1.size);
cl_assert(buf1.asize > 8);
git_buf_dispose(&buf1);
}
void test_buf_basic__printf(void)
{
git_buf buf2 = GIT_BUF_INIT;
git_buf_printf(&buf2, "%s %s %d ", "shoop", "da", 23);
cl_assert(git_buf_oom(&buf2) == 0);
cl_assert_equal_s(git_buf_cstr(&buf2), "shoop da 23 ");
git_buf_printf(&buf2, "%s %d", "woop", 42);
cl_assert(git_buf_oom(&buf2) == 0);
cl_assert_equal_s(git_buf_cstr(&buf2), "shoop da 23 woop 42");
git_buf_dispose(&buf2);
}
+59
View File
@@ -0,0 +1,59 @@
#include "clar_libgit2.h"
#include "buffer.h"
/* Override default allocators with ones that will fail predictably. */
static git_allocator std_alloc;
static git_allocator oom_alloc;
static void *oom_malloc(size_t n, const char *file, int line)
{
/* Reject any allocation of more than 100 bytes */
return (n > 100) ? NULL : std_alloc.gmalloc(n, file, line);
}
static void *oom_realloc(void *p, size_t n, const char *file, int line)
{
/* Reject any allocation of more than 100 bytes */
return (n > 100) ? NULL : std_alloc.grealloc(p, n, file, line);
}
void test_buf_oom__initialize(void)
{
git_stdalloc_init_allocator(&std_alloc);
git_stdalloc_init_allocator(&oom_alloc);
oom_alloc.gmalloc = oom_malloc;
oom_alloc.grealloc = oom_realloc;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ALLOCATOR, &oom_alloc));
}
void test_buf_oom__cleanup(void)
{
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ALLOCATOR, NULL));
}
void test_buf_oom__grow(void)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_grow(&buf, 42));
cl_assert(!git_buf_oom(&buf));
cl_assert(git_buf_grow(&buf, 101) == -1);
cl_assert(git_buf_oom(&buf));
git_buf_dispose(&buf);
}
void test_buf_oom__grow_by(void)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_grow_by(&buf, 42));
cl_assert(!git_buf_oom(&buf));
cl_assert(git_buf_grow_by(&buf, 101) == -1);
cl_assert(git_buf_oom(&buf));
}
+49
View File
@@ -0,0 +1,49 @@
#include "clar_libgit2.h"
#include "buffer.h"
static void expect_decode_pass(const char *expected, const char *encoded)
{
git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
/*
* ensure that we only read the given length of the input buffer
* by putting garbage at the end. this will ensure that we do
* not, eg, rely on nul-termination or walk off the end of the buf.
*/
cl_git_pass(git_buf_puts(&in, encoded));
cl_git_pass(git_buf_PUTS(&in, "TRAILER"));
cl_git_pass(git_buf_decode_percent(&out, in.ptr, strlen(encoded)));
cl_assert_equal_s(expected, git_buf_cstr(&out));
cl_assert_equal_i(strlen(expected), git_buf_len(&out));
git_buf_dispose(&in);
git_buf_dispose(&out);
}
void test_buf_percent__decode_succeeds(void)
{
expect_decode_pass("", "");
expect_decode_pass(" ", "%20");
expect_decode_pass("a", "a");
expect_decode_pass(" a", "%20a");
expect_decode_pass("a ", "a%20");
expect_decode_pass("github.com", "github.com");
expect_decode_pass("github.com", "githu%62.com");
expect_decode_pass("github.com", "github%2ecom");
expect_decode_pass("foo bar baz", "foo%20bar%20baz");
expect_decode_pass("foo bar baz", "foo%20bar%20baz");
expect_decode_pass("foo bar ", "foo%20bar%20");
}
void test_buf_percent__ignores_invalid(void)
{
expect_decode_pass("githu%%.com", "githu%%.com");
expect_decode_pass("github.co%2", "github.co%2");
expect_decode_pass("github%2.com", "github%2.com");
expect_decode_pass("githu%2z.com", "githu%2z.com");
expect_decode_pass("github.co%9z", "github.co%9z");
expect_decode_pass("github.co%2", "github.co%2");
expect_decode_pass("github.co%", "github.co%");
}
+88
View File
@@ -0,0 +1,88 @@
#include "clar_libgit2.h"
#include "buffer.h"
static void expect_quote_pass(const char *expected, const char *str)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_puts(&buf, str));
cl_git_pass(git_buf_quote(&buf));
cl_assert_equal_s(expected, git_buf_cstr(&buf));
cl_assert_equal_i(strlen(expected), git_buf_len(&buf));
git_buf_dispose(&buf);
}
void test_buf_quote__quote_succeeds(void)
{
expect_quote_pass("", "");
expect_quote_pass("foo", "foo");
expect_quote_pass("foo/bar/baz.c", "foo/bar/baz.c");
expect_quote_pass("foo bar", "foo bar");
expect_quote_pass("\"\\\"leading quote\"", "\"leading quote");
expect_quote_pass("\"slash\\\\y\"", "slash\\y");
expect_quote_pass("\"foo\\r\\nbar\"", "foo\r\nbar");
expect_quote_pass("\"foo\\177bar\"", "foo\177bar");
expect_quote_pass("\"foo\\001bar\"", "foo\001bar");
expect_quote_pass("\"foo\\377bar\"", "foo\377bar");
}
static void expect_unquote_pass(const char *expected, const char *quoted)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_puts(&buf, quoted));
cl_git_pass(git_buf_unquote(&buf));
cl_assert_equal_s(expected, git_buf_cstr(&buf));
cl_assert_equal_i(strlen(expected), git_buf_len(&buf));
git_buf_dispose(&buf);
}
static void expect_unquote_fail(const char *quoted)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_puts(&buf, quoted));
cl_git_fail(git_buf_unquote(&buf));
git_buf_dispose(&buf);
}
void test_buf_quote__unquote_succeeds(void)
{
expect_unquote_pass("", "\"\"");
expect_unquote_pass(" ", "\" \"");
expect_unquote_pass("foo", "\"foo\"");
expect_unquote_pass("foo bar", "\"foo bar\"");
expect_unquote_pass("foo\"bar", "\"foo\\\"bar\"");
expect_unquote_pass("foo\\bar", "\"foo\\\\bar\"");
expect_unquote_pass("foo\tbar", "\"foo\\tbar\"");
expect_unquote_pass("\vfoo\tbar\n", "\"\\vfoo\\tbar\\n\"");
expect_unquote_pass("foo\nbar", "\"foo\\012bar\"");
expect_unquote_pass("foo\r\nbar", "\"foo\\015\\012bar\"");
expect_unquote_pass("foo\r\nbar", "\"\\146\\157\\157\\015\\012\\142\\141\\162\"");
expect_unquote_pass("newline: \n", "\"newline: \\012\"");
expect_unquote_pass("0xff: \377", "\"0xff: \\377\"");
}
void test_buf_quote__unquote_fails(void)
{
expect_unquote_fail("no quotes at all");
expect_unquote_fail("\"no trailing quote");
expect_unquote_fail("no leading quote\"");
expect_unquote_fail("\"invalid \\z escape char\"");
expect_unquote_fail("\"\\q invalid escape char\"");
expect_unquote_fail("\"invalid escape char \\p\"");
expect_unquote_fail("\"invalid \\1 escape char \"");
expect_unquote_fail("\"invalid \\14 escape char \"");
expect_unquote_fail("\"invalid \\280 escape char\"");
expect_unquote_fail("\"invalid \\378 escape char\"");
expect_unquote_fail("\"invalid \\380 escape char\"");
expect_unquote_fail("\"invalid \\411 escape char\"");
expect_unquote_fail("\"truncated escape char \\\"");
expect_unquote_fail("\"truncated escape char \\0\"");
expect_unquote_fail("\"truncated escape char \\01\"");
}
+93
View File
@@ -0,0 +1,93 @@
#include "clar_libgit2.h"
#include "buffer.h"
static git_buf _buf;
void test_buf_splice__initialize(void) {
git_buf_init(&_buf, 16);
}
void test_buf_splice__cleanup(void) {
git_buf_dispose(&_buf);
}
void test_buf_splice__preprend(void)
{
git_buf_sets(&_buf, "world!");
cl_git_pass(git_buf_splice(&_buf, 0, 0, "Hello Dolly", strlen("Hello ")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__append(void)
{
git_buf_sets(&_buf, "Hello");
cl_git_pass(git_buf_splice(&_buf, git_buf_len(&_buf), 0, " world!", strlen(" world!")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__insert_at(void)
{
git_buf_sets(&_buf, "Hell world!");
cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), 0, "o", strlen("o")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__remove_at(void)
{
git_buf_sets(&_buf, "Hello world of warcraft!");
cl_git_pass(git_buf_splice(&_buf, strlen("Hello world"), strlen(" of warcraft"), "", 0));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__replace(void)
{
git_buf_sets(&_buf, "Hell0 w0rld!");
cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), strlen("0 w0"), "o wo", strlen("o wo")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__replace_with_longer(void)
{
git_buf_sets(&_buf, "Hello you!");
cl_git_pass(git_buf_splice(&_buf, strlen("Hello "), strlen("you"), "world", strlen("world")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__replace_with_shorter(void)
{
git_buf_sets(&_buf, "Brave new world!");
cl_git_pass(git_buf_splice(&_buf, 0, strlen("Brave new"), "Hello", strlen("Hello")));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__truncate(void)
{
git_buf_sets(&_buf, "Hello world!!");
cl_git_pass(git_buf_splice(&_buf, strlen("Hello world!"), strlen("!"), "", 0));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
void test_buf_splice__dont_do_anything(void)
{
git_buf_sets(&_buf, "Hello world!");
cl_git_pass(git_buf_splice(&_buf, 3, 0, "Hello", 0));
cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
}
+58
View File
@@ -0,0 +1,58 @@
#include "clar_libgit2.h"
#include "refs.h"
#include "repo/repo_helpers.h"
#include "path.h"
#include "futils.h"
static git_repository *g_repo;
void test_checkout_binaryunicode__initialize(void)
{
g_repo = cl_git_sandbox_init("binaryunicode");
}
void test_checkout_binaryunicode__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void execute_test(void)
{
git_oid oid, check;
git_commit *commit;
git_tree *tree;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/branch1"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
cl_git_pass(git_commit_tree(&tree, commit));
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_checkout_tree(g_repo, (git_object *)tree, &opts));
git_tree_free(tree);
git_commit_free(commit);
/* Verify that the lenna.jpg file was checked out correctly */
cl_git_pass(git_oid_fromstr(&check, "8ab005d890fe53f65eda14b23672f60d9f4ec5a1"));
cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/lenna.jpg", GIT_OBJECT_BLOB));
cl_assert_equal_oid(&oid, &check);
/* Verify that the text file was checked out correctly */
cl_git_pass(git_oid_fromstr(&check, "965b223880dd4249e2c66a0cc0b4cffe1dc40f5a"));
cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/utf16_withbom_noeol_crlf.txt", GIT_OBJECT_BLOB));
cl_assert_equal_oid(&oid, &check);
}
void test_checkout_binaryunicode__noautocrlf(void)
{
cl_repo_set_bool(g_repo, "core.autocrlf", false);
execute_test();
}
void test_checkout_binaryunicode__autocrlf(void)
{
cl_repo_set_bool(g_repo, "core.autocrlf", true);
execute_test();
}
+151
View File
@@ -0,0 +1,151 @@
#include "clar_libgit2.h"
#include "checkout_helpers.h"
#include "refs.h"
#include "futils.h"
#include "index.h"
void assert_on_branch(git_repository *repo, const char *branch)
{
git_reference *head;
git_buf bname = GIT_BUF_INIT;
cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
cl_assert_(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC, branch);
cl_git_pass(git_buf_joinpath(&bname, "refs/heads", branch));
cl_assert_equal_s(bname.ptr, git_reference_symbolic_target(head));
git_reference_free(head);
git_buf_dispose(&bname);
}
void reset_index_to_treeish(git_object *treeish)
{
git_object *tree;
git_index *index;
git_repository *repo = git_object_owner(treeish);
cl_git_pass(git_object_peel(&tree, treeish, GIT_OBJECT_TREE));
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_read_tree(index, (git_tree *)tree));
cl_git_pass(git_index_write(index));
git_object_free(tree);
git_index_free(index);
}
int checkout_count_callback(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload)
{
checkout_counts *ct = payload;
GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir);
if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) {
ct->n_conflicts++;
if (ct->debug) {
if (workdir) {
if (baseline) {
if (target)
fprintf(stderr, "M %s (conflicts with M %s)\n",
workdir->path, target->path);
else
fprintf(stderr, "M %s (conflicts with D %s)\n",
workdir->path, baseline->path);
} else {
if (target)
fprintf(stderr, "Existing %s (conflicts with A %s)\n",
workdir->path, target->path);
else
fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path);
}
} else {
if (baseline) {
if (target)
fprintf(stderr, "D %s (conflicts with M %s)\n",
target->path, baseline->path);
else
fprintf(stderr, "D %s (conflicts with D %s)\n",
baseline->path, baseline->path);
} else {
if (target)
fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path);
else
fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path);
}
}
}
}
if (why & GIT_CHECKOUT_NOTIFY_DIRTY) {
ct->n_dirty++;
if (ct->debug) {
if (workdir)
fprintf(stderr, "M %s\n", workdir->path);
else
fprintf(stderr, "D %s\n", baseline->path);
}
}
if (why & GIT_CHECKOUT_NOTIFY_UPDATED) {
ct->n_updates++;
if (ct->debug) {
if (baseline) {
if (target)
fprintf(stderr, "update: M %s\n", path);
else
fprintf(stderr, "update: D %s\n", path);
} else {
if (target)
fprintf(stderr, "update: A %s\n", path);
else
fprintf(stderr, "update: this makes no sense %s\n", path);
}
}
}
if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) {
ct->n_untracked++;
if (ct->debug)
fprintf(stderr, "? %s\n", path);
}
if (why & GIT_CHECKOUT_NOTIFY_IGNORED) {
ct->n_ignored++;
if (ct->debug)
fprintf(stderr, "I %s\n", path);
}
return 0;
}
void tick_index(git_index *index)
{
struct timespec ts;
struct p_timeval times[2];
cl_assert(index->on_disk);
cl_assert(git_index_path(index));
cl_git_pass(git_index_read(index, true));
ts = index->stamp.mtime;
times[0].tv_sec = ts.tv_sec;
times[0].tv_usec = ts.tv_nsec / 1000;
times[1].tv_sec = ts.tv_sec + 5;
times[1].tv_usec = ts.tv_nsec / 1000;
cl_git_pass(p_utimes(git_index_path(index), times));
cl_git_pass(git_index_read(index, true));
}
+31
View File
@@ -0,0 +1,31 @@
#include "buffer.h"
#include "git2/object.h"
#include "git2/repository.h"
extern void assert_on_branch(git_repository *repo, const char *branch);
extern void reset_index_to_treeish(git_object *treeish);
#define check_file_contents(PATH,EXP) \
cl_assert_equal_file(EXP,0,PATH)
#define check_file_contents_nocr(PATH,EXP) \
cl_assert_equal_file_ignore_cr(EXP,0,PATH)
typedef struct {
int n_conflicts;
int n_dirty;
int n_updates;
int n_untracked;
int n_ignored;
int debug;
} checkout_counts;
extern int checkout_count_callback(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload);
extern void tick_index(git_index *index);
File diff suppressed because it is too large Load Diff
+496
View File
@@ -0,0 +1,496 @@
#include "clar_libgit2.h"
#include "checkout_helpers.h"
#include "../filter/crlf.h"
#include "futils.h"
#include "git2/checkout.h"
#include "repository.h"
#include "index.h"
#include "posix.h"
static git_repository *g_repo;
static const char *systype;
static git_buf expected_fixture = GIT_BUF_INIT;
static int unlink_file(void *payload, git_buf *path)
{
char *fn;
cl_assert(fn = git_path_basename(path->ptr));
GIT_UNUSED(payload);
if (strcmp(fn, ".git"))
cl_must_pass(p_unlink(path->ptr));
git__free(fn);
return 0;
}
void test_checkout_crlf__initialize(void)
{
git_buf reponame = GIT_BUF_INIT;
g_repo = cl_git_sandbox_init("crlf");
/*
* remove the contents of the working directory so that we can
* check out over it.
*/
cl_git_pass(git_buf_puts(&reponame, "crlf"));
cl_git_pass(git_path_direach(&reponame, 0, unlink_file, NULL));
if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
systype = "windows";
else
systype = "posix";
git_buf_dispose(&reponame);
}
void test_checkout_crlf__cleanup(void)
{
cl_git_sandbox_cleanup();
if (expected_fixture.size) {
cl_fixture_cleanup(expected_fixture.ptr);
git_buf_dispose(&expected_fixture);
}
}
struct compare_data
{
const char *dirname;
const char *autocrlf;
const char *attrs;
};
static int compare_file(void *payload, git_buf *actual_path)
{
git_buf expected_path = GIT_BUF_INIT;
git_buf actual_contents = GIT_BUF_INIT;
git_buf expected_contents = GIT_BUF_INIT;
struct compare_data *cd = payload;
bool failed = true;
int cmp_git, cmp_gitattributes;
char *basename;
basename = git_path_basename(actual_path->ptr);
cmp_git = strcmp(basename, ".git");
cmp_gitattributes = strcmp(basename, ".gitattributes");
if (cmp_git == 0 || cmp_gitattributes == 0) {
failed = false;
goto done;
}
cl_git_pass(git_buf_joinpath(&expected_path, cd->dirname, basename));
if (!git_path_isfile(expected_path.ptr) ||
!git_path_isfile(actual_path->ptr))
goto done;
if (git_futils_readbuffer(&actual_contents, actual_path->ptr) < 0 ||
git_futils_readbuffer(&expected_contents, expected_path.ptr) < 0)
goto done;
if (actual_contents.size != expected_contents.size)
goto done;
if (memcmp(actual_contents.ptr, expected_contents.ptr, expected_contents.size) != 0)
goto done;
failed = false;
done:
if (failed) {
git_buf details = GIT_BUF_INIT;
git_buf_printf(&details, "filename=%s, system=%s, autocrlf=%s, attrs={%s}",
git_path_basename(actual_path->ptr), systype, cd->autocrlf, cd->attrs);
clar__fail(__FILE__, __LINE__,
"checked out contents did not match expected", details.ptr, 0);
git_buf_dispose(&details);
}
git__free(basename);
git_buf_dispose(&expected_contents);
git_buf_dispose(&actual_contents);
git_buf_dispose(&expected_path);
return 0;
}
static void test_checkout(const char *autocrlf, const char *attrs)
{
git_buf attrbuf = GIT_BUF_INIT;
git_buf expected_dirname = GIT_BUF_INIT;
git_buf systype_and_direction = GIT_BUF_INIT;
git_buf sandboxname = GIT_BUF_INIT;
git_buf reponame = GIT_BUF_INIT;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct compare_data compare_data = { NULL, autocrlf, attrs };
const char *c;
cl_git_pass(git_buf_puts(&reponame, "crlf"));
cl_git_pass(git_buf_puts(&systype_and_direction, systype));
cl_git_pass(git_buf_puts(&systype_and_direction, "_to_workdir"));
cl_git_pass(git_buf_puts(&sandboxname, "autocrlf_"));
cl_git_pass(git_buf_puts(&sandboxname, autocrlf));
if (*attrs) {
cl_git_pass(git_buf_puts(&sandboxname, ","));
for (c = attrs; *c; c++) {
if (*c == ' ')
cl_git_pass(git_buf_putc(&sandboxname, ','));
else if (*c == '=')
cl_git_pass(git_buf_putc(&sandboxname, '_'));
else
cl_git_pass(git_buf_putc(&sandboxname, *c));
}
cl_git_pass(git_buf_printf(&attrbuf, "* %s\n", attrs));
cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
}
cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);
cl_git_pass(git_buf_joinpath(&expected_dirname, systype_and_direction.ptr, sandboxname.ptr));
cl_git_pass(git_buf_joinpath(&expected_fixture, "crlf_data", expected_dirname.ptr));
cl_fixture_sandbox(expected_fixture.ptr);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_head(g_repo, &opts));
compare_data.dirname = sandboxname.ptr;
cl_git_pass(git_path_direach(&reponame, 0, compare_file, &compare_data));
cl_fixture_cleanup(expected_fixture.ptr);
git_buf_dispose(&expected_fixture);
git_buf_dispose(&attrbuf);
git_buf_dispose(&expected_fixture);
git_buf_dispose(&expected_dirname);
git_buf_dispose(&sandboxname);
git_buf_dispose(&systype_and_direction);
git_buf_dispose(&reponame);
}
static void empty_workdir(const char *name)
{
git_vector contents = GIT_VECTOR_INIT;
char *basename;
int cmp;
size_t i;
const char *fn;
cl_git_pass(git_path_dirload(&contents, name, 0, 0));
git_vector_foreach(&contents, i, fn) {
cl_assert(basename = git_path_basename(fn));
cmp = strncasecmp(basename, ".git", 4);
git__free(basename);
if (cmp)
cl_git_pass(p_unlink(fn));
}
git_vector_free_deep(&contents);
}
void test_checkout_crlf__matches_core_git(void)
{
const char *autocrlf[] = { "true", "false", "input", NULL };
const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
"text", "text eol=crlf", "text eol=lf",
"text=auto", "text=auto eol=crlf", "text=auto eol=lf",
NULL };
const char **a, **b;
for (a = autocrlf; *a; a++) {
for (b = attrs; *b; b++) {
empty_workdir("crlf");
test_checkout(*a, *b);
}
}
}
void test_checkout_crlf__detect_crlf_autocrlf_false(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", false);
git_checkout_head(g_repo, &opts);
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
{
git_index *index;
const git_index_entry *entry;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", false);
git_repository_index(&index, g_repo);
tick_index(index);
git_checkout_head(g_repo, &opts);
cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
git_index_free(index);
}
void test_checkout_crlf__detect_crlf_autocrlf_true(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__detect_crlf_autocrlf_true_utf8(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_repository_set_head(g_repo, "refs/heads/master");
git_checkout_head(g_repo, &opts);
check_file_contents("./crlf/few-utf8-chars-lf", FEW_UTF8_CRLF_RAW);
check_file_contents("./crlf/many-utf8-chars-lf", MANY_UTF8_CRLF_RAW);
check_file_contents("./crlf/few-utf8-chars-crlf", FEW_UTF8_CRLF_RAW);
check_file_contents("./crlf/many-utf8-chars-crlf", MANY_UTF8_CRLF_RAW);
}
void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
{
git_index *index;
const git_index_entry *entry;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_repository_index(&index, g_repo);
tick_index(index);
git_checkout_head(g_repo, &opts);
cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
git_index_free(index);
}
void test_checkout_crlf__with_ident(void)
{
git_index *index;
const git_index_entry *entry;
git_blob *blob;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_mkfile("crlf/.gitattributes",
"*.txt text\n*.bin binary\n"
"*.crlf text eol=crlf\n"
"*.lf text eol=lf\n"
"*.ident text ident\n"
"*.identcrlf ident text eol=crlf\n"
"*.identlf ident text eol=lf\n");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
/* add files with $Id$ */
cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "lf.ident"));
cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");
git_checkout_head(g_repo, &opts);
/* check that blobs have $Id$ */
cl_assert((entry = git_index_get_bypath(index, "lf.ident", 0)));
cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
cl_assert_equal_s(
ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
git_blob_free(blob);
cl_assert((entry = git_index_get_bypath(index, "more2.identcrlf", 0)));
cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
cl_assert_equal_s(
"\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
git_blob_free(blob);
/* check that filesystem is initially untouched - matching core Git */
cl_assert_equal_file(
ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");
/* check that forced checkout rewrites correctly */
p_unlink("crlf/lf.ident");
p_unlink("crlf/crlf.ident");
p_unlink("crlf/more1.identlf");
p_unlink("crlf/more2.identcrlf");
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_assert_equal_file(
ALL_LF_TEXT_AS_CRLF
"\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467 $\r\n",
0, "crlf/lf.ident");
cl_assert_equal_file(
ALL_CRLF_TEXT_RAW
"\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857 $\r\n\r\n",
0, "crlf/crlf.ident");
cl_assert_equal_file(
"$Id: f7830382dac1f1583422be5530fdfbd26289431b $\n"
MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");
cl_assert_equal_file(
"\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4 $\r\n"
MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
git_index_free(index);
}
void test_checkout_crlf__autocrlf_false_no_attrs(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", false);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__autocrlf_true_no_attrs(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
}
void test_checkout_crlf__autocrlf_input_no_attrs(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_string(g_repo, "core.autocrlf", "input");
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__autocrlf_false_text_auto_attr(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
cl_repo_set_bool(g_repo, "core.autocrlf", false);
cl_git_pass(git_checkout_head(g_repo, &opts));
if (GIT_EOL_NATIVE == GIT_EOL_CRLF) {
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
} else {
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
}
void test_checkout_crlf__autocrlf_true_text_auto_attr(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
}
void test_checkout_crlf__autocrlf_input_text_auto_attr(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
cl_repo_set_string(g_repo, "core.autocrlf", "input");
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__can_write_empty_file(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_pass(git_repository_set_head(g_repo, "refs/heads/empty-files"));
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/test1.txt", "");
check_file_contents("./crlf/test2.txt", "test2.txt's content\r\n");
check_file_contents("./crlf/test3.txt", "");
}
+266
View File
@@ -0,0 +1,266 @@
#include "clar_libgit2.h"
#include "refs.h"
#include "repo/repo_helpers.h"
#include "path.h"
#include "futils.h"
static git_repository *g_repo;
void test_checkout_head__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
}
void test_checkout_head__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_checkout_head__unborn_head_returns_GIT_EUNBORNBRANCH(void)
{
make_head_unborn(g_repo, NON_EXISTING_HEAD);
cl_assert_equal_i(GIT_EUNBORNBRANCH, git_checkout_head(g_repo, NULL));
}
void test_checkout_head__with_index_only_tree(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
/* let's start by getting things into a known state */
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_head(g_repo, &opts));
/* now let's stage some new stuff including a new directory */
cl_git_pass(git_repository_index(&index, g_repo));
p_mkdir("testrepo/newdir", 0777);
cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt"));
cl_git_pass(git_index_write(index));
cl_assert(git_path_isfile("testrepo/newdir/newfile.txt"));
cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) != NULL);
git_index_free(index);
/* okay, so now we have staged this new file; let's see if we can remove */
opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_git_pass(git_repository_index(&index, g_repo));
cl_assert(!git_path_isfile("testrepo/newdir/newfile.txt"));
cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) == NULL);
git_index_free(index);
}
void test_checkout_head__do_not_remove_untracked_file(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
cl_git_pass(p_mkdir("testrepo/tracked", 0755));
cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
cl_git_mkfile("testrepo/tracked/untracked", "untracked\n");
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
cl_git_pass(git_index_write(index));
git_index_free(index);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_assert(!git_path_isfile("testrepo/tracked/tracked"));
cl_assert(git_path_isfile("testrepo/tracked/untracked"));
}
void test_checkout_head__do_not_remove_untracked_file_in_subdir(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
cl_git_pass(p_mkdir("testrepo/tracked", 0755));
cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755));
cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
cl_git_mkfile("testrepo/tracked/subdir/tracked", "tracked\n");
cl_git_mkfile("testrepo/tracked/subdir/untracked", "untracked\n");
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
cl_git_pass(git_index_add_bypath(index, "tracked/subdir/tracked"));
cl_git_pass(git_index_write(index));
git_index_free(index);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_assert(!git_path_isfile("testrepo/tracked/tracked"));
cl_assert(!git_path_isfile("testrepo/tracked/subdir/tracked"));
cl_assert(git_path_isfile("testrepo/tracked/subdir/untracked"));
}
void test_checkout_head__do_remove_tracked_subdir(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
cl_git_pass(p_mkdir("testrepo/subdir", 0755));
cl_git_pass(p_mkdir("testrepo/subdir/tracked", 0755));
cl_git_mkfile("testrepo/subdir/tracked-file", "tracked\n");
cl_git_mkfile("testrepo/subdir/untracked-file", "untracked\n");
cl_git_mkfile("testrepo/subdir/tracked/tracked1", "tracked\n");
cl_git_mkfile("testrepo/subdir/tracked/tracked2", "tracked\n");
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "subdir/tracked-file"));
cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked1"));
cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked2"));
cl_git_pass(git_index_write(index));
git_index_free(index);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_assert(!git_path_isdir("testrepo/subdir/tracked"));
cl_assert(!git_path_isfile("testrepo/subdir/tracked-file"));
cl_assert(git_path_isfile("testrepo/subdir/untracked-file"));
}
void test_checkout_head__typechange_workdir(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_object *target;
struct stat st;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
cl_must_pass(p_chmod("testrepo/new.txt", 0755));
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_git_pass(p_stat("testrepo/new.txt", &st));
cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
git_object_free(target);
}
void test_checkout_head__typechange_index_and_workdir(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_object *target;
git_index *index;
struct stat st;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
cl_must_pass(p_chmod("testrepo/new.txt", 0755));
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "new.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_git_pass(p_stat("testrepo/new.txt", &st));
cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
git_object_free(target);
git_index_free(index);
}
void test_checkout_head__workdir_filemode_is_simplified(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_object *target, *branch;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_revparse_single(&target, g_repo, "a38d028f71eaa590febb7d716b1ca32350cf70da"));
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
cl_must_pass(p_chmod("testrepo/branch_file.txt", 0666));
/*
* Checkout should not fail with a conflict; though the file mode
* on disk is literally different to the base (0666 vs 0644), Git
* ignores the actual mode and simply treats both as non-executable.
*/
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
git_object_free(branch);
git_object_free(target);
}
void test_checkout_head__obeys_filemode_true(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_object *target, *branch;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
/* In this commit, `README` is executable */
cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
cl_repo_set_bool(g_repo, "core.filemode", true);
cl_must_pass(p_chmod("testrepo/README", 0644));
/*
* Checkout will fail with a conflict; the file mode is updated in
* the checkout target, but the contents have changed in our branch.
*/
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, branch, NULL));
git_object_free(branch);
git_object_free(target);
}
void test_checkout_head__obeys_filemode_false(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_object *target, *branch;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
/* In this commit, `README` is executable */
cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
cl_repo_set_bool(g_repo, "core.filemode", false);
cl_must_pass(p_chmod("testrepo/README", 0644));
/*
* Checkout will fail with a conflict; the file contents are updated
* in the checkout target, but the filemode has changed in our branch.
*/
cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
git_object_free(branch);
git_object_free(target);
}
+292
View File
@@ -0,0 +1,292 @@
#include "clar_libgit2.h"
#include "git2/checkout.h"
#include "refs.h"
#include "path.h"
#include "repository.h"
#ifdef GIT_WIN32
# include <windows.h>
#else
# include <dirent.h>
#endif
static git_repository *repo;
static git_object *obj;
static git_checkout_options checkout_opts;
void test_checkout_icase__initialize(void)
{
git_oid id;
git_config *cfg;
int icase = 0;
repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_repository_config_snapshot(&cfg, repo));
git_config_get_bool(&icase, cfg, "core.ignorecase");
git_config_free(cfg);
if (!icase)
cl_skip();
cl_git_pass(git_reference_name_to_id(&id, repo, "refs/heads/dir"));
cl_git_pass(git_object_lookup(&obj, repo, &id, GIT_OBJECT_ANY));
git_checkout_options_init(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
}
void test_checkout_icase__cleanup(void)
{
git_object_free(obj);
cl_git_sandbox_cleanup();
}
static char *get_filename(const char *in)
{
char *search_dirname, *search_filename, *filename = NULL;
git_buf out = GIT_BUF_INIT;
DIR *dir;
struct dirent *de;
cl_assert(search_dirname = git_path_dirname(in));
cl_assert(search_filename = git_path_basename(in));
cl_assert(dir = opendir(search_dirname));
while ((de = readdir(dir))) {
if (strcasecmp(de->d_name, search_filename) == 0) {
git_buf_join(&out, '/', search_dirname, de->d_name);
filename = git_buf_detach(&out);
break;
}
}
closedir(dir);
git__free(search_dirname);
git__free(search_filename);
git_buf_dispose(&out);
return filename;
}
static void assert_name_is(const char *expected)
{
char *actual;
size_t actual_len, expected_len, start;
cl_assert(actual = get_filename(expected));
expected_len = strlen(expected);
actual_len = strlen(actual);
cl_assert(actual_len >= expected_len);
start = actual_len - expected_len;
cl_assert_equal_s(expected, actual + start);
if (start)
cl_assert_equal_strn("/", actual + (start - 1), 1);
free(actual);
}
static int symlink_or_fake(git_repository *repo, const char *a, const char *b)
{
int symlinks;
cl_git_pass(git_repository__configmap_lookup(&symlinks, repo, GIT_CONFIGMAP_SYMLINKS));
if (symlinks)
return p_symlink(a, b);
else
return git_futils_fake_symlink(a, b);
}
void test_checkout_icase__refuses_to_overwrite_files_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_git_write2file("testrepo/BRANCH_FILE.txt", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/BRANCH_FILE.txt");
}
void test_checkout_icase__overwrites_files_for_files_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_write2file("testrepo/NEW.txt", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/new.txt");
}
void test_checkout_icase__refuses_to_overwrite_links_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/BRANCH_FILE.txt"));
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
cl_assert(!git_path_exists("tmp"));
assert_name_is("testrepo/BRANCH_FILE.txt");
}
void test_checkout_icase__overwrites_links_for_files_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/NEW.txt"));
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
cl_assert(!git_path_exists("tmp"));
assert_name_is("testrepo/new.txt");
}
void test_checkout_icase__overwrites_empty_folders_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(p_mkdir("testrepo/NEW.txt", 0777));
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/new.txt");
cl_assert(!git_path_isdir("testrepo/new.txt"));
}
void test_checkout_icase__refuses_to_overwrite_populated_folders_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(p_mkdir("testrepo/BRANCH_FILE.txt", 0777));
cl_git_write2file("testrepo/BRANCH_FILE.txt/foobar", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/BRANCH_FILE.txt");
cl_assert(git_path_isdir("testrepo/BRANCH_FILE.txt"));
}
void test_checkout_icase__overwrites_folders_for_files_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_must_pass(p_mkdir("testrepo/NEW.txt", 0777));
cl_git_write2file("testrepo/NEW.txt/foobar", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/new.txt");
cl_assert(!git_path_isdir("testrepo/new.txt"));
}
void test_checkout_icase__refuses_to_overwrite_files_for_folders(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_git_write2file("testrepo/A", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/A");
cl_assert(!git_path_isdir("testrepo/A"));
}
void test_checkout_icase__overwrites_files_for_folders_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_write2file("testrepo/A", "neue file\n", 10, \
O_WRONLY | O_CREAT | O_TRUNC, 0644);
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
assert_name_is("testrepo/a");
cl_assert(git_path_isdir("testrepo/a"));
}
void test_checkout_icase__refuses_to_overwrite_links_for_folders(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
cl_assert(!git_path_exists("b.txt"));
assert_name_is("testrepo/A");
}
void test_checkout_icase__overwrites_links_for_folders_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
cl_assert(!git_path_exists("b.txt"));
assert_name_is("testrepo/a");
}
void test_checkout_icase__ignores_unstaged_casechange(void)
{
git_reference *orig_ref, *br2_ref;
git_commit *orig, *br2;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_reference_lookup_resolved(&orig_ref, repo, "HEAD", 100));
cl_git_pass(git_commit_lookup(&orig, repo, git_reference_target(orig_ref)));
cl_git_pass(git_reset(repo, (git_object *)orig, GIT_RESET_HARD, NULL));
cl_rename("testrepo/branch_file.txt", "testrepo/Branch_File.txt");
cl_git_pass(git_reference_lookup_resolved(&br2_ref, repo, "refs/heads/br2", 100));
cl_git_pass(git_commit_lookup(&br2, repo, git_reference_target(br2_ref)));
cl_git_pass(git_checkout_tree(repo, (const git_object *)br2, &checkout_opts));
git_commit_free(orig);
git_commit_free(br2);
git_reference_free(orig_ref);
git_reference_free(br2_ref);
}
void test_checkout_icase__conflicts_with_casechanged_subtrees(void)
{
git_reference *orig_ref;
git_object *orig, *subtrees;
git_oid oid;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_reference_lookup_resolved(&orig_ref, repo, "HEAD", 100));
cl_git_pass(git_object_lookup(&orig, repo, git_reference_target(orig_ref), GIT_OBJECT_COMMIT));
cl_git_pass(git_reset(repo, (git_object *)orig, GIT_RESET_HARD, NULL));
cl_must_pass(p_mkdir("testrepo/AB", 0777));
cl_must_pass(p_mkdir("testrepo/AB/C", 0777));
cl_git_write2file("testrepo/AB/C/3.txt", "Foobar!\n", 8, O_RDWR|O_CREAT, 0666);
cl_git_pass(git_reference_name_to_id(&oid, repo, "refs/heads/subtrees"));
cl_git_pass(git_object_lookup(&subtrees, repo, &oid, GIT_OBJECT_ANY));
cl_git_fail(git_checkout_tree(repo, subtrees, &checkout_opts));
git_object_free(orig);
git_object_free(subtrees);
git_reference_free(orig_ref);
}
+826
View File
@@ -0,0 +1,826 @@
#include "clar_libgit2.h"
#include "checkout_helpers.h"
#include "git2/checkout.h"
#include "futils.h"
#include "repository.h"
#include "remote.h"
#include "repo/repo_helpers.h"
static git_repository *g_repo;
static git_buf g_global_path = GIT_BUF_INIT;
void test_checkout_index__initialize(void)
{
git_tree *tree;
g_repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_repository_head_tree(&tree, g_repo));
reset_index_to_treeish((git_object *)tree);
git_tree_free(tree);
cl_git_rewritefile(
"./testrepo/.gitattributes",
"* text eol=lf\n");
git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
&g_global_path);
}
void test_checkout_index__cleanup(void)
{
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
g_global_path.ptr);
git_buf_dispose(&g_global_path);
cl_git_sandbox_cleanup();
/* try to remove directories created by tests */
cl_fixture_cleanup("alternative");
cl_fixture_cleanup("symlink");
cl_fixture_cleanup("symlink.git");
cl_fixture_cleanup("tmp_global_path");
}
void test_checkout_index__cannot_checkout_a_bare_repository(void)
{
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("testrepo.git");
cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
}
void test_checkout_index__can_create_missing_files(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/README", "hey there\n");
check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
check_file_contents("./testrepo/new.txt", "my new file\n");
}
void test_checkout_index__can_remove_untracked_files(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
cl_git_mkfile("./testrepo/dir/one", "one\n");
cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
opts.checkout_strategy =
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_REMOVE_UNTRACKED;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
}
void test_checkout_index__honor_the_specified_pathspecs(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
char *entries[] = { "*.txt" };
opts.paths.strings = entries;
opts.paths.count = 1;
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
check_file_contents("./testrepo/new.txt", "my new file\n");
}
void test_checkout_index__honor_the_gitattributes_directives(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
const char *attributes =
"branch_file.txt text eol=crlf\n"
"new.txt text eol=lf\n";
cl_git_mkfile("./testrepo/.gitattributes", attributes);
cl_repo_set_bool(g_repo, "core.autocrlf", false);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/README", "hey there\n");
check_file_contents("./testrepo/new.txt", "my new file\n");
check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
}
void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
{
#ifdef GIT_WIN32
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
const char *expected_readme_text = "hey there\r\n";
cl_git_pass(p_unlink("./testrepo/.gitattributes"));
cl_repo_set_bool(g_repo, "core.autocrlf", true);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/README", expected_readme_text);
#endif
}
static void populate_symlink_workdir(void)
{
git_buf path = GIT_BUF_INIT;
git_repository *repo;
git_remote *origin;
git_object *target;
const char *url = git_repository_path(g_repo);
cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), "symlink.git"));
cl_git_pass(git_repository_init(&repo, path.ptr, true));
cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));
/* Delete the `origin` repo (if it exists) so we can recreate it. */
git_remote_delete(repo, GIT_REMOTE_ORIGIN);
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
git_remote_free(origin);
cl_git_pass(git_revparse_single(&target, repo, "remotes/origin/master"));
cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
git_object_free(target);
git_repository_free(repo);
git_buf_dispose(&path);
}
void test_checkout_index__honor_coresymlinks_default_true(void)
{
char link_data[GIT_PATH_MAX];
int link_size = GIT_PATH_MAX;
cl_must_pass(p_mkdir("symlink", 0777));
if (!git_path_supports_symlinks("symlink/test"))
cl_skip();
#ifdef GIT_WIN32
/*
* Windows explicitly requires the global configuration to have
* core.symlinks=true in addition to actual filesystem support.
*/
create_tmp_global_config("tmp_global_path", "core.symlinks", "true");
#endif
populate_symlink_workdir();
link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
cl_assert(link_size >= 0);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./symlink/link_to_new.txt", "my new file\n");
}
void test_checkout_index__honor_coresymlinks_default_false(void)
{
cl_must_pass(p_mkdir("symlink", 0777));
#ifndef GIT_WIN32
/*
* This test is largely for Windows platforms to ensure that
* we respect an unset core.symlinks even when the platform
* supports symlinks. Bail entirely on POSIX platforms that
* do support symlinks.
*/
if (git_path_supports_symlinks("symlink/test"))
cl_skip();
#endif
populate_symlink_workdir();
check_file_contents("./symlink/link_to_new.txt", "new.txt");
}
void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
if (git_path_supports_symlinks("testrepo/test")) {
cl_skip();
}
cl_repo_set_bool(g_repo, "core.symlinks", true);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
}
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
char link_data[GIT_PATH_MAX];
size_t link_size = GIT_PATH_MAX;
if (!git_path_supports_symlinks("testrepo/test")) {
cl_skip();
}
cl_repo_set_bool(g_repo, "core.symlinks", true);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
}
void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_repo_set_bool(g_repo, "core.symlinks", false);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/link_to_new.txt", "new.txt");
}
void test_checkout_index__donot_overwrite_modified_file_by_default(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
/* set this up to not return an error code on conflicts, but it
* still will not have permission to overwrite anything...
*/
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
}
void test_checkout_index__can_overwrite_modified_file(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/new.txt", "my new file\n");
}
void test_checkout_index__options_disable_filters(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
opts.disable_filters = false;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/new.txt", "my new file\r\n");
p_unlink("./testrepo/new.txt");
opts.disable_filters = true;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/new.txt", "my new file\n");
}
void test_checkout_index__options_dir_modes(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct stat st;
git_oid oid;
git_commit *commit;
mode_t um;
if (!cl_is_chmod_supported())
return;
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
reset_index_to_treeish((git_object *)commit);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
opts.dir_mode = 0701;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
/* umask will influence actual directory creation mode */
(void)p_umask(um = p_umask(022));
cl_git_pass(p_stat("./testrepo/a", &st));
/* Haiku & Hurd use other mode bits, so we must mask them out */
cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
/* File-mode test, since we're on the 'dir' branch */
cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
git_commit_free(commit);
}
void test_checkout_index__options_override_file_modes(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct stat st;
if (!cl_is_chmod_supported())
return;
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
opts.file_mode = 0700;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_git_pass(p_stat("./testrepo/new.txt", &st));
cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
}
void test_checkout_index__options_open_flags(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_mkfile("./testrepo/new.txt", "hi\n");
opts.checkout_strategy =
GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
}
struct notify_data {
const char *file;
const char *sha;
};
static int test_checkout_notify_cb(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload)
{
struct notify_data *expectations = (struct notify_data *)payload;
GIT_UNUSED(workdir);
cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
cl_assert_equal_s(expectations->file, path);
cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
return 0;
}
void test_checkout_index__can_notify_of_skipped_files(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct notify_data data;
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
/*
* $ git ls-tree HEAD
* 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
* 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
* 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
*/
data.file = "new.txt";
data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
opts.checkout_strategy = GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_ALLOW_CONFLICTS;
opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
opts.notify_cb = test_checkout_notify_cb;
opts.notify_payload = &data;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
}
static int dont_notify_cb(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload)
{
GIT_UNUSED(why);
GIT_UNUSED(path);
GIT_UNUSED(baseline);
GIT_UNUSED(target);
GIT_UNUSED(workdir);
GIT_UNUSED(payload);
cl_assert(false);
return 0;
}
void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_pass(p_unlink("./testrepo/.gitattributes"));
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
opts.checkout_strategy =
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_ALLOW_CONFLICTS;
opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
opts.notify_cb = dont_notify_cb;
opts.notify_payload = NULL;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
}
static void checkout_progress_counter(
const char *path, size_t cur, size_t tot, void *payload)
{
GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
(*(int *)payload)++;
}
void test_checkout_index__calls_progress_callback(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
int calls = 0;
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
opts.progress_cb = checkout_progress_counter;
opts.progress_payload = &calls;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert(calls > 0);
}
void test_checkout_index__can_overcome_name_clashes(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
git_index_clear(index);
cl_git_mkfile("./testrepo/path0", "content\r\n");
cl_git_pass(p_mkdir("./testrepo/path1", 0777));
cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
cl_git_pass(git_index_add_bypath(index, "path0"));
cl_git_pass(git_index_add_bypath(index, "path1/file1"));
cl_git_pass(p_unlink("./testrepo/path0"));
cl_git_pass(git_futils_rmdir_r(
"./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_mkfile("./testrepo/path1", "content\r\n");
cl_git_pass(p_mkdir("./testrepo/path0", 0777));
cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
cl_assert(git_path_isfile("./testrepo/path1"));
cl_assert(git_path_isfile("./testrepo/path0/file0"));
opts.checkout_strategy =
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_ALLOW_CONFLICTS;
cl_git_pass(git_checkout_index(g_repo, index, &opts));
cl_assert(git_path_isfile("./testrepo/path1"));
cl_assert(git_path_isfile("./testrepo/path0/file0"));
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, index, &opts));
cl_assert(git_path_isfile("./testrepo/path0"));
cl_assert(git_path_isfile("./testrepo/path1/file1"));
git_index_free(index);
}
void test_checkout_index__validates_struct_version(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
const git_error *err;
opts.version = 1024;
cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
err = git_error_last();
cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
opts.version = 0;
git_error_clear();
cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
err = git_error_last();
cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
}
void test_checkout_index__can_update_prefixed_files(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
cl_git_mkfile("./testrepo/READ", "content\n");
cl_git_mkfile("./testrepo/README.after", "content\n");
cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));
opts.checkout_strategy =
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_REMOVE_UNTRACKED;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
/* remove untracked will remove the .gitattributes file before the blobs
* were created, so they will have had crlf filtering applied on Windows
*/
check_file_contents_nocr("./testrepo/README", "hey there\n");
check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
cl_assert(!git_path_exists("testrepo/READ"));
cl_assert(!git_path_exists("testrepo/README.after"));
cl_assert(!git_path_exists("testrepo/branch_file"));
cl_assert(!git_path_exists("testrepo/branch_file.txt.after"));
}
void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
{
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
}
void test_checkout_index__issue_1397(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
}
void test_checkout_index__target_directory(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_counts cts;
memset(&cts, 0, sizeof(cts));
opts.checkout_strategy = GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING;
opts.target_directory = "alternative";
cl_assert(!git_path_isdir("alternative"));
opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
opts.notify_cb = checkout_count_callback;
opts.notify_payload = &cts;
/* create some files that *would* conflict if we were using the wd */
cl_git_mkfile("testrepo/README", "I'm in the way!\n");
cl_git_mkfile("testrepo/new.txt", "my new file\n");
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert_equal_i(0, cts.n_untracked);
cl_assert_equal_i(0, cts.n_ignored);
cl_assert_equal_i(4, cts.n_updates);
check_file_contents("./alternative/README", "hey there\n");
check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
check_file_contents("./alternative/new.txt", "my new file\n");
cl_git_pass(git_futils_rmdir_r(
"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_checkout_index__target_directory_from_bare(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_index *index;
git_object *head = NULL;
checkout_counts cts;
memset(&cts, 0, sizeof(cts));
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert(git_repository_is_bare(g_repo));
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
cl_git_pass(git_index_write(index));
git_index_free(index);
opts.checkout_strategy = GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING;
opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
opts.notify_cb = checkout_count_callback;
opts.notify_payload = &cts;
/* fail to checkout a bare repo */
cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
opts.target_directory = "alternative";
cl_assert(!git_path_isdir("alternative"));
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert_equal_i(0, cts.n_untracked);
cl_assert_equal_i(0, cts.n_ignored);
cl_assert_equal_i(3, cts.n_updates);
/* files will have been filtered if needed, so strip CR */
check_file_contents_nocr("./alternative/README", "hey there\n");
check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
check_file_contents_nocr("./alternative/new.txt", "my new file\n");
cl_git_pass(git_futils_rmdir_r(
"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
git_object_free(head);
}
void test_checkout_index__can_get_repo_from_index(void)
{
git_index *index;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_checkout_index(NULL, index, &opts));
check_file_contents("./testrepo/README", "hey there\n");
check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
check_file_contents("./testrepo/new.txt", "my new file\n");
git_index_free(index);
}
static void add_conflict(git_index *index, const char *path)
{
git_index_entry entry;
memset(&entry, 0, sizeof(git_index_entry));
entry.mode = 0100644;
entry.path = path;
git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
cl_git_pass(git_index_add(index, &entry));
git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
cl_git_pass(git_index_add(index, &entry));
git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
cl_git_pass(git_index_add(index, &entry));
}
void test_checkout_index__writes_conflict_file(void)
{
git_index *index;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf conflicting_buf = GIT_BUF_INIT;
cl_git_pass(git_repository_index(&index, g_repo));
add_conflict(index, "conflicting.txt");
cl_git_pass(git_index_write(index));
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
cl_assert(strcmp(conflicting_buf.ptr,
"<<<<<<< ours\n"
"this file is changed in master and branch\n"
"=======\n"
"this file is changed in branch and master\n"
">>>>>>> theirs\n") == 0);
git_buf_dispose(&conflicting_buf);
git_index_free(index);
}
void test_checkout_index__adding_conflict_removes_stage_0(void)
{
git_index *new_index, *index;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_git_pass(git_index_new(&new_index));
add_conflict(new_index, "new.txt");
cl_git_pass(git_checkout_index(g_repo, new_index, &opts));
cl_git_pass(git_repository_index(&index, g_repo));
cl_assert(git_index_get_bypath(index, "new.txt", 0) == NULL);
cl_assert(git_index_get_bypath(index, "new.txt", 1) != NULL);
cl_assert(git_index_get_bypath(index, "new.txt", 2) != NULL);
cl_assert(git_index_get_bypath(index, "new.txt", 3) != NULL);
git_index_free(index);
git_index_free(new_index);
}
void test_checkout_index__conflicts_honor_coreautocrlf(void)
{
#ifdef GIT_WIN32
git_index *index;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf conflicting_buf = GIT_BUF_INIT;
cl_git_pass(p_unlink("./testrepo/.gitattributes"));
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_pass(git_repository_index(&index, g_repo));
add_conflict(index, "conflicting.txt");
cl_git_pass(git_index_write(index));
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
cl_assert(strcmp(conflicting_buf.ptr,
"<<<<<<< ours\r\n"
"this file is changed in master and branch\r\n"
"=======\r\n"
"this file is changed in branch and master\r\n"
">>>>>>> theirs\r\n") == 0);
git_buf_dispose(&conflicting_buf);
git_index_free(index);
#endif
}
+387
View File
@@ -0,0 +1,387 @@
#include "clar_libgit2.h"
#include "checkout_helpers.h"
#include "git2/checkout.h"
#include "repository.h"
#include "buffer.h"
#include "futils.h"
static const char *repo_name = "nasty";
static git_repository *repo;
static git_checkout_options checkout_opts;
void test_checkout_nasty__initialize(void)
{
repo = cl_git_sandbox_init(repo_name);
GIT_INIT_STRUCTURE(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
}
void test_checkout_nasty__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void test_checkout_passes(const char *refname, const char *filename)
{
git_oid commit_id;
git_commit *commit;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, repo_name, filename));
cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
opts.checkout_strategy = GIT_CHECKOUT_FORCE |
GIT_CHECKOUT_DONT_UPDATE_INDEX;
cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
cl_assert(!git_path_exists(path.ptr));
git_commit_free(commit);
git_buf_dispose(&path);
}
static void test_checkout_fails(const char *refname, const char *filename)
{
git_oid commit_id;
git_commit *commit;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, repo_name, filename));
cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_fail(git_checkout_tree(repo, (const git_object *)commit, &opts));
cl_assert(!git_path_exists(path.ptr));
git_commit_free(commit);
git_buf_dispose(&path);
}
/* A tree that contains ".git" as a tree, with a blob inside
* (".git/foobar").
*/
void test_checkout_nasty__dotgit_tree(void)
{
test_checkout_fails("refs/heads/dotgit_tree", ".git/foobar");
}
/* A tree that contains ".GIT" as a tree, with a blob inside
* (".GIT/foobar").
*/
void test_checkout_nasty__dotcapitalgit_tree(void)
{
test_checkout_fails("refs/heads/dotcapitalgit_tree", ".GIT/foobar");
}
/* A tree that contains a tree ".", with a blob inside ("./foobar").
*/
void test_checkout_nasty__dot_tree(void)
{
test_checkout_fails("refs/heads/dot_tree", "foobar");
}
/* A tree that contains a tree ".", with a tree ".git", with a blob
* inside ("./.git/foobar").
*/
void test_checkout_nasty__dot_dotgit_tree(void)
{
test_checkout_fails("refs/heads/dot_dotgit_tree", ".git/foobar");
}
/* A tree that contains a tree, with a tree "..", with a tree ".git", with a
* blob inside ("foo/../.git/foobar").
*/
void test_checkout_nasty__dotdot_dotgit_tree(void)
{
test_checkout_fails("refs/heads/dotdot_dotgit_tree", ".git/foobar");
}
/* A tree that contains a tree, with a tree "..", with a blob inside
* ("foo/../foobar").
*/
void test_checkout_nasty__dotdot_tree(void)
{
test_checkout_fails("refs/heads/dotdot_tree", "foobar");
}
/* A tree that contains a blob with the rogue name ".git/foobar" */
void test_checkout_nasty__dotgit_path(void)
{
test_checkout_fails("refs/heads/dotgit_path", ".git/foobar");
}
/* A tree that contains a blob with the rogue name ".GIT/foobar" */
void test_checkout_nasty__dotcapitalgit_path(void)
{
test_checkout_fails("refs/heads/dotcapitalgit_path", ".GIT/foobar");
}
/* A tree that contains a blob with the rogue name "./.git/foobar" */
void test_checkout_nasty__dot_dotgit_path(void)
{
test_checkout_fails("refs/heads/dot_dotgit_path", ".git/foobar");
}
/* A tree that contains a blob with the rogue name "./.GIT/foobar" */
void test_checkout_nasty__dot_dotcapitalgit_path(void)
{
test_checkout_fails("refs/heads/dot_dotcapitalgit_path", ".GIT/foobar");
}
/* A tree that contains a blob with the rogue name "foo/../.git/foobar" */
void test_checkout_nasty__dotdot_dotgit_path(void)
{
test_checkout_fails("refs/heads/dotdot_dotgit_path", ".git/foobar");
}
/* A tree that contains a blob with the rogue name "foo/../.GIT/foobar" */
void test_checkout_nasty__dotdot_dotcapitalgit_path(void)
{
test_checkout_fails("refs/heads/dotdot_dotcapitalgit_path", ".GIT/foobar");
}
/* A tree that contains a blob with the rogue name "foo/." */
void test_checkout_nasty__dot_path(void)
{
test_checkout_fails("refs/heads/dot_path", "./foobar");
}
/* A tree that contains a blob with the rogue name "foo/." */
void test_checkout_nasty__dot_path_two(void)
{
test_checkout_fails("refs/heads/dot_path_two", "foo/.");
}
/* A tree that contains a blob with the rogue name "foo/../foobar" */
void test_checkout_nasty__dotdot_path(void)
{
test_checkout_fails("refs/heads/dotdot_path", "foobar");
}
/* A tree that contains an entry with a backslash ".git\foobar" */
void test_checkout_nasty__dotgit_backslash_path(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
#endif
}
/* A tree that contains an entry with a backslash ".GIT\foobar" */
void test_checkout_nasty__dotcapitalgit_backslash_path(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
#endif
}
/* A tree that contains an entry with a backslash ".\.GIT\foobar" */
void test_checkout_nasty__dot_backslash_dotcapitalgit_path(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dot_backslash_dotcapitalgit_path", ".GIT/foobar");
#endif
}
/* A tree that contains an entry ".git.", because Win32 APIs will drop the
* trailing slash.
*/
void test_checkout_nasty__dot_git_dot(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
#endif
}
/* A tree that contains an entry "git~1", because that is typically the
* short name for ".git".
*/
void test_checkout_nasty__git_tilde1(void)
{
test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
test_checkout_fails("refs/heads/git_tilde1", "git~1/foobar");
}
/* A tree that contains an entry "git~2", when we have forced the short
* name for ".git" into "GIT~2".
*/
void test_checkout_nasty__git_custom_shortname(void)
{
#ifdef GIT_WIN32
if (!cl_sandbox_supports_8dot3())
clar__skip();
cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
test_checkout_fails("refs/heads/git_tilde2", ".git/foobar");
#endif
}
/* A tree that contains an entry "git~3", which should be allowed, since
* it is not the typical short name ("GIT~1") or the actual short name
* ("GIT~2") for ".git".
*/
void test_checkout_nasty__only_looks_like_a_git_shortname(void)
{
#ifdef GIT_WIN32
git_oid commit_id;
git_commit *commit;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
cl_git_pass(git_reference_name_to_id(&commit_id, repo, "refs/heads/git_tilde3"));
cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
cl_assert(git_path_exists("nasty/git~3/foobar"));
git_commit_free(commit);
#endif
}
/* A tree that contains an entry "git:", because Win32 APIs will reject
* that as looking too similar to a drive letter.
*/
void test_checkout_nasty__dot_git_colon(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dot_git_colon", ".git/foobar");
#endif
}
/* A tree that contains an entry "git:foo", because Win32 APIs will turn
* that into ".git".
*/
void test_checkout_nasty__dot_git_colon_stuff(void)
{
#ifdef GIT_WIN32
test_checkout_fails("refs/heads/dot_git_colon_stuff", ".git/foobar");
#endif
}
/* A tree that contains an entry ".git::$INDEX_ALLOCATION" because NTFS
* will interpret that as a synonym to ".git", even when mounted via SMB
* on macOS.
*/
void test_checkout_nasty__dotgit_alternate_data_stream(void)
{
test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git/dummy-file");
test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git::$INDEX_ALLOCATION/dummy-file");
}
/* Trees that contains entries with a tree ".git" that contain
* byte sequences:
* { 0xe2, 0x80, 0x8c }
* { 0xe2, 0x80, 0x8d }
* { 0xe2, 0x80, 0x8e }
* { 0xe2, 0x80, 0x8f }
* { 0xe2, 0x80, 0xaa }
* { 0xe2, 0x80, 0xab }
* { 0xe2, 0x80, 0xac }
* { 0xe2, 0x80, 0xad }
* { 0xe2, 0x81, 0xae }
* { 0xe2, 0x81, 0xaa }
* { 0xe2, 0x81, 0xab }
* { 0xe2, 0x81, 0xac }
* { 0xe2, 0x81, 0xad }
* { 0xe2, 0x81, 0xae }
* { 0xe2, 0x81, 0xaf }
* { 0xef, 0xbb, 0xbf }
* Because these map to characters that HFS filesystems "ignore". Thus
* ".git<U+200C>" will map to ".git".
*/
void test_checkout_nasty__dot_git_hfs_ignorable(void)
{
#ifdef __APPLE__
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
#endif
}
void test_checkout_nasty__honors_core_protecthfs(void)
{
cl_repo_set_bool(repo, "core.protectHFS", true);
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
}
void test_checkout_nasty__honors_core_protectntfs(void)
{
cl_repo_set_bool(repo, "core.protectNTFS", true);
test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
}
void test_checkout_nasty__symlink1(void)
{
test_checkout_passes("refs/heads/symlink1", ".git/foobar");
}
void test_checkout_nasty__symlink2(void)
{
test_checkout_passes("refs/heads/symlink2", ".git/foobar");
}
void test_checkout_nasty__symlink3(void)
{
test_checkout_passes("refs/heads/symlink3", ".git/foobar");
}
void test_checkout_nasty__gitmodules_symlink(void)
{
cl_repo_set_bool(repo, "core.protectHFS", true);
test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
cl_repo_set_bool(repo, "core.protectHFS", false);
cl_repo_set_bool(repo, "core.protectNTFS", true);
test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
cl_repo_set_bool(repo, "core.protectNTFS", false);
test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
}
File diff suppressed because it is too large Load Diff
+335
View File
@@ -0,0 +1,335 @@
#include "clar_libgit2.h"
#include "diff_generate.h"
#include "git2/checkout.h"
#include "path.h"
#include "posix.h"
#include "futils.h"
static git_repository *g_repo = NULL;
/*
From the test repo used for this test:
--------------------------------------
This is a test repo for libgit2 where tree entries have type changes
The key types that could be found in tree entries are:
1 - GIT_FILEMODE_NEW = 0000000
2 - GIT_FILEMODE_TREE = 0040000
3 - GIT_FILEMODE_BLOB = 0100644
4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
5 - GIT_FILEMODE_LINK = 0120000
6 - GIT_FILEMODE_COMMIT = 0160000
I will try to have every type of transition somewhere in the history
of this repo.
Commits
-------
Initial commit - a(1) b(1) c(1) d(1) e(1)
Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
*/
static const char *g_typechange_oids[] = {
"79b9f23e85f55ea36a472a902e875bc1121a94cb",
"9bdb75b73836a99e3dbeea640a81de81031fdc29",
"0e7ed140b514b8cae23254cb8656fe1674403aff",
"9d0235c7a7edc0889a18f97a42ee6db9fe688447",
"9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
"1b63caae4a5ca96f78e8dfefc376c6a39a142475",
"6eae26c90e8ccc4d16208972119c40635489c6f0",
NULL
};
static bool g_typechange_empty[] = {
true, false, false, false, false, false, true, true
};
static const int g_typechange_expected_conflicts[] = {
1, 2, 3, 3, 2, 3, 2
};
static const int g_typechange_expected_untracked[] = {
6, 4, 3, 2, 3, 2, 5
};
void test_checkout_typechange__initialize(void)
{
g_repo = cl_git_sandbox_init("typechanges");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
}
void test_checkout_typechange__cleanup(void)
{
cl_git_sandbox_cleanup();
cl_fixture_cleanup("submod2_target");
}
static void assert_file_exists(const char *path)
{
cl_assert_(git_path_isfile(path), path);
}
static void assert_dir_exists(const char *path)
{
cl_assert_(git_path_isdir(path), path);
}
static void assert_workdir_matches_tree(
git_repository *repo, const git_oid *id, const char *root, bool recurse)
{
git_object *obj;
git_tree *tree;
size_t i, max_i;
git_buf path = GIT_BUF_INIT;
if (!root)
root = git_repository_workdir(repo);
cl_assert(root);
cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJECT_ANY));
cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJECT_TREE));
git_object_free(obj);
max_i = git_tree_entrycount(tree);
for (i = 0; i < max_i; ++i) {
const git_tree_entry *te = git_tree_entry_byindex(tree, i);
cl_assert(te);
cl_git_pass(git_buf_joinpath(&path, root, git_tree_entry_name(te)));
switch (git_tree_entry_type(te)) {
case GIT_OBJECT_COMMIT:
assert_dir_exists(path.ptr);
break;
case GIT_OBJECT_TREE:
assert_dir_exists(path.ptr);
if (recurse)
assert_workdir_matches_tree(
repo, git_tree_entry_id(te), path.ptr, true);
break;
case GIT_OBJECT_BLOB:
switch (git_tree_entry_filemode(te)) {
case GIT_FILEMODE_BLOB:
case GIT_FILEMODE_BLOB_EXECUTABLE:
assert_file_exists(path.ptr);
/* because of cross-platform, don't confirm exec bit yet */
break;
case GIT_FILEMODE_LINK:
cl_assert_(git_path_exists(path.ptr), path.ptr);
/* because of cross-platform, don't confirm link yet */
break;
default:
cl_assert(false); /* really?! */
}
break;
default:
cl_assert(false); /* really?!! */
}
}
git_tree_free(tree);
git_buf_dispose(&path);
}
void test_checkout_typechange__checkout_typechanges_safe(void)
{
int i;
git_object *obj;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_git_pass(
git_repository_set_head_detached(g_repo, git_object_id(obj)));
assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
git_object_free(obj);
if (!g_typechange_empty[i]) {
cl_assert(git_path_isdir("typechanges"));
cl_assert(git_path_exists("typechanges/a"));
cl_assert(git_path_exists("typechanges/b"));
cl_assert(git_path_exists("typechanges/c"));
cl_assert(git_path_exists("typechanges/d"));
cl_assert(git_path_exists("typechanges/e"));
} else {
cl_assert(git_path_isdir("typechanges"));
cl_assert(!git_path_exists("typechanges/a"));
cl_assert(!git_path_exists("typechanges/b"));
cl_assert(!git_path_exists("typechanges/c"));
cl_assert(!git_path_exists("typechanges/d"));
cl_assert(!git_path_exists("typechanges/e"));
}
}
}
typedef struct {
int conflicts;
int dirty;
int updates;
int untracked;
int ignored;
} notify_counts;
static int notify_counter(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload)
{
notify_counts *cts = payload;
GIT_UNUSED(path);
GIT_UNUSED(baseline);
GIT_UNUSED(target);
GIT_UNUSED(workdir);
switch (why) {
case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break;
case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break;
case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break;
case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break;
case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break;
default: break;
}
return 0;
}
static void force_create_file(const char *file)
{
int error = git_futils_rmdir_r(file, NULL,
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
cl_assert(!error || error == GIT_ENOTFOUND);
cl_git_pass(git_futils_mkpath2file(file, 0777));
cl_git_rewritefile(file, "yowza!!");
}
static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload)
{
git_buf submodulepath = GIT_BUF_INIT;
git_buf dirtypath = GIT_BUF_INIT;
git_repository *submodule_repo;
GIT_UNUSED(name);
GIT_UNUSED(payload);
/* remove submodule directory in preparation for init and repo_init */
cl_git_pass(git_buf_joinpath(
&submodulepath,
git_repository_workdir(g_repo),
git_submodule_path(sm)
));
git_futils_rmdir_r(git_buf_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES);
/* initialize submodule's repository */
cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0));
/* create a file in the submodule workdir to make it dirty */
cl_git_pass(
git_buf_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty"));
force_create_file(git_buf_cstr(&dirtypath));
git_buf_dispose(&dirtypath);
git_buf_dispose(&submodulepath);
git_repository_free(submodule_repo);
return 0;
}
void test_checkout_typechange__checkout_with_conflicts(void)
{
int i;
git_object *obj;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
notify_counts cts = {0};
opts.notify_flags =
GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED;
opts.notify_cb = notify_counter;
opts.notify_payload = &cts;
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
force_create_file("typechanges/a/blocker");
force_create_file("typechanges/b");
force_create_file("typechanges/c/sub/sub/file");
git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
force_create_file("typechanges/untracked");
cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL));
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
memset(&cts, 0, sizeof(cts));
cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]);
cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]);
cl_assert_equal_i(cts.dirty, 0);
cl_assert_equal_i(cts.updates, 0);
cl_assert_equal_i(cts.ignored, 0);
opts.checkout_strategy =
GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
memset(&cts, 0, sizeof(cts));
cl_assert(git_path_exists("typechanges/untracked"));
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_assert_equal_i(0, cts.conflicts);
cl_assert(!git_path_exists("typechanges/untracked"));
cl_git_pass(
git_repository_set_head_detached(g_repo, git_object_id(obj)));
assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
git_object_free(obj);
}
}
void test_checkout_typechange__status_char(void)
{
size_t i;
git_oid oid;
git_commit *commit;
git_diff *diff;
const git_diff_delta *delta;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
char expected[8] = {'M', 'M', 'R', 'T', 'D', 'R', 'A', 'R'};
git_oid_fromstr(&oid, "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a");
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
diffopts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
cl_git_pass(git_diff__commit(&diff, g_repo, commit, &diffopts));
cl_git_pass(git_diff_find_similar(diff, NULL));
for (i = 0; i < git_diff_num_deltas(diff); i++) {
delta = git_diff_get_delta(diff, i);
cl_assert_equal_i(expected[i], git_diff_status_char(delta->status));
}
git_diff_free(diff);
git_commit_free(commit);
}
+106
View File
@@ -0,0 +1,106 @@
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
#include "git2/cherrypick.h"
#include "../merge/merge_helpers.h"
#define TEST_REPO_PATH "cherrypick"
static git_repository *repo;
void test_cherrypick_bare__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_cherrypick_bare__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_cherrypick_bare__automerge(void)
{
git_commit *head = NULL, *commit = NULL;
git_index *index = NULL;
git_oid head_oid, cherry_oid;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
{ 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
{ 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
};
git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
git_oid_fromstr(&cherry_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 3));
git_index_free(index);
git_commit_free(head);
git_commit_free(commit);
}
void test_cherrypick_bare__conflicts(void)
{
git_commit *head = NULL, *commit = NULL;
git_index *index = NULL;
git_oid head_oid, cherry_oid;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
{ 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
{ 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
{ 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
};
git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 7));
git_index_free(index);
git_commit_free(head);
git_commit_free(commit);
}
void test_cherrypick_bare__orphan(void)
{
git_commit *head = NULL, *commit = NULL;
git_index *index = NULL;
git_oid head_oid, cherry_oid;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
{ 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
{ 0100644, "85a4a1d791973644f24c72f5e89420d3064cc452", 0, "file3.txt" },
{ 0100644, "9ccb9bf50c011fd58dcbaa65df917bf79539717f", 0, "orphan.txt" },
};
git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
git_oid_fromstr(&cherry_oid, "74f06b5bfec6d33d7264f73606b57a7c0b963819");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 4));
git_index_free(index);
git_commit_free(head);
git_commit_free(commit);
}
+470
View File
@@ -0,0 +1,470 @@
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
#include "git2/cherrypick.h"
#include "../merge/merge_helpers.h"
#define TEST_REPO_PATH "cherrypick"
static git_repository *repo;
static git_index *repo_index;
/* Fixture setup and teardown */
void test_cherrypick_workdir__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_repository_index(&repo_index, repo);
}
void test_cherrypick_workdir__cleanup(void)
{
git_index_free(repo_index);
cl_git_sandbox_cleanup();
}
/* git reset --hard d3d77487660ee3c0194ee01dc5eaf478782b1c7e
* git cherry-pick cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick 964ea3da044d9083181a88ba6701de9e35778bf4
* git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d
*/
void test_cherrypick_workdir__automerge(void)
{
git_oid head_oid;
git_signature *signature = NULL;
size_t i;
const char *cherrypick_oids[] = {
"cfc4f0999a8367568e049af4f72e452d40828a15",
"964ea3da044d9083181a88ba6701de9e35778bf4",
"a43a050c588d4e92f11a6b139680923e9728477d",
};
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
{ 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
{ 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
{ 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
{ 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" },
{ 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
{ 0100644, "f06427bee380364bc7e0cb26a9245158e4726ce0", 0, "file1.txt" },
{ 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" },
{ 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
};
cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0));
git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e");
for (i = 0; i < 3; ++i) {
git_commit *head = NULL, *commit = NULL;
git_oid cherry_oid, cherrypicked_oid, cherrypicked_tree_oid;
git_tree *cherrypicked_tree = NULL;
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, cherrypick_oids[i]);
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, NULL));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_git_pass(git_index_write_tree(&cherrypicked_tree_oid, repo_index));
cl_git_pass(git_tree_lookup(&cherrypicked_tree, repo, &cherrypicked_tree_oid));
cl_git_pass(git_commit_create(&cherrypicked_oid, repo, "HEAD", signature, signature, NULL,
"Cherry picked!", cherrypicked_tree, 1, (const git_commit **)&head));
cl_assert(merge_test_index(repo_index, merge_index_entries + i * 3, 3));
git_oid_cpy(&head_oid, &cherrypicked_oid);
git_tree_free(cherrypicked_tree);
git_commit_free(head);
git_commit_free(commit);
}
git_signature_free(signature);
}
/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d*/
void test_cherrypick_workdir__empty_result(void)
{
git_oid head_oid;
git_signature *signature = NULL;
git_commit *head = NULL, *commit = NULL;
git_oid cherry_oid;
const char *cherrypick_oid = "a43a050c588d4e92f11a6b139680923e9728477d";
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt" },
};
cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0));
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
/* Create an untracked file that should not conflict */
cl_git_mkfile(TEST_REPO_PATH "/file4.txt", "");
cl_assert(git_path_exists(TEST_REPO_PATH "/file4.txt"));
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, cherrypick_oid);
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, NULL));
/* The resulting tree should not have changed, the change was already on HEAD */
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(head);
git_commit_free(commit);
git_signature_free(signature);
}
/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8
* git cherry-pick e9b63f3655b2ad80c0ff587389b5a9589a3a7110
*/
void test_cherrypick_workdir__conflicts(void)
{
git_commit *head = NULL, *commit = NULL;
git_oid head_oid, cherry_oid;
git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
{ 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
{ 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
{ 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
};
git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, NULL));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_assert(merge_test_index(repo_index, merge_index_entries, 7));
cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_assert(strcmp(git_buf_cstr(&mergemsg_buf),
"Change all files\n" \
"\n" \
"Conflicts:\n" \
"\tfile2.txt\n" \
"\tfile3.txt\n") == 0);
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
TEST_REPO_PATH "/file2.txt"));
cl_assert(strcmp(git_buf_cstr(&conflicting_buf),
"!File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"File 2!!\n" \
"File 2\n" \
"File 2\n" \
"File 2\n" \
"<<<<<<< HEAD\n" \
"File 2\n" \
"=======\n" \
"File 2!\n" \
"File 2\n" \
"File 2!\n" \
">>>>>>> e9b63f3... Change all files\n") == 0);
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
TEST_REPO_PATH "/file3.txt"));
cl_assert(strcmp(git_buf_cstr(&conflicting_buf),
"!File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"File 3!!\n" \
"File 3\n" \
"File 3\n" \
"File 3\n" \
"<<<<<<< HEAD\n" \
"=======\n" \
"File 3!\n" \
"File 3!\n" \
">>>>>>> e9b63f3... Change all files\n") == 0);
git_commit_free(commit);
git_commit_free(head);
git_buf_dispose(&mergemsg_buf);
git_buf_dispose(&conflicting_buf);
}
/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8
* git cherry-pick -X ours e9b63f3655b2ad80c0ff587389b5a9589a3a7110
*/
void test_cherrypick_workdir__conflict_use_ours(void)
{
git_commit *head = NULL, *commit = NULL;
git_oid head_oid, cherry_oid;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
{ 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
{ 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
{ 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
};
struct merge_index_entry merge_filesystem_entries[] = {
{ 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
{ 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 0, "file2.txt" },
{ 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 0, "file3.txt" },
};
/* leave the index in a conflicted state, but checkout "ours" to the workdir */
opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 7));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
/* resolve conflicts in the index by taking "ours" */
opts.merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS;
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_filesystem_entries, 3));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc
*/
void test_cherrypick_workdir__rename(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" },
};
opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
opts.merge_opts.rename_threshold = 50;
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard 44cd2ed2052c9c68f9a439d208e9614dc2a55c70
* git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc
*/
void test_cherrypick_workdir__both_renamed(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_buf mergemsg_buf = GIT_BUF_INIT;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
{ 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 1, "file3.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt.renamed" },
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" },
};
opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
opts.merge_opts.rename_threshold = 50;
git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 5));
cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_assert(strcmp(git_buf_cstr(&mergemsg_buf),
"Renamed file3.txt -> file3.txt.renamed\n" \
"\n" \
"Conflicts:\n" \
"\tfile3.txt\n" \
"\tfile3.txt.renamed\n" \
"\tfile3.txt.renamed_on_branch\n") == 0);
git_buf_dispose(&mergemsg_buf);
git_commit_free(commit);
git_commit_free(head);
}
void test_cherrypick_workdir__nonmerge_fails_mainline_specified(void)
{
git_reference *head;
git_commit *commit;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJECT_COMMIT));
opts.mainline = 1;
cl_must_fail(git_cherrypick(repo, commit, &opts));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
git_reference_free(head);
git_commit_free(commit);
}
/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick abe4603bc7cd5b8167a267e0e2418fd2348f8cff
*/
void test_cherrypick_workdir__merge_fails_without_mainline_specified(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_must_fail(git_cherrypick(repo, commit, NULL));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick -m1 abe4603bc7cd5b8167a267e0e2418fd2348f8cff
*/
void test_cherrypick_workdir__merge_first_parent(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "f90f9dcbdac2cce5cc166346160e19cb693ef4e8", 0, "file1.txt" },
{ 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 0, "file2.txt" },
{ 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 0, "file3.txt" },
};
opts.mainline = 1;
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
* git cherry-pick -m2 abe4603bc7cd5b8167a267e0e2418fd2348f8cff
*/
void test_cherrypick_workdir__merge_second_parent(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "487434cace79238a7091e2220611d4f20a765690", 0, "file1.txt" },
{ 0100644, "e5183bfd18e3a0a691fadde2f0d5610b73282d31", 0, "file2.txt" },
{ 0100644, "409a1bec58bf35348e8b62b72bb9c1f45cf5a587", 0, "file3.txt" },
};
opts.mainline = 2;
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(commit);
git_commit_free(head);
}
+770
View File
@@ -0,0 +1,770 @@
/*
* Copyright (c) Vicent Marti. All rights reserved.
*
* This file is part of clar, distributed under the ISC license.
* For full terms see the included COPYING file.
*/
#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>
#include <wchar.h>
/* required for sandboxing */
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
# /* included for fs.h */
# include "win32/path_w32.h"
# include <windows.h>
# include <io.h>
# include <shellapi.h>
# include <direct.h>
# define _MAIN_CC __cdecl
# ifndef stat
# define stat(path, st) _stat(path, st)
# endif
# ifndef mkdir
# define mkdir(path, mode) _mkdir(path)
# endif
# ifndef chdir
# define chdir(path) _chdir(path)
# endif
# ifndef access
# define access(path, mode) _access(path, mode)
# endif
# ifndef strdup
# define strdup(str) _strdup(str)
# endif
# ifndef strcasecmp
# define strcasecmp(a,b) _stricmp(a,b)
# endif
# ifndef __MINGW32__
# pragma comment(lib, "shell32")
# ifndef strncpy
# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
# endif
# ifndef W_OK
# define W_OK 02
# endif
# ifndef S_ISDIR
# define S_ISDIR(x) ((x & _S_IFDIR) != 0)
# endif
# define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
# else
# define p_snprintf snprintf
# endif
# ifndef PRIuZ
# define PRIuZ "Iu"
# endif
# ifndef PRIxZ
# define PRIxZ "Ix"
# endif
# if defined(_MSC_VER) || defined(__MINGW32__)
typedef struct stat STAT_T;
# else
typedef struct _stat STAT_T;
# endif
#else
# include <sys/wait.h> /* waitpid(2) */
# include <unistd.h>
# define _MAIN_CC
# define p_snprintf snprintf
# ifndef PRIuZ
# define PRIuZ "zu"
# endif
# ifndef PRIxZ
# define PRIxZ "zx"
# endif
typedef struct stat STAT_T;
#endif
#include "clar.h"
static void fs_rm(const char *_source);
static void fs_copy(const char *_source, const char *dest);
static const char *
fixture_path(const char *base, const char *fixture_name);
struct clar_error {
const char *file;
size_t line_number;
const char *error_msg;
char *description;
struct clar_error *next;
};
struct clar_explicit {
size_t suite_idx;
const char *filter;
struct clar_explicit *next;
};
struct clar_report {
const char *test;
int test_number;
const char *suite;
enum cl_test_status status;
struct clar_error *errors;
struct clar_error *last_error;
struct clar_report *next;
};
struct clar_summary {
const char *filename;
FILE *fp;
};
static struct {
enum cl_test_status test_status;
const char *active_test;
const char *active_suite;
int total_skipped;
int total_errors;
int tests_ran;
int suites_ran;
int report_errors_only;
int exit_on_error;
int report_suite_names;
int write_summary;
char *summary_filename;
struct clar_summary *summary;
struct clar_explicit *explicit;
struct clar_explicit *last_explicit;
struct clar_report *reports;
struct clar_report *last_report;
void (*local_cleanup)(void *);
void *local_cleanup_payload;
jmp_buf trampoline;
int trampoline_enabled;
cl_trace_cb *pfn_trace_cb;
void *trace_payload;
} _clar;
struct clar_func {
const char *name;
void (*ptr)(void);
};
struct clar_suite {
const char *name;
struct clar_func initialize;
struct clar_func cleanup;
const struct clar_func *tests;
size_t test_count;
int enabled;
};
/* From clar_print_*.c */
static void clar_print_init(int test_count, int suite_count, const char *suite_names);
static void clar_print_shutdown(int test_count, int suite_count, int error_count);
static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed);
static void clar_print_onsuite(const char *suite_name, int suite_index);
static void clar_print_onabort(const char *msg, ...);
/* From clar_sandbox.c */
static void clar_unsandbox(void);
static int clar_sandbox(void);
/* From summary.h */
static struct clar_summary *clar_summary_init(const char *filename);
static int clar_summary_shutdown(struct clar_summary *fp);
/* Load the declarations for the test suite */
#include "clar.suite"
#define CL_TRACE(ev) \
do { \
if (_clar.pfn_trace_cb) \
_clar.pfn_trace_cb(ev, \
_clar.active_suite, \
_clar.active_test, \
_clar.trace_payload); \
} while (0)
void cl_trace_register(cl_trace_cb *cb, void *payload)
{
_clar.pfn_trace_cb = cb;
_clar.trace_payload = payload;
}
/* Core test functions */
static void
clar_report_errors(struct clar_report *report)
{
struct clar_error *error;
int i = 1;
for (error = report->errors; error; error = error->next)
clar_print_error(i++, _clar.last_report, error);
}
static void
clar_report_all(void)
{
struct clar_report *report;
struct clar_error *error;
int i = 1;
for (report = _clar.reports; report; report = report->next) {
if (report->status != CL_TEST_FAILURE)
continue;
for (error = report->errors; error; error = error->next)
clar_print_error(i++, report, error);
}
}
static void
clar_run_test(
const struct clar_func *test,
const struct clar_func *initialize,
const struct clar_func *cleanup)
{
_clar.trampoline_enabled = 1;
CL_TRACE(CL_TRACE__TEST__BEGIN);
if (setjmp(_clar.trampoline) == 0) {
if (initialize->ptr != NULL)
initialize->ptr();
CL_TRACE(CL_TRACE__TEST__RUN_BEGIN);
test->ptr();
CL_TRACE(CL_TRACE__TEST__RUN_END);
}
_clar.trampoline_enabled = 0;
if (_clar.last_report->status == CL_TEST_NOTRUN)
_clar.last_report->status = CL_TEST_OK;
if (_clar.local_cleanup != NULL)
_clar.local_cleanup(_clar.local_cleanup_payload);
if (cleanup->ptr != NULL)
cleanup->ptr();
CL_TRACE(CL_TRACE__TEST__END);
_clar.tests_ran++;
/* remove any local-set cleanup methods */
_clar.local_cleanup = NULL;
_clar.local_cleanup_payload = NULL;
if (_clar.report_errors_only) {
clar_report_errors(_clar.last_report);
} else {
clar_print_ontest(test->name, _clar.tests_ran, _clar.last_report->status);
}
}
static void
clar_run_suite(const struct clar_suite *suite, const char *filter)
{
const struct clar_func *test = suite->tests;
size_t i, matchlen;
struct clar_report *report;
if (!suite->enabled)
return;
if (_clar.exit_on_error && _clar.total_errors)
return;
if (!_clar.report_errors_only)
clar_print_onsuite(suite->name, ++_clar.suites_ran);
_clar.active_suite = suite->name;
_clar.active_test = NULL;
CL_TRACE(CL_TRACE__SUITE_BEGIN);
if (filter) {
size_t suitelen = strlen(suite->name);
matchlen = strlen(filter);
if (matchlen <= suitelen) {
filter = NULL;
} else {
filter += suitelen;
while (*filter == ':')
++filter;
matchlen = strlen(filter);
}
}
for (i = 0; i < suite->test_count; ++i) {
if (filter && strncmp(test[i].name, filter, matchlen))
continue;
_clar.active_test = test[i].name;
report = calloc(1, sizeof(struct clar_report));
report->suite = _clar.active_suite;
report->test = _clar.active_test;
report->test_number = _clar.tests_ran;
report->status = CL_TEST_NOTRUN;
if (_clar.reports == NULL)
_clar.reports = report;
if (_clar.last_report != NULL)
_clar.last_report->next = report;
_clar.last_report = report;
clar_run_test(&test[i], &suite->initialize, &suite->cleanup);
if (_clar.exit_on_error && _clar.total_errors)
return;
}
_clar.active_test = NULL;
CL_TRACE(CL_TRACE__SUITE_END);
}
static void
clar_usage(const char *arg)
{
printf("Usage: %s [options]\n\n", arg);
printf("Options:\n");
printf(" -sname Run only the suite with `name` (can go to individual test name)\n");
printf(" -iname Include the suite with `name`\n");
printf(" -xname Exclude the suite with `name`\n");
printf(" -v Increase verbosity (show suite names)\n");
printf(" -q Only report tests that had an error\n");
printf(" -Q Quit as soon as a test fails\n");
printf(" -l Print suite names\n");
printf(" -r[filename] Write summary file (to the optional filename)\n");
exit(-1);
}
static void
clar_parse_args(int argc, char **argv)
{
int i;
/* Verify options before execute */
for (i = 1; i < argc; ++i) {
char *argument = argv[i];
if (argument[0] != '-' || argument[1] == '\0'
|| strchr("sixvqQlr", argument[1]) == NULL) {
clar_usage(argv[0]);
}
}
for (i = 1; i < argc; ++i) {
char *argument = argv[i];
switch (argument[1]) {
case 's':
case 'i':
case 'x': { /* given suite name */
int offset = (argument[2] == '=') ? 3 : 2, found = 0;
char action = argument[1];
size_t j, arglen, suitelen, cmplen;
argument += offset;
arglen = strlen(argument);
if (arglen == 0)
clar_usage(argv[0]);
for (j = 0; j < _clar_suite_count; ++j) {
suitelen = strlen(_clar_suites[j].name);
cmplen = (arglen < suitelen) ? arglen : suitelen;
if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
int exact = (arglen >= suitelen);
/* Do we have a real suite prefix separated by a
* trailing '::' or just a matching substring? */
if (arglen > suitelen && (argument[suitelen] != ':'
|| argument[suitelen + 1] != ':'))
continue;
++found;
if (!exact)
_clar.report_suite_names = 1;
switch (action) {
case 's': {
struct clar_explicit *explicit =
calloc(1, sizeof(struct clar_explicit));
assert(explicit);
explicit->suite_idx = j;
explicit->filter = argument;
if (_clar.explicit == NULL)
_clar.explicit = explicit;
if (_clar.last_explicit != NULL)
_clar.last_explicit->next = explicit;
_clar_suites[j].enabled = 1;
_clar.last_explicit = explicit;
break;
}
case 'i': _clar_suites[j].enabled = 1; break;
case 'x': _clar_suites[j].enabled = 0; break;
}
if (exact)
break;
}
}
if (!found) {
clar_print_onabort("No suite matching '%s' found.\n", argument);
exit(-1);
}
break;
}
case 'q':
_clar.report_errors_only = 1;
break;
case 'Q':
_clar.exit_on_error = 1;
break;
case 'l': {
size_t j;
printf("Test suites (use -s<name> to run just one):\n");
for (j = 0; j < _clar_suite_count; ++j)
printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
exit(0);
}
case 'v':
_clar.report_suite_names = 1;
break;
case 'r':
_clar.write_summary = 1;
free(_clar.summary_filename);
_clar.summary_filename = strdup(*(argument + 2) ? (argument + 2) : "summary.xml");
break;
default:
assert(!"Unexpected commandline argument!");
}
}
}
void
clar_test_init(int argc, char **argv)
{
clar_print_init(
(int)_clar_callback_count,
(int)_clar_suite_count,
""
);
if ((_clar.summary_filename = getenv("CLAR_SUMMARY")) != NULL) {
_clar.write_summary = 1;
_clar.summary_filename = strdup(_clar.summary_filename);
}
if (argc > 1)
clar_parse_args(argc, argv);
if (_clar.write_summary &&
!(_clar.summary = clar_summary_init(_clar.summary_filename))) {
clar_print_onabort("Failed to open the summary file\n");
exit(-1);
}
if (clar_sandbox() < 0) {
clar_print_onabort("Failed to sandbox the test runner.\n");
exit(-1);
}
}
int
clar_test_run(void)
{
size_t i;
struct clar_explicit *explicit;
if (_clar.explicit) {
for (explicit = _clar.explicit; explicit; explicit = explicit->next)
clar_run_suite(&_clar_suites[explicit->suite_idx], explicit->filter);
} else {
for (i = 0; i < _clar_suite_count; ++i)
clar_run_suite(&_clar_suites[i], NULL);
}
return _clar.total_errors;
}
void
clar_test_shutdown(void)
{
struct clar_explicit *explicit, *explicit_next;
struct clar_report *report, *report_next;
clar_print_shutdown(
_clar.tests_ran,
(int)_clar_suite_count,
_clar.total_errors
);
clar_unsandbox();
if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) {
clar_print_onabort("Failed to write the summary file\n");
exit(-1);
}
for (explicit = _clar.explicit; explicit; explicit = explicit_next) {
explicit_next = explicit->next;
free(explicit);
}
for (report = _clar.reports; report; report = report_next) {
report_next = report->next;
free(report);
}
free(_clar.summary_filename);
}
int
clar_test(int argc, char **argv)
{
int errors;
clar_test_init(argc, argv);
errors = clar_test_run();
clar_test_shutdown();
return errors;
}
static void abort_test(void)
{
if (!_clar.trampoline_enabled) {
clar_print_onabort(
"Fatal error: a cleanup method raised an exception.");
clar_report_errors(_clar.last_report);
exit(-1);
}
CL_TRACE(CL_TRACE__TEST__LONGJMP);
longjmp(_clar.trampoline, -1);
}
void clar__skip(void)
{
_clar.last_report->status = CL_TEST_SKIP;
_clar.total_skipped++;
abort_test();
}
void clar__fail(
const char *file,
size_t line,
const char *error_msg,
const char *description,
int should_abort)
{
struct clar_error *error = calloc(1, sizeof(struct clar_error));
if (_clar.last_report->errors == NULL)
_clar.last_report->errors = error;
if (_clar.last_report->last_error != NULL)
_clar.last_report->last_error->next = error;
_clar.last_report->last_error = error;
error->file = file;
error->line_number = line;
error->error_msg = error_msg;
if (description != NULL)
error->description = strdup(description);
_clar.total_errors++;
_clar.last_report->status = CL_TEST_FAILURE;
if (should_abort)
abort_test();
}
void clar__assert(
int condition,
const char *file,
size_t line,
const char *error_msg,
const char *description,
int should_abort)
{
if (condition)
return;
clar__fail(file, line, error_msg, description, should_abort);
}
void clar__assert_equal(
const char *file,
size_t line,
const char *err,
int should_abort,
const char *fmt,
...)
{
va_list args;
char buf[4096];
int is_equal = 1;
va_start(args, fmt);
if (!strcmp("%s", fmt)) {
const char *s1 = va_arg(args, const char *);
const char *s2 = va_arg(args, const char *);
is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);
if (!is_equal) {
if (s1 && s2) {
int pos;
for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
s1, s2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
}
}
}
else if(!strcmp("%.*s", fmt)) {
const char *s1 = va_arg(args, const char *);
const char *s2 = va_arg(args, const char *);
int len = va_arg(args, int);
is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len);
if (!is_equal) {
if (s1 && s2) {
int pos;
for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)",
len, s1, len, s2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2);
}
}
}
else if (!strcmp("%ls", fmt)) {
const wchar_t *wcs1 = va_arg(args, const wchar_t *);
const wchar_t *wcs2 = va_arg(args, const wchar_t *);
is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2);
if (!is_equal) {
if (wcs1 && wcs2) {
int pos;
for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
wcs1, wcs2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
}
}
}
else if(!strcmp("%.*ls", fmt)) {
const wchar_t *wcs1 = va_arg(args, const wchar_t *);
const wchar_t *wcs2 = va_arg(args, const wchar_t *);
int len = va_arg(args, int);
is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len);
if (!is_equal) {
if (wcs1 && wcs2) {
int pos;
for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
len, wcs1, len, wcs2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
}
}
}
else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
is_equal = (sz1 == sz2);
if (!is_equal) {
int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
strncat(buf, " != ", sizeof(buf) - offset);
p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2);
}
}
else if (!strcmp("%p", fmt)) {
void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
is_equal = (p1 == p2);
if (!is_equal)
p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
}
else {
int i1 = va_arg(args, int), i2 = va_arg(args, int);
is_equal = (i1 == i2);
if (!is_equal) {
int offset = p_snprintf(buf, sizeof(buf), fmt, i1);
strncat(buf, " != ", sizeof(buf) - offset);
p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2);
}
}
va_end(args);
if (!is_equal)
clar__fail(file, line, err, buf, should_abort);
}
void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
{
_clar.local_cleanup = cleanup;
_clar.local_cleanup_payload = opaque;
}
#include "clar/sandbox.h"
#include "clar/fixtures.h"
#include "clar/fs.h"
#include "clar/print.h"
#include "clar/summary.h"
+165
View File
@@ -0,0 +1,165 @@
/*
* Copyright (c) Vicent Marti. All rights reserved.
*
* This file is part of clar, distributed under the ISC license.
* For full terms see the included COPYING file.
*/
#ifndef __CLAR_TEST_H__
#define __CLAR_TEST_H__
#include <stdlib.h>
enum cl_test_status {
CL_TEST_OK,
CL_TEST_FAILURE,
CL_TEST_SKIP,
CL_TEST_NOTRUN,
};
/** Setup clar environment */
void clar_test_init(int argc, char *argv[]);
int clar_test_run(void);
void clar_test_shutdown(void);
/** One shot setup & run */
int clar_test(int argc, char *argv[]);
const char *clar_sandbox_path(void);
void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
void cl_fs_cleanup(void);
/**
* cl_trace_* is a hook to provide a simple global tracing
* mechanism.
*
* The goal here is to let main() provide clar-proper
* with a callback to optionally write log info for
* test operations into the same stream used by their
* actual tests. This would let them print test names
* and maybe performance data as they choose.
*
* The goal is NOT to alter the flow of control or to
* override test selection/skipping. (So the callback
* does not return a value.)
*
* The goal is NOT to duplicate the existing
* pass/fail/skip reporting. (So the callback
* does not accept a status/errorcode argument.)
*
*/
typedef enum cl_trace_event {
CL_TRACE__SUITE_BEGIN,
CL_TRACE__SUITE_END,
CL_TRACE__TEST__BEGIN,
CL_TRACE__TEST__END,
CL_TRACE__TEST__RUN_BEGIN,
CL_TRACE__TEST__RUN_END,
CL_TRACE__TEST__LONGJMP,
} cl_trace_event;
typedef void (cl_trace_cb)(
cl_trace_event ev,
const char *suite_name,
const char *test_name,
void *payload);
/**
* Register a callback into CLAR to send global trace events.
* Pass NULL to disable.
*/
void cl_trace_register(cl_trace_cb *cb, void *payload);
#ifdef CLAR_FIXTURE_PATH
const char *cl_fixture(const char *fixture_name);
void cl_fixture_sandbox(const char *fixture_name);
void cl_fixture_cleanup(const char *fixture_name);
const char *cl_fixture_basename(const char *fixture_name);
#endif
/**
* Assertion macros with explicit error message
*/
#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 1)
#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 1)
/**
* Check macros with explicit error message
*/
#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 0)
#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 0)
/**
* Assertion macros with no error message
*/
#define cl_must_pass(expr) cl_must_pass_(expr, NULL)
#define cl_must_fail(expr) cl_must_fail_(expr, NULL)
#define cl_assert(expr) cl_assert_(expr, NULL)
/**
* Check macros with no error message
*/
#define cl_check_pass(expr) cl_check_pass_(expr, NULL)
#define cl_check_fail(expr) cl_check_fail_(expr, NULL)
#define cl_check(expr) cl_check_(expr, NULL)
/**
* Forced failure/warning
*/
#define cl_fail(desc) clar__fail(__FILE__, __LINE__, "Test failed.", desc, 1)
#define cl_warning(desc) clar__fail(__FILE__, __LINE__, "Warning during test execution:", desc, 0)
#define cl_skip() clar__skip()
/**
* Typed assertion macros
*/
#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
void clar__skip(void);
void clar__fail(
const char *file,
size_t line,
const char *error,
const char *description,
int should_abort);
void clar__assert(
int condition,
const char *file,
size_t line,
const char *error,
const char *description,
int should_abort);
void clar__assert_equal(
const char *file,
size_t line,
const char *err,
int should_abort,
const char *fmt,
...);
#endif
+50
View File
@@ -0,0 +1,50 @@
static const char *
fixture_path(const char *base, const char *fixture_name)
{
static char _path[4096];
size_t root_len;
root_len = strlen(base);
strncpy(_path, base, sizeof(_path));
if (_path[root_len - 1] != '/')
_path[root_len++] = '/';
if (fixture_name[0] == '/')
fixture_name++;
strncpy(_path + root_len,
fixture_name,
sizeof(_path) - root_len);
return _path;
}
#ifdef CLAR_FIXTURE_PATH
const char *cl_fixture(const char *fixture_name)
{
return fixture_path(CLAR_FIXTURE_PATH, fixture_name);
}
void cl_fixture_sandbox(const char *fixture_name)
{
fs_copy(cl_fixture(fixture_name), _clar_path);
}
const char *cl_fixture_basename(const char *fixture_name)
{
const char *p;
for (p = fixture_name; *p; p++) {
if (p[0] == '/' && p[1] && p[1] != '/')
fixture_name = p+1;
}
return fixture_name;
}
void cl_fixture_cleanup(const char *fixture_name)
{
fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name)));
}
#endif
+317
View File
@@ -0,0 +1,317 @@
#ifdef _WIN32
#define RM_RETRY_COUNT 5
#define RM_RETRY_DELAY 10
#ifdef __MINGW32__
/* These security-enhanced functions are not available
* in MinGW, so just use the vanilla ones */
#define wcscpy_s(a, b, c) wcscpy((a), (c))
#define wcscat_s(a, b, c) wcscat((a), (c))
#endif /* __MINGW32__ */
static int
fs__dotordotdot(WCHAR *_tocheck)
{
return _tocheck[0] == '.' &&
(_tocheck[1] == '\0' ||
(_tocheck[1] == '.' && _tocheck[2] == '\0'));
}
static int
fs_rmdir_rmdir(WCHAR *_wpath)
{
unsigned retries = 1;
while (!RemoveDirectoryW(_wpath)) {
/* Only retry when we have retries remaining, and the
* error was ERROR_DIR_NOT_EMPTY. */
if (retries++ > RM_RETRY_COUNT ||
ERROR_DIR_NOT_EMPTY != GetLastError())
return -1;
/* Give whatever has a handle to a child item some time
* to release it before trying again */
Sleep(RM_RETRY_DELAY * retries * retries);
}
return 0;
}
static void
fs_rmdir_helper(WCHAR *_wsource)
{
git_win32_path buffer;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buffer_prefix_len;
/* Set up the buffer and capture the length */
wcscpy_s(buffer, GIT_WIN_PATH_UTF16, _wsource);
wcscat_s(buffer, GIT_WIN_PATH_UTF16, L"\\");
buffer_prefix_len = wcslen(buffer);
/* FindFirstFile needs a wildcard to match multiple items */
wcscat_s(buffer, GIT_WIN_PATH_UTF16, L"*");
find_handle = FindFirstFileW(buffer, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
do {
/* FindFirstFile/FindNextFile gives back . and ..
* entries at the beginning */
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buffer + buffer_prefix_len, GIT_WIN_PATH_UTF16 - buffer_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_rmdir_helper(buffer);
else {
/* If set, the +R bit must be cleared before deleting */
if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(buffer));
}
}
while (FindNextFileW(find_handle, &find_data));
/* Ensure that we successfully completed the enumeration */
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
/* Close the find handle */
FindClose(find_handle);
/* Now that the directory is empty, remove it */
cl_assert(0 == fs_rmdir_rmdir(_wsource));
}
static int
fs_rm_wait(WCHAR *_wpath)
{
unsigned retries = 1;
DWORD last_error;
do {
if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
last_error = GetLastError();
else
last_error = ERROR_SUCCESS;
/* Is the item gone? */
if (ERROR_FILE_NOT_FOUND == last_error ||
ERROR_PATH_NOT_FOUND == last_error)
return 0;
Sleep(RM_RETRY_DELAY * retries * retries);
}
while (retries++ <= RM_RETRY_COUNT);
return -1;
}
static void
fs_rm(const char *_source)
{
git_win32_path wsource;
DWORD attrs;
/* The input path is UTF-8. Convert it to wide characters
* for use with the Windows API */
cl_assert(git_win32_path_from_utf8(wsource, _source));
/* Does the item exist? If not, we have no work to do */
attrs = GetFileAttributesW(wsource);
if (INVALID_FILE_ATTRIBUTES == attrs)
return;
if (FILE_ATTRIBUTE_DIRECTORY & attrs)
fs_rmdir_helper(wsource);
else {
/* The item is a file. Strip the +R bit */
if (FILE_ATTRIBUTE_READONLY & attrs)
cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(wsource));
}
/* Wait for the DeleteFile or RemoveDirectory call to complete */
cl_assert(0 == fs_rm_wait(wsource));
}
static void
fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
{
git_win32_path buf_source, buf_dest;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buf_source_prefix_len, buf_dest_prefix_len;
wcscpy_s(buf_source, GIT_WIN_PATH_UTF16, _wsource);
wcscat_s(buf_source, GIT_WIN_PATH_UTF16, L"\\");
buf_source_prefix_len = wcslen(buf_source);
wcscpy_s(buf_dest, GIT_WIN_PATH_UTF16, _wdest);
wcscat_s(buf_dest, GIT_WIN_PATH_UTF16, L"\\");
buf_dest_prefix_len = wcslen(buf_dest);
/* Get an enumerator for the items in the source. */
wcscat_s(buf_source, GIT_WIN_PATH_UTF16, L"*");
find_handle = FindFirstFileW(buf_source, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
/* Create the target directory. */
cl_assert(CreateDirectoryW(_wdest, NULL));
do {
/* FindFirstFile/FindNextFile gives back . and ..
* entries at the beginning */
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buf_source + buf_source_prefix_len, GIT_WIN_PATH_UTF16 - buf_source_prefix_len, find_data.cFileName);
wcscpy_s(buf_dest + buf_dest_prefix_len, GIT_WIN_PATH_UTF16 - buf_dest_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_copydir_helper(buf_source, buf_dest);
else
cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
}
while (FindNextFileW(find_handle, &find_data));
/* Ensure that we successfully completed the enumeration */
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
/* Close the find handle */
FindClose(find_handle);
}
static void
fs_copy(const char *_source, const char *_dest)
{
git_win32_path wsource, wdest;
DWORD source_attrs, dest_attrs;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
/* The input paths are UTF-8. Convert them to wide characters
* for use with the Windows API. */
cl_assert(git_win32_path_from_utf8(wsource, _source));
cl_assert(git_win32_path_from_utf8(wdest, _dest));
/* Check the source for existence */
source_attrs = GetFileAttributesW(wsource);
cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
/* Check the target for existence */
dest_attrs = GetFileAttributesW(wdest);
if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
/* Target exists; append last path part of source to target.
* Use FindFirstFile to parse the path */
find_handle = FindFirstFileW(wsource, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
wcscat_s(wdest, GIT_WIN_PATH_UTF16, L"\\");
wcscat_s(wdest, GIT_WIN_PATH_UTF16, find_data.cFileName);
FindClose(find_handle);
/* Check the new target for existence */
cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
}
if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
fs_copydir_helper(wsource, wdest);
else
cl_assert(CopyFileW(wsource, wdest, TRUE));
}
void
cl_fs_cleanup(void)
{
fs_rm(fixture_path(_clar_path, "*"));
}
#else
#include <errno.h>
#include <string.h>
static int
shell_out(char * const argv[])
{
int status, piderr;
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr,
"System error: `fork()` call failed (%d) - %s\n",
errno, strerror(errno));
exit(-1);
}
if (pid == 0) {
execv(argv[0], argv);
}
do {
piderr = waitpid(pid, &status, WUNTRACED);
} while (piderr < 0 && (errno == EAGAIN || errno == EINTR));
return WEXITSTATUS(status);
}
static void
fs_copy(const char *_source, const char *dest)
{
char *argv[5];
char *source;
size_t source_len;
source = strdup(_source);
source_len = strlen(source);
if (source[source_len - 1] == '/')
source[source_len - 1] = 0;
argv[0] = "/bin/cp";
argv[1] = "-R";
argv[2] = source;
argv[3] = (char *)dest;
argv[4] = NULL;
cl_must_pass_(
shell_out(argv),
"Failed to copy test fixtures to sandbox"
);
free(source);
}
static void
fs_rm(const char *source)
{
char *argv[4];
argv[0] = "/bin/rm";
argv[1] = "-Rf";
argv[2] = (char *)source;
argv[3] = NULL;
cl_must_pass_(
shell_out(argv),
"Failed to cleanup the sandbox"
);
}
void
cl_fs_cleanup(void)
{
clar_unsandbox();
clar_sandbox();
}
#endif
+67
View File
@@ -0,0 +1,67 @@
static void clar_print_init(int test_count, int suite_count, const char *suite_names)
{
(void)test_count;
printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
}
static void clar_print_shutdown(int test_count, int suite_count, int error_count)
{
(void)test_count;
(void)suite_count;
(void)error_count;
printf("\n\n");
clar_report_all();
}
static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error)
{
printf(" %d) Failure:\n", num);
printf("%s::%s [%s:%"PRIuZ"]\n",
report->suite,
report->test,
error->file,
error->line_number);
printf(" %s\n", error->error_msg);
if (error->description != NULL)
printf(" %s\n", error->description);
printf("\n");
fflush(stdout);
}
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status status)
{
(void)test_name;
(void)test_number;
switch(status) {
case CL_TEST_OK: printf("."); break;
case CL_TEST_FAILURE: printf("F"); break;
case CL_TEST_SKIP: printf("S"); break;
case CL_TEST_NOTRUN: printf("N"); break;
}
fflush(stdout);
}
static void clar_print_onsuite(const char *suite_name, int suite_index)
{
if (_clar.report_suite_names)
printf("\n%s", suite_name);
(void)suite_index;
}
static void clar_print_onabort(const char *msg, ...)
{
va_list argp;
va_start(argp, msg);
vfprintf(stderr, msg, argp);
va_end(argp);
}
+151
View File
@@ -0,0 +1,151 @@
#ifdef __APPLE__
#include <sys/syslimits.h>
#endif
static char _clar_path[4096];
static int
is_valid_tmp_path(const char *path)
{
STAT_T st;
if (stat(path, &st) != 0)
return 0;
if (!S_ISDIR(st.st_mode))
return 0;
return (access(path, W_OK) == 0);
}
static int
find_tmp_path(char *buffer, size_t length)
{
#ifndef _WIN32
static const size_t var_count = 5;
static const char *env_vars[] = {
"CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
};
size_t i;
for (i = 0; i < var_count; ++i) {
const char *env = getenv(env_vars[i]);
if (!env)
continue;
if (is_valid_tmp_path(env)) {
#ifdef __APPLE__
if (length >= PATH_MAX && realpath(env, buffer) != NULL)
return 0;
#endif
strncpy(buffer, env, length);
return 0;
}
}
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
#ifdef __APPLE__
if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
return 0;
#endif
strncpy(buffer, "/tmp", length);
return 0;
}
#else
DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
if (env_len > 0 && env_len < (DWORD)length)
return 0;
if (GetTempPath((DWORD)length, buffer))
return 0;
#endif
/* This system doesn't like us, try to use the current directory */
if (is_valid_tmp_path(".")) {
strncpy(buffer, ".", length);
return 0;
}
return -1;
}
static void clar_unsandbox(void)
{
if (_clar_path[0] == '\0')
return;
cl_must_pass(chdir(".."));
fs_rm(_clar_path);
}
static int build_sandbox_path(void)
{
#ifdef CLAR_TMPDIR
const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
#else
const char path_tail[] = "clar_tmp_XXXXXX";
#endif
size_t len;
if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
return -1;
len = strlen(_clar_path);
#ifdef _WIN32
{ /* normalize path to POSIX forward slashes */
size_t i;
for (i = 0; i < len; ++i) {
if (_clar_path[i] == '\\')
_clar_path[i] = '/';
}
}
#endif
if (_clar_path[len - 1] != '/') {
_clar_path[len++] = '/';
}
strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
#if defined(__MINGW32__)
if (_mktemp(_clar_path) == NULL)
return -1;
if (mkdir(_clar_path, 0700) != 0)
return -1;
#elif defined(_WIN32)
if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
return -1;
if (mkdir(_clar_path, 0700) != 0)
return -1;
#else
if (mkdtemp(_clar_path) == NULL)
return -1;
#endif
return 0;
}
static int clar_sandbox(void)
{
if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
return -1;
if (chdir(_clar_path) != 0)
return -1;
return 0;
}
const char *clar_sandbox_path(void)
{
return _clar_path;
}
+134
View File
@@ -0,0 +1,134 @@
#include <stdio.h>
#include <time.h>
int clar_summary_close_tag(
struct clar_summary *summary, const char *tag, int indent)
{
const char *indt;
if (indent == 0) indt = "";
else if (indent == 1) indt = "\t";
else indt = "\t\t";
return fprintf(summary->fp, "%s</%s>\n", indt, tag);
}
int clar_summary_testsuites(struct clar_summary *summary)
{
return fprintf(summary->fp, "<testsuites>\n");
}
int clar_summary_testsuite(struct clar_summary *summary,
int idn, const char *name, const char *pkg, time_t timestamp,
double elapsed, int test_count, int fail_count, int error_count)
{
struct tm *tm = localtime(&timestamp);
char iso_dt[20];
if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0)
return -1;
return fprintf(summary->fp, "\t<testsuite "
" id=\"%d\""
" name=\"%s\""
" package=\"%s\""
" hostname=\"localhost\""
" timestamp=\"%s\""
" time=\"%.2f\""
" tests=\"%d\""
" failures=\"%d\""
" errors=\"%d\">\n",
idn, name, pkg, iso_dt, elapsed, test_count, fail_count, error_count);
}
int clar_summary_testcase(struct clar_summary *summary,
const char *name, const char *classname, double elapsed)
{
return fprintf(summary->fp,
"\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n",
name, classname, elapsed);
}
int clar_summary_failure(struct clar_summary *summary,
const char *type, const char *message, const char *desc)
{
return fprintf(summary->fp,
"\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n",
type, message, desc);
}
struct clar_summary *clar_summary_init(const char *filename)
{
struct clar_summary *summary;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return NULL;
if ((summary = malloc(sizeof(struct clar_summary))) == NULL) {
fclose(fp);
return NULL;
}
summary->filename = filename;
summary->fp = fp;
return summary;
}
int clar_summary_shutdown(struct clar_summary *summary)
{
struct clar_report *report;
const char *last_suite = NULL;
if (clar_summary_testsuites(summary) < 0)
goto on_error;
report = _clar.reports;
while (report != NULL) {
struct clar_error *error = report->errors;
if (last_suite == NULL || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_testsuite(summary, 0, report->suite, "",
time(NULL), 0, _clar.tests_ran, _clar.total_errors, 0) < 0)
goto on_error;
}
last_suite = report->suite;
clar_summary_testcase(summary, report->test, "what", 0);
while (error != NULL) {
if (clar_summary_failure(summary, "assert",
error->error_msg, error->description) < 0)
goto on_error;
error = error->next;
}
if (clar_summary_close_tag(summary, "testcase", 2) < 0)
goto on_error;
report = report->next;
if (!report || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_close_tag(summary, "testsuite", 1) < 0)
goto on_error;
}
}
if (clar_summary_close_tag(summary, "testsuites", 0) < 0 ||
fclose(summary->fp) != 0)
goto on_error;
printf("written summary file to %s\n", summary->filename);
free(summary);
return 0;
on_error:
fclose(summary->fp);
free(summary);
return -1;
}
+625
View File
@@ -0,0 +1,625 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "git2/sys/repository.h"
void cl_git_report_failure(
int error, int expected, const char *file, int line, const char *fncall)
{
char msg[4096];
const git_error *last = git_error_last();
if (expected)
p_snprintf(msg, 4096, "error %d (expected %d) - %s",
error, expected, last ? last->message : "<no message>");
else if (error || last)
p_snprintf(msg, 4096, "error %d - %s",
error, last ? last->message : "<no message>");
else
p_snprintf(msg, 4096, "no error, expected non-zero return");
clar__assert(0, file, line, fncall, msg, 1);
}
void cl_git_mkfile(const char *filename, const char *content)
{
int fd;
fd = p_creat(filename, 0666);
cl_assert(fd != -1);
if (content) {
cl_must_pass(p_write(fd, content, strlen(content)));
} else {
cl_must_pass(p_write(fd, filename, strlen(filename)));
cl_must_pass(p_write(fd, "\n", 1));
}
cl_must_pass(p_close(fd));
}
void cl_git_write2file(
const char *path, const char *content, size_t content_len,
int flags, unsigned int mode)
{
int fd;
cl_assert(path && content);
cl_assert((fd = p_open(path, flags, mode)) >= 0);
if (!content_len)
content_len = strlen(content);
cl_must_pass(p_write(fd, content, content_len));
cl_must_pass(p_close(fd));
}
void cl_git_append2file(const char *path, const char *content)
{
cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
}
void cl_git_rewritefile(const char *path, const char *content)
{
cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
void cl_git_rmfile(const char *filename)
{
cl_must_pass(p_unlink(filename));
}
char *cl_getenv(const char *name)
{
git_buf out = GIT_BUF_INIT;
int error = git__getenv(&out, name);
cl_assert(error >= 0 || error == GIT_ENOTFOUND);
if (error == GIT_ENOTFOUND)
return NULL;
if (out.size == 0) {
char *dup = git__strdup("");
cl_assert(dup);
return dup;
}
return git_buf_detach(&out);
}
bool cl_is_env_set(const char *name)
{
char *env = cl_getenv(name);
bool result = (env != NULL);
git__free(env);
return result;
}
#ifdef GIT_WIN32
#include "win32/utf-conv.h"
int cl_setenv(const char *name, const char *value)
{
wchar_t *wide_name, *wide_value = NULL;
cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0);
if (value) {
cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0);
cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
} else {
/* Windows XP returns 0 (failed) when passing NULL for lpValue when
* lpName does not exist in the environment block. This behavior
* seems to have changed in later versions. Don't check the return value
* of SetEnvironmentVariable when passing NULL for lpValue. */
SetEnvironmentVariableW(wide_name, NULL);
}
git__free(wide_name);
git__free(wide_value);
return 0;
}
/* This function performs retries on calls to MoveFile in order
* to provide enhanced reliability in the face of antivirus
* agents that may be scanning the source (or in the case that
* the source is a directory, a child of the source). */
int cl_rename(const char *source, const char *dest)
{
git_win32_path source_utf16;
git_win32_path dest_utf16;
unsigned retries = 1;
cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0);
cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0);
while (!MoveFileW(source_utf16, dest_utf16)) {
/* Only retry if the error is ERROR_ACCESS_DENIED;
* this may indicate that an antivirus agent is
* preventing the rename from source to target */
if (retries > 5 ||
ERROR_ACCESS_DENIED != GetLastError())
return -1;
/* With 5 retries and a coefficient of 10ms, the maximum
* delay here is 550 ms */
Sleep(10 * retries * retries);
retries++;
}
return 0;
}
#else
#include <stdlib.h>
int cl_setenv(const char *name, const char *value)
{
return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
}
int cl_rename(const char *source, const char *dest)
{
return p_rename(source, dest);
}
#endif
static const char *_cl_sandbox = NULL;
static git_repository *_cl_repo = NULL;
git_repository *cl_git_sandbox_init(const char *sandbox)
{
/* Get the name of the sandbox folder which will be created */
const char *basename = cl_fixture_basename(sandbox);
/* Copy the whole sandbox folder from our fixtures to our test sandbox
* area. After this it can be accessed with `./sandbox`
*/
cl_fixture_sandbox(sandbox);
_cl_sandbox = sandbox;
cl_git_pass(p_chdir(basename));
/* If this is not a bare repo, then rename `sandbox/.gitted` to
* `sandbox/.git` which must be done since we cannot store a folder
* named `.git` inside the fixtures folder of our libgit2 repo.
*/
if (p_access(".gitted", F_OK) == 0)
cl_git_pass(cl_rename(".gitted", ".git"));
/* If we have `gitattributes`, rename to `.gitattributes`. This may
* be necessary if we don't want the attributes to be applied in the
* libgit2 repo, but just during testing.
*/
if (p_access("gitattributes", F_OK) == 0)
cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
/* As with `gitattributes`, we may need `gitignore` just for testing. */
if (p_access("gitignore", F_OK) == 0)
cl_git_pass(cl_rename("gitignore", ".gitignore"));
cl_git_pass(p_chdir(".."));
/* Now open the sandbox repository and make it available for tests */
cl_git_pass(git_repository_open(&_cl_repo, basename));
/* Adjust configs after copying to new filesystem */
cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
return _cl_repo;
}
git_repository *cl_git_sandbox_init_new(const char *sandbox)
{
cl_git_pass(git_repository_init(&_cl_repo, sandbox, false));
_cl_sandbox = sandbox;
return _cl_repo;
}
git_repository *cl_git_sandbox_reopen(void)
{
if (_cl_repo) {
git_repository_free(_cl_repo);
_cl_repo = NULL;
cl_git_pass(git_repository_open(
&_cl_repo, cl_fixture_basename(_cl_sandbox)));
}
return _cl_repo;
}
void cl_git_sandbox_cleanup(void)
{
if (_cl_repo) {
git_repository_free(_cl_repo);
_cl_repo = NULL;
}
if (_cl_sandbox) {
cl_fixture_cleanup(_cl_sandbox);
_cl_sandbox = NULL;
}
}
bool cl_toggle_filemode(const char *filename)
{
struct stat st1, st2;
cl_must_pass(p_stat(filename, &st1));
cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
cl_must_pass(p_stat(filename, &st2));
return (st1.st_mode != st2.st_mode);
}
bool cl_is_chmod_supported(void)
{
static int _is_supported = -1;
if (_is_supported < 0) {
cl_git_mkfile("filemode.t", "Test if filemode can be modified");
_is_supported = cl_toggle_filemode("filemode.t");
cl_must_pass(p_unlink("filemode.t"));
}
return _is_supported;
}
const char* cl_git_fixture_url(const char *fixturename)
{
return cl_git_path_url(cl_fixture(fixturename));
}
const char* cl_git_path_url(const char *path)
{
static char url[4096];
const char *in_buf;
git_buf path_buf = GIT_BUF_INIT;
git_buf url_buf = GIT_BUF_INIT;
cl_git_pass(git_path_prettify_dir(&path_buf, path, NULL));
cl_git_pass(git_buf_puts(&url_buf, "file://"));
#ifdef GIT_WIN32
/*
* A FILE uri matches the following format: file://[host]/path
* where "host" can be empty and "path" is an absolute path to the resource.
*
* In this test, no hostname is used, but we have to ensure the leading triple slashes:
*
* *nix: file:///usr/home/...
* Windows: file:///C:/Users/...
*/
cl_git_pass(git_buf_putc(&url_buf, '/'));
#endif
in_buf = git_buf_cstr(&path_buf);
/*
* A very hacky Url encoding that only takes care of escaping the spaces
*/
while (*in_buf) {
if (*in_buf == ' ')
cl_git_pass(git_buf_puts(&url_buf, "%20"));
else
cl_git_pass(git_buf_putc(&url_buf, *in_buf));
in_buf++;
}
cl_assert(url_buf.size < 4096);
strncpy(url, git_buf_cstr(&url_buf), 4096);
git_buf_dispose(&url_buf);
git_buf_dispose(&path_buf);
return url;
}
const char *cl_git_sandbox_path(int is_dir, ...)
{
const char *path = NULL;
static char _temp[GIT_PATH_MAX];
git_buf buf = GIT_BUF_INIT;
va_list arg;
cl_git_pass(git_buf_sets(&buf, clar_sandbox_path()));
va_start(arg, is_dir);
while ((path = va_arg(arg, const char *)) != NULL) {
cl_git_pass(git_buf_joinpath(&buf, buf.ptr, path));
}
va_end(arg);
cl_git_pass(git_path_prettify(&buf, buf.ptr, NULL));
if (is_dir)
git_path_to_dir(&buf);
/* make sure we won't truncate */
cl_assert(git_buf_len(&buf) < sizeof(_temp));
git_buf_copy_cstr(_temp, sizeof(_temp), &buf);
git_buf_dispose(&buf);
return _temp;
}
typedef struct {
const char *filename;
size_t filename_len;
} remove_data;
static int remove_placeholders_recurs(void *_data, git_buf *path)
{
remove_data *data = (remove_data *)_data;
size_t pathlen;
if (git_path_isdir(path->ptr) == true)
return git_path_direach(path, 0, remove_placeholders_recurs, data);
pathlen = path->size;
if (pathlen < data->filename_len)
return 0;
/* if path ends in '/'+filename (or equals filename) */
if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
(pathlen == data->filename_len ||
path->ptr[pathlen - data->filename_len - 1] == '/'))
return p_unlink(path->ptr);
return 0;
}
int cl_git_remove_placeholders(const char *directory_path, const char *filename)
{
int error;
remove_data data;
git_buf buffer = GIT_BUF_INIT;
if (git_path_isdir(directory_path) == false)
return -1;
if (git_buf_sets(&buffer, directory_path) < 0)
return -1;
data.filename = filename;
data.filename_len = strlen(filename);
error = remove_placeholders_recurs(&data, &buffer);
git_buf_dispose(&buffer);
return error;
}
#define CL_COMMIT_NAME "Libgit2 Tester"
#define CL_COMMIT_EMAIL "libgit2-test@github.com"
#define CL_COMMIT_MSG "Test commit of tree "
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg)
{
git_index *index;
git_oid commit_id, tree_id;
git_object *parent = NULL;
git_reference *ref = NULL;
git_tree *tree = NULL;
char buf[128];
int free_sig = (sig == NULL);
/* it is fine if looking up HEAD fails - we make this the first commit */
git_revparse_ext(&parent, &ref, repo, "HEAD");
/* write the index content as a tree */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
if (sig)
cl_assert(sig->name && sig->email);
else if (!time)
cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
else
cl_git_pass(git_signature_new(
&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
if (!msg) {
strcpy(buf, CL_COMMIT_MSG);
git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
msg = buf;
}
cl_git_pass(git_commit_create_v(
&commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
if (out)
git_oid_cpy(out, &commit_id);
git_object_free(parent);
git_reference_free(ref);
if (free_sig)
git_signature_free(sig);
git_tree_free(tree);
}
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
{
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_bool(config, cfg, value != 0));
git_config_free(config);
}
int cl_repo_get_bool(git_repository *repo, const char *cfg)
{
int val = 0;
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
if (git_config_get_bool(&val, config, cfg) < 0)
git_error_clear();
git_config_free(config);
return val;
}
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value)
{
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_string(config, cfg, value));
git_config_free(config);
}
/* this is essentially the code from git__unescape modified slightly */
static size_t strip_cr_from_buf(char *start, size_t len)
{
char *scan, *trail, *end = start + len;
for (scan = trail = start; scan < end; trail++, scan++) {
while (*scan == '\r')
scan++; /* skip '\r' */
if (trail != scan)
*trail = *scan;
}
*trail = '\0';
return (trail - start);
}
void clar__assert_equal_file(
const char *expected_data,
size_t expected_bytes,
int ignore_cr,
const char *path,
const char *file,
int line)
{
char buf[4000];
ssize_t bytes, total_bytes = 0;
int fd = p_open(path, O_RDONLY | O_BINARY);
cl_assert(fd >= 0);
if (expected_data && !expected_bytes)
expected_bytes = strlen(expected_data);
while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
clar__assert(
bytes > 0, file, line, "error reading from file", path, 1);
if (ignore_cr)
bytes = strip_cr_from_buf(buf, bytes);
if (memcmp(expected_data, buf, bytes) != 0) {
int pos;
for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(
buf, sizeof(buf), "file content mismatch at byte %"PRIdZ,
(ssize_t)(total_bytes + pos));
p_close(fd);
clar__fail(file, line, path, buf, 1);
}
expected_data += bytes;
total_bytes += bytes;
}
p_close(fd);
clar__assert(!bytes, file, line, "error reading from file", path, 1);
clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ,
(size_t)expected_bytes, (size_t)total_bytes);
}
static char *_cl_restore_home = NULL;
void cl_fake_home_cleanup(void *payload)
{
char *restore = _cl_restore_home;
_cl_restore_home = NULL;
GIT_UNUSED(payload);
if (restore) {
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
git__free(restore);
}
}
void cl_fake_home(void)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &path));
_cl_restore_home = git_buf_detach(&path);
cl_set_cleanup(cl_fake_home_cleanup, NULL);
if (!git_path_exists("home"))
cl_must_pass(p_mkdir("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_buf_dispose(&path);
}
void cl_sandbox_set_search_path_defaults(void)
{
git_buf path = GIT_BUF_INIT;
git_buf_joinpath(&path, clar_sandbox_path(), "__config");
if (!git_path_exists(path.ptr))
cl_must_pass(p_mkdir(path.ptr, 0777));
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, path.ptr);
git_buf_dispose(&path);
}
#ifdef GIT_WIN32
bool cl_sandbox_supports_8dot3(void)
{
git_buf longpath = GIT_BUF_INIT;
char *shortname;
bool supported;
cl_git_pass(
git_buf_joinpath(&longpath, clar_sandbox_path(), "longer_than_8dot3"));
cl_git_write2file(longpath.ptr, "", 0, O_RDWR|O_CREAT, 0666);
shortname = git_win32_path_8dot3_name(longpath.ptr);
supported = (shortname != NULL);
git__free(shortname);
git_buf_dispose(&longpath);
return supported;
}
#endif
+232
View File
@@ -0,0 +1,232 @@
#ifndef __CLAR_LIBGIT2__
#define __CLAR_LIBGIT2__
#include "clar.h"
#include <git2.h>
#include "common.h"
#include "posix.h"
/**
* Replace for `clar_must_pass` that passes the last library error as the
* test failure message.
*
* Use this wrapper around all `git_` library calls that return error codes!
*/
#define cl_git_pass(expr) cl_git_expect((expr), 0, __FILE__, __LINE__)
#define cl_git_fail_with(error, expr) cl_git_expect((expr), error, __FILE__, __LINE__)
#define cl_git_expect(expr, expected, file, line) do { \
int _lg2_error; \
git_error_clear(); \
if ((_lg2_error = (expr)) != expected) \
cl_git_report_failure(_lg2_error, expected, file, line, "Function call failed: " #expr); \
} while (0)
/**
* Wrapper for `clar_must_fail` -- this one is
* just for consistency. Use with `git_` library
* calls that are supposed to fail!
*/
#define cl_git_fail(expr) do { \
if ((expr) == 0) \
git_error_clear(), \
cl_git_report_failure(0, 0, __FILE__, __LINE__, "Function call succeeded: " #expr); \
} while (0)
/**
* Like cl_git_pass, only for Win32 error code conventions
*/
#define cl_win32_pass(expr) do { \
int _win32_res; \
if ((_win32_res = (expr)) == 0) { \
git_error_set(GIT_ERROR_OS, "Returned: %d, system error code: %lu", _win32_res, GetLastError()); \
cl_git_report_failure(_win32_res, 0, __FILE__, __LINE__, "System call failed: " #expr); \
} \
} while(0)
/**
* Thread safe assertions; you cannot use `cl_git_report_failure` from a
* child thread since it will try to `longjmp` to abort and "the effect of
* a call to longjmp() where initialization of the jmp_buf structure was
* not performed in the calling thread is undefined."
*
* Instead, callers can provide a clar thread error context to a thread,
* which will populate and return it on failure. Callers can check the
* status with `cl_git_thread_check`.
*/
typedef struct {
int error;
const char *file;
int line;
const char *expr;
char error_msg[4096];
} cl_git_thread_err;
#ifdef GIT_THREADS
# define cl_git_thread_pass(threaderr, expr) cl_git_thread_pass_(threaderr, (expr), __FILE__, __LINE__)
#else
# define cl_git_thread_pass(threaderr, expr) cl_git_pass(expr)
#endif
#define cl_git_thread_pass_(__threaderr, __expr, __file, __line) do { \
git_error_clear(); \
if ((((cl_git_thread_err *)__threaderr)->error = (__expr)) != 0) { \
const git_error *_last = git_error_last(); \
((cl_git_thread_err *)__threaderr)->file = __file; \
((cl_git_thread_err *)__threaderr)->line = __line; \
((cl_git_thread_err *)__threaderr)->expr = "Function call failed: " #__expr; \
p_snprintf(((cl_git_thread_err *)__threaderr)->error_msg, 4096, "thread 0x%" PRIxZ " - error %d - %s", \
git_thread_currentid(), ((cl_git_thread_err *)__threaderr)->error, \
_last ? _last->message : "<no message>"); \
git_thread_exit(__threaderr); \
} \
} while (0)
GIT_INLINE(void) cl_git_thread_check(void *data)
{
cl_git_thread_err *threaderr = (cl_git_thread_err *)data;
if (threaderr->error != 0)
clar__assert(0, threaderr->file, threaderr->line, threaderr->expr, threaderr->error_msg, 1);
}
void cl_git_report_failure(int, int, const char *, int, const char *);
#define cl_assert_at_line(expr,file,line) \
clar__assert((expr) != 0, file, line, "Expression is not true: " #expr, NULL, 1)
GIT_INLINE(void) clar__assert_in_range(
int lo, int val, int hi,
const char *file, int line, const char *err, int should_abort)
{
if (lo > val || hi < val) {
char buf[128];
p_snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
clar__fail(file, line, err, buf, should_abort);
}
}
#define cl_assert_equal_sz(sz1,sz2) do { \
size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
} while (0)
#define cl_assert_in_range(L,V,H) \
clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
#define cl_assert_equal_file(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,(int)__LINE__)
#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,(int)__LINE__)
void clar__assert_equal_file(
const char *expected_data,
size_t expected_size,
int ignore_cr,
const char *path,
const char *file,
int line);
GIT_INLINE(void) clar__assert_equal_oid(
const char *file, int line, const char *desc,
const git_oid *one, const git_oid *two)
{
if (git_oid_cmp(one, two)) {
char err[] = "\"........................................\" != \"........................................\"";
git_oid_fmt(&err[1], one);
git_oid_fmt(&err[47], two);
clar__fail(file, line, desc, err, 1);
}
}
#define cl_assert_equal_oid(one, two) \
clar__assert_equal_oid(__FILE__, __LINE__, \
"OID mismatch: " #one " != " #two, (one), (two))
/*
* Some utility macros for building long strings
*/
#define REP4(STR) STR STR STR STR
#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP256(STR) REP16(REP16(STR))
#define REP1024(STR) REP4(REP256(STR))
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
void cl_git_append2file(const char *filename, const char *new_content);
void cl_git_rewritefile(const char *filename, const char *new_content);
void cl_git_write2file(const char *path, const char *data,
size_t datalen, int flags, unsigned int mode);
void cl_git_rmfile(const char *filename);
bool cl_toggle_filemode(const char *filename);
bool cl_is_chmod_supported(void);
/* Environment wrappers */
char *cl_getenv(const char *name);
bool cl_is_env_set(const char *name);
int cl_setenv(const char *name, const char *value);
/* Reliable rename */
int cl_rename(const char *source, const char *dest);
/* Git sandbox setup helpers */
git_repository *cl_git_sandbox_init(const char *sandbox);
git_repository *cl_git_sandbox_init_new(const char *name);
void cl_git_sandbox_cleanup(void);
git_repository *cl_git_sandbox_reopen(void);
/*
* build a sandbox-relative from path segments
* is_dir will add a trailing slash
* vararg must be a NULL-terminated char * list
*/
const char *cl_git_sandbox_path(int is_dir, ...);
/* Local-repo url helpers */
const char* cl_git_fixture_url(const char *fixturename);
const char* cl_git_path_url(const char *path);
/* Test repository cleaner */
int cl_git_remove_placeholders(const char *directory_path, const char *filename);
/* commit creation helpers */
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg);
/* config setting helpers */
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
int cl_repo_get_bool(git_repository *repo, const char *cfg);
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
/* set up a fake "home" directory and set libgit2 GLOBAL search path.
*
* automatically configures cleanup function to restore the regular search
* path, although you can call it explicitly if you wish (with NULL).
*/
void cl_fake_home(void);
void cl_fake_home_cleanup(void *);
void cl_sandbox_set_search_path_defaults(void);
#ifdef GIT_WIN32
# define cl_msleep(x) Sleep(x)
#else
# define cl_msleep(x) usleep(1000 * (x))
#endif
#ifdef GIT_WIN32
bool cl_sandbox_supports_8dot3(void);
#endif
#endif
+31
View File
@@ -0,0 +1,31 @@
#include "clar_libgit2.h"
#include "clar_libgit2_timer.h"
#include "buffer.h"
void cl_perf_timer__init(cl_perf_timer *t)
{
memset(t, 0, sizeof(cl_perf_timer));
}
void cl_perf_timer__start(cl_perf_timer *t)
{
t->time_started = git__timer();
}
void cl_perf_timer__stop(cl_perf_timer *t)
{
double time_now = git__timer();
t->last = time_now - t->time_started;
t->sum += t->last;
}
double cl_perf_timer__last(const cl_perf_timer *t)
{
return t->last;
}
double cl_perf_timer__sum(const cl_perf_timer *t)
{
return t->sum;
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef __CLAR_LIBGIT2_TIMER__
#define __CLAR_LIBGIT2_TIMER__
struct cl_perf_timer
{
/* cummulative running time across all start..stop intervals */
double sum;
/* value of last start..stop interval */
double last;
/* clock value at start */
double time_started;
};
#define CL_PERF_TIMER_INIT {0}
typedef struct cl_perf_timer cl_perf_timer;
void cl_perf_timer__init(cl_perf_timer *t);
void cl_perf_timer__start(cl_perf_timer *t);
void cl_perf_timer__stop(cl_perf_timer *t);
/**
* return value of last start..stop interval in seconds.
*/
double cl_perf_timer__last(const cl_perf_timer *t);
/**
* return cummulative running time across all start..stop
* intervals in seconds.
*/
double cl_perf_timer__sum(const cl_perf_timer *t);
#endif /* __CLAR_LIBGIT2_TIMER__ */
+278
View File
@@ -0,0 +1,278 @@
#include "clar_libgit2_trace.h"
#if defined(GIT_TRACE)
#include "clar_libgit2.h"
#include "clar_libgit2_timer.h"
#include "trace.h"
struct method {
const char *name;
void (*git_trace_cb)(git_trace_level_t level, const char *msg);
void (*close)(void);
};
static const char *message_prefix(git_trace_level_t level)
{
switch (level) {
case GIT_TRACE_NONE:
return "[NONE]: ";
case GIT_TRACE_FATAL:
return "[FATAL]: ";
case GIT_TRACE_ERROR:
return "[ERROR]: ";
case GIT_TRACE_WARN:
return "[WARN]: ";
case GIT_TRACE_INFO:
return "[INFO]: ";
case GIT_TRACE_DEBUG:
return "[DEBUG]: ";
case GIT_TRACE_TRACE:
return "[TRACE]: ";
default:
return "[?????]: ";
}
}
static void _git_trace_cb__printf(git_trace_level_t level, const char *msg)
{
printf("%s%s\n", message_prefix(level), msg);
}
#if defined(GIT_WIN32)
static void _git_trace_cb__debug(git_trace_level_t level, const char *msg)
{
OutputDebugString(message_prefix(level));
OutputDebugString(msg);
OutputDebugString("\n");
printf("%s%s\n", message_prefix(level), msg);
}
#else
#define _git_trace_cb__debug _git_trace_cb__printf
#endif
static void _trace_printf_close(void)
{
fflush(stdout);
}
#define _trace_debug_close _trace_printf_close
static struct method s_methods[] = {
{ "printf", _git_trace_cb__printf, _trace_printf_close },
{ "debug", _git_trace_cb__debug, _trace_debug_close },
/* TODO add file method */
{0},
};
static int s_trace_loaded = 0;
static int s_trace_level = GIT_TRACE_NONE;
static struct method *s_trace_method = NULL;
static int s_trace_tests = 0;
static int set_method(const char *name)
{
int k;
if (!name || !*name)
name = "printf";
for (k=0; (s_methods[k].name); k++) {
if (strcmp(name, s_methods[k].name) == 0) {
s_trace_method = &s_methods[k];
return 0;
}
}
fprintf(stderr, "Unknown CLAR_TRACE_METHOD: '%s'\n", name);
return -1;
}
/**
* Lookup CLAR_TRACE_LEVEL and CLAR_TRACE_METHOD from
* the environment and set the above s_trace_* fields.
*
* If CLAR_TRACE_LEVEL is not set, we disable tracing.
*
* TODO If set, we assume GIT_TRACE_TRACE level, which
* logs everything. Later, we may want to parse the
* value of the environment variable and set a specific
* level.
*
* We assume the "printf" method. This can be changed
* with the CLAR_TRACE_METHOD environment variable.
* Currently, this is only needed on Windows for a "debug"
* version which also writes to the debug output window
* in Visual Studio.
*
* TODO add a "file" method that would open and write
* to a well-known file. This would help keep trace
* output and clar output separate.
*
*/
static void _load_trace_params(void)
{
char *sz_level;
char *sz_method;
char *sz_tests;
s_trace_loaded = 1;
sz_level = cl_getenv("CLAR_TRACE_LEVEL");
if (!sz_level || !*sz_level) {
s_trace_level = GIT_TRACE_NONE;
s_trace_method = NULL;
return;
}
/* TODO Parse sz_level and set s_trace_level. */
s_trace_level = GIT_TRACE_TRACE;
sz_method = cl_getenv("CLAR_TRACE_METHOD");
if (set_method(sz_method) < 0)
set_method(NULL);
sz_tests = cl_getenv("CLAR_TRACE_TESTS");
if (sz_tests != NULL)
s_trace_tests = 1;
}
#define HR "================================================================"
/**
* Timer to report the take spend in a test's run() method.
*/
static cl_perf_timer s_timer_run = CL_PERF_TIMER_INIT;
/**
* Timer to report total time in a test (init, run, cleanup).
*/
static cl_perf_timer s_timer_test = CL_PERF_TIMER_INIT;
void _cl_trace_cb__event_handler(
cl_trace_event ev,
const char *suite_name,
const char *test_name,
void *payload)
{
GIT_UNUSED(payload);
if (!s_trace_tests)
return;
switch (ev) {
case CL_TRACE__SUITE_BEGIN:
git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
#if 0 && defined(GIT_MSVC_CRTDBG)
git_win32__crtdbg_stacktrace__dump(
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
suite_name);
#endif
break;
case CL_TRACE__SUITE_END:
#if 0 && defined(GIT_MSVC_CRTDBG)
/* As an example of checkpointing, dump leaks within this suite.
* This may generate false positives for things like the global
* TLS error state and maybe the odb cache since they aren't
* freed until the global shutdown and outside the scope of this
* set of tests.
*
* This may under-report if the test itself uses a checkpoint.
* See tests/trace/windows/stacktrace.c
*/
git_win32__crtdbg_stacktrace__dump(
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
suite_name);
#endif
git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
break;
case CL_TRACE__TEST__BEGIN:
git_trace(GIT_TRACE_TRACE, "\n%s::%s: Begin Test", suite_name, test_name);
cl_perf_timer__init(&s_timer_test);
cl_perf_timer__start(&s_timer_test);
break;
case CL_TRACE__TEST__END:
cl_perf_timer__stop(&s_timer_test);
git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%.3f %.3f)", suite_name, test_name,
cl_perf_timer__last(&s_timer_run),
cl_perf_timer__last(&s_timer_test));
break;
case CL_TRACE__TEST__RUN_BEGIN:
git_trace(GIT_TRACE_TRACE, "%s::%s: Begin Run", suite_name, test_name);
cl_perf_timer__init(&s_timer_run);
cl_perf_timer__start(&s_timer_run);
break;
case CL_TRACE__TEST__RUN_END:
cl_perf_timer__stop(&s_timer_run);
git_trace(GIT_TRACE_TRACE, "%s::%s: End Run", suite_name, test_name);
break;
case CL_TRACE__TEST__LONGJMP:
cl_perf_timer__stop(&s_timer_run);
git_trace(GIT_TRACE_TRACE, "%s::%s: Aborted", suite_name, test_name);
break;
default:
break;
}
}
/**
* Setup/Enable git_trace() based upon settings user's environment.
*/
void cl_global_trace_register(void)
{
if (!s_trace_loaded)
_load_trace_params();
if (s_trace_level == GIT_TRACE_NONE)
return;
if (s_trace_method == NULL)
return;
if (s_trace_method->git_trace_cb == NULL)
return;
git_trace_set(s_trace_level, s_trace_method->git_trace_cb);
cl_trace_register(_cl_trace_cb__event_handler, NULL);
}
/**
* If we turned on git_trace() earlier, turn it off.
*
* This is intended to let us close/flush any buffered
* IO if necessary.
*
*/
void cl_global_trace_disable(void)
{
cl_trace_register(NULL, NULL);
git_trace_set(GIT_TRACE_NONE, NULL);
if (s_trace_method && s_trace_method->close)
s_trace_method->close();
/* Leave s_trace_ vars set so they can restart tracing
* since we only want to hit the environment variables
* once.
*/
}
#else /* GIT_TRACE */
void cl_global_trace_register(void)
{
}
void cl_global_trace_disable(void)
{
}
#endif /* GIT_TRACE*/
+7
View File
@@ -0,0 +1,7 @@
#ifndef __CLAR_LIBGIT2_TRACE__
#define __CLAR_LIBGIT2_TRACE__
void cl_global_trace_register(void);
void cl_global_trace_disable(void);
#endif
+85
View File
@@ -0,0 +1,85 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "repository.h"
static git_clone_options g_options;
static git_repository *g_repo;
static git_repository *g_repo_cloned;
void test_clone_empty__initialize(void)
{
git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
git_fetch_options dummy_options = GIT_FETCH_OPTIONS_INIT;
cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.fetch_opts = dummy_options;
}
void test_clone_empty__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void cleanup_repository(void *path)
{
cl_fixture_cleanup((const char *)path);
git_repository_free(g_repo_cloned);
g_repo_cloned = NULL;
}
void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
{
char *local_name = "refs/heads/master";
const char *expected_tracked_branch_name = "refs/remotes/origin/master";
const char *expected_remote_name = "origin";
git_buf buf = GIT_BUF_INIT;
git_reference *ref;
cl_set_cleanup(&cleanup_repository, "./empty");
g_options.bare = true;
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
/* Although the HEAD is unborn... */
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
/* ...one can still retrieve the name of the remote tracking reference */
cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, local_name));
cl_assert_equal_s(expected_tracked_branch_name, buf.ptr);
git_buf_dispose(&buf);
/* ...and the name of the remote... */
cl_git_pass(git_branch_remote_name(&buf, g_repo_cloned, expected_tracked_branch_name));
cl_assert_equal_s(expected_remote_name, buf.ptr);
git_buf_dispose(&buf);
/* ...even when the remote HEAD is unborn as well */
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
expected_tracked_branch_name));
}
void test_clone_empty__can_clone_an_empty_local_repo(void)
{
cl_set_cleanup(&cleanup_repository, "./empty");
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
}
void test_clone_empty__can_clone_an_empty_standard_repo(void)
{
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
cl_set_cleanup(&cleanup_repository, "./empty");
cl_git_pass(git_clone(&g_repo_cloned, "./empty_standard_repo", "./empty", &g_options));
}
+213
View File
@@ -0,0 +1,213 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "clone.h"
#include "buffer.h"
#include "path.h"
#include "posix.h"
#include "futils.h"
static int file_url(git_buf *buf, const char *host, const char *path)
{
if (path[0] == '/')
path++;
git_buf_clear(buf);
return git_buf_printf(buf, "file://%s/%s", host, path);
}
#ifdef GIT_WIN32
static int git_style_unc_path(git_buf *buf, const char *host, const char *path)
{
git_buf_clear(buf);
if (host)
git_buf_printf(buf, "//%s/", host);
if (path[0] == '/')
path++;
if (git__isalpha(path[0]) && path[1] == ':' && path[2] == '/') {
git_buf_printf(buf, "%c$/", path[0]);
path += 3;
}
git_buf_puts(buf, path);
return git_buf_oom(buf) ? -1 : 0;
}
static int unc_path(git_buf *buf, const char *host, const char *path)
{
char *c;
if (git_style_unc_path(buf, host, path) < 0)
return -1;
for (c = buf->ptr; *c; c++)
if (*c == '/')
*c = '\\';
return 0;
}
#endif
void test_clone_local__should_clone_local(void)
{
git_buf buf = GIT_BUF_INIT;
/* we use a fixture path because it needs to exist for us to want to clone */
const char *path = cl_fixture("testrepo.git");
cl_git_pass(file_url(&buf, "", path));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
cl_git_pass(file_url(&buf, "localhost", path));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
cl_git_pass(file_url(&buf, "other-host.mycompany.com", path));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
/* Ensure that file:/// urls are percent decoded: .git == %2e%67%69%74 */
cl_git_pass(file_url(&buf, "", path));
git_buf_shorten(&buf, 4);
cl_git_pass(git_buf_puts(&buf, "%2e%67%69%74"));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL));
cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(0, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
git_buf_dispose(&buf);
}
void test_clone_local__hardlinks(void)
{
git_repository *repo;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
git_buf buf = GIT_BUF_INIT;
struct stat st;
/*
* In this first clone, we just copy over, since the temp dir
* will often be in a different filesystem, so we cannot
* link. It also allows us to control the number of links
*/
opts.bare = true;
opts.local = GIT_CLONE_LOCAL_NO_LINKS;
cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
git_repository_free(repo);
/* This second clone is in the same filesystem, so we can hardlink */
opts.local = GIT_CLONE_LOCAL;
cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone2.git", &opts));
#ifndef GIT_WIN32
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(2, st.st_nlink);
#endif
git_repository_free(repo);
git_buf_clear(&buf);
opts.local = GIT_CLONE_LOCAL_NO_LINKS;
cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone3.git", &opts));
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(1, st.st_nlink);
git_repository_free(repo);
/* this one should automatically use links */
cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL));
#ifndef GIT_WIN32
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(3, st.st_nlink);
#endif
git_buf_dispose(&buf);
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_clone_local__standard_unc_paths_are_written_git_style(void)
{
#ifdef GIT_WIN32
git_repository *repo;
git_remote *remote;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
git_buf unc = GIT_BUF_INIT, git_unc = GIT_BUF_INIT;
/* we use a fixture path because it needs to exist for us to want to clone */
const char *path = cl_fixture("testrepo.git");
cl_git_pass(unc_path(&unc, "localhost", path));
cl_git_pass(git_style_unc_path(&git_unc, "localhost", path));
cl_git_pass(git_clone(&repo, unc.ptr, "./clone.git", &opts));
cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
cl_assert_equal_s(git_unc.ptr, git_remote_url(remote));
git_remote_free(remote);
git_repository_free(repo);
git_buf_dispose(&unc);
git_buf_dispose(&git_unc);
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
#endif
}
void test_clone_local__git_style_unc_paths(void)
{
#ifdef GIT_WIN32
git_repository *repo;
git_remote *remote;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
git_buf git_unc = GIT_BUF_INIT;
/* we use a fixture path because it needs to exist for us to want to clone */
const char *path = cl_fixture("testrepo.git");
cl_git_pass(git_style_unc_path(&git_unc, "localhost", path));
cl_git_pass(git_clone(&repo, git_unc.ptr, "./clone.git", &opts));
cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
cl_assert_equal_s(git_unc.ptr, git_remote_url(remote));
git_remote_free(remote);
git_repository_free(repo);
git_buf_dispose(&git_unc);
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
#endif
}
+353
View File
@@ -0,0 +1,353 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "../submodule/submodule_helpers.h"
#include "remote.h"
#include "futils.h"
#include "repository.h"
#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
static git_clone_options g_options;
static git_repository *g_repo;
static git_reference* g_ref;
static git_remote* g_remote;
void test_clone_nonetwork__initialize(void)
{
git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.checkout_opts = dummy_opts;
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_options.fetch_opts = dummy_fetch;
}
void test_clone_nonetwork__cleanup(void)
{
if (g_repo) {
git_repository_free(g_repo);
g_repo = NULL;
}
if (g_ref) {
git_reference_free(g_ref);
g_ref = NULL;
}
if (g_remote) {
git_remote_free(g_remote);
g_remote = NULL;
}
cl_fixture_cleanup("./foo");
}
void test_clone_nonetwork__bad_urls(void)
{
/* Clone should clean up the mess if the URL isn't a git repository */
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(!git_path_exists("./foo"));
g_options.bare = true;
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(!git_path_exists("./foo"));
cl_git_fail(git_clone(&g_repo, "git://example.com:asdf", "./foo", &g_options));
cl_git_fail(git_clone(&g_repo, "https://example.com:asdf/foo", "./foo", &g_options));
cl_git_fail(git_clone(&g_repo, "git://github.com/git://github.com/foo/bar.git.git",
"./foo", &g_options));
cl_git_fail(git_clone(&g_repo, "arrbee:my/bad:password@github.com:1111/strange:words.git",
"./foo", &g_options));
}
void test_clone_nonetwork__do_not_clean_existing_directory(void)
{
/* Clone should not remove the directory if it already exists, but
* Should clean up entries it creates. */
p_mkdir("./foo", GIT_DIR_MODE);
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(git_path_is_empty_dir("./foo"));
/* Try again with a bare repository. */
g_options.bare = true;
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(git_path_is_empty_dir("./foo"));
}
void test_clone_nonetwork__local(void)
{
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
void test_clone_nonetwork__local_absolute_path(void)
{
const char *local_src;
local_src = cl_fixture("testrepo.git");
cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
}
void test_clone_nonetwork__local_bare(void)
{
g_options.bare = true;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
{
cl_git_mkfile("./foo", "Bar!");
cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
{
p_mkdir("./foo", GIT_DIR_MODE);
cl_git_mkfile("./foo/bar", "Baz!");
cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
int custom_origin_name_remote_create(
git_remote **out,
git_repository *repo,
const char *name,
const char *url,
void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(payload);
return git_remote_create(out, repo, "my_origin", url);
}
void test_clone_nonetwork__custom_origin_name(void)
{
g_options.remote_cb = custom_origin_name_remote_create;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_remote_lookup(&g_remote, g_repo, "my_origin"));
}
void test_clone_nonetwork__defaults(void)
{
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
cl_assert(g_repo);
cl_git_pass(git_remote_lookup(&g_remote, g_repo, "origin"));
}
void test_clone_nonetwork__cope_with_already_existing_directory(void)
{
p_mkdir("./foo", GIT_DIR_MODE);
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
{
git_buf path = GIT_BUF_INIT;
g_options.checkout_opts.checkout_strategy = 0;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
git_buf_dispose(&path);
}
void test_clone_nonetwork__can_checkout_given_branch(void)
{
g_options.checkout_branch = "test";
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_assert_equal_i(0, git_repository_head_unborn(g_repo));
cl_git_pass(git_repository_head(&g_ref, g_repo));
cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
cl_assert(git_path_exists("foo/readme.txt"));
}
static int clone_cancel_fetch_transfer_progress_cb(
const git_indexer_progress *stats, void *data)
{
GIT_UNUSED(stats); GIT_UNUSED(data);
return -54321;
}
void test_clone_nonetwork__can_cancel_clone_in_fetch(void)
{
g_options.checkout_branch = "test";
g_options.fetch_opts.callbacks.transfer_progress =
clone_cancel_fetch_transfer_progress_cb;
cl_git_fail_with(git_clone(
&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options),
-54321);
cl_assert(!g_repo);
cl_assert(!git_path_exists("foo/readme.txt"));
}
static int clone_cancel_checkout_cb(
git_checkout_notify_t why,
const char *path,
const git_diff_file *b,
const git_diff_file *t,
const git_diff_file *w,
void *payload)
{
const char *at_file = payload;
GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w);
if (!strcmp(path, at_file))
return -12345;
return 0;
}
void test_clone_nonetwork__can_cancel_clone_in_checkout(void)
{
g_options.checkout_branch = "test";
g_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED;
g_options.checkout_opts.notify_cb = clone_cancel_checkout_cb;
g_options.checkout_opts.notify_payload = "readme.txt";
cl_git_fail_with(git_clone(
&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options),
-12345);
cl_assert(!g_repo);
cl_assert(!git_path_exists("foo/readme.txt"));
}
void test_clone_nonetwork__can_detached_head(void)
{
git_object *obj;
git_repository *cloned;
git_reference *cloned_head;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
cl_assert(git_repository_head_detached(cloned));
cl_git_pass(git_repository_head(&cloned_head, cloned));
cl_assert_equal_oid(git_object_id(obj), git_reference_target(cloned_head));
git_object_free(obj);
git_reference_free(cloned_head);
git_repository_free(cloned);
cl_fixture_cleanup("./foo1");
}
void test_clone_nonetwork__clone_tag_to_tree(void)
{
git_repository *stage;
git_index_entry entry;
git_index *index;
git_odb *odb;
git_oid tree_id;
git_tree *tree;
git_reference *tag;
git_tree_entry *tentry;
const char *file_path = "some/deep/path.txt";
const char *file_content = "some content\n";
const char *tag_name = "refs/tags/tree-tag";
stage = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_odb(&odb, stage));
cl_git_pass(git_index_new(&index));
memset(&entry, 0, sizeof(git_index_entry));
entry.path = file_path;
entry.mode = GIT_FILEMODE_BLOB;
cl_git_pass(git_odb_write(&entry.id, odb, file_content, strlen(file_content), GIT_OBJECT_BLOB));
cl_git_pass(git_index_add(index, &entry));
cl_git_pass(git_index_write_tree_to(&tree_id, index, stage));
cl_git_pass(git_reference_create(&tag, stage, tag_name, &tree_id, 0, NULL));
git_reference_free(tag);
git_odb_free(odb);
git_index_free(index);
g_options.local = GIT_CLONE_NO_LOCAL;
cl_git_pass(git_clone(&g_repo, cl_git_path_url(git_repository_path(stage)), "./foo", &g_options));
git_repository_free(stage);
cl_git_pass(git_reference_lookup(&tag, g_repo, tag_name));
cl_git_pass(git_tree_lookup(&tree, g_repo, git_reference_target(tag)));
git_reference_free(tag);
cl_git_pass(git_tree_entry_bypath(&tentry, tree, file_path));
git_tree_entry_free(tentry);
git_tree_free(tree);
cl_fixture_cleanup("testrepo.git");
}
static void assert_correct_reflog(const char *name)
{
git_reflog *log;
const git_reflog_entry *entry;
git_buf expected_message = GIT_BUF_INIT;
git_buf_printf(&expected_message,
"clone: from %s", cl_git_fixture_url("testrepo.git"));
cl_git_pass(git_reflog_read(&log, g_repo, name));
cl_assert_equal_i(1, git_reflog_entrycount(log));
entry = git_reflog_entry_byindex(log, 0);
cl_assert_equal_s(expected_message.ptr, git_reflog_entry_message(entry));
git_reflog_free(log);
git_buf_dispose(&expected_message);
}
void test_clone_nonetwork__clone_updates_reflog_properly(void)
{
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
assert_correct_reflog("HEAD");
assert_correct_reflog("refs/heads/master");
}
static void cleanup_repository(void *path)
{
if (g_repo) {
git_repository_free(g_repo);
g_repo = NULL;
}
cl_fixture_cleanup((const char *)path);
}
void test_clone_nonetwork__clone_from_empty_sets_upstream(void)
{
git_config *config;
git_repository *repo;
const char *str;
/* Create an empty repo to clone from */
cl_set_cleanup(&cleanup_repository, "./test1");
cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
cl_set_cleanup(&cleanup_repository, "./repowithunborn");
cl_git_pass(git_clone(&repo, "./test1", "./repowithunborn", NULL));
cl_git_pass(git_repository_config_snapshot(&config, repo));
cl_git_pass(git_config_get_string(&str, config, "branch.master.remote"));
cl_assert_equal_s("origin", str);
cl_git_pass(git_config_get_string(&str, config, "branch.master.merge"));
cl_assert_equal_s("refs/heads/master", str);
git_config_free(config);
git_repository_free(repo);
cl_fixture_cleanup("./repowithunborn");
}
+51
View File
@@ -0,0 +1,51 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "git2/transport.h"
#include "git2/sys/transport.h"
#include "futils.h"
static int custom_transport(
git_transport **out,
git_remote *owner,
void *payload)
{
*((int*)payload) = 1;
return git_transport_local(out, owner, payload);
}
static int custom_transport_remote_create(
git_remote **out,
git_repository *repo,
const char *name,
const char *url,
void *payload)
{
int error;
GIT_UNUSED(payload);
if ((error = git_remote_create(out, repo, name, url)) < 0)
return error;
return 0;
}
void test_clone_transport__custom_transport(void)
{
git_repository *repo;
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
int custom_transport_used = 0;
clone_opts.remote_cb = custom_transport_remote_create;
clone_opts.fetch_opts.callbacks.transport = custom_transport;
clone_opts.fetch_opts.callbacks.payload = &custom_transport_used;
cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./custom_transport.git", &clone_opts));
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r("./custom_transport.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(custom_transport_used == 1);
}
+186
View File
@@ -0,0 +1,186 @@
#include "clar_libgit2.h"
#include "commit.h"
#include "git2/commit.h"
static git_repository *_repo;
void test_commit_commit__initialize(void)
{
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
}
void test_commit_commit__cleanup(void)
{
git_repository_free(_repo);
_repo = NULL;
cl_fixture_cleanup("testrepo.git");
}
void test_commit_commit__create_unexisting_update_ref(void)
{
git_oid oid;
git_tree *tree;
git_commit *commit;
git_signature *s;
git_reference *ref;
git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
git_oid_fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
cl_git_fail(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
NULL, "some msg", tree, 1, (const git_commit **) &commit));
/* fail because the parent isn't the tip of the branch anymore */
cl_git_fail(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
NULL, "some msg", tree, 1, (const git_commit **) &commit));
cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
cl_assert_equal_oid(&oid, git_reference_target(ref));
git_tree_free(tree);
git_commit_free(commit);
git_signature_free(s);
git_reference_free(ref);
}
void test_commit_commit__create_initial_commit(void)
{
git_oid oid;
git_tree *tree;
git_commit *commit;
git_signature *s;
git_reference *ref;
git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
git_oid_fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
cl_git_fail(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
NULL, "initial commit", tree, 0, NULL));
cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
cl_assert_equal_oid(&oid, git_reference_target(ref));
git_tree_free(tree);
git_commit_free(commit);
git_signature_free(s);
git_reference_free(ref);
}
void test_commit_commit__create_initial_commit_parent_not_current(void)
{
git_oid oid;
git_oid original_oid;
git_tree *tree;
git_commit *commit;
git_signature *s;
git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
git_oid_fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
cl_git_pass(git_reference_name_to_id(&original_oid, _repo, "HEAD"));
cl_git_fail(git_commit_create(&oid, _repo, "HEAD", s, s,
NULL, "initial commit", tree, 0, NULL));
cl_git_pass(git_reference_name_to_id(&oid, _repo, "HEAD"));
cl_assert_equal_oid(&oid, &original_oid);
git_tree_free(tree);
git_commit_free(commit);
git_signature_free(s);
}
void assert_commit_summary(const char *expected, const char *given)
{
git_commit *dummy;
cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
dummy->raw_message = git__strdup(given);
cl_assert_equal_s(expected, git_commit_summary(dummy));
git_commit__free(dummy);
}
void assert_commit_body(const char *expected, const char *given)
{
git_commit *dummy;
cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
dummy->raw_message = git__strdup(given);
cl_assert_equal_s(expected, git_commit_body(dummy));
git_commit__free(dummy);
}
void test_commit_commit__summary(void)
{
assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline");
assert_commit_summary("One-liner with trailing newline", "One-liner with trailing newline\n");
assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n");
assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)");
assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
assert_commit_summary("\tLeading tabs", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t");
assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed ");
assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed");
assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed");
assert_commit_summary("Newlines are replaced by spaces", "Newlines\nare\nreplaced by spaces\n");
assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */
assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n");
assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n ");
assert_commit_summary(" Trailing newlines are" , " \n Trailing newlines \n are \n\n collapsed \n ");
assert_commit_summary(" Trailing spaces are stripped", " \n Trailing spaces \n are stripped \n\n \n \t ");
assert_commit_summary("", "");
assert_commit_summary("", " ");
assert_commit_summary("", "\n");
assert_commit_summary("", "\n \n");
}
void test_commit_commit__body(void)
{
assert_commit_body(NULL, "One-liner with no trailing newline");
assert_commit_body(NULL, "One-liner with trailing newline\n");
assert_commit_body(NULL, "\n\nTrimmed leading&trailing newlines\n\n");
assert_commit_body("(There are more!)", "\nFirst paragraph only\n\n(There are more!)");
assert_commit_body("(Yes, unwrapped!)", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
assert_commit_body("are preserved", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
assert_commit_body("are preserved", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
assert_commit_body(NULL, "Trailing tabs\tare removed\t\t");
assert_commit_body(NULL, "Trailing spaces are removed ");
assert_commit_body("are removed", "Trailing tabs\t\n\nare removed");
assert_commit_body("are removed", "Trailing spaces \n\nare removed");
assert_commit_body(NULL,"Newlines\nare\nreplaced by spaces\n");
assert_commit_body(NULL , "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */
assert_commit_body(NULL , " \nSpaces before newlines \nare \ncollapsed \n");
assert_commit_body(NULL , " \n Spaces around newlines \n are \n collapsed \n ");
assert_commit_body("collapsed" , " \n Trailing newlines \n are \n\n collapsed \n ");
assert_commit_body(NULL, " \n Trailing spaces \n are stripped \n\n \n \t ");
assert_commit_body(NULL , "");
assert_commit_body(NULL , " ");
assert_commit_body(NULL , "\n");
assert_commit_body(NULL , "\n \n");
}
+60
View File
@@ -0,0 +1,60 @@
#include "clar_libgit2.h"
static git_repository *_repo;
static git_commit *commit;
void test_commit_parent__initialize(void)
{
git_oid oid;
cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
git_oid_fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
}
void test_commit_parent__cleanup(void)
{
git_commit_free(commit);
commit = NULL;
git_repository_free(_repo);
_repo = NULL;
}
static void assert_nth_gen_parent(unsigned int gen, const char *expected_oid)
{
git_commit *parent = NULL;
int error;
error = git_commit_nth_gen_ancestor(&parent, commit, gen);
if (expected_oid != NULL) {
cl_assert_equal_i(0, error);
cl_assert_equal_i(0, git_oid_streq(git_commit_id(parent), expected_oid));
} else
cl_assert_equal_i(GIT_ENOTFOUND, error);
git_commit_free(parent);
}
/*
* $ git show be35~0
* commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
*
* $ git show be35~1
* commit 9fd738e8f7967c078dceed8190330fc8648ee56a
*
* $ git show be35~3
* commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
*
* $ git show be35~42
* fatal: ambiguous argument 'be35~42': unknown revision or path not in the working tree.
*/
void test_commit_parent__can_retrieve_nth_generation_parent(void)
{
assert_nth_gen_parent(0, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
assert_nth_gen_parent(1, "9fd738e8f7967c078dceed8190330fc8648ee56a");
assert_nth_gen_parent(3, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
assert_nth_gen_parent(42, NULL);
}
+553
View File
@@ -0,0 +1,553 @@
#include "clar_libgit2.h"
#include <git2/types.h>
#include "commit.h"
#include "signature.h"
/* Fixture setup */
static git_repository *g_repo;
void test_commit_parse__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
}
void test_commit_parse__cleanup(void)
{
cl_git_sandbox_cleanup();
}
/* Header parsing */
typedef struct {
const char *line;
const char *header;
} parse_test_case;
static parse_test_case passing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
{ "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
{ "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
{ "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
{ "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
{ "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
{ NULL, NULL }
};
static parse_test_case failing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
{ "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
{ "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
{ "", "tree " },
{ "", "" },
{ NULL, NULL }
};
void test_commit_parse__header(void)
{
git_oid oid;
parse_test_case *testcase;
for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
cl_assert(line == line_end);
}
for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
}
}
/* Signature parsing */
typedef struct {
const char *string;
const char *header;
const char *name;
const char *email;
git_time_t time;
int offset;
} passing_signature_test_case;
passing_signature_test_case passing_signature_cases[] = {
{"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 12345, 0},
{"author Vicent Marti <> 12345 \n", "author ", "Vicent Marti", "", 12345, 0},
{"author Vicent Marti <tanoku@gmail.com> 231301 +1020\n", "author ", "Vicent Marti", "tanoku@gmail.com", 231301, 620},
{"author Vicent Marti with an outrageously long name which will probably overflow the buffer <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti with an outrageously long name which will probably overflow the buffer", "tanoku@gmail.com", 12345, 0},
{"author Vicent Marti <tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com", 12345, 0},
{"committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
{"committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 60},
{"committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, -60},
/* Parse a signature without an author field */
{"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
/* Parse a signature without an author field */
{"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
/* Parse a signature with an empty author field */
{"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
/* Parse a signature with an empty email field */
{"committer Vicent Marti <> 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
/* Parse a signature with an empty email field */
{"committer Vicent Marti < > 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
/* Parse a signature with empty name and email */
{"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
/* Parse a signature with empty name and email */
{"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
/* Parse a signature with empty name and email */
{"committer < > 123456 -0100 \n", "committer ", "", "", 123456, -60},
/* Parse an obviously invalid signature */
{"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
/* Parse an obviously invalid signature */
{"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
/* Parse an obviously invalid signature */
{"committer <>\n", "committer ", "", "", 0, 0},
{"committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
{"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
{"author Vicent Marti <tanoku@gmail.com>\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
/* a variety of dates */
{"author Vicent Marti <tanoku@gmail.com> 0 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
{"author Vicent Marti <tanoku@gmail.com> 1234567890 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 1234567890, 0},
{"author Vicent Marti <tanoku@gmail.com> 2147483647 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0x7fffffff, 0},
{"author Vicent Marti <tanoku@gmail.com> 4294967295 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0xffffffff, 0},
{"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
{"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
{NULL,NULL,NULL,NULL,0,0}
};
typedef struct {
const char *string;
const char *header;
} failing_signature_test_case;
failing_signature_test_case failing_signature_cases[] = {
{"committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n", "committer "},
{"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author "},
{"author Vicent Marti <tanoku@gmail.com> 12345 \n", "committer "},
{"author Vicent Marti 12345 \n", "author "},
{"author Vicent Marti <broken@email 12345 \n", "author "},
{"committer Vicent Marti ><\n", "committer "},
{"author ", "author "},
{NULL, NULL,}
};
void test_commit_parse__signature(void)
{
passing_signature_test_case *passcase;
failing_signature_test_case *failcase;
for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
{
const char *str = passcase->string;
size_t len = strlen(passcase->string);
struct git_signature person = {0};
cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
cl_assert_equal_s(passcase->name, person.name);
cl_assert_equal_s(passcase->email, person.email);
cl_assert_equal_i((int)passcase->time, (int)person.when.time);
cl_assert_equal_i(passcase->offset, person.when.offset);
git__free(person.name); git__free(person.email);
}
for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
{
const char *str = failcase->string;
size_t len = strlen(failcase->string);
git_signature person = {0};
cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
git__free(person.name); git__free(person.email);
}
}
static char *failing_commit_cases[] = {
/* empty commit */
"",
/* random garbage */
"asd97sa9du902e9a0jdsuusad09as9du098709aweu8987sd\n",
/* broken endlines 1 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\r\n\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\r\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
\r\n\
a test commit with broken endlines\r\n",
/* broken endlines 2 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
\
another test commit with broken endlines",
/* starting endlines */
"\ntree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a test commit with a starting endline\n",
/* corrupted commit 1 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent 05452d6349abcd67aa396df",
/* corrupted commit 2 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent ",
/* corrupted commit 3 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
parent ",
/* corrupted commit 4 */
"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
par",
};
static char *passing_commit_cases[] = {
/* simple commit with no message */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n",
/* simple commit, no parent */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a simple commit which works\n",
/* simple commit, no parent, no newline in message */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a simple commit which works",
/* simple commit, 1 parent */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent e90810b8df3e80c413d903f631643c716887138d\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a simple commit which works\n",
/* simple commit with GPG signature */
"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
gpgsig -----BEGIN PGP SIGNATURE-----\n\
Version: GnuPG v1.4.12 (Darwin)\n\
\n\
iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
cpxtDQQMGYFpXK/71stq\n\
=ozeK\n\
-----END PGP SIGNATURE-----\n\
\n\
a simple commit which works\n",
/* some tools create two author entries */
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
author Helpful Coworker <helpful@coworker> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
a simple commit which works",
};
static int parse_commit(git_commit **out, const char *buffer)
{
git_commit *commit;
git_odb_object fake_odb_object;
int error;
commit = (git_commit*)git__malloc(sizeof(git_commit));
memset(commit, 0x0, sizeof(git_commit));
commit->object.repo = g_repo;
memset(&fake_odb_object, 0x0, sizeof(git_odb_object));
fake_odb_object.buffer = (char *)buffer;
fake_odb_object.cached.size = strlen(fake_odb_object.buffer);
error = git_commit__parse(commit, &fake_odb_object);
*out = commit;
return error;
}
void test_commit_parse__entire_commit(void)
{
const int failing_commit_count = ARRAY_SIZE(failing_commit_cases);
const int passing_commit_count = ARRAY_SIZE(passing_commit_cases);
int i;
git_commit *commit;
for (i = 0; i < failing_commit_count; ++i) {
cl_git_fail(parse_commit(&commit, failing_commit_cases[i]));
git_commit__free(commit);
}
for (i = 0; i < passing_commit_count; ++i) {
cl_git_pass(parse_commit(&commit, passing_commit_cases[i]));
if (!i)
cl_assert_equal_s("", git_commit_message(commit));
else
cl_assert(git__prefixcmp(
git_commit_message(commit), "a simple commit which works") == 0);
git_commit__free(commit);
}
}
/* query the details on a parsed commit */
void test_commit_parse__details0(void) {
static const char *commit_ids[] = {
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
};
const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
unsigned int i;
for (i = 0; i < commit_count; ++i) {
git_oid id;
git_commit *commit;
const git_signature *author, *committer;
const char *message;
git_time_t commit_time;
unsigned int parents, p;
git_commit *parent = NULL, *old_parent = NULL;
git_oid_fromstr(&id, commit_ids[i]);
cl_git_pass(git_commit_lookup(&commit, g_repo, &id));
message = git_commit_message(commit);
author = git_commit_author(commit);
committer = git_commit_committer(commit);
commit_time = git_commit_time(commit);
parents = git_commit_parentcount(commit);
cl_assert_equal_s("Scott Chacon", author->name);
cl_assert_equal_s("schacon@gmail.com", author->email);
cl_assert_equal_s("Scott Chacon", committer->name);
cl_assert_equal_s("schacon@gmail.com", committer->email);
cl_assert(message != NULL);
cl_assert(commit_time > 0);
cl_assert(parents <= 2);
for (p = 0;p < parents;p++) {
if (old_parent != NULL)
git_commit_free(old_parent);
old_parent = parent;
cl_git_pass(git_commit_parent(&parent, commit, p));
cl_assert(parent != NULL);
cl_assert(git_commit_author(parent) != NULL); /* is it really a commit? */
}
git_commit_free(old_parent);
git_commit_free(parent);
cl_git_fail(git_commit_parent(&parent, commit, parents));
git_commit_free(commit);
}
}
void test_commit_parse__leading_lf(void)
{
git_commit *commit;
const char *buffer =
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent e90810b8df3e80c413d903f631643c716887138d\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
\n\
\n\
This commit has a few LF at the start of the commit message";
const char *message =
"This commit has a few LF at the start of the commit message";
const char *raw_message =
"\n\
\n\
This commit has a few LF at the start of the commit message";
cl_git_pass(parse_commit(&commit, buffer));
cl_assert_equal_s(message, git_commit_message(commit));
cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
git_commit__free(commit);
}
void test_commit_parse__only_lf(void)
{
git_commit *commit;
const char *buffer =
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent e90810b8df3e80c413d903f631643c716887138d\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
\n\
\n";
const char *message = "";
const char *raw_message = "\n\n";
cl_git_pass(parse_commit(&commit, buffer));
cl_assert_equal_s(message, git_commit_message(commit));
cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
git_commit__free(commit);
}
void test_commit_parse__arbitrary_field(void)
{
git_commit *commit;
git_buf buf = GIT_BUF_INIT;
const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
Version: GnuPG v1.4.12 (Darwin)\n\
\n\
iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
cpxtDQQMGYFpXK/71stq\n\
=ozeK\n\
-----END PGP SIGNATURE-----";
cl_git_pass(parse_commit(&commit, passing_commit_cases[4]));
cl_git_pass(git_commit_header_field(&buf, commit, "tree"));
cl_assert_equal_s("6b79e22d69bf46e289df0345a14ca059dfc9bdf6", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_commit_header_field(&buf, commit, "parent"));
cl_assert_equal_s("34734e478d6cf50c27c9d69026d93974d052c454", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_commit_header_field(&buf, commit, "gpgsig"));
cl_assert_equal_s(gpgsig, buf.ptr);
git_buf_clear(&buf);
cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "awesomeness"));
cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "par"));
git_commit__free(commit);
cl_git_pass(parse_commit(&commit, passing_commit_cases[0]));
cl_git_pass(git_commit_header_field(&buf, commit, "committer"));
cl_assert_equal_s("Vicent Marti <tanoku@gmail.com> 1273848544 +0200", buf.ptr);
git_buf_dispose(&buf);
git_commit__free(commit);
}
void test_commit_parse__extract_signature(void)
{
git_odb *odb;
git_oid commit_id;
git_buf signature = GIT_BUF_INIT, signed_data = GIT_BUF_INIT;
const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
Version: GnuPG v1.4.12 (Darwin)\n\
\n\
iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
cpxtDQQMGYFpXK/71stq\n\
=ozeK\n\
-----END PGP SIGNATURE-----";
const char *data = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which works\n";
const char *oneline_signature = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
author Some User <someuser@gmail.com> 1454537944 -0700\n\
committer Some User <someuser@gmail.com> 1454537944 -0700\n\
gpgsig bad\n\
\n\
corrupt signature\n";
const char *oneline_data = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
author Some User <someuser@gmail.com> 1454537944 -0700\n\
committer Some User <someuser@gmail.com> 1454537944 -0700\n\
\n\
corrupt signature\n";
cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJECT_COMMIT));
cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
cl_assert_equal_s(gpgsig, signature.ptr);
cl_assert_equal_s(data, signed_data.ptr);
git_buf_clear(&signature);
git_buf_clear(&signed_data);
cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, "gpgsig"));
cl_assert_equal_s(gpgsig, signature.ptr);
cl_assert_equal_s(data, signed_data.ptr);
/* Try to parse a tree */
cl_git_pass(git_oid_fromstr(&commit_id, "45dd856fdd4d89b884c340ba0e047752d9b085d6"));
cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
/* Try to parse an unsigned commit */
cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[1], strlen(passing_commit_cases[1]), GIT_OBJECT_COMMIT));
cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
cl_assert_equal_i(GIT_ERROR_OBJECT, git_error_last()->klass);
/* Parse the commit with a single-line signature */
git_buf_clear(&signature);
git_buf_clear(&signed_data);
cl_git_pass(git_odb_write(&commit_id, odb, oneline_signature, strlen(oneline_signature), GIT_OBJECT_COMMIT));
cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
cl_assert_equal_s("bad", signature.ptr);
cl_assert_equal_s(oneline_data, signed_data.ptr);
git_buf_dispose(&signature);
git_buf_dispose(&signed_data);
}
+148
View File
@@ -0,0 +1,148 @@
#include "clar_libgit2.h"
#include "signature.h"
static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
{
git_signature *sign;
int error = 0;
if ((error = git_signature_new(&sign, name, email, time, offset)) < 0)
return error;
git_signature_free((git_signature *)sign);
return error;
}
static void assert_name_and_email(
const char *expected_name,
const char *expected_email,
const char *name,
const char *email)
{
git_signature *sign;
cl_git_pass(git_signature_new(&sign, name, email, 1234567890, 60));
cl_assert_equal_s(expected_name, sign->name);
cl_assert_equal_s(expected_email, sign->email);
git_signature_free(sign);
}
void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
{
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com ");
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com \n");
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n");
}
void test_commit_signature__leading_and_trailing_crud_is_trimmed(void)
{
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\"");
assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com");
assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com");
}
void test_commit_signature__timezone_does_not_read_oob(void)
{
const char *header = "A <a@example.com> 1461698487 +1234", *header_end;
git_signature *sig;
/* Let the buffer end midway between the timezone offeset's "+12" and "34" */
header_end = header + strlen(header) - 2;
sig = git__calloc(1, sizeof(git_signature));
cl_assert(sig);
cl_git_pass(git_signature__parse(sig, &header, header_end, NULL, '\0'));
cl_assert_equal_s(sig->name, "A");
cl_assert_equal_s(sig->email, "a@example.com");
cl_assert_equal_i(sig->when.time, 1461698487);
cl_assert_equal_i(sig->when.offset, 12);
git_signature_free(sig);
}
void test_commit_signature__angle_brackets_in_names_are_not_supported(void)
{
cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil>Haack", "phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("<Phil Haack>", "phil@haack", 1234567890, 60));
}
void test_commit_signature__angle_brackets_in_email_are_not_supported(void)
{
cl_git_fail(try_build_signature("Phil Haack", ">phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil Haack", "phil@>haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil Haack", "<phil@haack>", 1234567890, 60));
}
void test_commit_signature__create_empties(void)
{
/* can not create a signature with empty name or email */
cl_git_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60));
cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60));
cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
}
void test_commit_signature__create_one_char(void)
{
/* creating a one character signature */
assert_name_and_email("x", "foo@bar.baz", "x", "foo@bar.baz");
}
void test_commit_signature__create_two_char(void)
{
/* creating a two character signature */
assert_name_and_email("xx", "foo@bar.baz", "xx", "foo@bar.baz");
}
void test_commit_signature__create_zero_char(void)
{
/* creating a zero character signature */
git_signature *sign;
cl_git_fail(git_signature_new(&sign, "", "x@y.z", 1234567890, 60));
cl_assert(sign == NULL);
}
void test_commit_signature__from_buf(void)
{
git_signature *sign;
cl_git_pass(git_signature_from_buffer(&sign, "Test User <test@test.tt> 1461698487 +0200"));
cl_assert_equal_s("Test User", sign->name);
cl_assert_equal_s("test@test.tt", sign->email);
cl_assert_equal_i(1461698487, sign->when.time);
cl_assert_equal_i(120, sign->when.offset);
git_signature_free(sign);
}
void test_commit_signature__from_buf_with_neg_zero_offset(void)
{
git_signature *sign;
cl_git_pass(git_signature_from_buffer(&sign, "Test User <test@test.tt> 1461698487 -0000"));
cl_assert_equal_s("Test User", sign->name);
cl_assert_equal_s("test@test.tt", sign->email);
cl_assert_equal_i(1461698487, sign->when.time);
cl_assert_equal_i(0, sign->when.offset);
cl_assert_equal_i('-', sign->when.sign);
git_signature_free(sign);
}
void test_commit_signature__pos_and_neg_zero_offsets_dont_match(void)
{
git_signature *with_neg_zero;
git_signature *with_pos_zero;
cl_git_pass(git_signature_from_buffer(&with_neg_zero, "Test User <test@test.tt> 1461698487 -0000"));
cl_git_pass(git_signature_from_buffer(&with_pos_zero, "Test User <test@test.tt> 1461698487 +0000"));
cl_assert(!git_signature__equal(with_neg_zero, with_pos_zero));
git_signature_free((git_signature *)with_neg_zero);
git_signature_free((git_signature *)with_pos_zero);
}
+424
View File
@@ -0,0 +1,424 @@
#include "clar_libgit2.h"
#include "git2/sys/commit.h"
static const char *committer_name = "Vicent Marti";
static const char *committer_email = "vicent@github.com";
static const char *commit_message = "This commit has been created in memory\n\
This is a commit created in memory and it will be written back to disk\n";
static const char *tree_id_str = "1810dff58d8a660512d4832e740f692884338ccd";
static const char *parent_id_str = "8496071c1b46c854b31185ea97743be6a8774479";
static const char *root_commit_message = "This is a root commit\n\
This is a root commit and should be the only one in this branch\n";
static const char *root_reflog_message = "commit (initial): This is a root commit \
This is a root commit and should be the only one in this branch";
static char *head_old;
static git_reference *head, *branch;
static git_commit *commit;
/* Fixture setup */
static git_repository *g_repo;
void test_commit_write__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
}
void test_commit_write__cleanup(void)
{
git_reference_free(head);
head = NULL;
git_reference_free(branch);
branch = NULL;
git_commit_free(commit);
commit = NULL;
git__free(head_old);
head_old = NULL;
cl_git_sandbox_cleanup();
cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
}
/* write a new commit object from memory to disk */
void test_commit_write__from_memory(void)
{
git_oid tree_id, parent_id, commit_id;
git_signature *author, *committer;
const git_signature *author1, *committer1;
git_commit *parent;
git_tree *tree;
git_oid_fromstr(&tree_id, tree_id_str);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
git_oid_fromstr(&parent_id, parent_id_str);
cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
/* create signatures */
cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
cl_git_pass(git_commit_create_v(
&commit_id, /* out id */
g_repo,
NULL, /* do not update the HEAD */
author,
committer,
NULL,
commit_message,
tree,
1, parent));
git_object_free((git_object *)parent);
git_object_free((git_object *)tree);
git_signature_free(committer);
git_signature_free(author);
cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
/* Check attributes were set correctly */
author1 = git_commit_author(commit);
cl_assert(author1 != NULL);
cl_assert_equal_s(committer_name, author1->name);
cl_assert_equal_s(committer_email, author1->email);
cl_assert(author1->when.time == 987654321);
cl_assert(author1->when.offset == 90);
committer1 = git_commit_committer(commit);
cl_assert(committer1 != NULL);
cl_assert_equal_s(committer_name, committer1->name);
cl_assert_equal_s(committer_email, committer1->email);
cl_assert(committer1->when.time == 123456789);
cl_assert(committer1->when.offset == 60);
cl_assert_equal_s(commit_message, git_commit_message(commit));
}
void test_commit_write__into_buf(void)
{
git_oid tree_id;
git_signature *author, *committer;
git_tree *tree;
git_commit *parent;
git_oid parent_id;
git_buf commit = GIT_BUF_INIT;
git_oid_fromstr(&tree_id, tree_id_str);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
/* create signatures */
cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
git_oid_fromstr(&parent_id, parent_id_str);
cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
cl_git_pass(git_commit_create_buffer(&commit, g_repo, author, committer,
NULL, root_commit_message, tree, 1, (const git_commit **) &parent));
cl_assert_equal_s(commit.ptr,
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
author Vicent Marti <vicent@github.com> 987654321 +0130\n\
committer Vicent Marti <vicent@github.com> 123456789 +0100\n\
\n\
This is a root commit\n\
This is a root commit and should be the only one in this branch\n\
");
git_buf_dispose(&commit);
git_tree_free(tree);
git_commit_free(parent);
git_signature_free(author);
git_signature_free(committer);
}
/* create a root commit */
void test_commit_write__root(void)
{
git_oid tree_id, commit_id;
const git_oid *branch_oid;
git_signature *author, *committer;
const char *branch_name = "refs/heads/root-commit-branch";
git_tree *tree;
git_reflog *log;
const git_reflog_entry *entry;
git_oid_fromstr(&tree_id, tree_id_str);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
/* create signatures */
cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
/* First we need to update HEAD so it points to our non-existant branch */
cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC);
head_old = git__strdup(git_reference_symbolic_target(head));
cl_assert(head_old != NULL);
git_reference_free(head);
cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1, NULL));
cl_git_pass(git_commit_create_v(
&commit_id, /* out id */
g_repo,
"HEAD",
author,
committer,
NULL,
root_commit_message,
tree,
0));
git_object_free((git_object *)tree);
git_signature_free(author);
/*
* The fact that creating a commit works has already been
* tested. Here we just make sure it's our commit and that it was
* written as a root commit.
*/
cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
cl_assert(git_commit_parentcount(commit) == 0);
cl_git_pass(git_reference_lookup(&branch, g_repo, branch_name));
branch_oid = git_reference_target(branch);
cl_assert_equal_oid(branch_oid, &commit_id);
cl_assert_equal_s(root_commit_message, git_commit_message(commit));
cl_git_pass(git_reflog_read(&log, g_repo, branch_name));
cl_assert_equal_i(1, git_reflog_entrycount(log));
entry = git_reflog_entry_byindex(log, 0);
cl_assert_equal_s(committer->email, git_reflog_entry_committer(entry)->email);
cl_assert_equal_s(committer->name, git_reflog_entry_committer(entry)->name);
cl_assert_equal_s(root_reflog_message, git_reflog_entry_message(entry));
git_signature_free(committer);
git_reflog_free(log);
}
static int create_commit_from_ids(
git_oid *result,
const git_oid *tree_id,
const git_oid *parent_id)
{
git_signature *author, *committer;
const git_oid *parent_ids[1];
int ret;
cl_git_pass(git_signature_new(
&committer, committer_name, committer_email, 123456789, 60));
cl_git_pass(git_signature_new(
&author, committer_name, committer_email, 987654321, 90));
parent_ids[0] = parent_id;
ret = git_commit_create_from_ids(
result,
g_repo,
NULL,
author,
committer,
NULL,
root_commit_message,
tree_id,
1,
parent_ids);
git_signature_free(committer);
git_signature_free(author);
return ret;
}
void test_commit_write__can_write_invalid_objects(void)
{
git_oid expected_id, tree_id, parent_id, commit_id;
cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
/* this is a valid tree and parent */
git_oid_fromstr(&tree_id, tree_id_str);
git_oid_fromstr(&parent_id, parent_id_str);
git_oid_fromstr(&expected_id, "c8571bbec3a72c4bcad31648902e5a453f1adece");
cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
cl_assert_equal_oid(&expected_id, &commit_id);
/* this is a wholly invented tree id */
git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890");
git_oid_fromstr(&parent_id, parent_id_str);
git_oid_fromstr(&expected_id, "996008340b8e68d69bf3c28d7c57fb7ec3c8e202");
cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
cl_assert_equal_oid(&expected_id, &commit_id);
/* this is a wholly invented parent id */
git_oid_fromstr(&tree_id, tree_id_str);
git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890");
git_oid_fromstr(&expected_id, "d78f660cab89d9791ca6714b57978bf2a7e709fd");
cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
cl_assert_equal_oid(&expected_id, &commit_id);
/* these are legitimate objects, but of the wrong type */
git_oid_fromstr(&tree_id, parent_id_str);
git_oid_fromstr(&parent_id, tree_id_str);
git_oid_fromstr(&expected_id, "5d80c07414e3f18792949699dfcacadf7748f361");
cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
cl_assert_equal_oid(&expected_id, &commit_id);
}
void test_commit_write__can_validate_objects(void)
{
git_oid tree_id, parent_id, commit_id;
/* this is a valid tree and parent */
git_oid_fromstr(&tree_id, tree_id_str);
git_oid_fromstr(&parent_id, parent_id_str);
cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
/* this is a wholly invented tree id */
git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890");
git_oid_fromstr(&parent_id, parent_id_str);
cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
/* this is a wholly invented parent id */
git_oid_fromstr(&tree_id, tree_id_str);
git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890");
cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
/* these are legitimate objects, but of the wrong type */
git_oid_fromstr(&tree_id, parent_id_str);
git_oid_fromstr(&parent_id, tree_id_str);
cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
}
void test_commit_write__attach_signature_checks_objects(void)
{
const char *sig = "magic word: pretty please";
const char *badtree = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which does not work\n";
const char *badparent = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which does not work\n";
git_oid id;
cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badtree, sig, "magicsig"));
cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badparent, sig, "magicsig"));
}
void test_commit_write__attach_singleline_signature(void)
{
const char *sig = "magic word: pretty please";
const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which works\n";
const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
magicsig magic word: pretty please\n\
\n\
a simple commit which works\n";
git_oid id;
git_odb *odb;
git_odb_object *obj;
cl_git_pass(git_commit_create_with_signature(&id, g_repo, data, sig, "magicsig"));
cl_git_pass(git_repository_odb(&odb, g_repo));
cl_git_pass(git_odb_read(&obj, odb, &id));
cl_assert_equal_s(complete, git_odb_object_data(obj));
git_odb_object_free(obj);
git_odb_free(odb);
}
void test_commit_write__attach_multiline_signature(void)
{
const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
Version: GnuPG v1.4.12 (Darwin)\n\
\n\
iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
cpxtDQQMGYFpXK/71stq\n\
=ozeK\n\
-----END PGP SIGNATURE-----";
const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which works\n";
const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
parent 8496071c1b46c854b31185ea97743be6a8774479\n\
author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
gpgsig -----BEGIN PGP SIGNATURE-----\n\
Version: GnuPG v1.4.12 (Darwin)\n\
\n\
iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
cpxtDQQMGYFpXK/71stq\n\
=ozeK\n\
-----END PGP SIGNATURE-----\n\
\n\
a simple commit which works\n";
git_oid one, two;
git_odb *odb;
git_odb_object *obj;
cl_git_pass(git_commit_create_with_signature(&one, g_repo, data, gpgsig, "gpgsig"));
cl_git_pass(git_commit_create_with_signature(&two, g_repo, data, gpgsig, NULL));
cl_assert(!git_oid_cmp(&one, &two));
cl_git_pass(git_repository_odb(&odb, g_repo));
cl_git_pass(git_odb_read(&obj, odb, &one));
cl_assert_equal_s(complete, git_odb_object_data(obj));
git_odb_object_free(obj);
git_odb_free(odb);
}
+37
View File
@@ -0,0 +1,37 @@
#include "clar_libgit2.h"
void test_config_add__initialize(void)
{
cl_fixture_sandbox("config/config10");
}
void test_config_add__cleanup(void)
{
cl_fixture_cleanup("config10");
}
void test_config_add__to_existing_section(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
cl_git_pass(git_config_set_int32(cfg, "empty.tmp", 5));
cl_git_pass(git_config_get_int32(&i, cfg, "empty.tmp"));
cl_assert(i == 5);
cl_git_pass(git_config_delete_entry(cfg, "empty.tmp"));
git_config_free(cfg);
}
void test_config_add__to_new_section(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
cl_git_pass(git_config_set_int32(cfg, "section.tmp", 5));
cl_git_pass(git_config_get_int32(&i, cfg, "section.tmp"));
cl_assert(i == 5);
cl_git_pass(git_config_delete_entry(cfg, "section.tmp"));
git_config_free(cfg);
}
+24
View File
@@ -0,0 +1,24 @@
#include "clar_libgit2.h"
#include "git2/sys/config.h"
void test_config_backend__checks_version(void)
{
git_config *cfg;
git_config_backend backend = GIT_CONFIG_BACKEND_INIT;
const git_error *err;
backend.version = 1024;
cl_git_pass(git_config_new(&cfg));
cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
git_error_clear();
backend.version = 1024;
cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = git_error_last();
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
git_config_free(cfg);
}
+148
View File
@@ -0,0 +1,148 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
#include "repository.h"
#ifdef GIT_WIN32
# define ROOT_PREFIX "C:"
#else
# define ROOT_PREFIX
#endif
static git_repository *_repo;
void test_config_conditionals__initialize(void)
{
_repo = cl_git_sandbox_init("empty_standard_repo");
}
void test_config_conditionals__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void assert_condition_includes(const char *keyword, const char *path, bool expected)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_git_pass(git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path));
cl_git_pass(git_buf_puts(&buf, "path = other\n"));
cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
_repo = cl_git_sandbox_reopen();
cl_git_pass(git_repository_config(&cfg, _repo));
if (expected) {
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
} else {
cl_git_fail_with(GIT_ENOTFOUND,
git_config_get_string_buf(&buf, cfg, "foo.bar"));
}
git_buf_dispose(&buf);
git_config_free(cfg);
}
static char *sandbox_path(git_buf *buf, const char *suffix)
{
char *path = p_realpath(clar_sandbox_path(), NULL);
cl_assert(path);
cl_git_pass(git_buf_attach(buf, path, 0));
cl_git_pass(git_buf_joinpath(buf, buf->ptr, suffix));
return buf->ptr;
}
void test_config_conditionals__gitdir(void)
{
git_buf path = GIT_BUF_INIT;
assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
assert_condition_includes("gitdir", "empty_stand", false);
assert_condition_includes("gitdir", "empty_stand/", false);
assert_condition_includes("gitdir", "empty_stand/.git", false);
assert_condition_includes("gitdir", "empty_stand/.git/", false);
assert_condition_includes("gitdir", "empty_stand*/", true);
assert_condition_includes("gitdir", "empty_stand*/.git", true);
assert_condition_includes("gitdir", "empty_stand*/.git/", false);
assert_condition_includes("gitdir", "empty_standard_repo", false);
assert_condition_includes("gitdir", "empty_standard_repo/", true);
assert_condition_includes("gitdir", "empty_standard_repo/.git", true);
assert_condition_includes("gitdir", "empty_standard_repo/.git/", false);
assert_condition_includes("gitdir", "./", false);
assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
assert_condition_includes("gitdir", "~/empty_standard_repo", false);
assert_condition_includes("gitdir", sandbox_path(&path, "/"), true);
assert_condition_includes("gitdir", sandbox_path(&path, "/*"), false);
assert_condition_includes("gitdir", sandbox_path(&path, "/**"), true);
assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo"), false);
assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo/"), true);
assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo/"), true);
assert_condition_includes("gitdir", sandbox_path(&path, "Empty_Standard_Repo"), false);
assert_condition_includes("gitdir", sandbox_path(&path, "Empty_Standard_Repo/"), false);
git_buf_dispose(&path);
}
void test_config_conditionals__gitdir_i(void)
{
git_buf path = GIT_BUF_INIT;
assert_condition_includes("gitdir/i", sandbox_path(&path, "empty_standard_repo/"), true);
assert_condition_includes("gitdir/i", sandbox_path(&path, "EMPTY_STANDARD_REPO/"), true);
git_buf_dispose(&path);
}
void test_config_conditionals__invalid_conditional_fails(void)
{
assert_condition_includes("foobar", ".git", false);
}
static void set_head(git_repository *repo, const char *name)
{
cl_git_pass(git_repository_create_head(git_repository_path(repo), name));
}
void test_config_conditionals__onbranch(void)
{
assert_condition_includes("onbranch", "master", true);
assert_condition_includes("onbranch", "m*", true);
assert_condition_includes("onbranch", "*", true);
assert_condition_includes("onbranch", "master/", false);
assert_condition_includes("onbranch", "foo", false);
set_head(_repo, "foo");
assert_condition_includes("onbranch", "master", false);
assert_condition_includes("onbranch", "foo", true);
assert_condition_includes("onbranch", "f*o", true);
set_head(_repo, "dir/ref");
assert_condition_includes("onbranch", "dir/ref", true);
assert_condition_includes("onbranch", "dir/", true);
assert_condition_includes("onbranch", "dir/*", true);
assert_condition_includes("onbranch", "dir/**", true);
assert_condition_includes("onbranch", "**", true);
assert_condition_includes("onbranch", "dir", false);
assert_condition_includes("onbranch", "dir*", false);
set_head(_repo, "dir/subdir/ref");
assert_condition_includes("onbranch", "dir/subdir/", true);
assert_condition_includes("onbranch", "dir/subdir/*", true);
assert_condition_includes("onbranch", "dir/subdir/ref", true);
assert_condition_includes("onbranch", "dir/", true);
assert_condition_includes("onbranch", "dir/**", true);
assert_condition_includes("onbranch", "**", true);
assert_condition_includes("onbranch", "dir", false);
assert_condition_includes("onbranch", "dir*", false);
assert_condition_includes("onbranch", "dir/*", false);
}
+68
View File
@@ -0,0 +1,68 @@
#include "clar_libgit2.h"
#include "config_helpers.h"
#include "repository.h"
#include "buffer.h"
void assert_config_entry_existence(
git_repository *repo,
const char *name,
bool is_supposed_to_exist)
{
git_config *config;
git_config_entry *entry = NULL;
int result;
cl_git_pass(git_repository_config__weakptr(&config, repo));
result = git_config_get_entry(&entry, config, name);
git_config_entry_free(entry);
if (is_supposed_to_exist)
cl_git_pass(result);
else
cl_assert_equal_i(GIT_ENOTFOUND, result);
}
void assert_config_entry_value(
git_repository *repo,
const char *name,
const char *expected_value)
{
git_config *config;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_repository_config__weakptr(&config, repo));
cl_git_pass(git_config_get_string_buf(&buf, config, name));
cl_assert_equal_s(expected_value, git_buf_cstr(&buf));
git_buf_dispose(&buf);
}
static int count_config_entries_cb(
const git_config_entry *entry,
void *payload)
{
int *how_many = (int *)payload;
GIT_UNUSED(entry);
(*how_many)++;
return 0;
}
int count_config_entries_match(git_repository *repo, const char *pattern)
{
git_config *config;
int how_many = 0;
cl_git_pass(git_repository_config(&config, repo));
cl_assert_equal_i(0, git_config_foreach_match(
config, pattern, count_config_entries_cb, &how_many));
git_config_free(config);
return how_many;
}
+13
View File
@@ -0,0 +1,13 @@
extern void assert_config_entry_existence(
git_repository *repo,
const char *name,
bool is_supposed_to_exist);
extern void assert_config_entry_value(
git_repository *repo,
const char *name,
const char *expected_value);
extern int count_config_entries_match(
git_repository *repo,
const char *pattern);
+73
View File
@@ -0,0 +1,73 @@
#include "clar_libgit2.h"
void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void)
{
int error;
git_config *cfg;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
cl_git_fail(error);
cl_assert_equal_i(GIT_EEXISTS, error);
git_config_free(cfg);
}
void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(void)
{
git_config *cfg;
git_buf buf = {0};
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
cl_assert_equal_s("don't find me!", buf.ptr);
git_buf_dispose(&buf);
git_config_free(cfg);
}
void test_config_configlevel__can_read_from_a_single_level_focused_file_after_parent_config_has_been_freed(void)
{
git_config *cfg;
git_config *single_level_cfg;
git_buf buf = {0};
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
git_config_free(cfg);
cl_git_pass(git_config_get_string_buf(&buf, single_level_cfg, "core.stringglobal"));
cl_assert_equal_s("don't find me!", buf.ptr);
git_buf_dispose(&buf);
git_config_free(single_level_cfg);
}
void test_config_configlevel__fetching_a_level_from_an_empty_compound_config_returns_ENOTFOUND(void)
{
git_config *cfg;
git_config *local_cfg;
cl_git_pass(git_config_new(&cfg));
cl_assert_equal_i(GIT_ENOTFOUND, git_config_open_level(&local_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
git_config_free(cfg);
}
+171
View File
@@ -0,0 +1,171 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
void test_config_global__initialize(void)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_futils_mkdir_r("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
cl_git_pass(git_futils_mkdir_r("xdg/git", 0777));
cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
cl_git_pass(git_futils_mkdir_r("etc", 0777));
cl_git_pass(git_path_prettify(&path, "etc", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
git_buf_dispose(&path);
}
void test_config_global__cleanup(void)
{
cl_sandbox_set_search_path_defaults();
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_config_global__open_global(void)
{
git_config *cfg, *global, *selected, *dummy;
int32_t value;
cl_git_mkfile("home/.gitconfig", "[global]\n test = 4567\n");
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_int32(&value, cfg, "global.test"));
cl_assert_equal_i(4567, value);
cl_git_pass(git_config_open_level(&global, cfg, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_get_int32(&value, global, "global.test"));
cl_assert_equal_i(4567, value);
cl_git_fail(git_config_open_level(&dummy, cfg, GIT_CONFIG_LEVEL_XDG));
cl_git_pass(git_config_open_global(&selected, cfg));
cl_git_pass(git_config_get_int32(&value, selected, "global.test"));
cl_assert_equal_i(4567, value);
git_config_free(selected);
git_config_free(global);
git_config_free(cfg);
}
void test_config_global__open_symlinked_global(void)
{
#ifndef GIT_WIN32
git_config *cfg;
int32_t value;
cl_git_mkfile("home/.gitconfig.linked", "[global]\n test = 4567\n");
cl_must_pass(symlink(".gitconfig.linked", "home/.gitconfig"));
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_int32(&value, cfg, "global.test"));
cl_assert_equal_i(4567, value);
git_config_free(cfg);
#endif
}
void test_config_global__lock_missing_global_config(void)
{
git_config *cfg;
git_config_entry *entry;
git_transaction *transaction;
p_unlink("home/.gitconfig"); /* No global config */
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_lock(&transaction, cfg));
cl_git_pass(git_config_set_string(cfg, "assertion.fail", "boom"));
cl_git_pass(git_transaction_commit(transaction));
git_transaction_free(transaction);
/* cfg is updated */
cl_git_pass(git_config_get_entry(&entry, cfg, "assertion.fail"));
cl_assert_equal_s("boom", entry->value);
git_config_entry_free(entry);
git_config_free(cfg);
/* We can reread the new value from the global config */
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_get_entry(&entry, cfg, "assertion.fail"));
cl_assert_equal_s("boom", entry->value);
git_config_entry_free(entry);
git_config_free(cfg);
}
void test_config_global__open_xdg(void)
{
git_config *cfg, *xdg, *selected;
const char *str = "teststring";
const char *key = "this.variable";
git_buf buf = {0};
cl_git_mkfile("xdg/git/config", "# XDG config\n[core]\n test = 1\n");
cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
cl_git_pass(git_config_open_global(&selected, cfg));
cl_git_pass(git_config_set_string(xdg, key, str));
cl_git_pass(git_config_get_string_buf(&buf, selected, key));
cl_assert_equal_s(str, buf.ptr);
git_buf_dispose(&buf);
git_config_free(selected);
git_config_free(xdg);
git_config_free(cfg);
}
void test_config_global__open_programdata(void)
{
git_config *cfg;
git_repository *repo;
git_buf config_path = GIT_BUF_INIT;
git_buf var_contents = GIT_BUF_INIT;
if (cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
cl_skip();
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH,
GIT_CONFIG_LEVEL_PROGRAMDATA, &config_path));
if (!git_path_isdir(config_path.ptr))
cl_git_pass(p_mkdir(config_path.ptr, 0777));
cl_git_pass(git_buf_puts(&config_path, "/config"));
cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr));
cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level"));
git_buf_dispose(&config_path);
git_config_free(cfg);
git_config_open_default(&cfg);
cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
cl_assert_equal_s("even higher level", var_contents.ptr);
git_config_free(cfg);
git_buf_dispose(&var_contents);
cl_git_pass(git_repository_init(&repo, "./foo.git", true));
cl_git_pass(git_repository_config(&cfg, repo));
cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
cl_assert_equal_s("even higher level", var_contents.ptr);
git_config_free(cfg);
git_buf_dispose(&var_contents);
git_repository_free(repo);
cl_fixture_cleanup("./foo.git");
}
+234
View File
@@ -0,0 +1,234 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
static git_config *cfg;
static git_buf buf;
void test_config_include__initialize(void)
{
cfg = NULL;
git_buf_init(&buf, 0);
}
void test_config_include__cleanup(void)
{
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_include__relative(void)
{
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
}
void test_config_include__absolute(void)
{
cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
git_buf_dispose(&buf);
cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("config-include-absolute"));
}
void test_config_include__homedir(void)
{
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("config-include-homedir"));
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__ordering(void)
{
cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
cl_git_mkfile("including",
"[foo \"bar\"]\nfrotz = hello\n"
"[include]\npath = included\n"
"[foo \"bar\"]\nbaz = huzzah\n");
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.frotz"));
cl_assert_equal_s("hiya", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("included"));
cl_git_pass(p_unlink("including"));
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__depth(void)
{
cl_git_mkfile("a", "[include]\npath = b");
cl_git_mkfile("b", "[include]\npath = a");
cl_git_fail(git_config_open_ondisk(&cfg, "a"));
cl_git_pass(p_unlink("a"));
cl_git_pass(p_unlink("b"));
}
void test_config_include__empty_path_sanely_handled(void)
{
cl_git_mkfile("a", "[include]\npath");
cl_git_pass(git_config_open_ondisk(&cfg, "a"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "include.path"));
cl_assert_equal_s("", git_buf_cstr(&buf));
cl_git_pass(p_unlink("a"));
}
void test_config_include__missing(void)
{
cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz");
git_error_clear();
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_assert(git_error_last() == NULL);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_git_pass(p_unlink("including"));
}
void test_config_include__missing_homedir(void)
{
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
git_error_clear();
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_assert(git_error_last() == NULL);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("including"));
}
#define replicate10(s) s s s s s s s s s s
void test_config_include__depth2(void)
{
const char *content = "[include]\n" replicate10(replicate10("path=bottom\n"));
cl_git_mkfile("top-level", "[include]\npath = middle\n[foo]\nbar = baz");
cl_git_mkfile("middle", content);
cl_git_mkfile("bottom", "[foo]\nbar2 = baz2");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
cl_assert_equal_s("baz2", git_buf_cstr(&buf));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("middle"));
cl_git_pass(p_unlink("bottom"));
}
void test_config_include__removing_include_removes_values(void)
{
cl_git_mkfile("top-level", "[include]\npath = included");
cl_git_mkfile("included", "[foo]\nbar = value");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_mkfile("top-level", "");
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
void test_config_include__rewriting_include_refreshes_values(void)
{
cl_git_mkfile("top-level", "[include]\npath = first\n[include]\npath = second");
cl_git_mkfile("first", "[first]\nfoo = bar");
cl_git_mkfile("second", "[second]\nfoo = bar");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_mkfile("first", "[first]\nother = value");
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other"));
cl_assert_equal_s(buf.ptr, "value");
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("first"));
cl_git_pass(p_unlink("second"));
}
void test_config_include__included_variables_cannot_be_deleted(void)
{
cl_git_mkfile("top-level", "[include]\npath = included\n");
cl_git_mkfile("included", "[foo]\nbar = value");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_delete_entry(cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
void test_config_include__included_variables_cannot_be_modified(void)
{
cl_git_mkfile("top-level", "[include]\npath = included\n");
cl_git_mkfile("included", "[foo]\nbar = value");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
void test_config_include__variables_in_included_override_including(void)
{
int i;
cl_git_mkfile("top-level", "[foo]\nbar = 1\n[include]\npath = included");
cl_git_mkfile("included", "[foo]\nbar = 2");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
cl_assert_equal_i(i, 2);
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
void test_config_include__variables_in_including_override_included(void)
{
int i;
cl_git_mkfile("top-level", "[include]\npath = included\n[foo]\nbar = 1");
cl_git_mkfile("included", "[foo]\nbar = 2");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
cl_assert_equal_i(i, 1);
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
+139
View File
@@ -0,0 +1,139 @@
#include "clar_libgit2.h"
#include "config_backend.h"
static git_config_backend *backend;
void test_config_memory__initialize(void)
{
backend = NULL;
}
void test_config_memory__cleanup(void)
{
git_config_backend_free(backend);
}
static void assert_config_contains(git_config_backend *backend,
const char *name, const char *value)
{
git_config_entry *entry;
cl_git_pass(git_config_backend_get_string(&entry, backend, name));
cl_assert_equal_s(entry->value, value);
}
struct expected_entry {
const char *name;
const char *value;
int seen;
};
static int contains_all_cb(const git_config_entry *entry, void *payload)
{
struct expected_entry *entries = (struct expected_entry *) payload;
int i;
for (i = 0; entries[i].name; i++) {
if (strcmp(entries[i].name, entry->name) ||
strcmp(entries[i].value , entry->value))
continue;
if (entries[i].seen)
cl_fail("Entry seen more than once");
entries[i].seen = 1;
return 0;
}
cl_fail("Unexpected entry");
return -1;
}
static void assert_config_contains_all(git_config_backend *backend,
struct expected_entry *entries)
{
int i;
cl_git_pass(git_config_backend_foreach(backend, contains_all_cb, entries));
for (i = 0; entries[i].name; i++)
cl_assert(entries[i].seen);
}
static void setup_backend(const char *cfg)
{
cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
cl_git_pass(git_config_backend_open(backend, 0, NULL));
}
void test_config_memory__write_operations_fail(void)
{
setup_backend("");
cl_git_fail(git_config_backend_set_string(backend, "general.foo", "var"));
cl_git_fail(git_config_backend_delete(backend, "general.foo"));
cl_git_fail(git_config_backend_lock(backend));
cl_git_fail(git_config_backend_unlock(backend, 0));
}
void test_config_memory__simple(void)
{
setup_backend(
"[general]\n"
"foo=bar\n");
assert_config_contains(backend, "general.foo", "bar");
}
void test_config_memory__malformed_fails_to_open(void)
{
const char *cfg =
"[general\n"
"foo=bar\n";
cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
cl_git_fail(git_config_backend_open(backend, 0, NULL));
}
void test_config_memory__multiple_vars(void)
{
setup_backend(
"[general]\n"
"foo=bar\n"
"key=value\n");
assert_config_contains(backend, "general.foo", "bar");
assert_config_contains(backend, "general.key", "value");
}
void test_config_memory__multiple_sections(void)
{
setup_backend(
"[general]\n"
"foo=bar\n"
"\n"
"[other]\n"
"key=value\n");
assert_config_contains(backend, "general.foo", "bar");
assert_config_contains(backend, "other.key", "value");
}
void test_config_memory__multivar_gets_correct_string(void)
{
setup_backend(
"[general]\n"
"foo=bar1\n"
"foo=bar2\n");
assert_config_contains(backend, "general.foo", "bar2");
}
void test_config_memory__foreach_sees_multivar(void)
{
struct expected_entry entries[] = {
{ "general.foo", "bar1", 0 },
{ "general.foo", "bar2", 0 },
{ NULL, NULL, 0 },
};
setup_backend(
"[general]\n"
"foo=bar1\n"
"foo=bar2\n");
assert_config_contains_all(backend, entries);
}
+288
View File
@@ -0,0 +1,288 @@
#include "clar_libgit2.h"
static const char *_name = "remote.ab.url";
void test_config_multivar__initialize(void)
{
cl_fixture_sandbox("config");
}
void test_config_multivar__cleanup(void)
{
cl_fixture_cleanup("config");
}
static int mv_read_cb(const git_config_entry *entry, void *data)
{
int *n = (int *) data;
if (!strcmp(entry->name, _name))
(*n)++;
return 0;
}
void test_config_multivar__foreach(void)
{
git_config *cfg;
int n = 0;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
}
static int cb(const git_config_entry *entry, void *data)
{
int *n = (int *) data;
GIT_UNUSED(entry);
(*n)++;
return 0;
}
static void check_get_multivar_foreach(
git_config *cfg, int expected, int expected_patterned)
{
int n = 0;
if (expected > 0) {
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(expected, n);
} else {
cl_assert_equal_i(GIT_ENOTFOUND,
git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
}
n = 0;
if (expected_patterned > 0) {
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
cl_assert_equal_i(expected_patterned, n);
} else {
cl_assert_equal_i(GIT_ENOTFOUND,
git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
}
}
static void check_get_multivar(git_config *cfg, int expected)
{
git_config_iterator *iter;
git_config_entry *entry;
int n = 0;
cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL));
while (git_config_next(&entry, iter) == 0)
n++;
cl_assert_equal_i(expected, n);
git_config_iterator_free(iter);
}
void test_config_multivar__get(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
check_get_multivar_foreach(cfg, 2, 1);
/* add another that has the _name entry */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry at the end */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* drop original file */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
check_get_multivar_foreach(cfg, 1, 1);
/* drop other file with match */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 0, 0);
/* reload original file (add different place in order) */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 2, 1);
check_get_multivar(cfg, 2);
git_config_free(cfg);
}
void test_config_multivar__add(void)
{
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(n, 3);
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
cl_assert_equal_i(n, 1);
git_config_free(cfg);
/* We know it works in memory, let's see if the file is written correctly */
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(n, 3);
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
cl_assert_equal_i(n, 1);
git_config_free(cfg);
}
void test_config_multivar__add_new(void)
{
const char *var = "a.brand.new";
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
cl_git_pass(git_config_set_multivar(cfg, var, "$^", "variable"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
cl_assert_equal_i(n, 1);
git_config_free(cfg);
}
void test_config_multivar__replace(void)
{
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
}
void test_config_multivar__replace_multiple(void)
{
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
cl_assert_equal_i(n, 2);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
cl_assert_equal_i(n, 2);
git_config_free(cfg);
}
void test_config_multivar__delete(void)
{
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(2, n);
cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(1, n);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(1, n);
git_config_free(cfg);
}
void test_config_multivar__delete_multiple(void)
{
git_config *cfg;
int n;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
cl_git_pass(git_config_delete_multivar(cfg, _name, "git"));
n = 0;
cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
git_config_free(cfg);
}
void test_config_multivar__delete_notfound(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
cl_git_fail_with(git_config_delete_multivar(cfg, "remote.ab.noturl", "git"), GIT_ENOTFOUND);
git_config_free(cfg);
}
+34
View File
@@ -0,0 +1,34 @@
#include "clar_libgit2.h"
#include "filebuf.h"
#include "futils.h"
#include "posix.h"
#define TEST_CONFIG "git-new-config"
void test_config_new__write_new_config(void)
{
git_config *config;
git_buf buf = GIT_BUF_INIT;
cl_git_mkfile(TEST_CONFIG, "");
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
cl_git_pass(git_config_set_string(config, "color.ui", "auto"));
cl_git_pass(git_config_set_string(config, "core.editor", "ed"));
git_config_free(config);
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
cl_git_pass(git_config_get_string_buf(&buf, config, "color.ui"));
cl_assert_equal_s("auto", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, config, "core.editor"));
cl_assert_equal_s("ed", git_buf_cstr(&buf));
git_buf_dispose(&buf);
git_config_free(config);
p_unlink(TEST_CONFIG);
}
+913
View File
@@ -0,0 +1,913 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "path.h"
static git_buf buf = GIT_BUF_INIT;
void test_config_read__cleanup(void)
{
git_buf_dispose(&buf);
}
void test_config_read__simple_read(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
cl_assert(i == 0);
cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
cl_assert(i == 1);
cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
cl_assert(i == 0);
cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
cl_assert(i == 1);
git_config_free(cfg);
}
void test_config_read__case_sensitive(void)
{
git_config *cfg;
int i;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.that.other"));
cl_assert_equal_s("true", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.other"));
cl_assert_equal_s("yes", git_buf_cstr(&buf));
cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
cl_assert(i == 1);
cl_git_pass(git_config_get_bool(&i, cfg, "this.That.other"));
cl_assert(i == 1);
/* This one doesn't exist */
cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
git_config_free(cfg);
}
/*
* If \ is the last non-space character on the line, we read the next
* one, separating each line with SP.
*/
void test_config_read__multiline_value(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.and"));
cl_assert_equal_s("one one one two two three three", git_buf_cstr(&buf));
git_config_free(cfg);
}
static void clean_test_config(void *unused)
{
GIT_UNUSED(unused);
cl_fixture_cleanup("./testconfig");
}
void test_config_read__multiline_value_and_eof(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[header]\n key1 = foo\\\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
cl_assert_equal_s("foo", git_buf_cstr(&buf));
git_config_free(cfg);
}
void test_config_read__multiline_eof(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[header]\n key1 = \\\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
cl_assert_equal_s("", git_buf_cstr(&buf));
git_config_free(cfg);
}
/*
* This kind of subsection declaration is case-insensitive
*/
void test_config_read__subsection_header(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "section.subsection.var"));
cl_assert_equal_s("hello", git_buf_cstr(&buf));
/* The subsection is transformed to lower-case */
cl_must_fail(git_config_get_string_buf(&buf, cfg, "section.subSectIon.var"));
git_config_free(cfg);
}
void test_config_read__lone_variable(void)
{
git_config *cfg;
int i;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
cl_git_fail(git_config_get_int32(&i, cfg, "some.section.variable"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variable"));
cl_assert_equal_s("", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
cl_assert(i == 1);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variableeq"));
cl_assert_equal_s("", git_buf_cstr(&buf));
cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variableeq"));
cl_assert(i == 0);
git_config_free(cfg);
}
void test_config_read__number_suffixes(void)
{
git_config *cfg;
int64_t i;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
cl_assert(i == 1);
cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
cl_assert(i == 1 * 1024);
cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
cl_assert(i == 1 * 1024);
cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
cl_assert(i == 1 * 1024 * 1024);
cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
cl_assert(i == 1 * 1024 * 1024);
cl_git_pass(git_config_get_int64(&i, cfg, "number.g"));
cl_assert(i == 1 * 1024 * 1024 * 1024);
cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
cl_assert(i == 1 * 1024 * 1024 * 1024);
git_config_free(cfg);
}
void test_config_read__blank_lines(void)
{
git_config *cfg;
int i;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
cl_assert(i == 1);
cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
cl_assert(i == 0);
git_config_free(cfg);
}
void test_config_read__invalid_ext_headers(void)
{
git_config *cfg;
cl_must_fail(git_config_open_ondisk(&cfg, cl_fixture("config/config7")));
}
void test_config_read__empty_files(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config8")));
git_config_free(cfg);
}
void test_config_read__symbol_headers(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
git_config_free(cfg);
}
void test_config_read__header_in_last_line(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config10")));
git_config_free(cfg);
}
void test_config_read__prefixes(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.ab.url"));
cl_assert_equal_s("http://example.com/git/ab", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.abba.url"));
cl_assert_equal_s("http://example.com/git/abba", git_buf_cstr(&buf));
git_config_free(cfg);
}
void test_config_read__escaping_quotes(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.editor"));
cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", git_buf_cstr(&buf));
git_config_free(cfg);
}
void test_config_read__invalid_escape_sequence(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[header]\n key1 = \\\\\\;\n key2 = value2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
static int count_cfg_entries_and_compare_levels(
const git_config_entry *entry, void *payload)
{
int *count = payload;
if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17"))
cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL);
else
cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM);
(*count)++;
return 0;
}
static int cfg_callback_countdown(const git_config_entry *entry, void *payload)
{
int *count = payload;
GIT_UNUSED(entry);
(*count)--;
if (*count == 0)
return -100;
return 0;
}
void test_config_read__foreach(void)
{
git_config *cfg;
int count, ret;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
cl_assert_equal_i(7, count);
count = 3;
cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
cl_assert_equal_i(-100, ret);
git_config_free(cfg);
}
void test_config_read__iterator(void)
{
const char *keys[] = {
"core.dummy2",
"core.verylong",
"core.dummy",
"remote.ab.url",
"remote.abba.url",
"core.dummy2",
"core.global"
};
git_config *cfg;
git_config_iterator *iter;
git_config_entry *entry;
int count, ret;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_iterator_new(&iter, cfg));
while ((ret = git_config_next(&entry, iter)) == 0) {
cl_assert_equal_s(entry->name, keys[count]);
count++;
}
git_config_iterator_free(iter);
cl_assert_equal_i(GIT_ITEROVER, ret);
cl_assert_equal_i(7, count);
count = 3;
cl_git_pass(git_config_iterator_new(&iter, cfg));
git_config_iterator_free(iter);
git_config_free(cfg);
}
static int count_cfg_entries(const git_config_entry *entry, void *payload)
{
int *count = payload;
GIT_UNUSED(entry);
(*count)++;
return 0;
}
void test_config_read__foreach_match(void)
{
git_config *cfg;
int count;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
count = 0;
cl_git_pass(
git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
cl_assert_equal_i(3, count);
count = 0;
cl_git_pass(
git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
cl_assert_equal_i(2, count);
count = 0;
cl_git_pass(
git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
cl_assert_equal_i(2, count);
count = 0;
cl_git_pass(
git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
cl_assert_equal_i(2, count);
count = 0;
cl_git_pass(
git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
cl_assert_equal_i(0, count);
git_config_free(cfg);
}
static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
{
git_config_iterator *iter;
git_config_entry *entry;
int count, error;
cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
count = 0;
while ((error = git_config_next(&entry, iter)) == 0)
count++;
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_i(expected, count);
git_config_iterator_free(iter);
}
void test_config_read__iterator_invalid_glob(void)
{
git_config *cfg;
git_config_iterator *iter;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
cl_git_fail(git_config_iterator_glob_new(&iter, cfg, "*"));
git_config_free(cfg);
}
void test_config_read__iterator_glob(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
check_glob_iter(cfg, "core.*", 3);
check_glob_iter(cfg, "remote\\.ab.*", 2);
check_glob_iter(cfg, ".*url$", 2);
check_glob_iter(cfg, ".*dummy.*", 2);
check_glob_iter(cfg, ".*nomatch.*", 0);
git_config_free(cfg);
}
void test_config_read__whitespace_not_required_around_assignment(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "a.b"));
cl_assert_equal_s("c", git_buf_cstr(&buf));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "d.e"));
cl_assert_equal_s("f", git_buf_cstr(&buf));
git_config_free(cfg);
}
void test_config_read__read_git_config_entry(void)
{
git_config *cfg;
git_config_entry *entry;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
cl_assert_equal_s("core.dummy2", entry->name);
cl_assert_equal_s("42", entry->value);
cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
git_config_entry_free(entry);
git_config_free(cfg);
}
/*
* At the beginning of the test:
* - config9 has: core.dummy2=42
* - config15 has: core.dummy2=7
* - config16 has: core.dummy2=28
*/
void test_config_read__local_config_overrides_global_config_overrides_system_config(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(28, i);
git_config_free(cfg);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(7, i);
git_config_free(cfg);
}
/*
* At the beginning of the test:
* - config9 has: core.global does not exist
* - config15 has: core.global=17
* - config16 has: core.global=29
*
* And also:
* - config9 has: core.system does not exist
* - config15 has: core.system does not exist
* - config16 has: core.system=11
*/
void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
cl_assert_equal_i(17, i);
cl_git_pass(git_config_get_int32(&i, cfg, "core.system"));
cl_assert_equal_i(11, i);
git_config_free(cfg);
}
void test_config_read__parent_dir_is_file(void)
{
git_config *cfg;
int count;
cl_git_pass(git_config_new(&cfg));
/*
* Verify we can add non-existing files when the parent directory is not
* a directory.
*/
cl_git_pass(git_config_add_file_ondisk(cfg, "/dev/null/.gitconfig",
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
count = 0;
cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
cl_assert_equal_i(0, count);
git_config_free(cfg);
}
/*
* At the beginning of the test, config18 has:
* int32global = 28
* int64global = 9223372036854775803
* boolglobal = true
* stringglobal = I'm a global config value!
*
* And config19 has:
* int32global = -1
* int64global = -2
* boolglobal = false
* stringglobal = don't find me!
*
*/
void test_config_read__simple_read_from_specific_level(void)
{
git_config *cfg, *cfg_specific;
int i;
int64_t l, expected = +9223372036854775803;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global"));
cl_assert_equal_i(28, i);
cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global"));
cl_assert(l == expected);
cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal"));
cl_assert_equal_b(true, i);
cl_git_pass(git_config_get_string_buf(&buf, cfg_specific, "core.stringglobal"));
cl_assert_equal_s("I'm a global config value!", git_buf_cstr(&buf));
git_config_free(cfg_specific);
git_config_free(cfg);
}
void test_config_read__can_load_and_parse_an_empty_config_file(void)
{
git_config *cfg;
int i;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
git_config_free(cfg);
}
void test_config_read__corrupt_header(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\"");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__corrupt_header2(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n lib = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__corrupt_header3(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n lib = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_key_chars(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[foo]\n has_underscore = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_rewritefile("./testconfig", "[foo]\n has/slash = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_rewritefile("./testconfig", "[foo]\n has+plus = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_rewritefile("./testconfig", "[no_key]\n = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__lone_variable_with_trailing_whitespace(void)
{
git_config *cfg;
int b;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[foo]\n lonevariable \n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_bool(&b, cfg, "foo.lonevariable"));
cl_assert_equal_b(true, b);
git_config_free(cfg);
}
void test_config_read__override_variable(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s("two", git_buf_cstr(&buf));
git_config_free(cfg);
}
void test_config_read__path(void)
{
git_config *cfg;
git_buf path = GIT_BUF_INIT;
git_buf old_path = GIT_BUF_INIT;
git_buf home_path = GIT_BUF_INIT;
git_buf expected_path = GIT_BUF_INIT;
cl_git_pass(p_mkdir("fakehome", 0777));
cl_git_pass(git_path_prettify(&home_path, "fakehome", NULL));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &old_path));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, home_path.ptr));
cl_git_mkfile("./testconfig", "[some]\n path = ~/somefile");
cl_git_pass(git_path_join_unrooted(&expected_path, "somefile", home_path.ptr, NULL));
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
cl_assert_equal_s(expected_path.ptr, path.ptr);
git_buf_dispose(&path);
cl_git_mkfile("./testconfig", "[some]\n path = ~/");
cl_git_pass(git_path_join_unrooted(&expected_path, "", home_path.ptr, NULL));
cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
cl_assert_equal_s(expected_path.ptr, path.ptr);
git_buf_dispose(&path);
cl_git_mkfile("./testconfig", "[some]\n path = ~");
cl_git_pass(git_buf_sets(&expected_path, home_path.ptr));
cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
cl_assert_equal_s(expected_path.ptr, path.ptr);
git_buf_dispose(&path);
cl_git_mkfile("./testconfig", "[some]\n path = ~user/foo");
cl_git_fail(git_config_get_path(&path, cfg, "some.path"));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, old_path.ptr));
git_buf_dispose(&old_path);
git_buf_dispose(&home_path);
git_buf_dispose(&expected_path);
git_config_free(cfg);
}
void test_config_read__crlf_style_line_endings(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_read__trailing_crlf(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n\r\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_read__bom(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some]\n var = value\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_read__arbitrary_whitespace_before_subsection(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some \t \"subsection\"]\n var = value\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.subsection.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_read__no_whitespace_after_subsection(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some \"subsection\" ]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_space_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some section]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_quoted_first_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[\"some\"]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_unquoted_subsection(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub section]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_quoted_third_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub \"section\"]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__single_line(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s(buf.ptr, "value");
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"]var = value");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
cl_assert_equal_s(buf.ptr, "value");
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
static int read_nosection_cb(const git_config_entry *entry, void *payload) {
int *seen = (int*)payload;
if (strcmp(entry->name, "key") == 0) {
(*seen)++;
}
return 0;
}
/* This would ideally issue a warning, if we had a way to do so. */
void test_config_read__nosection(void)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
int seen = 0;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection")));
/*
* Given a key with no section, we do not allow reading it,
* but we do include it in an iteration over the config
* store. This appears to match how git's own APIs (and
* git-config(1)) behave.
*/
cl_git_fail_with(git_config_get_string_buf(&buf, cfg, "key"), GIT_EINVALIDSPEC);
cl_git_pass(git_config_foreach(cfg, read_nosection_cb, &seen));
cl_assert_equal_i(seen, 1);
git_buf_dispose(&buf);
git_config_free(cfg);
}
+65
View File
@@ -0,0 +1,65 @@
#include "clar_libgit2.h"
#include "config_backend.h"
#include "config.h"
#include "path.h"
static git_config *cfg;
void test_config_readonly__initialize(void)
{
cl_git_pass(git_config_new(&cfg));
}
void test_config_readonly__cleanup(void)
{
git_config_free(cfg);
cfg = NULL;
}
void test_config_readonly__writing_to_readonly_fails(void)
{
git_config_backend *backend;
cl_git_pass(git_config_backend_from_file(&backend, "global"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_path_exists("global"));
}
void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
{
git_config_backend *backend;
cl_git_pass(git_config_backend_from_file(&backend, "global"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_backend_from_file(&backend, "local"));
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(git_path_exists("local"));
cl_assert(!git_path_exists("global"));
cl_git_pass(p_unlink("local"));
}
void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
{
git_config_backend *backend;
cl_git_pass(git_config_backend_from_file(&backend, "local"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_backend_from_file(&backend, "global"));
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_path_exists("local"));
cl_assert(git_path_exists("global"));
cl_git_pass(p_unlink("global"));
}
+89
View File
@@ -0,0 +1,89 @@
#include "clar_libgit2.h"
#include "config.h"
static git_repository *g_repo = NULL;
static git_config *g_config = NULL;
void test_config_rename__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_config(&g_config, g_repo));
}
void test_config_rename__cleanup(void)
{
git_config_free(g_config);
g_config = NULL;
cl_git_sandbox_cleanup();
g_repo = NULL;
}
void test_config_rename__can_rename(void)
{
git_config_entry *ce;
cl_git_pass(git_config_get_entry(
&ce, g_config, "branch.track-local.remote"));
cl_assert_equal_s(".", ce->value);
git_config_entry_free(ce);
cl_git_fail(git_config_get_entry(
&ce, g_config, "branch.local-track.remote"));
cl_git_pass(git_config_rename_section(
g_repo, "branch.track-local", "branch.local-track"));
cl_git_pass(git_config_get_entry(
&ce, g_config, "branch.local-track.remote"));
cl_assert_equal_s(".", ce->value);
git_config_entry_free(ce);
cl_git_fail(git_config_get_entry(
&ce, g_config, "branch.track-local.remote"));
}
void test_config_rename__prevent_overwrite(void)
{
git_config_entry *ce;
cl_git_pass(git_config_set_string(
g_config, "branch.local-track.remote", "yellow"));
cl_git_pass(git_config_get_entry(
&ce, g_config, "branch.local-track.remote"));
cl_assert_equal_s("yellow", ce->value);
git_config_entry_free(ce);
cl_git_pass(git_config_rename_section(
g_repo, "branch.track-local", "branch.local-track"));
cl_git_pass(git_config_get_entry(
&ce, g_config, "branch.local-track.remote"));
cl_assert_equal_s(".", ce->value);
git_config_entry_free(ce);
/* so, we don't currently prevent overwrite... */
/* {
const git_error *err;
cl_assert((err = git_error_last()) != NULL);
cl_assert(err->message != NULL);
} */
}
static void assert_invalid_config_section_name(
git_repository *repo, const char *name)
{
cl_git_fail_with(
git_config_rename_section(repo, "branch.remoteless", name),
GIT_EINVALIDSPEC);
}
void test_config_rename__require_a_valid_new_name(void)
{
assert_invalid_config_section_name(g_repo, "");
assert_invalid_config_section_name(g_repo, "bra\nch");
assert_invalid_config_section_name(g_repo, "branc#");
assert_invalid_config_section_name(g_repo, "bra\nch.duh");
assert_invalid_config_section_name(g_repo, "branc#.duh");
}
+139
View File
@@ -0,0 +1,139 @@
#include "clar_libgit2.h"
#include "config_backend.h"
static git_config *cfg;
static git_config *snapshot;
void test_config_snapshot__cleanup(void)
{
git_config_free(cfg);
cfg = NULL;
git_config_free(snapshot);
snapshot = NULL;
}
void test_config_snapshot__create_snapshot(void)
{
int32_t i;
cl_git_mkfile("config", "[old]\nvalue = 5\n");
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
cl_assert_equal_i(5, i);
cl_git_pass(git_config_snapshot(&snapshot, cfg));
/* Change the value on the file itself (simulate external process) */
cl_git_mkfile("config", "[old]\nvalue = 56\n");
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
cl_assert_equal_i(56, i);
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(5, i);
/* Change the value on the file itself (simulate external process) */
cl_git_mkfile("config", "[old]\nvalue = 999\n");
/* Old snapshot should still have the old value */
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(5, i);
/* New snapshot should see new value */
git_config_free(snapshot);
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(999, i);
cl_git_pass(p_unlink("config"));
}
static int count_me(const git_config_entry *entry, void *payload)
{
int *n = (int *) payload;
GIT_UNUSED(entry);
(*n)++;
return 0;
}
void test_config_snapshot__multivar(void)
{
int count;
count = 0;
cl_git_mkfile("config", "[old]\nvalue = 5\nvalue = 6\n");
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count));
cl_assert_equal_i(2, count);
count = 0;
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
cl_assert_equal_i(2, count);
cl_git_pass(p_unlink("config"));
}
void test_config_snapshot__includes(void)
{
int i;
cl_git_mkfile("including", "[include]\npath = included");
cl_git_mkfile("included", "[section]\nkey = 1\n");
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
/* Rewrite "included" config */
cl_git_mkfile("included", "[section]\nkey = 11\n");
/* Assert that the live config changed, but snapshot remained the same */
cl_git_pass(git_config_get_int32(&i, cfg, "section.key"));
cl_assert_equal_i(i, 11);
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
cl_git_pass(p_unlink("including"));
cl_git_pass(p_unlink("included"));
}
void test_config_snapshot__snapshot(void)
{
git_config *snapshot_snapshot;
int i;
cl_git_mkfile("configfile", "[section]\nkey = 1\n");
cl_git_pass(git_config_open_ondisk(&cfg, "configfile"));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_snapshot(&snapshot_snapshot, snapshot));
cl_git_pass(git_config_get_int32(&i, snapshot_snapshot, "section.key"));
cl_assert_equal_i(i, 1);
git_config_free(snapshot_snapshot);
cl_git_pass(p_unlink("configfile"));
}
void test_config_snapshot__snapshot_from_in_memony(void)
{
const char *configuration = "[section]\nkey = 1\n";
git_config_backend *backend;
int i;
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_backend_from_string(&backend, configuration, strlen(configuration)));
cl_git_pass(git_config_add_backend(cfg, backend, 0, NULL, 0));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
}
+201
View File
@@ -0,0 +1,201 @@
#include "clar_libgit2.h"
#include "filebuf.h"
#include "futils.h"
#include "posix.h"
#define TEST_CONFIG "git-test-config"
static git_buf buf = GIT_BUF_INIT;
void test_config_stress__initialize(void)
{
git_filebuf file = GIT_FILEBUF_INIT;
cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0, 0666));
git_filebuf_printf(&file, "[color]\n\tui = auto\n");
git_filebuf_printf(&file, "[core]\n\teditor = \n");
cl_git_pass(git_filebuf_commit(&file));
}
void test_config_stress__cleanup(void)
{
git_buf_dispose(&buf);
p_unlink(TEST_CONFIG);
}
void test_config_stress__dont_break_on_invalid_input(void)
{
git_config *config;
cl_assert(git_path_exists(TEST_CONFIG));
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
cl_git_pass(git_config_get_string_buf(&buf, config, "color.ui"));
cl_git_pass(git_config_get_string_buf(&buf, config, "core.editor"));
git_config_free(config);
}
void assert_config_value(git_config *config, const char *key, const char *value)
{
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, config, key));
cl_assert_equal_s(value, git_buf_cstr(&buf));
}
void test_config_stress__comments(void)
{
git_config *config;
cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
assert_config_value(config, "some.section.test2", "hello");
assert_config_value(config, "some.section.test3", "welcome");
assert_config_value(config, "some.section.other", "hello! \" ; ; ; ");
assert_config_value(config, "some.section.other2", "cool! \" # # # ");
assert_config_value(config, "some.section.multi", "hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
assert_config_value(config, "some.section.multi2", "good, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
assert_config_value(config, "some.section.back", "this is \ba phrase");
git_config_free(config);
}
void test_config_stress__escape_subsection_names(void)
{
git_config *config;
cl_assert(git_path_exists("git-test-config"));
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
git_config_free(config);
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
assert_config_value(config, "some.sec\\tion.other", "foo");
git_config_free(config);
}
void test_config_stress__trailing_backslash(void)
{
git_config *config;
const char *path = "C:\\iam\\some\\windows\\path\\";
cl_assert(git_path_exists("git-test-config"));
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
cl_git_pass(git_config_set_string(config, "windows.path", path));
git_config_free(config);
cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
assert_config_value(config, "windows.path", path);
git_config_free(config);
}
void test_config_stress__complex(void)
{
git_config *config;
const char *path = "./config-immediate-multiline";
cl_git_mkfile(path, "[imm]\n multi = \"\\\nfoo\"");
cl_git_pass(git_config_open_ondisk(&config, path));
assert_config_value(config, "imm.multi", "foo");
git_config_free(config);
}
void test_config_stress__quick_write(void)
{
git_config *config_w, *config_r;
const char *path = "./config-quick-write";
const char *key = "quick.write";
int32_t i;
/* Create an external writer for one instance with the other one */
cl_git_pass(git_config_open_ondisk(&config_w, path));
cl_git_pass(git_config_open_ondisk(&config_r, path));
/* Write and read in the same second (repeat to increase the chance of it happening) */
for (i = 0; i < 10; i++) {
int32_t val;
cl_git_pass(git_config_set_int32(config_w, key, i));
cl_msleep(1);
cl_git_pass(git_config_get_int32(&val, config_r, key));
cl_assert_equal_i(i, val);
}
git_config_free(config_r);
git_config_free(config_w);
}
static int foreach_cb(const git_config_entry *entry, void *payload)
{
if (!strcmp(entry->name, "key.value")) {
*(char **)payload = git__strdup(entry->value);
return 0;
}
return -1;
}
void test_config_stress__foreach_refreshes(void)
{
git_config *config_w, *config_r;
char *value = NULL;
cl_git_pass(git_config_open_ondisk(&config_w, "./cfg"));
cl_git_pass(git_config_open_ondisk(&config_r, "./cfg"));
cl_git_pass(git_config_set_string(config_w, "key.value", "1"));
cl_git_pass(git_config_foreach_match(config_r, "key.value", foreach_cb, &value));
cl_assert_equal_s(value, "1");
git_config_free(config_r);
git_config_free(config_w);
git__free(value);
}
void test_config_stress__foreach_refreshes_snapshot(void)
{
git_config *config, *snapshot;
char *value = NULL;
cl_git_pass(git_config_open_ondisk(&config, "./cfg"));
cl_git_pass(git_config_set_string(config, "key.value", "1"));
cl_git_pass(git_config_snapshot(&snapshot, config));
cl_git_pass(git_config_foreach_match(snapshot, "key.value", foreach_cb, &value));
cl_assert_equal_s(value, "1");
git_config_free(snapshot);
git_config_free(config);
git__free(value);
}
void test_config_stress__huge_section_with_many_values(void)
{
git_config *config;
if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
cl_skip();
/*
* The config file is structured in such a way that is
* has a section header that is approximately 500kb of
* size followed by 40k entries. While the resulting
* configuration file itself is roughly 650kb in size and
* thus considered to be rather small, in the past we'd
* balloon to more than 20GB of memory (20000x500kb)
* while parsing the file. It thus was a trivial way to
* cause an out-of-memory situation and thus cause denial
* of service, e.g. via gitmodules.
*/
cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config-oom")));
git_config_free(config);
}
+49
View File
@@ -0,0 +1,49 @@
#include "clar_libgit2.h"
#include "config.h"
static git_config *cfg;
void test_config_validkeyname__initialize(void)
{
cl_fixture_sandbox("config/config10");
cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
}
void test_config_validkeyname__cleanup(void)
{
git_config_free(cfg);
cfg = NULL;
cl_fixture_cleanup("config10");
}
static void assert_invalid_config_key_name(const char *name)
{
git_buf buf = GIT_BUF_INIT;
cl_git_fail_with(git_config_get_string_buf(&buf, cfg, name),
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_set_string(cfg, name, "42"),
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_delete_entry(cfg, name),
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL),
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
GIT_EINVALIDSPEC);
}
void test_config_validkeyname__accessing_requires_a_valid_name(void)
{
assert_invalid_config_key_name("");
assert_invalid_config_key_name(".");
assert_invalid_config_key_name("..");
assert_invalid_config_key_name("core.");
assert_invalid_config_key_name("d#ff.dirstat.lines");
assert_invalid_config_key_name("diff.dirstat.lines#");
assert_invalid_config_key_name("dif\nf.dirstat.lines");
assert_invalid_config_key_name("dif.dir\nstat.lines");
assert_invalid_config_key_name("dif.dirstat.li\nes");
}
+746
View File
@@ -0,0 +1,746 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
#include "git2/sys/config.h"
#include "config.h"
void test_config_write__initialize(void)
{
cl_fixture_sandbox("config/config9");
cl_fixture_sandbox("config/config15");
cl_fixture_sandbox("config/config17");
}
void test_config_write__cleanup(void)
{
cl_fixture_cleanup("config9");
cl_fixture_cleanup("config15");
cl_fixture_cleanup("config17");
}
void test_config_write__replace_value(void)
{
git_config *cfg;
int i;
int64_t l, expected = +9223372036854775803;
/* By freeing the config, we make sure we flush the values */
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
cl_assert(i == 5);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_int64(cfg, "core.verylong", expected));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_int64(&l, cfg, "core.verylong"));
cl_assert(l == expected);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_int64(cfg, "core.verylong", 1));
git_config_free(cfg);
}
void test_config_write__delete_value(void)
{
git_config *cfg;
int32_t i;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_delete_entry(cfg, "core.dummy"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_assert(git_config_get_int32(&i, cfg, "core.dummy") == GIT_ENOTFOUND);
cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
git_config_free(cfg);
}
/*
* At the beginning of the test:
* - config9 has: core.dummy2=42
* - config15 has: core.dummy2=7
*/
void test_config_write__delete_value_at_specific_level(void)
{
git_config *cfg, *cfg_specific;
int32_t i;
cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert(i == 7);
git_config_free(cfg);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_delete_entry(cfg_specific, "core.dummy2"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND);
cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7));
git_config_free(cfg_specific);
git_config_free(cfg);
}
/*
* This test exposes a bug where duplicate empty section headers could prevent
* deletion of config entries.
*/
void test_config_write__delete_value_with_duplicate_header(void)
{
const char *file_name = "config-duplicate-header";
const char *entry_name = "remote.origin.url";
git_config *cfg;
git_config_entry *entry;
/* This config can occur after removing and re-adding the origin remote */
const char *file_content =
"[remote \"origin\"]\n" \
"[branch \"master\"]\n" \
" remote = \"origin\"\n" \
"[remote \"origin\"]\n" \
" url = \"foo\"\n";
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, file_content);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
/* Delete that entry */
cl_git_pass(git_config_delete_entry(cfg, entry_name));
/* Reopen the file and make sure the entry no longer exists */
git_config_entry_free(entry);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_fail(git_config_get_entry(&entry, cfg, entry_name));
/* Cleanup */
git_config_entry_free(entry);
git_config_free(cfg);
}
/*
* This test exposes a bug where duplicate section headers could cause
* config_write to add a new entry when one already exists.
*/
void test_config_write__add_value_with_duplicate_header(void)
{
const char *file_name = "config-duplicate-insert";
const char *entry_name = "foo.c";
const char *old_val = "old";
const char *new_val = "new";
const char *str;
git_config *cfg, *snapshot;
/* c = old should be replaced by c = new.
* The bug causes c = new to be inserted under the first 'foo' header.
*/
const char *file_content =
"[foo]\n" \
" a = b\n" \
"[other]\n" \
" a = b\n" \
"[foo]\n" \
" c = old\n";
/* Write the test config */
cl_git_mkfile(file_name, file_content);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
/* make sure the expected entry (foo.c) exists */
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
cl_assert_equal_s(old_val, str);
git_config_free(snapshot);
/* Try setting foo.c to something else */
cl_git_pass(git_config_set_string(cfg, entry_name, new_val));
git_config_free(cfg);
/* Reopen the file and make sure the new value was set */
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
cl_assert_equal_s(new_val, str);
/* Cleanup */
git_config_free(snapshot);
git_config_free(cfg);
}
void test_config_write__overwrite_value_with_duplicate_header(void)
{
const char *file_name = "config-duplicate-header";
const char *entry_name = "remote.origin.url";
git_config *cfg;
git_config_entry *entry;
/* This config can occur after removing and re-adding the origin remote */
const char *file_content =
"[remote \"origin\"]\n" \
"[branch \"master\"]\n" \
" remote = \"origin\"\n" \
"[remote \"origin\"]\n" \
" url = \"foo\"\n";
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, file_content);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
/* Update that entry */
cl_git_pass(git_config_set_string(cfg, entry_name, "newurl"));
/* Reopen the file and make sure the entry was updated */
git_config_entry_free(entry);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
cl_assert_equal_s("newurl", entry->value);
/* Cleanup */
git_config_entry_free(entry);
git_config_free(cfg);
}
static int multivar_cb(const git_config_entry *entry, void *data)
{
int *n = (int *)data;
cl_assert_equal_s(entry->value, "newurl");
(*n)++;
return 0;
}
void test_config_write__overwrite_multivar_within_duplicate_header(void)
{
const char *file_name = "config-duplicate-header";
const char *entry_name = "remote.origin.url";
git_config *cfg;
git_config_entry *entry;
int n = 0;
/* This config can occur after removing and re-adding the origin remote */
const char *file_content =
"[remote \"origin\"]\n" \
" url = \"bar\"\n" \
"[branch \"master\"]\n" \
" remote = \"origin\"\n" \
"[remote \"origin\"]\n" \
" url = \"foo\"\n";
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, file_content);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
/* Update that entry */
cl_git_pass(git_config_set_multivar(cfg, entry_name, ".*", "newurl"));
git_config_entry_free(entry);
git_config_free(cfg);
/* Reopen the file and make sure the entry was updated */
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_get_multivar_foreach(cfg, entry_name, NULL, multivar_cb, &n));
cl_assert_equal_i(2, n);
/* Cleanup */
git_config_free(cfg);
}
void test_config_write__write_subsection(void)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_string(cfg, "my.own.var", "works"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "my.own.var"));
cl_assert_equal_s("works", git_buf_cstr(&buf));
git_buf_dispose(&buf);
git_config_free(cfg);
}
void test_config_write__delete_inexistent(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_assert(git_config_delete_entry(cfg, "core.imaginary") == GIT_ENOTFOUND);
git_config_free(cfg);
}
void test_config_write__value_containing_quotes(void)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf));
git_buf_clear(&buf);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf));
git_buf_clear(&buf);
git_config_free(cfg);
/* The code path for values that already exist is different, check that one as well */
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_string(cfg, "core.somevar", "this also \"has\" quotes"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf));
git_buf_clear(&buf);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf));
git_buf_dispose(&buf);
git_config_free(cfg);
}
void test_config_write__escape_value(void)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes and \t"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf));
git_buf_clear(&buf);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf));
git_buf_dispose(&buf);
git_config_free(cfg);
}
void test_config_write__add_value_at_specific_level(void)
{
git_config *cfg, *cfg_specific;
int i;
int64_t l, expected = +9223372036854775803;
git_buf buf = GIT_BUF_INIT;
/* open config15 as global level config file */
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28));
cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected));
cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true));
cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!"));
git_config_free(cfg_specific);
git_config_free(cfg);
/* open config15 as local level config file */
cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global"));
cl_assert_equal_i(28, i);
cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global"));
cl_assert(l == expected);
cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal"));
cl_assert_equal_b(true, i);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
cl_assert_equal_s("I'm a global config value!", git_buf_cstr(&buf));
git_buf_dispose(&buf);
git_config_free(cfg);
}
void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
{
git_config *cfg;
int i;
cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
cl_git_pass(git_config_set_int32(cfg, "core.newline", 7));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
cl_git_pass(git_config_get_int32(&i, cfg, "core.newline"));
cl_assert_equal_i(7, i);
git_config_free(cfg);
}
void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void)
{
git_config *cfg;
int i;
cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
cl_git_pass(git_config_set_int32(cfg, "diff.context", 10));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
cl_git_pass(git_config_get_int32(&i, cfg, "diff.context"));
cl_assert_equal_i(10, i);
git_config_free(cfg);
}
void test_config_write__add_value_which_needs_quotes(void)
{
git_config *cfg, *base;
const char* str1;
const char* str2;
const char* str3;
const char* str4;
const char* str5;
cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&base, "config17"));
cl_git_pass(git_config_snapshot(&cfg, base));
cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
cl_assert_equal_s(" Something", str1);
cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
cl_assert_equal_s("Something ", str2);
cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
cl_assert_equal_s("some#thing", str3);
cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
cl_assert_equal_s("some;thing", str4);
cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
cl_assert_equal_s(" some\"thing", str5);
git_config_free(cfg);
git_config_free(base);
}
void test_config_write__can_set_a_value_to_NULL(void)
{
git_repository *repository;
git_config *config;
repository = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_config(&config, repository));
cl_git_fail(git_config_set_string(config, "a.b.c", NULL));
git_config_free(config);
cl_git_sandbox_cleanup();
}
void test_config_write__can_set_an_empty_value(void)
{
git_repository *repository;
git_config *config;
git_buf buf = {0};
repository = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_config(&config, repository));
cl_git_pass(git_config_set_string(config, "core.somevar", ""));
cl_git_pass(git_config_get_string_buf(&buf, config, "core.somevar"));
cl_assert_equal_s("", buf.ptr);
git_buf_dispose(&buf);
git_config_free(config);
cl_git_sandbox_cleanup();
}
void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
cl_git_mkfile("config9.lock", "[core]\n");
cl_git_fail_with(git_config_set_string(cfg, "core.dump", "boom"), GIT_ELOCKED);
git_config_free(cfg);
}
void test_config_write__outside_change(void)
{
int32_t tmp;
git_config *cfg;
const char *filename = "config-ext-change";
cl_git_mkfile(filename, "[old]\nvalue = 5\n");
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
/* Change the value on the file itself (simulate external process) */
cl_git_mkfile(filename, "[old]\nvalue = 6\n");
cl_git_pass(git_config_set_int32(cfg, "new.value", 7));
cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
cl_assert_equal_i(6, tmp);
git_config_free(cfg);
}
#define FOO_COMMENT \
"; another comment!\n"
#define SECTION_FOO \
"\n" \
" \n" \
" [section \"foo\"] \n" \
" # here's a comment\n" \
"\tname = \"value\"\n" \
" name2 = \"value2\"\n" \
#define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT
#define SECTION_BAR \
"[section \"bar\"]\t\n" \
"\t \n" \
" barname=\"value\"\n"
void test_config_write__preserves_whitespace_and_comments(void)
{
const char *file_name = "config-duplicate-header";
const char *n;
git_config *cfg;
git_buf newfile = GIT_BUF_INIT;
/* This config can occur after removing and re-adding the origin remote */
const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR;
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, file_content);
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
/* Ensure that we didn't needlessly mangle the config file */
cl_git_pass(git_futils_readbuffer(&newfile, file_name));
n = newfile.ptr;
cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO));
n += strlen(SECTION_FOO);
cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n"));
n += strlen("\tother = otherval\n");
cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT));
n += strlen(FOO_COMMENT);
cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR));
n += strlen(SECTION_BAR);
cl_assert_equal_s("[newsection]\n\tnewname = new_value\n", n);
git_buf_dispose(&newfile);
git_config_free(cfg);
}
void test_config_write__preserves_entry_with_name_only(void)
{
const char *file_name = "config-empty-value";
git_config *cfg;
git_buf newfile = GIT_BUF_INIT;
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, "[section \"foo\"]\n\tname\n");
cl_git_pass(git_config_open_ondisk(&cfg, file_name));
cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
cl_git_pass(git_futils_readbuffer(&newfile, file_name));
cl_assert_equal_s("[section \"foo\"]\n\tname\n\tother = otherval\n[newsection]\n\tnewname = new_value\n", newfile.ptr);
git_buf_dispose(&newfile);
git_config_free(cfg);
}
void test_config_write__to_empty_file(void)
{
git_config *cfg;
const char *filename = "config-file";
git_buf result = GIT_BUF_INIT;
cl_git_mkfile(filename, "");
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_set_string(cfg, "section.name", "value"));
git_config_free(cfg);
cl_git_pass(git_futils_readbuffer(&result, "config-file"));
cl_assert_equal_s("[section]\n\tname = value\n", result.ptr);
git_buf_dispose(&result);
}
void test_config_write__to_file_with_only_comment(void)
{
git_config *cfg;
const char *filename = "config-file";
git_buf result = GIT_BUF_INIT;
cl_git_mkfile(filename, "\n\n");
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_set_string(cfg, "section.name", "value"));
git_config_free(cfg);
cl_git_pass(git_futils_readbuffer(&result, "config-file"));
cl_assert_equal_s("\n\n[section]\n\tname = value\n", result.ptr);
git_buf_dispose(&result);
}
void test_config_write__locking(void)
{
git_config *cfg, *cfg2;
git_config_entry *entry;
git_transaction *tx;
const char *filename = "locked-file";
/* Open the config and lock it */
cl_git_mkfile(filename, "[section]\n\tname = value\n");
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("value", entry->value);
git_config_entry_free(entry);
cl_git_pass(git_config_lock(&tx, cfg));
/* Change entries in the locked backend */
cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value"));
/* We can see that the file we read from hasn't changed */
cl_git_pass(git_config_open_ondisk(&cfg2, filename));
cl_git_pass(git_config_get_entry(&entry, cfg2, "section.name"));
cl_assert_equal_s("value", entry->value);
git_config_entry_free(entry);
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg2, "section2.name3"));
git_config_free(cfg2);
/* And we also get the old view when we read from the locked config */
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("value", entry->value);
git_config_entry_free(entry);
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
cl_git_pass(git_transaction_commit(tx));
git_transaction_free(tx);
/* Now that we've unlocked it, we should see both updates */
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("other value", entry->value);
git_config_entry_free(entry);
cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
cl_assert_equal_s("more value", entry->value);
git_config_entry_free(entry);
git_config_free(cfg);
/* We should also see the changes after reopening the config */
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("other value", entry->value);
git_config_entry_free(entry);
cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
cl_assert_equal_s("more value", entry->value);
git_config_entry_free(entry);
git_config_free(cfg);
}
void test_config_write__repeated(void)
{
const char *filename = "config-repeated";
git_config *cfg;
git_buf result = GIT_BUF_INIT;
const char *expected = "[sample \"prefix\"]\n\
\tsetting1 = someValue1\n\
\tsetting2 = someValue2\n\
\tsetting3 = someValue3\n\
\tsetting4 = someValue4\n\
";
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting1", "someValue1"));
cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting2", "someValue2"));
cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting3", "someValue3"));
cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting4", "someValue4"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_futils_readbuffer(&result, filename));
cl_assert_equal_s(expected, result.ptr);
git_buf_dispose(&result);
git_config_free(cfg);
}
void test_config_write__preserve_case(void)
{
const char *filename = "config-preserve-case";
git_config *cfg;
git_buf result = GIT_BUF_INIT;
const char *expected = "[sOMe]\n" \
"\tThInG = foo\n" \
"\tOtheR = thing\n";
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_futils_readbuffer(&result, filename));
cl_assert_equal_s(expected, result.ptr);
git_buf_dispose(&result);
git_config_free(cfg);
}
+57
View File
@@ -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);
}
+64
View File
@@ -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);
}
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
#include "clar_libgit2.h"
#include "futils.h"
#include "path.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_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_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_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_path_isdir("an_dir"));
}
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_path_isfile(path));
cl_git_pass(p_stat(path, &st));
cl_assert(st.st_nlink > 1);
}
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_path_isdir("src"));
cl_assert(git_path_isdir("src/b"));
cl_assert(git_path_isdir("src/c/d"));
cl_assert(git_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_path_isdir("t1"));
cl_assert(git_path_isdir("t1/b"));
cl_assert(git_path_isdir("t1/c"));
cl_assert(git_path_isdir("t1/c/d"));
cl_assert(!git_path_isdir("t1/c/e"));
cl_assert(git_path_isfile("t1/f1"));
cl_assert(git_path_isfile("t1/b/f2"));
cl_assert(git_path_isfile("t1/c/f3"));
cl_assert(git_path_isfile("t1/c/d/f4"));
cl_assert(!git_path_isfile("t1/c/d/.f5"));
cl_git_pass(git_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_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_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_path_isdir("t2"));
cl_assert(git_path_isdir("t2/b"));
cl_assert(git_path_isdir("t2/c"));
cl_assert(git_path_isdir("t2/c/d"));
cl_assert(git_path_isdir("t2/c/e"));
cl_assert(git_path_isfile("t2/f1"));
cl_assert(git_path_isfile("t2/b/f2"));
cl_assert(git_path_isfile("t2/c/f3"));
cl_assert(git_path_isfile("t2/c/d/f4"));
cl_assert(git_path_isfile("t2/c/d/.f5"));
#ifndef GIT_WIN32
cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
#endif
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_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_path_isdir("t3"));
cl_assert(git_path_isdir("t3"));
cl_assert(git_path_isdir("t3/b"));
cl_assert(git_path_isdir("t3/c"));
cl_assert(git_path_isdir("t3/c/d"));
cl_assert(git_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));
}
+306
View File
@@ -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_buf 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_buf_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_buf_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_buf *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_BUF_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_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_BUF_INIT
};
/* traverse a subfolder */
void test_core_dirent__traverse_subfolder(void)
{
cl_set_cleanup(&dirent_cleanup__cb, &sub);
setup(&sub);
cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
check_counts(&sub);
}
static walk_data sub_slash = {
"sub/",
sub_names,
GIT_BUF_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_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_BUF_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_path_direach(&empty.path, 0, one_entry, &empty));
check_counts(&empty);
/* make sure callback not called */
cl_assert(git_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_BUF_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_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_path_is_empty_dir("empty_dir"));
cl_git_mkfile("empty_dir/content", "whatever\n");
cl_assert(!git_path_is_empty_dir("empty_dir"));
cl_assert(!git_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_path_is_empty_dir("empty_dir"));
cl_assert(git_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_path_diriter *diriter, walk_data *walk)
{
const char *fullpath, *filename;
size_t fullpath_len, filename_len;
cl_git_pass(git_path_diriter_fullpath(&fullpath, &fullpath_len, diriter));
cl_git_pass(git_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_path_diriter diriter = GIT_PATH_DIRITER_INIT;
int error;
cl_set_cleanup(&dirent_cleanup__cb, &sub);
setup(&sub);
cl_git_pass(git_path_diriter_init(&diriter, sub.path.ptr, 0));
while ((error = git_path_diriter_next(&diriter)) == 0)
handle_next(&diriter, &sub);
cl_assert_equal_i(error, GIT_ITEROVER);
git_path_diriter_free(&diriter);
check_counts(&sub);
}
void test_core_dirent__diriter_at_directory_root(void)
{
git_path_diriter diriter = GIT_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_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_path_diriter_init(&diriter, root_path, 0));
while ((error = git_path_diriter_next(&diriter)) == 0) {
cl_git_pass(git_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_path_diriter_free(&diriter);
git__free(root_path);
}
+42
View File
@@ -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) == 1489279344088ULL);
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, 1489279344088ULL) == 6);
cl_assert(!memcmp(buf, "\xaa\xaa\xfe\xdc\xbaX", 6));
cl_assert(git_encode_varint(buf, 1, 1489279344088ULL) == -1);
}
+321
View File
@@ -0,0 +1,321 @@
#include "clar_libgit2.h"
#include "futils.h"
#include "sysdir.h"
#include "path.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_buf path = GIT_BUF_INIT, found = GIT_BUF_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_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_buf_joinpath(&path, path.ptr, testfile));
cl_git_mkfile(path.ptr, "find me");
git_buf_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_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_buf_dispose(&path);
git_buf_dispose(&found);
}
void test_core_env__1(void)
{
git_buf path = GIT_BUF_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_buf_dispose(&path);
}
static void check_global_searchpath(
const char *path, int position, const char *file, git_buf *temp)
{
git_buf out = GIT_BUF_INIT;
/* build and set new path */
if (position < 0)
cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, path, "$PATH"));
else if (position > 0)
cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, "$PATH", path));
else
cl_git_pass(git_buf_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_buf_dispose(&out);
}
void test_core_env__2(void)
{
git_buf path = GIT_BUF_INIT, found = GIT_BUF_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_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_buf_joinpath(&path, path.ptr, testfile));
cl_git_mkfile(path.ptr, "find me");
git_buf_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_buf_joinpath(&path, path.ptr, testfile));
(void)p_unlink(path.ptr);
(void)p_rmdir(*val);
}
git_buf_dispose(&path);
git_buf_dispose(&found);
}
void test_core_env__substitution(void)
{
git_buf buf = GIT_BUF_INIT, expected = GIT_BUF_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_buf_clear(&buf);
cl_git_pass(git_buf_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_buf_join(&expected, GIT_PATH_LIST_SEPARATOR, "/tmp/a", "/tmp/b"));
cl_assert_equal_s(expected.ptr, buf.ptr);
git_buf_dispose(&expected);
git_buf_dispose(&buf);
}
+222
View File
@@ -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);
}
+35
View File
@@ -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
}
+267
View File
@@ -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_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_path_exists(test_lock));
cl_git_fail(git_filebuf_commit(&file));
p_close(fd);
git_filebuf_cleanup(&file);
cl_assert_equal_i(false, git_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_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_path_exists("linkdir/target.lock"));
cl_git_pass(git_filebuf_commit(&file));
cl_assert_equal_i(true, git_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_path_exists("linkdir/target.lock"));
cl_git_pass(git_filebuf_commit(&file));
cl_assert_equal_i(true, git_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_buf source = GIT_BUF_INIT, target = GIT_BUF_INIT;
if (!git_path_supports_symlinks(clar_sandbox_path()))
cl_skip();
cl_git_pass(git_buf_joinpath(&source, clar_sandbox_path(), "linkdir/link"));
cl_git_pass(git_buf_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_path_exists("linkdir/target.lock"));
cl_git_pass(git_filebuf_commit(&file));
cl_assert_equal_i(true, git_path_exists("linkdir/target"));
git_filebuf_cleanup(&file);
git_buf_dispose(&source);
git_buf_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_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"));
}
+48
View File
@@ -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);
}
+89
View File
@@ -0,0 +1,89 @@
#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_buf out = GIT_BUF_INIT,
append = GIT_BUF_INIT;
/* create a new file */
git_buf_puts(&out, "hello!\n");
git_buf_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_buf_puts(&append, "And some more!\n");
git_buf_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_buf_dispose(&out);
git_buf_dispose(&append);
}
void test_core_futils__write_hidden_file(void)
{
#ifndef GIT_WIN32
cl_skip();
#else
git_buf out = GIT_BUF_INIT, append = GIT_BUF_INIT;
bool hidden;
git_buf_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_buf_puts(&append, "And some more!\n");
git_buf_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_buf_dispose(&out);
git_buf_dispose(&append);
#endif
}
void test_core_futils__recursive_rmdir_keeps_symlink_targets(void)
{
if (!git_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_path_exists("dir-target"));
cl_assert(git_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"));
}
+22
View File
@@ -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);
}
+78
View File
@@ -0,0 +1,78 @@
#include "clar_libgit2.h"
#include "path.h"
#ifdef GIT_USE_ICONV
static git_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_path_iconv_init_precompose(&ic));
#endif
}
void test_core_iconv__cleanup(void)
{
#ifdef GIT_USE_ICONV
git_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_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_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_path_iconv_t */
data = nfd; datalen = nfdlen;
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
cl_assert_equal_s(nfc, data);
data = nfd; datalen = nfdlen;
cl_git_pass(git_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_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
}
+54
View File
@@ -0,0 +1,54 @@
#include "clar_libgit2.h"
void test_core_init__returns_count(void)
{
/* libgit2_clar 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
}
+632
View File
@@ -0,0 +1,632 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "buffer.h"
#include "path.h"
#ifdef GIT_WIN32
# include "win32/reparse.h"
#endif
void test_core_link__cleanup(void)
{
#ifdef GIT_WIN32
RemoveDirectory("lstat_junction");
RemoveDirectory("lstat_dangling");
RemoveDirectory("lstat_dangling_dir");
RemoveDirectory("lstat_dangling_junction");
RemoveDirectory("stat_junction");
RemoveDirectory("stat_dangling");
RemoveDirectory("stat_dangling_dir");
RemoveDirectory("stat_dangling_junction");
#endif
}
#ifdef GIT_WIN32
static bool 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_buf unparsed_buf = GIT_BUF_INIT;
wchar_t *subst_utf16, *print_utf16;
DWORD ioctl_ret;
int subst_utf16_len, subst_byte_len, print_utf16_len, print_byte_len, ret;
USHORT reparse_buflen;
size_t i;
/* Junction targets must be the unparsed name, starting with \??\, using
* backslashes instead of forward, and end in a trailing backslash.
* eg: \??\C:\Foo\
*/
git_buf_puts(&unparsed_buf, "\\??\\");
for (i = 0; i < strlen(old); i++)
git_buf_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]);
git_buf_putc(&unparsed_buf, '\\');
subst_utf16_len = git__utf8_to_16(NULL, 0, git_buf_cstr(&unparsed_buf));
subst_byte_len = subst_utf16_len * sizeof(WCHAR);
print_utf16_len = subst_utf16_len - 4;
print_byte_len = subst_byte_len - (4 * sizeof(WCHAR));
/* The junction must be an empty directory before the junction attribute
* can be added.
*/
cl_win32_pass(CreateDirectoryA(new, NULL));
handle = CreateFileA(new, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
cl_win32_pass(handle != INVALID_HANDLE_VALUE);
reparse_buflen = (USHORT)(REPARSE_DATA_HEADER_SIZE +
REPARSE_DATA_MOUNTPOINT_HEADER_SIZE +
subst_byte_len + sizeof(WCHAR) +
print_byte_len + sizeof(WCHAR));
reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen);
cl_assert(reparse_buf);
subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer;
print_utf16 = subst_utf16 + subst_utf16_len + 1;
ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1,
git_buf_cstr(&unparsed_buf));
cl_assert_equal_i(subst_utf16_len, ret);
ret = git__utf8_to_16(print_utf16,
print_utf16_len + 1, git_buf_cstr(&unparsed_buf) + 4);
cl_assert_equal_i(print_utf16_len, ret);
reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0;
reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len;
reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len;
reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE;
cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
reparse_buf, reparse_buflen, NULL, 0, &ioctl_ret, NULL));
CloseHandle(handle);
LocalFree(reparse_buf);
git_buf_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_buf target_path = GIT_BUF_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_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_target");
cl_git_rewritefile("lstat_target", "This is the target of a symbolic link.\n");
do_symlink(git_buf_cstr(&target_path), "lstat_symlink", 0);
cl_must_pass(p_lstat("lstat_target", &st));
cl_assert(S_ISREG(st.st_mode));
cl_assert_equal_i(39, st.st_size);
cl_must_pass(p_lstat("lstat_symlink", &st));
cl_assert(S_ISLNK(st.st_mode));
cl_assert_equal_i(git_buf_len(&target_path), st.st_size);
git_buf_dispose(&target_path);
}
void test_core_link__lstat_symlink_directory(void)
{
git_buf target_path = GIT_BUF_INIT;
struct stat st;
if (!should_run())
clar__skip();
git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget");
p_mkdir("lstat_dirtarget", 0777);
do_symlink(git_buf_cstr(&target_path), "lstat_dirlink", 1);
cl_must_pass(p_lstat("lstat_dirtarget", &st));
cl_assert(S_ISDIR(st.st_mode));
cl_must_pass(p_lstat("lstat_dirlink", &st));
cl_assert(S_ISLNK(st.st_mode));
cl_assert_equal_i(git_buf_len(&target_path), st.st_size);
git_buf_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_buf target_path = GIT_BUF_INIT;
struct stat st;
git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget");
p_mkdir("stat_junctarget", 0777);
do_junction(git_buf_cstr(&target_path), "stat_junction");
cl_must_pass(p_stat("stat_junctarget", &st));
cl_assert(S_ISDIR(st.st_mode));
cl_must_pass(p_stat("stat_junction", &st));
cl_assert(S_ISDIR(st.st_mode));
git_buf_dispose(&target_path);
#endif
}
void test_core_link__stat_dangling_junction(void)
{
#ifdef GIT_WIN32
git_buf target_path = GIT_BUF_INIT;
struct stat st;
git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget");
p_mkdir("stat_nonexistent_junctarget", 0777);
do_junction(git_buf_cstr(&target_path), "stat_dangling_junction");
RemoveDirectory("stat_nonexistent_junctarget");
cl_must_fail(p_stat("stat_nonexistent_junctarget", &st));
cl_must_fail(p_stat("stat_dangling_junction", &st));
git_buf_dispose(&target_path);
#endif
}
void test_core_link__lstat_junction(void)
{
#ifdef GIT_WIN32
git_buf target_path = GIT_BUF_INIT;
struct stat st;
git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget");
p_mkdir("lstat_junctarget", 0777);
do_junction(git_buf_cstr(&target_path), "lstat_junction");
cl_must_pass(p_lstat("lstat_junctarget", &st));
cl_assert(S_ISDIR(st.st_mode));
cl_must_pass(p_lstat("lstat_junction", &st));
cl_assert(S_ISLNK(st.st_mode));
git_buf_dispose(&target_path);
#endif
}
void test_core_link__lstat_dangling_junction(void)
{
#ifdef GIT_WIN32
git_buf target_path = GIT_BUF_INIT;
struct stat st;
git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget");
p_mkdir("lstat_nonexistent_junctarget", 0777);
do_junction(git_buf_cstr(&target_path), "lstat_dangling_junction");
RemoveDirectory("lstat_nonexistent_junctarget");
cl_must_fail(p_lstat("lstat_nonexistent_junctarget", &st));
cl_must_pass(p_lstat("lstat_dangling_junction", &st));
cl_assert(S_ISLNK(st.st_mode));
cl_assert_equal_i(git_buf_len(&target_path), st.st_size);
git_buf_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_buf target_path = GIT_BUF_INIT;
int len;
char buf[2048];
if (!should_run())
clar__skip();
git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_target");
cl_git_rewritefile("readlink_target", "This is the target of a symlink\n");
do_symlink(git_buf_cstr(&target_path), "readlink_link", 0);
len = p_readlink("readlink_link", buf, 2048);
cl_must_pass(len);
buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_dispose(&target_path);
}
void test_core_link__readlink_dangling(void)
{
git_buf target_path = GIT_BUF_INIT;
int len;
char buf[2048];
if (!should_run())
clar__skip();
git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent");
do_symlink(git_buf_cstr(&target_path), "readlink_dangling", 0);
len = p_readlink("readlink_dangling", buf, 2048);
cl_must_pass(len);
buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_dispose(&target_path);
}
void test_core_link__readlink_multiple(void)
{
git_buf target_path = GIT_BUF_INIT,
path3 = GIT_BUF_INIT, path2 = GIT_BUF_INIT, path1 = GIT_BUF_INIT;
int len;
char buf[2048];
if (!should_run())
clar__skip();
git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_final");
git_buf_join(&path3, '/', clar_sandbox_path(), "readlink_3");
git_buf_join(&path2, '/', clar_sandbox_path(), "readlink_2");
git_buf_join(&path1, '/', clar_sandbox_path(), "readlink_1");
do_symlink(git_buf_cstr(&target_path), git_buf_cstr(&path3), 0);
do_symlink(git_buf_cstr(&path3), git_buf_cstr(&path2), 0);
do_symlink(git_buf_cstr(&path2), git_buf_cstr(&path1), 0);
len = p_readlink("readlink_1", buf, 2048);
cl_must_pass(len);
buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(&path2), buf);
git_buf_dispose(&path1);
git_buf_dispose(&path2);
git_buf_dispose(&path3);
git_buf_dispose(&target_path);
}
+46
View File
@@ -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", "");
}

Some files were not shown because too many files have changed in this diff Show More