]>
git.proxmox.com Git - libgit2.git/blob - tests/config/write.c
1 #include "clar_libgit2.h"
5 void test_config_write__initialize(void)
7 cl_fixture_sandbox("config/config9");
8 cl_fixture_sandbox("config/config15");
9 cl_fixture_sandbox("config/config17");
12 void test_config_write__cleanup(void)
14 cl_fixture_cleanup("config9");
15 cl_fixture_cleanup("config15");
16 cl_fixture_cleanup("config17");
19 void test_config_write__replace_value(void)
23 int64_t l
, expected
= +9223372036854775803;
25 /* By freeing the config, we make sure we flush the values */
26 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
27 cl_git_pass(git_config_set_int32(cfg
, "core.dummy", 5));
30 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
31 cl_git_pass(git_config_get_int32(&i
, cfg
, "core.dummy"));
35 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
36 cl_git_pass(git_config_set_int32(cfg
, "core.dummy", 1));
39 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
40 cl_git_pass(git_config_set_int64(cfg
, "core.verylong", expected
));
43 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
44 cl_git_pass(git_config_get_int64(&l
, cfg
, "core.verylong"));
45 cl_assert(l
== expected
);
48 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
49 cl_must_fail(git_config_get_int32(&i
, cfg
, "core.verylong"));
52 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
53 cl_git_pass(git_config_set_int64(cfg
, "core.verylong", 1));
57 void test_config_write__delete_value(void)
62 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
63 cl_git_pass(git_config_set_int32(cfg
, "core.dummy", 5));
66 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
67 cl_git_pass(git_config_delete_entry(cfg
, "core.dummy"));
70 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
71 cl_assert(git_config_get_int32(&i
, cfg
, "core.dummy") == GIT_ENOTFOUND
);
72 cl_git_pass(git_config_set_int32(cfg
, "core.dummy", 1));
77 * At the beginning of the test:
78 * - config9 has: core.dummy2=42
79 * - config15 has: core.dummy2=7
81 void test_config_write__delete_value_at_specific_level(void)
83 git_config
*cfg
, *cfg_specific
;
86 cl_git_pass(git_config_open_ondisk(&cfg
, "config15"));
87 cl_git_pass(git_config_get_int32(&i
, cfg
, "core.dummy2"));
91 cl_git_pass(git_config_new(&cfg
));
92 cl_git_pass(git_config_add_file_ondisk(cfg
, "config9",
93 GIT_CONFIG_LEVEL_LOCAL
, 0));
94 cl_git_pass(git_config_add_file_ondisk(cfg
, "config15",
95 GIT_CONFIG_LEVEL_GLOBAL
, 0));
97 cl_git_pass(git_config_open_level(&cfg_specific
, cfg
, GIT_CONFIG_LEVEL_GLOBAL
));
99 cl_git_pass(git_config_delete_entry(cfg_specific
, "core.dummy2"));
100 git_config_free(cfg
);
102 cl_git_pass(git_config_open_ondisk(&cfg
, "config15"));
103 cl_assert(git_config_get_int32(&i
, cfg
, "core.dummy2") == GIT_ENOTFOUND
);
104 cl_git_pass(git_config_set_int32(cfg
, "core.dummy2", 7));
106 git_config_free(cfg_specific
);
107 git_config_free(cfg
);
111 * This test exposes a bug where duplicate empty section headers could prevent
112 * deletion of config entries.
114 void test_config_write__delete_value_with_duplicate_header(void)
116 const char *file_name
= "config-duplicate-header";
117 const char *entry_name
= "remote.origin.url";
119 git_config_entry
*entry
;
121 /* This config can occur after removing and re-adding the origin remote */
122 const char *file_content
=
123 "[remote \"origin\"]\n" \
124 "[branch \"master\"]\n" \
125 " remote = \"origin\"\n" \
126 "[remote \"origin\"]\n" \
129 /* Write the test config and make sure the expected entry exists */
130 cl_git_mkfile(file_name
, file_content
);
131 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
132 cl_git_pass(git_config_get_entry(&entry
, cfg
, entry_name
));
134 /* Delete that entry */
135 cl_git_pass(git_config_delete_entry(cfg
, entry_name
));
137 /* Reopen the file and make sure the entry no longer exists */
138 git_config_entry_free(entry
);
139 git_config_free(cfg
);
140 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
141 cl_git_fail(git_config_get_entry(&entry
, cfg
, entry_name
));
144 git_config_entry_free(entry
);
145 git_config_free(cfg
);
149 * This test exposes a bug where duplicate section headers could cause
150 * config_write to add a new entry when one already exists.
152 void test_config_write__add_value_with_duplicate_header(void)
154 const char *file_name
= "config-duplicate-insert";
155 const char *entry_name
= "foo.c";
156 const char *old_val
= "old";
157 const char *new_val
= "new";
159 git_config
*cfg
, *snapshot
;
161 /* c = old should be replaced by c = new.
162 * The bug causes c = new to be inserted under the first 'foo' header.
164 const char *file_content
=
172 /* Write the test config */
173 cl_git_mkfile(file_name
, file_content
);
174 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
176 /* make sure the expected entry (foo.c) exists */
177 cl_git_pass(git_config_snapshot(&snapshot
, cfg
));
178 cl_git_pass(git_config_get_string(&str
, snapshot
, entry_name
));
179 cl_assert_equal_s(old_val
, str
);
180 git_config_free(snapshot
);
182 /* Try setting foo.c to something else */
183 cl_git_pass(git_config_set_string(cfg
, entry_name
, new_val
));
184 git_config_free(cfg
);
186 /* Reopen the file and make sure the new value was set */
187 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
188 cl_git_pass(git_config_snapshot(&snapshot
, cfg
));
189 cl_git_pass(git_config_get_string(&str
, snapshot
, entry_name
));
190 cl_assert_equal_s(new_val
, str
);
193 git_config_free(snapshot
);
194 git_config_free(cfg
);
197 void test_config_write__overwrite_value_with_duplicate_header(void)
199 const char *file_name
= "config-duplicate-header";
200 const char *entry_name
= "remote.origin.url";
202 git_config_entry
*entry
;
204 /* This config can occur after removing and re-adding the origin remote */
205 const char *file_content
=
206 "[remote \"origin\"]\n" \
207 "[branch \"master\"]\n" \
208 " remote = \"origin\"\n" \
209 "[remote \"origin\"]\n" \
212 /* Write the test config and make sure the expected entry exists */
213 cl_git_mkfile(file_name
, file_content
);
214 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
215 cl_git_pass(git_config_get_entry(&entry
, cfg
, entry_name
));
217 /* Update that entry */
218 cl_git_pass(git_config_set_string(cfg
, entry_name
, "newurl"));
220 /* Reopen the file and make sure the entry was updated */
221 git_config_entry_free(entry
);
222 git_config_free(cfg
);
223 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
224 cl_git_pass(git_config_get_entry(&entry
, cfg
, entry_name
));
226 cl_assert_equal_s("newurl", entry
->value
);
229 git_config_entry_free(entry
);
230 git_config_free(cfg
);
233 void test_config_write__overwrite_multivar_within_duplicate_header(void)
235 const char *file_name
= "config-duplicate-header";
236 const char *entry_name
= "remote.origin.url";
238 git_config_entry
*entry
;
240 /* This config can occur after removing and re-adding the origin remote */
241 const char *file_content
=
242 "[remote \"origin\"]\n" \
244 "[branch \"master\"]\n" \
245 " remote = \"origin\"\n" \
246 "[remote \"origin\"]\n" \
249 /* Write the test config and make sure the expected entry exists */
250 cl_git_mkfile(file_name
, file_content
);
251 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
252 cl_git_pass(git_config_get_entry(&entry
, cfg
, entry_name
));
254 /* Update that entry */
255 cl_git_pass(git_config_set_multivar(cfg
, entry_name
, ".*", "newurl"));
257 /* Reopen the file and make sure the entry was updated */
258 git_config_entry_free(entry
);
259 git_config_free(cfg
);
260 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
261 cl_git_pass(git_config_get_entry(&entry
, cfg
, entry_name
));
263 cl_assert_equal_s("newurl", entry
->value
);
266 git_config_entry_free(entry
);
267 git_config_free(cfg
);
270 void test_config_write__write_subsection(void)
273 git_buf buf
= GIT_BUF_INIT
;
275 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
276 cl_git_pass(git_config_set_string(cfg
, "my.own.var", "works"));
277 git_config_free(cfg
);
279 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
280 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "my.own.var"));
281 cl_assert_equal_s("works", git_buf_cstr(&buf
));
284 git_config_free(cfg
);
287 void test_config_write__delete_inexistent(void)
291 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
292 cl_assert(git_config_delete_entry(cfg
, "core.imaginary") == GIT_ENOTFOUND
);
293 git_config_free(cfg
);
296 void test_config_write__value_containing_quotes(void)
299 git_buf buf
= GIT_BUF_INIT
;
301 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
302 cl_git_pass(git_config_set_string(cfg
, "core.somevar", "this \"has\" quotes"));
303 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
304 cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf
));
306 git_config_free(cfg
);
308 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
309 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
310 cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf
));
312 git_config_free(cfg
);
314 /* The code path for values that already exist is different, check that one as well */
315 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
316 cl_git_pass(git_config_set_string(cfg
, "core.somevar", "this also \"has\" quotes"));
317 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
318 cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf
));
320 git_config_free(cfg
);
322 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
323 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
324 cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf
));
326 git_config_free(cfg
);
329 void test_config_write__escape_value(void)
332 git_buf buf
= GIT_BUF_INIT
;
334 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
335 cl_git_pass(git_config_set_string(cfg
, "core.somevar", "this \"has\" quotes and \t"));
336 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
337 cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf
));
339 git_config_free(cfg
);
341 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
342 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.somevar"));
343 cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf
));
345 git_config_free(cfg
);
348 void test_config_write__add_value_at_specific_level(void)
350 git_config
*cfg
, *cfg_specific
;
352 int64_t l
, expected
= +9223372036854775803;
353 git_buf buf
= GIT_BUF_INIT
;
355 // open config15 as global level config file
356 cl_git_pass(git_config_new(&cfg
));
357 cl_git_pass(git_config_add_file_ondisk(cfg
, "config9",
358 GIT_CONFIG_LEVEL_LOCAL
, 0));
359 cl_git_pass(git_config_add_file_ondisk(cfg
, "config15",
360 GIT_CONFIG_LEVEL_GLOBAL
, 0));
362 cl_git_pass(git_config_open_level(&cfg_specific
, cfg
, GIT_CONFIG_LEVEL_GLOBAL
));
364 cl_git_pass(git_config_set_int32(cfg_specific
, "core.int32global", 28));
365 cl_git_pass(git_config_set_int64(cfg_specific
, "core.int64global", expected
));
366 cl_git_pass(git_config_set_bool(cfg_specific
, "core.boolglobal", true));
367 cl_git_pass(git_config_set_string(cfg_specific
, "core.stringglobal", "I'm a global config value!"));
368 git_config_free(cfg_specific
);
369 git_config_free(cfg
);
371 // open config15 as local level config file
372 cl_git_pass(git_config_open_ondisk(&cfg
, "config15"));
374 cl_git_pass(git_config_get_int32(&i
, cfg
, "core.int32global"));
375 cl_assert_equal_i(28, i
);
376 cl_git_pass(git_config_get_int64(&l
, cfg
, "core.int64global"));
377 cl_assert(l
== expected
);
378 cl_git_pass(git_config_get_bool(&i
, cfg
, "core.boolglobal"));
379 cl_assert_equal_b(true, i
);
380 cl_git_pass(git_config_get_string_buf(&buf
, cfg
, "core.stringglobal"));
381 cl_assert_equal_s("I'm a global config value!", git_buf_cstr(&buf
));
384 git_config_free(cfg
);
387 void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
392 cl_git_pass(git_config_open_ondisk(&cfg
, "config17"));
393 cl_git_pass(git_config_set_int32(cfg
, "core.newline", 7));
394 git_config_free(cfg
);
396 cl_git_pass(git_config_open_ondisk(&cfg
, "config17"));
397 cl_git_pass(git_config_get_int32(&i
, cfg
, "core.newline"));
398 cl_assert_equal_i(7, i
);
400 git_config_free(cfg
);
403 void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void)
408 cl_git_pass(git_config_open_ondisk(&cfg
, "config17"));
409 cl_git_pass(git_config_set_int32(cfg
, "diff.context", 10));
410 git_config_free(cfg
);
412 cl_git_pass(git_config_open_ondisk(&cfg
, "config17"));
413 cl_git_pass(git_config_get_int32(&i
, cfg
, "diff.context"));
414 cl_assert_equal_i(10, i
);
416 git_config_free(cfg
);
419 void test_config_write__add_value_which_needs_quotes(void)
421 git_config
*cfg
, *base
;
428 cl_git_pass(git_config_open_ondisk(&cfg
, "config17"));
429 cl_git_pass(git_config_set_string(cfg
, "core.startwithspace", " Something"));
430 cl_git_pass(git_config_set_string(cfg
, "core.endwithspace", "Something "));
431 cl_git_pass(git_config_set_string(cfg
, "core.containscommentchar1", "some#thing"));
432 cl_git_pass(git_config_set_string(cfg
, "core.containscommentchar2", "some;thing"));
433 cl_git_pass(git_config_set_string(cfg
, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
434 git_config_free(cfg
);
436 cl_git_pass(git_config_open_ondisk(&base
, "config17"));
437 cl_git_pass(git_config_snapshot(&cfg
, base
));
438 cl_git_pass(git_config_get_string(&str1
, cfg
, "core.startwithspace"));
439 cl_assert_equal_s(" Something", str1
);
440 cl_git_pass(git_config_get_string(&str2
, cfg
, "core.endwithspace"));
441 cl_assert_equal_s("Something ", str2
);
442 cl_git_pass(git_config_get_string(&str3
, cfg
, "core.containscommentchar1"));
443 cl_assert_equal_s("some#thing", str3
);
444 cl_git_pass(git_config_get_string(&str4
, cfg
, "core.containscommentchar2"));
445 cl_assert_equal_s("some;thing", str4
);
446 cl_git_pass(git_config_get_string(&str5
, cfg
, "core.startwhithsapceandcontainsdoublequote"));
447 cl_assert_equal_s(" some\"thing", str5
);
448 git_config_free(cfg
);
449 git_config_free(base
);
452 void test_config_write__can_set_a_value_to_NULL(void)
454 git_repository
*repository
;
457 repository
= cl_git_sandbox_init("testrepo.git");
459 cl_git_pass(git_repository_config(&config
, repository
));
460 cl_git_fail(git_config_set_string(config
, "a.b.c", NULL
));
461 git_config_free(config
);
463 cl_git_sandbox_cleanup();
466 void test_config_write__can_set_an_empty_value(void)
468 git_repository
*repository
;
472 repository
= cl_git_sandbox_init("testrepo.git");
473 cl_git_pass(git_repository_config(&config
, repository
));
475 cl_git_pass(git_config_set_string(config
, "core.somevar", ""));
476 cl_git_pass(git_config_get_string_buf(&buf
, config
, "core.somevar"));
477 cl_assert_equal_s("", buf
.ptr
);
480 git_config_free(config
);
481 cl_git_sandbox_cleanup();
484 void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
488 cl_git_pass(git_config_open_ondisk(&cfg
, "config9"));
490 cl_git_mkfile("config9.lock", "[core]\n");
492 cl_git_fail_with(git_config_set_string(cfg
, "core.dump", "boom"), GIT_ELOCKED
);
494 git_config_free(cfg
);
497 void test_config_write__outside_change(void)
501 const char *filename
= "config-ext-change";
503 cl_git_mkfile(filename
, "[old]\nvalue = 5\n");
505 cl_git_pass(git_config_open_ondisk(&cfg
, filename
));
507 cl_git_pass(git_config_get_int32(&tmp
, cfg
, "old.value"));
509 /* Change the value on the file itself (simulate external process) */
510 cl_git_mkfile(filename
, "[old]\nvalue = 6\n");
512 cl_git_pass(git_config_set_int32(cfg
, "new.value", 7));
514 cl_git_pass(git_config_get_int32(&tmp
, cfg
, "old.value"));
515 cl_assert_equal_i(6, tmp
);
517 git_config_free(cfg
);
520 #define SECTION_FOO \
523 " [section \"foo\"] \n" \
524 " # here's a comment\n" \
525 "\tname = \"value\"\n" \
526 " name2 = \"value2\"\n" \
527 "; another comment!\n"
529 #define SECTION_BAR \
530 "[section \"bar\"]\t\n" \
532 " barname=\"value\"\n"
535 void test_config_write__preserves_whitespace_and_comments(void)
537 const char *file_name
= "config-duplicate-header";
540 git_buf newfile
= GIT_BUF_INIT
;
542 /* This config can occur after removing and re-adding the origin remote */
543 const char *file_content
= SECTION_FOO SECTION_BAR
;
545 /* Write the test config and make sure the expected entry exists */
546 cl_git_mkfile(file_name
, file_content
);
547 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
548 cl_git_pass(git_config_set_string(cfg
, "section.foo.other", "otherval"));
549 cl_git_pass(git_config_set_string(cfg
, "newsection.newname", "new_value"));
551 /* Ensure that we didn't needlessly mangle the config file */
552 cl_git_pass(git_futils_readbuffer(&newfile
, file_name
));
555 cl_assert_equal_strn(SECTION_FOO
, n
, strlen(SECTION_FOO
));
556 n
+= strlen(SECTION_FOO
);
558 cl_assert_equal_strn("\tother = otherval\n", n
, strlen("\tother = otherval\n"));
559 n
+= strlen("\tother = otherval\n");
561 cl_assert_equal_strn(SECTION_BAR
, n
, strlen(SECTION_BAR
));
562 n
+= strlen(SECTION_BAR
);
564 cl_assert_equal_s("[newsection]\n\tnewname = new_value\n", n
);
566 git_buf_free(&newfile
);
567 git_config_free(cfg
);
570 void test_config_write__preserves_entry_with_name_only(void)
572 const char *file_name
= "config-empty-value";
574 git_buf newfile
= GIT_BUF_INIT
;
576 /* Write the test config and make sure the expected entry exists */
577 cl_git_mkfile(file_name
, "[section \"foo\"]\n\tname\n");
578 cl_git_pass(git_config_open_ondisk(&cfg
, file_name
));
579 cl_git_pass(git_config_set_string(cfg
, "newsection.newname", "new_value"));
580 cl_git_pass(git_config_set_string(cfg
, "section.foo.other", "otherval"));
582 cl_git_pass(git_futils_readbuffer(&newfile
, file_name
));
583 cl_assert_equal_s("[section \"foo\"]\n\tname\n\tother = otherval\n[newsection]\n\tnewname = new_value\n", newfile
.ptr
);
585 git_buf_free(&newfile
);
586 git_config_free(cfg
);
589 void test_config_write__to_empty_file(void)
592 const char *filename
= "config-file";
593 git_buf result
= GIT_BUF_INIT
;
595 cl_git_mkfile(filename
, "");
596 cl_git_pass(git_config_open_ondisk(&cfg
, filename
));
597 cl_git_pass(git_config_set_string(cfg
, "section.name", "value"));
598 git_config_free(cfg
);
600 cl_git_pass(git_futils_readbuffer(&result
, "config-file"));
601 cl_assert_equal_s("[section]\n\tname = value\n", result
.ptr
);
603 git_buf_free(&result
);
606 void test_config_write__to_file_with_only_comment(void)
609 const char *filename
= "config-file";
610 git_buf result
= GIT_BUF_INIT
;
612 cl_git_mkfile(filename
, "\n\n");
613 cl_git_pass(git_config_open_ondisk(&cfg
, filename
));
614 cl_git_pass(git_config_set_string(cfg
, "section.name", "value"));
615 git_config_free(cfg
);
617 cl_git_pass(git_futils_readbuffer(&result
, "config-file"));
618 cl_assert_equal_s("\n\n[section]\n\tname = value\n", result
.ptr
);
620 git_buf_free(&result
);