#include "clar_libgit2.h" #include "buffer.h" #include "fileops.h" #include "git2/sys/config.h" #include "config_file.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, 0)); cl_git_pass(git_config_add_file_ondisk(cfg, "config15", GIT_CONFIG_LEVEL_GLOBAL, 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_free(&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_free(&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_free(&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, 0)); cl_git_pass(git_config_add_file_ondisk(cfg, "config15", GIT_CONFIG_LEVEL_GLOBAL, 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_free(&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_free(&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_free(&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_free(&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_free(&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_free(&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_free(&result); git_config_free(cfg); }