]> git.proxmox.com Git - libgit2.git/blob - tests/config/write.c
78ed7f15b76cd5646f60786716077f702fe84acf
[libgit2.git] / tests / config / write.c
1 #include "clar_libgit2.h"
2 #include "buffer.h"
3 #include "futils.h"
4 #include "git2/sys/config.h"
5 #include "config.h"
6
7 void test_config_write__initialize(void)
8 {
9 cl_fixture_sandbox("config/config9");
10 cl_fixture_sandbox("config/config15");
11 cl_fixture_sandbox("config/config17");
12 }
13
14 void test_config_write__cleanup(void)
15 {
16 cl_fixture_cleanup("config9");
17 cl_fixture_cleanup("config15");
18 cl_fixture_cleanup("config17");
19 }
20
21 void test_config_write__replace_value(void)
22 {
23 git_config *cfg;
24 int i;
25 int64_t l, expected = +9223372036854775803;
26
27 /* By freeing the config, we make sure we flush the values */
28 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
29 cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
30 git_config_free(cfg);
31
32 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
33 cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
34 cl_assert(i == 5);
35 git_config_free(cfg);
36
37 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
38 cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
39 git_config_free(cfg);
40
41 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
42 cl_git_pass(git_config_set_int64(cfg, "core.verylong", expected));
43 git_config_free(cfg);
44
45 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
46 cl_git_pass(git_config_get_int64(&l, cfg, "core.verylong"));
47 cl_assert(l == expected);
48 git_config_free(cfg);
49
50 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
51 cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
52 git_config_free(cfg);
53
54 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
55 cl_git_pass(git_config_set_int64(cfg, "core.verylong", 1));
56 git_config_free(cfg);
57 }
58
59 void test_config_write__delete_value(void)
60 {
61 git_config *cfg;
62 int32_t i;
63
64 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
65 cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
66 git_config_free(cfg);
67
68 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
69 cl_git_pass(git_config_delete_entry(cfg, "core.dummy"));
70 git_config_free(cfg);
71
72 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
73 cl_assert(git_config_get_int32(&i, cfg, "core.dummy") == GIT_ENOTFOUND);
74 cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
75 git_config_free(cfg);
76 }
77
78 /*
79 * At the beginning of the test:
80 * - config9 has: core.dummy2=42
81 * - config15 has: core.dummy2=7
82 */
83 void test_config_write__delete_value_at_specific_level(void)
84 {
85 git_config *cfg, *cfg_specific;
86 int32_t i;
87
88 cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
89 cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
90 cl_assert(i == 7);
91 git_config_free(cfg);
92
93 cl_git_pass(git_config_new(&cfg));
94 cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
95 GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
96 cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
97 GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
98
99 cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
100
101 cl_git_pass(git_config_delete_entry(cfg_specific, "core.dummy2"));
102 git_config_free(cfg);
103
104 cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
105 cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND);
106 cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7));
107
108 git_config_free(cfg_specific);
109 git_config_free(cfg);
110 }
111
112 /*
113 * This test exposes a bug where duplicate empty section headers could prevent
114 * deletion of config entries.
115 */
116 void test_config_write__delete_value_with_duplicate_header(void)
117 {
118 const char *file_name = "config-duplicate-header";
119 const char *entry_name = "remote.origin.url";
120 git_config *cfg;
121 git_config_entry *entry;
122
123 /* This config can occur after removing and re-adding the origin remote */
124 const char *file_content =
125 "[remote \"origin\"]\n" \
126 "[branch \"master\"]\n" \
127 " remote = \"origin\"\n" \
128 "[remote \"origin\"]\n" \
129 " url = \"foo\"\n";
130
131 /* Write the test config and make sure the expected entry exists */
132 cl_git_mkfile(file_name, file_content);
133 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
134 cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
135
136 /* Delete that entry */
137 cl_git_pass(git_config_delete_entry(cfg, entry_name));
138
139 /* Reopen the file and make sure the entry no longer exists */
140 git_config_entry_free(entry);
141 git_config_free(cfg);
142 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
143 cl_git_fail(git_config_get_entry(&entry, cfg, entry_name));
144
145 /* Cleanup */
146 git_config_entry_free(entry);
147 git_config_free(cfg);
148 }
149
150 /*
151 * This test exposes a bug where duplicate section headers could cause
152 * config_write to add a new entry when one already exists.
153 */
154 void test_config_write__add_value_with_duplicate_header(void)
155 {
156 const char *file_name = "config-duplicate-insert";
157 const char *entry_name = "foo.c";
158 const char *old_val = "old";
159 const char *new_val = "new";
160 const char *str;
161 git_config *cfg, *snapshot;
162
163 /* c = old should be replaced by c = new.
164 * The bug causes c = new to be inserted under the first 'foo' header.
165 */
166 const char *file_content =
167 "[foo]\n" \
168 " a = b\n" \
169 "[other]\n" \
170 " a = b\n" \
171 "[foo]\n" \
172 " c = old\n";
173
174 /* Write the test config */
175 cl_git_mkfile(file_name, file_content);
176 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
177
178 /* make sure the expected entry (foo.c) exists */
179 cl_git_pass(git_config_snapshot(&snapshot, cfg));
180 cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
181 cl_assert_equal_s(old_val, str);
182 git_config_free(snapshot);
183
184 /* Try setting foo.c to something else */
185 cl_git_pass(git_config_set_string(cfg, entry_name, new_val));
186 git_config_free(cfg);
187
188 /* Reopen the file and make sure the new value was set */
189 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
190 cl_git_pass(git_config_snapshot(&snapshot, cfg));
191 cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
192 cl_assert_equal_s(new_val, str);
193
194 /* Cleanup */
195 git_config_free(snapshot);
196 git_config_free(cfg);
197 }
198
199 void test_config_write__overwrite_value_with_duplicate_header(void)
200 {
201 const char *file_name = "config-duplicate-header";
202 const char *entry_name = "remote.origin.url";
203 git_config *cfg;
204 git_config_entry *entry;
205
206 /* This config can occur after removing and re-adding the origin remote */
207 const char *file_content =
208 "[remote \"origin\"]\n" \
209 "[branch \"master\"]\n" \
210 " remote = \"origin\"\n" \
211 "[remote \"origin\"]\n" \
212 " url = \"foo\"\n";
213
214 /* Write the test config and make sure the expected entry exists */
215 cl_git_mkfile(file_name, file_content);
216 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
217 cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
218
219 /* Update that entry */
220 cl_git_pass(git_config_set_string(cfg, entry_name, "newurl"));
221
222 /* Reopen the file and make sure the entry was updated */
223 git_config_entry_free(entry);
224 git_config_free(cfg);
225 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
226 cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
227
228 cl_assert_equal_s("newurl", entry->value);
229
230 /* Cleanup */
231 git_config_entry_free(entry);
232 git_config_free(cfg);
233 }
234
235 static int multivar_cb(const git_config_entry *entry, void *data)
236 {
237 int *n = (int *)data;
238
239 cl_assert_equal_s(entry->value, "newurl");
240
241 (*n)++;
242
243 return 0;
244 }
245
246 void test_config_write__overwrite_multivar_within_duplicate_header(void)
247 {
248 const char *file_name = "config-duplicate-header";
249 const char *entry_name = "remote.origin.url";
250 git_config *cfg;
251 git_config_entry *entry;
252 int n = 0;
253
254 /* This config can occur after removing and re-adding the origin remote */
255 const char *file_content =
256 "[remote \"origin\"]\n" \
257 " url = \"bar\"\n" \
258 "[branch \"master\"]\n" \
259 " remote = \"origin\"\n" \
260 "[remote \"origin\"]\n" \
261 " url = \"foo\"\n";
262
263 /* Write the test config and make sure the expected entry exists */
264 cl_git_mkfile(file_name, file_content);
265 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
266 cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
267
268 /* Update that entry */
269 cl_git_pass(git_config_set_multivar(cfg, entry_name, ".*", "newurl"));
270 git_config_entry_free(entry);
271 git_config_free(cfg);
272
273 /* Reopen the file and make sure the entry was updated */
274 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
275 cl_git_pass(git_config_get_multivar_foreach(cfg, entry_name, NULL, multivar_cb, &n));
276 cl_assert_equal_i(2, n);
277
278 /* Cleanup */
279 git_config_free(cfg);
280 }
281
282 void test_config_write__write_subsection(void)
283 {
284 git_config *cfg;
285 git_buf buf = GIT_BUF_INIT;
286
287 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
288 cl_git_pass(git_config_set_string(cfg, "my.own.var", "works"));
289 git_config_free(cfg);
290
291 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
292 cl_git_pass(git_config_get_string_buf(&buf, cfg, "my.own.var"));
293 cl_assert_equal_s("works", git_buf_cstr(&buf));
294
295 git_buf_dispose(&buf);
296 git_config_free(cfg);
297 }
298
299 void test_config_write__delete_inexistent(void)
300 {
301 git_config *cfg;
302
303 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
304 cl_assert(git_config_delete_entry(cfg, "core.imaginary") == GIT_ENOTFOUND);
305 git_config_free(cfg);
306 }
307
308 void test_config_write__value_containing_quotes(void)
309 {
310 git_config *cfg;
311 git_buf buf = GIT_BUF_INIT;
312
313 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
314 cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes"));
315 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
316 cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf));
317 git_buf_clear(&buf);
318 git_config_free(cfg);
319
320 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
321 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
322 cl_assert_equal_s("this \"has\" quotes", git_buf_cstr(&buf));
323 git_buf_clear(&buf);
324 git_config_free(cfg);
325
326 /* The code path for values that already exist is different, check that one as well */
327 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
328 cl_git_pass(git_config_set_string(cfg, "core.somevar", "this also \"has\" quotes"));
329 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
330 cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf));
331 git_buf_clear(&buf);
332 git_config_free(cfg);
333
334 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
335 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
336 cl_assert_equal_s("this also \"has\" quotes", git_buf_cstr(&buf));
337 git_buf_dispose(&buf);
338 git_config_free(cfg);
339 }
340
341 void test_config_write__escape_value(void)
342 {
343 git_config *cfg;
344 git_buf buf = GIT_BUF_INIT;
345
346 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
347 cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes and \t"));
348 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
349 cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf));
350 git_buf_clear(&buf);
351 git_config_free(cfg);
352
353 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
354 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
355 cl_assert_equal_s("this \"has\" quotes and \t", git_buf_cstr(&buf));
356 git_buf_dispose(&buf);
357 git_config_free(cfg);
358 }
359
360 void test_config_write__add_value_at_specific_level(void)
361 {
362 git_config *cfg, *cfg_specific;
363 int i;
364 int64_t l, expected = +9223372036854775803;
365 git_buf buf = GIT_BUF_INIT;
366
367 /* open config15 as global level config file */
368 cl_git_pass(git_config_new(&cfg));
369 cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
370 GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
371 cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
372 GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
373
374 cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
375
376 cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28));
377 cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected));
378 cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true));
379 cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!"));
380 git_config_free(cfg_specific);
381 git_config_free(cfg);
382
383 /* open config15 as local level config file */
384 cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
385
386 cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global"));
387 cl_assert_equal_i(28, i);
388 cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global"));
389 cl_assert(l == expected);
390 cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal"));
391 cl_assert_equal_b(true, i);
392 cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
393 cl_assert_equal_s("I'm a global config value!", git_buf_cstr(&buf));
394
395 git_buf_dispose(&buf);
396 git_config_free(cfg);
397 }
398
399 void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
400 {
401 git_config *cfg;
402 int i;
403
404 cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
405 cl_git_pass(git_config_set_int32(cfg, "core.newline", 7));
406 git_config_free(cfg);
407
408 cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
409 cl_git_pass(git_config_get_int32(&i, cfg, "core.newline"));
410 cl_assert_equal_i(7, i);
411
412 git_config_free(cfg);
413 }
414
415 void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void)
416 {
417 git_config *cfg;
418 int i;
419
420 cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
421 cl_git_pass(git_config_set_int32(cfg, "diff.context", 10));
422 git_config_free(cfg);
423
424 cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
425 cl_git_pass(git_config_get_int32(&i, cfg, "diff.context"));
426 cl_assert_equal_i(10, i);
427
428 git_config_free(cfg);
429 }
430
431 void test_config_write__add_value_which_needs_quotes(void)
432 {
433 git_config *cfg, *base;
434 const char* str1;
435 const char* str2;
436 const char* str3;
437 const char* str4;
438 const char* str5;
439
440 cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
441 cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
442 cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
443 cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
444 cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
445 cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
446 git_config_free(cfg);
447
448 cl_git_pass(git_config_open_ondisk(&base, "config17"));
449 cl_git_pass(git_config_snapshot(&cfg, base));
450 cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
451 cl_assert_equal_s(" Something", str1);
452 cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
453 cl_assert_equal_s("Something ", str2);
454 cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
455 cl_assert_equal_s("some#thing", str3);
456 cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
457 cl_assert_equal_s("some;thing", str4);
458 cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
459 cl_assert_equal_s(" some\"thing", str5);
460 git_config_free(cfg);
461 git_config_free(base);
462 }
463
464 void test_config_write__can_set_a_value_to_NULL(void)
465 {
466 git_repository *repository;
467 git_config *config;
468
469 repository = cl_git_sandbox_init("testrepo.git");
470
471 cl_git_pass(git_repository_config(&config, repository));
472 cl_git_fail(git_config_set_string(config, "a.b.c", NULL));
473 git_config_free(config);
474
475 cl_git_sandbox_cleanup();
476 }
477
478 void test_config_write__can_set_an_empty_value(void)
479 {
480 git_repository *repository;
481 git_config *config;
482 git_buf buf = {0};
483
484 repository = cl_git_sandbox_init("testrepo.git");
485 cl_git_pass(git_repository_config(&config, repository));
486
487 cl_git_pass(git_config_set_string(config, "core.somevar", ""));
488 cl_git_pass(git_config_get_string_buf(&buf, config, "core.somevar"));
489 cl_assert_equal_s("", buf.ptr);
490
491 git_buf_dispose(&buf);
492 git_config_free(config);
493 cl_git_sandbox_cleanup();
494 }
495
496 void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
497 {
498 git_config *cfg;
499
500 cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
501
502 cl_git_mkfile("config9.lock", "[core]\n");
503
504 cl_git_fail_with(git_config_set_string(cfg, "core.dump", "boom"), GIT_ELOCKED);
505
506 git_config_free(cfg);
507 }
508
509 void test_config_write__outside_change(void)
510 {
511 int32_t tmp;
512 git_config *cfg;
513 const char *filename = "config-ext-change";
514
515 cl_git_mkfile(filename, "[old]\nvalue = 5\n");
516
517 cl_git_pass(git_config_open_ondisk(&cfg, filename));
518
519 cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
520
521 /* Change the value on the file itself (simulate external process) */
522 cl_git_mkfile(filename, "[old]\nvalue = 6\n");
523
524 cl_git_pass(git_config_set_int32(cfg, "new.value", 7));
525
526 cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
527 cl_assert_equal_i(6, tmp);
528
529 git_config_free(cfg);
530 }
531
532 #define FOO_COMMENT \
533 "; another comment!\n"
534
535 #define SECTION_FOO \
536 "\n" \
537 " \n" \
538 " [section \"foo\"] \n" \
539 " # here's a comment\n" \
540 "\tname = \"value\"\n" \
541 " name2 = \"value2\"\n" \
542
543 #define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT
544
545 #define SECTION_BAR \
546 "[section \"bar\"]\t\n" \
547 "\t \n" \
548 " barname=\"value\"\n"
549
550
551 void test_config_write__preserves_whitespace_and_comments(void)
552 {
553 const char *file_name = "config-duplicate-header";
554 const char *n;
555 git_config *cfg;
556 git_buf newfile = GIT_BUF_INIT;
557
558 /* This config can occur after removing and re-adding the origin remote */
559 const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR;
560
561 /* Write the test config and make sure the expected entry exists */
562 cl_git_mkfile(file_name, file_content);
563 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
564 cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
565 cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
566
567 /* Ensure that we didn't needlessly mangle the config file */
568 cl_git_pass(git_futils_readbuffer(&newfile, file_name));
569 n = newfile.ptr;
570
571 cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO));
572 n += strlen(SECTION_FOO);
573 cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n"));
574 n += strlen("\tother = otherval\n");
575 cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT));
576 n += strlen(FOO_COMMENT);
577
578 cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR));
579 n += strlen(SECTION_BAR);
580
581 cl_assert_equal_s("[newsection]\n\tnewname = new_value\n", n);
582
583 git_buf_dispose(&newfile);
584 git_config_free(cfg);
585 }
586
587 void test_config_write__preserves_entry_with_name_only(void)
588 {
589 const char *file_name = "config-empty-value";
590 git_config *cfg;
591 git_buf newfile = GIT_BUF_INIT;
592
593 /* Write the test config and make sure the expected entry exists */
594 cl_git_mkfile(file_name, "[section \"foo\"]\n\tname\n");
595 cl_git_pass(git_config_open_ondisk(&cfg, file_name));
596 cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
597 cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
598
599 cl_git_pass(git_futils_readbuffer(&newfile, file_name));
600 cl_assert_equal_s("[section \"foo\"]\n\tname\n\tother = otherval\n[newsection]\n\tnewname = new_value\n", newfile.ptr);
601
602 git_buf_dispose(&newfile);
603 git_config_free(cfg);
604 }
605
606 void test_config_write__to_empty_file(void)
607 {
608 git_config *cfg;
609 const char *filename = "config-file";
610 git_buf result = GIT_BUF_INIT;
611
612 cl_git_mkfile(filename, "");
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);
616
617 cl_git_pass(git_futils_readbuffer(&result, "config-file"));
618 cl_assert_equal_s("[section]\n\tname = value\n", result.ptr);
619
620 git_buf_dispose(&result);
621 }
622
623 void test_config_write__to_file_with_only_comment(void)
624 {
625 git_config *cfg;
626 const char *filename = "config-file";
627 git_buf result = GIT_BUF_INIT;
628
629 cl_git_mkfile(filename, "\n\n");
630 cl_git_pass(git_config_open_ondisk(&cfg, filename));
631 cl_git_pass(git_config_set_string(cfg, "section.name", "value"));
632 git_config_free(cfg);
633
634 cl_git_pass(git_futils_readbuffer(&result, "config-file"));
635 cl_assert_equal_s("\n\n[section]\n\tname = value\n", result.ptr);
636
637 git_buf_dispose(&result);
638 }
639
640 void test_config_write__locking(void)
641 {
642 git_config *cfg, *cfg2;
643 git_config_entry *entry;
644 git_transaction *tx;
645 const char *filename = "locked-file";
646
647 /* Open the config and lock it */
648 cl_git_mkfile(filename, "[section]\n\tname = value\n");
649 cl_git_pass(git_config_open_ondisk(&cfg, filename));
650 cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
651 cl_assert_equal_s("value", entry->value);
652 git_config_entry_free(entry);
653 cl_git_pass(git_config_lock(&tx, cfg));
654
655 /* Change entries in the locked backend */
656 cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
657 cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value"));
658
659 /* We can see that the file we read from hasn't changed */
660 cl_git_pass(git_config_open_ondisk(&cfg2, filename));
661 cl_git_pass(git_config_get_entry(&entry, cfg2, "section.name"));
662 cl_assert_equal_s("value", entry->value);
663 git_config_entry_free(entry);
664 cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg2, "section2.name3"));
665 git_config_free(cfg2);
666
667 /* And we also get the old view when we read from the locked config */
668 cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
669 cl_assert_equal_s("value", entry->value);
670 git_config_entry_free(entry);
671 cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
672
673 cl_git_pass(git_transaction_commit(tx));
674 git_transaction_free(tx);
675
676 /* Now that we've unlocked it, we should see both updates */
677 cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
678 cl_assert_equal_s("other value", entry->value);
679 git_config_entry_free(entry);
680 cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
681 cl_assert_equal_s("more value", entry->value);
682 git_config_entry_free(entry);
683
684 git_config_free(cfg);
685
686 /* We should also see the changes after reopening the config */
687 cl_git_pass(git_config_open_ondisk(&cfg, filename));
688 cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
689 cl_assert_equal_s("other value", entry->value);
690 git_config_entry_free(entry);
691 cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
692 cl_assert_equal_s("more value", entry->value);
693 git_config_entry_free(entry);
694
695 git_config_free(cfg);
696 }
697
698 void test_config_write__repeated(void)
699 {
700 const char *filename = "config-repeated";
701 git_config *cfg;
702 git_buf result = GIT_BUF_INIT;
703 const char *expected = "[sample \"prefix\"]\n\
704 \tsetting1 = someValue1\n\
705 \tsetting2 = someValue2\n\
706 \tsetting3 = someValue3\n\
707 \tsetting4 = someValue4\n\
708 ";
709 cl_git_pass(git_config_open_ondisk(&cfg, filename));
710 cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting1", "someValue1"));
711 cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting2", "someValue2"));
712 cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting3", "someValue3"));
713 cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting4", "someValue4"));
714 git_config_free(cfg);
715
716 cl_git_pass(git_config_open_ondisk(&cfg, filename));
717
718 cl_git_pass(git_futils_readbuffer(&result, filename));
719 cl_assert_equal_s(expected, result.ptr);
720 git_buf_dispose(&result);
721
722 git_config_free(cfg);
723 }
724
725 void test_config_write__preserve_case(void)
726 {
727 const char *filename = "config-preserve-case";
728 git_config *cfg;
729 git_buf result = GIT_BUF_INIT;
730 const char *expected = "[sOMe]\n" \
731 "\tThInG = foo\n" \
732 "\tOtheR = thing\n";
733
734 cl_git_pass(git_config_open_ondisk(&cfg, filename));
735 cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
736 cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
737 git_config_free(cfg);
738
739 cl_git_pass(git_config_open_ondisk(&cfg, filename));
740
741 cl_git_pass(git_futils_readbuffer(&result, filename));
742 cl_assert_equal_s(expected, result.ptr);
743 git_buf_dispose(&result);
744
745 git_config_free(cfg);
746 }