mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-07 17:55:28 +00:00

This changes the behavior of callbacks so that the callback error code is not converted into GIT_EUSER and instead we propagate the return value through to the caller. Instead of using the giterr_capture and giterr_restore functions, we now rely on all functions to pass back the return value from a callback. To avoid having a return value with no error message, the user can call the public giterr_set_str or some such function to set an error message. There is a new helper 'giterr_set_callback' that functions can invoke after making a callback which ensures that some error message was set in case the callback did not set one. In places where the sign of the callback return value is meaningful (e.g. positive to skip, negative to abort), only the negative values are returned back to the caller, obviously, since the other values allow for continuing the loop. The hardest parts of this were in the checkout code where positive return values were overloaded as meaningful values for checkout. I fixed this by adding an output parameter to many of the internal checkout functions and removing the overload. This added some code, but it is probably a better implementation. There is some funkiness in the network code where user provided callbacks could be returning a positive or a negative value and we want to rely on that to cancel the loop. There are still a couple places where an user error might get turned into GIT_EUSER there, I think, though none exercised by the tests.
570 lines
14 KiB
C
570 lines
14 KiB
C
#include "clar_libgit2.h"
|
|
|
|
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;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "this.that.other"));
|
|
cl_assert_equal_s(str, "true");
|
|
cl_git_pass(git_config_get_string(&str, cfg, "this.That.other"));
|
|
cl_assert_equal_s(str, "yes");
|
|
|
|
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;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "this.That.and"));
|
|
cl_assert_equal_s(str, "one one one two two three three");
|
|
|
|
git_config_free(cfg);
|
|
}
|
|
|
|
/*
|
|
* This kind of subsection declaration is case-insensitive
|
|
*/
|
|
void test_config_read__subsection_header(void)
|
|
{
|
|
git_config *cfg;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "section.subsection.var"));
|
|
cl_assert_equal_s(str, "hello");
|
|
|
|
/* The subsection is transformed to lower-case */
|
|
cl_must_fail(git_config_get_string(&str, cfg, "section.subSectIon.var"));
|
|
|
|
git_config_free(cfg);
|
|
}
|
|
|
|
void test_config_read__lone_variable(void)
|
|
{
|
|
git_config *cfg;
|
|
const char *str;
|
|
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(&str, cfg, "some.section.variable"));
|
|
cl_assert_equal_s(str, "");
|
|
|
|
cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
|
|
cl_assert(i == 1);
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "some.section.variableeq"));
|
|
cl_assert_equal_s(str, "");
|
|
|
|
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;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
|
|
cl_git_pass(git_config_get_string(&str, cfg, "remote.ab.url"));
|
|
cl_assert_equal_s(str, "http://example.com/git/ab");
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "remote.abba.url"));
|
|
cl_assert_equal_s(str, "http://example.com/git/abba");
|
|
|
|
git_config_free(cfg);
|
|
}
|
|
|
|
void test_config_read__escaping_quotes(void)
|
|
{
|
|
git_config *cfg;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
|
|
cl_git_pass(git_config_get_string(&str, cfg, "core.editor"));
|
|
cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", str);
|
|
|
|
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, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 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)
|
|
{
|
|
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, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 0));
|
|
|
|
count = 0;
|
|
cl_git_pass(git_config_iterator_new(&iter, cfg));
|
|
|
|
while ((ret = git_config_next(&entry, iter)) == 0) {
|
|
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_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;
|
|
const char *str;
|
|
|
|
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "a.b"));
|
|
cl_assert_equal_s(str, "c");
|
|
|
|
cl_git_pass(git_config_get_string(&str, cfg, "d.e"));
|
|
cl_assert_equal_s(str, "f");
|
|
|
|
git_config_free(cfg);
|
|
}
|
|
|
|
void test_config_read__read_git_config_entry(void)
|
|
{
|
|
git_config *cfg;
|
|
const 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, 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_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, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
|
|
GIT_CONFIG_LEVEL_LOCAL, 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, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 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, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
|
|
GIT_CONFIG_LEVEL_LOCAL, 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);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
const char *s;
|
|
|
|
cl_git_pass(git_config_new(&cfg));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
|
|
GIT_CONFIG_LEVEL_GLOBAL, 0));
|
|
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
|
|
GIT_CONFIG_LEVEL_SYSTEM, 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(&s, cfg_specific, "core.stringglobal"));
|
|
cl_assert_equal_s("I'm a global config value!", s);
|
|
|
|
git_config_free(cfg_specific);
|
|
git_config_free(cfg);
|
|
}
|
|
|
|
static void clean_test_config(void *unused)
|
|
{
|
|
GIT_UNUSED(unused);
|
|
cl_fixture_cleanup("./testconfig");
|
|
}
|
|
|
|
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__override_variable(void)
|
|
{
|
|
git_config *cfg;
|
|
const char *str;
|
|
|
|
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(&str, cfg, "some.var"));
|
|
cl_assert_equal_s(str, "two");
|
|
|
|
git_config_free(cfg);
|
|
}
|