1 package ini
2
3 import (
4 "bytes"
5 "flag"
6 "io"
7 "path/filepath"
8 "runtime"
9 "testing"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 )
14
15 const (
16 confData = `
17 ; Package name
18 NAME = ini
19 ; Package version
20 VERSION = v1
21 ; Package import path
22 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
23
24 # Information about package author
25 # Bio can be written in multiple lines.
26 [author]
27 NAME = Unknwon ; Succeeding comment
28 E-MAIL = fake@localhost
29 GITHUB = https://github.com/%(NAME)s
30 BIO = """Gopher.
31 Coding addict.
32 Good man.
33 """ # Succeeding comment`
34 minimalConf = "testdata/minimal.ini"
35 fullConf = "testdata/full.ini"
36 notFoundConf = "testdata/404.ini"
37 )
38
39 var update = flag.Bool("update", false, "Update .golden files")
40
41 func TestLoad(t *testing.T) {
42 t.Run("load from good data sources", func(t *testing.T) {
43 f, err := Load(
44 "testdata/minimal.ini",
45 []byte("NAME = ini\nIMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s"),
46 bytes.NewReader([]byte(`VERSION = v1`)),
47 io.NopCloser(bytes.NewReader([]byte("[author]\nNAME = Unknwon"))),
48 )
49 require.NoError(t, err)
50 require.NotNil(t, f)
51
52
53 sec := f.Section("")
54 assert.Equal(t, "ini", sec.Key("NAME").String())
55 assert.Equal(t, "v1", sec.Key("VERSION").String())
56 assert.Equal(t, "gopkg.in/ini.v1", sec.Key("IMPORT_PATH").String())
57
58 sec = f.Section("author")
59 assert.Equal(t, "Unknwon", sec.Key("NAME").String())
60 assert.Equal(t, "u@gogs.io", sec.Key("E-MAIL").String())
61 })
62
63 t.Run("load from bad data sources", func(t *testing.T) {
64 t.Run("invalid input", func(t *testing.T) {
65 _, err := Load(notFoundConf)
66 require.Error(t, err)
67 })
68
69 t.Run("unsupported type", func(t *testing.T) {
70 _, err := Load(123)
71 require.Error(t, err)
72 })
73 })
74
75 t.Run("cannot properly parse INI files containing `#` or `;` in value", func(t *testing.T) {
76 f, err := Load([]byte(`
77 [author]
78 NAME = U#n#k#n#w#o#n
79 GITHUB = U;n;k;n;w;o;n
80 `))
81 require.NoError(t, err)
82 require.NotNil(t, f)
83
84 sec := f.Section("author")
85 nameValue := sec.Key("NAME").String()
86 githubValue := sec.Key("GITHUB").String()
87 assert.Equal(t, "U", nameValue)
88 assert.Equal(t, "U", githubValue)
89 })
90
91 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) {
92 f, err := Load([]byte(`
93 [long]
94 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
95 foo
96 bar
97 foobar
98 barfoo
99 -----END RSA PRIVATE KEY-----
100 `))
101 require.Error(t, err)
102 assert.Nil(t, f)
103 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error())
104 })
105
106 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) {
107 f, err := Load([]byte(`
108 [long]
109 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
110 1foo
111 2bar
112 3foobar
113 4barfoo
114 5foo
115 6bar
116 7foobar
117 8barfoo
118 9foo
119 10bar
120 11foobar
121 12barfoo
122 13foo
123 14bar
124 15foobar
125 16barfoo
126 17foo
127 18bar
128 19foobar
129 20barfoo
130 21foo
131 22bar
132 23foobar
133 24barfoo
134 25foo
135 26bar
136 27foobar
137 28barfoo
138 29foo
139 30bar
140 31foobar
141 32barfoo
142 33foo
143 34bar
144 35foobar
145 36barfoo
146 37foo
147 38bar
148 39foobar
149 40barfoo
150 41foo
151 42bar
152 43foobar
153 44barfoo
154 45foo
155 46bar
156 47foobar
157 48barfoo
158 49foo
159 50bar
160 51foobar
161 52barfoo
162 53foo
163 54bar
164 55foobar
165 56barfoo
166 57foo
167 58bar
168 59foobar
169 60barfoo
170 61foo
171 62bar
172 63foobar
173 64barfoo
174 65foo
175 66bar
176 67foobar
177 68barfoo
178 69foo
179 70bar
180 71foobar
181 72barfoo
182 73foo
183 74bar
184 75foobar
185 76barfoo
186 77foo
187 78bar
188 79foobar
189 80barfoo
190 81foo
191 82bar
192 83foobar
193 84barfoo
194 85foo
195 86bar
196 87foobar
197 88barfoo
198 89foo
199 90bar
200 91foobar
201 92barfoo
202 93foo
203 94bar
204 95foobar
205 96barfoo
206 -----END RSA PRIVATE KEY-----
207 `))
208 require.Error(t, err)
209 assert.Nil(t, f)
210 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error())
211 })
212 }
213
214 func TestLooseLoad(t *testing.T) {
215 f, err := LoadSources(LoadOptions{Loose: true}, notFoundConf, minimalConf)
216 require.NoError(t, err)
217 require.NotNil(t, f)
218
219 t.Run("inverse case", func(t *testing.T) {
220 _, err = Load(notFoundConf)
221 require.Error(t, err)
222 })
223 }
224
225 func TestInsensitiveLoad(t *testing.T) {
226 t.Run("insensitive to section and key names", func(t *testing.T) {
227 f, err := InsensitiveLoad(minimalConf)
228 require.NoError(t, err)
229 require.NotNil(t, f)
230
231 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String())
232
233 t.Run("write out", func(t *testing.T) {
234 var buf bytes.Buffer
235 _, err := f.WriteTo(&buf)
236 require.NoError(t, err)
237 assert.Equal(t, `[author]
238 e-mail = u@gogs.io
239 `,
240 buf.String(),
241 )
242 })
243
244 t.Run("inverse case", func(t *testing.T) {
245 f, err := Load(minimalConf)
246 require.NoError(t, err)
247 require.NotNil(t, f)
248
249 assert.Empty(t, f.Section("Author").Key("e-mail").String())
250 })
251 })
252
253
254 t.Run("insensitive load with default section", func(t *testing.T) {
255 f, err := InsensitiveLoad([]byte(`
256 user = unknwon
257 [profile]
258 email = unknwon@local
259 `))
260 require.NoError(t, err)
261 require.NotNil(t, f)
262
263 assert.Equal(t, "unknwon", f.Section(DefaultSection).Key("user").String())
264 })
265 }
266
267 func TestLoadSources(t *testing.T) {
268 t.Run("with true `AllowPythonMultilineValues`", func(t *testing.T) {
269 t.Run("ignore nonexistent files", func(t *testing.T) {
270 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true, Loose: true}, notFoundConf, minimalConf)
271 require.NoError(t, err)
272 require.NotNil(t, f)
273
274 t.Run("inverse case", func(t *testing.T) {
275 _, err = LoadSources(LoadOptions{AllowPythonMultilineValues: true}, notFoundConf)
276 require.Error(t, err)
277 })
278 })
279
280 t.Run("insensitive to section and key names", func(t *testing.T) {
281 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true, Insensitive: true}, minimalConf)
282 require.NoError(t, err)
283 require.NotNil(t, f)
284
285 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String())
286
287 t.Run("write out", func(t *testing.T) {
288 var buf bytes.Buffer
289 _, err := f.WriteTo(&buf)
290 require.NoError(t, err)
291 assert.Equal(t, `[author]
292 e-mail = u@gogs.io
293 `,
294 buf.String(),
295 )
296 })
297
298 t.Run("inverse case", func(t *testing.T) {
299 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, minimalConf)
300 require.NoError(t, err)
301 require.NotNil(t, f)
302
303 assert.Empty(t, f.Section("Author").Key("e-mail").String())
304 })
305 })
306
307 t.Run("insensitive to sections and sensitive to key names", func(t *testing.T) {
308 f, err := LoadSources(LoadOptions{InsensitiveSections: true}, minimalConf)
309 require.NoError(t, err)
310 require.NotNil(t, f)
311
312 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("E-MAIL").String())
313
314 t.Run("write out", func(t *testing.T) {
315 var buf bytes.Buffer
316 _, err := f.WriteTo(&buf)
317 require.NoError(t, err)
318 assert.Equal(t, `[author]
319 E-MAIL = u@gogs.io
320 `,
321 buf.String(),
322 )
323 })
324
325 t.Run("inverse case", func(t *testing.T) {
326 f, err := LoadSources(LoadOptions{}, minimalConf)
327 require.NoError(t, err)
328 require.NotNil(t, f)
329
330 assert.Empty(t, f.Section("Author").Key("e-mail").String())
331 })
332 })
333
334 t.Run("sensitive to sections and insensitive to key names", func(t *testing.T) {
335 f, err := LoadSources(LoadOptions{InsensitiveKeys: true}, minimalConf)
336 require.NoError(t, err)
337 require.NotNil(t, f)
338
339 assert.Equal(t, "u@gogs.io", f.Section("author").Key("e-mail").String())
340
341 t.Run("write out", func(t *testing.T) {
342 var buf bytes.Buffer
343 _, err := f.WriteTo(&buf)
344 require.NoError(t, err)
345 assert.Equal(t, `[author]
346 e-mail = u@gogs.io
347 `,
348 buf.String(),
349 )
350 })
351
352 t.Run("inverse case", func(t *testing.T) {
353 f, err := LoadSources(LoadOptions{}, minimalConf)
354 require.NoError(t, err)
355 require.NotNil(t, f)
356
357 assert.Empty(t, f.Section("Author").Key("e-mail").String())
358 })
359 })
360
361 t.Run("ignore continuation lines", func(t *testing.T) {
362 f, err := LoadSources(LoadOptions{
363 AllowPythonMultilineValues: true,
364 IgnoreContinuation: true,
365 }, []byte(`
366 key1=a\b\
367 key2=c\d\
368 key3=value`))
369 require.NoError(t, err)
370 require.NotNil(t, f)
371
372 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String())
373 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String())
374 assert.Equal(t, "value", f.Section("").Key("key3").String())
375
376 t.Run("inverse case", func(t *testing.T) {
377 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
378 key1=a\b\
379 key2=c\d\`))
380 require.NoError(t, err)
381 require.NotNil(t, f)
382
383 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String())
384 })
385 })
386
387 t.Run("ignore inline comments", func(t *testing.T) {
388 f, err := LoadSources(LoadOptions{
389 AllowPythonMultilineValues: true,
390 IgnoreInlineComment: true,
391 }, []byte(`
392 key1=value ;comment
393 key2=value2 #comment2
394 key3=val#ue #comment3`))
395 require.NoError(t, err)
396 require.NotNil(t, f)
397
398 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String())
399 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String())
400 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String())
401
402 t.Run("inverse case", func(t *testing.T) {
403 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
404 key1=value ;comment
405 key2=value2 #comment2`))
406 require.NoError(t, err)
407 require.NotNil(t, f)
408
409 assert.Equal(t, `value`, f.Section("").Key("key1").String())
410 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment)
411 assert.Equal(t, `value2`, f.Section("").Key("key2").String())
412 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment)
413 })
414 })
415
416 t.Run("skip unrecognizable lines", func(t *testing.T) {
417 f, err := LoadSources(LoadOptions{
418 SkipUnrecognizableLines: true,
419 }, []byte(`
420 GenerationDepth: 13
421
422 BiomeRarityScale: 100
423
424 ################
425 # Biome Groups #
426 ################
427
428 BiomeGroup(NormalBiomes, 3, 99, RoofedForestEnchanted, ForestSakura, FloatingJungle
429 BiomeGroup(IceBiomes, 4, 85, Ice Plains)
430
431 = RainForest
432 `))
433 require.NoError(t, err)
434 require.NotNil(t, f)
435
436 assert.Equal(t, "13", f.Section("").Key("GenerationDepth").String())
437 assert.Equal(t, "100", f.Section("").Key("BiomeRarityScale").String())
438 assert.False(t, f.Section("").HasKey("BiomeGroup"))
439 })
440
441 t.Run("allow boolean type keys", func(t *testing.T) {
442 f, err := LoadSources(LoadOptions{
443 AllowPythonMultilineValues: true,
444 AllowBooleanKeys: true,
445 }, []byte(`
446 key1=hello
447 #key2
448 key3`))
449 require.NoError(t, err)
450 require.NotNil(t, f)
451
452 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings())
453 assert.True(t, f.Section("").Key("key3").MustBool(false))
454
455 t.Run("write out", func(t *testing.T) {
456 var buf bytes.Buffer
457 _, err := f.WriteTo(&buf)
458 require.NoError(t, err)
459 assert.Equal(t, `key1 = hello
460 # key2
461 key3
462 `,
463 buf.String(),
464 )
465 })
466
467 t.Run("inverse case", func(t *testing.T) {
468 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
469 key1=hello
470 #key2
471 key3`))
472 require.Error(t, err)
473 })
474 })
475
476 t.Run("allow shadow keys", func(t *testing.T) {
477 f, err := LoadSources(LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(`
478 [remote "origin"]
479 url = https://github.com/Antergone/test1.git
480 url = https://github.com/Antergone/test2.git
481 fetch = +refs/heads/*:refs/remotes/origin/*`))
482 require.NoError(t, err)
483 require.NotNil(t, f)
484
485 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String())
486 assert.Equal(
487 t,
488 []string{
489 "https://github.com/Antergone/test1.git",
490 "https://github.com/Antergone/test2.git",
491 },
492 f.Section(`remote "origin"`).Key("url").ValueWithShadows(),
493 )
494 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String())
495
496 t.Run("write out", func(t *testing.T) {
497 var buf bytes.Buffer
498 _, err := f.WriteTo(&buf)
499 require.NoError(t, err)
500 assert.Equal(t, `[remote "origin"]
501 url = https://github.com/Antergone/test1.git
502 url = https://github.com/Antergone/test2.git
503 fetch = +refs/heads/*:refs/remotes/origin/*
504 `,
505 buf.String(),
506 )
507 })
508
509 t.Run("inverse case", func(t *testing.T) {
510 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
511 [remote "origin"]
512 url = https://github.com/Antergone/test1.git
513 url = https://github.com/Antergone/test2.git`))
514 require.NoError(t, err)
515 require.NotNil(t, f)
516
517 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String())
518 })
519 })
520
521 t.Run("unescape double quotes inside value", func(t *testing.T) {
522 f, err := LoadSources(LoadOptions{
523 AllowPythonMultilineValues: true,
524 UnescapeValueDoubleQuotes: true,
525 }, []byte(`
526 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
527 require.NoError(t, err)
528 require.NotNil(t, f)
529
530 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String())
531
532 t.Run("inverse case", func(t *testing.T) {
533 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
534 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
535 require.NoError(t, err)
536 require.NotNil(t, f)
537
538 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String())
539 })
540 })
541
542 t.Run("unescape comment symbols inside value", func(t *testing.T) {
543 f, err := LoadSources(LoadOptions{
544 AllowPythonMultilineValues: true,
545 IgnoreInlineComment: true,
546 UnescapeValueCommentSymbols: true,
547 }, []byte(`
548 key = test value <span style="color: %s\; background: %s">more text</span>
549 `))
550 require.NoError(t, err)
551 require.NotNil(t, f)
552
553 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String())
554 })
555
556 t.Run("can parse small python-compatible INI files", func(t *testing.T) {
557 f, err := LoadSources(LoadOptions{
558 AllowPythonMultilineValues: true,
559 Insensitive: true,
560 UnparseableSections: []string{"core_lesson", "comments"},
561 }, []byte(`
562 [long]
563 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
564 foo
565 bar
566 foobar
567 barfoo
568 -----END RSA PRIVATE KEY-----
569 multiline_list =
570 first
571 second
572 third
573 `))
574 require.NoError(t, err)
575 require.NotNil(t, f)
576
577 assert.Equal(t, "-----BEGIN RSA PRIVATE KEY-----\n foo\n bar\n foobar\n barfoo\n -----END RSA PRIVATE KEY-----", f.Section("long").Key("long_rsa_private_key").String())
578 assert.Equal(t, "\n first\n second\n third", f.Section("long").Key("multiline_list").String())
579 })
580
581 t.Run("can parse big python-compatible INI files", func(t *testing.T) {
582 f, err := LoadSources(LoadOptions{
583 AllowPythonMultilineValues: true,
584 Insensitive: true,
585 UnparseableSections: []string{"core_lesson", "comments"},
586 }, []byte(`
587 [long]
588 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
589 1foo
590 2bar
591 3foobar
592 4barfoo
593 5foo
594 6bar
595 7foobar
596 8barfoo
597 9foo
598 10bar
599 11foobar
600 12barfoo
601 13foo
602 14bar
603 15foobar
604 16barfoo
605 17foo
606 18bar
607 19foobar
608 20barfoo
609 21foo
610 22bar
611 23foobar
612 24barfoo
613 25foo
614 26bar
615 27foobar
616 28barfoo
617 29foo
618 30bar
619 31foobar
620 32barfoo
621 33foo
622 34bar
623 35foobar
624 36barfoo
625 37foo
626 38bar
627 39foobar
628 40barfoo
629 41foo
630 42bar
631 43foobar
632 44barfoo
633 45foo
634 46bar
635 47foobar
636 48barfoo
637 49foo
638 50bar
639 51foobar
640 52barfoo
641 53foo
642 54bar
643 55foobar
644 56barfoo
645 57foo
646 58bar
647 59foobar
648 60barfoo
649 61foo
650 62bar
651 63foobar
652 64barfoo
653 65foo
654 66bar
655 67foobar
656 68barfoo
657 69foo
658 70bar
659 71foobar
660 72barfoo
661 73foo
662 74bar
663 75foobar
664 76barfoo
665 77foo
666 78bar
667 79foobar
668 80barfoo
669 81foo
670 82bar
671 83foobar
672 84barfoo
673 85foo
674 86bar
675 87foobar
676 88barfoo
677 89foo
678 90bar
679 91foobar
680 92barfoo
681 93foo
682 94bar
683 95foobar
684 96barfoo
685 -----END RSA PRIVATE KEY-----
686 `))
687 require.NoError(t, err)
688 require.NotNil(t, f)
689
690 assert.Equal(t, `-----BEGIN RSA PRIVATE KEY-----
691 1foo
692 2bar
693 3foobar
694 4barfoo
695 5foo
696 6bar
697 7foobar
698 8barfoo
699 9foo
700 10bar
701 11foobar
702 12barfoo
703 13foo
704 14bar
705 15foobar
706 16barfoo
707 17foo
708 18bar
709 19foobar
710 20barfoo
711 21foo
712 22bar
713 23foobar
714 24barfoo
715 25foo
716 26bar
717 27foobar
718 28barfoo
719 29foo
720 30bar
721 31foobar
722 32barfoo
723 33foo
724 34bar
725 35foobar
726 36barfoo
727 37foo
728 38bar
729 39foobar
730 40barfoo
731 41foo
732 42bar
733 43foobar
734 44barfoo
735 45foo
736 46bar
737 47foobar
738 48barfoo
739 49foo
740 50bar
741 51foobar
742 52barfoo
743 53foo
744 54bar
745 55foobar
746 56barfoo
747 57foo
748 58bar
749 59foobar
750 60barfoo
751 61foo
752 62bar
753 63foobar
754 64barfoo
755 65foo
756 66bar
757 67foobar
758 68barfoo
759 69foo
760 70bar
761 71foobar
762 72barfoo
763 73foo
764 74bar
765 75foobar
766 76barfoo
767 77foo
768 78bar
769 79foobar
770 80barfoo
771 81foo
772 82bar
773 83foobar
774 84barfoo
775 85foo
776 86bar
777 87foobar
778 88barfoo
779 89foo
780 90bar
781 91foobar
782 92barfoo
783 93foo
784 94bar
785 95foobar
786 96barfoo
787 -----END RSA PRIVATE KEY-----`,
788 f.Section("long").Key("long_rsa_private_key").String(),
789 )
790 })
791
792 t.Run("allow unparsable sections", func(t *testing.T) {
793 f, err := LoadSources(LoadOptions{
794 AllowPythonMultilineValues: true,
795 Insensitive: true,
796 UnparseableSections: []string{"core_lesson", "comments"},
797 }, []byte(`
798 Lesson_Location = 87
799 Lesson_Status = C
800 Score = 3
801 Time = 00:02:30
802
803 [CORE_LESSON]
804 my lesson state data – 1111111111111111111000000000000000001110000
805 111111111111111111100000000000111000000000 – end my lesson state data
806
807 [COMMENTS]
808 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
809 `))
810 require.NoError(t, err)
811 require.NotNil(t, f)
812
813 assert.Equal(t, "3", f.Section("").Key("score").String())
814 assert.Empty(t, f.Section("").Body())
815 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000
816 111111111111111111100000000000111000000000 – end my lesson state data`,
817 f.Section("core_lesson").Body(),
818 )
819 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body())
820
821 t.Run("write out", func(t *testing.T) {
822 var buf bytes.Buffer
823 _, err := f.WriteTo(&buf)
824 require.NoError(t, err)
825 assert.Equal(t, `lesson_location = 87
826 lesson_status = C
827 score = 3
828 time = 00:02:30
829
830 [core_lesson]
831 my lesson state data – 1111111111111111111000000000000000001110000
832 111111111111111111100000000000111000000000 – end my lesson state data
833
834 [comments]
835 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
836 `,
837 buf.String(),
838 )
839 })
840
841 t.Run("inverse case", func(t *testing.T) {
842 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
843 [CORE_LESSON]
844 my lesson state data – 1111111111111111111000000000000000001110000
845 111111111111111111100000000000111000000000 – end my lesson state data`))
846 require.Error(t, err)
847 })
848 })
849
850 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) {
851 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) {
852 f, err := LoadSources(
853 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false},
854 []byte(`
855 [author]
856 NAME = U#n#k#n#w#o#n
857 GITHUB = U;n;k;n;w;o;n
858 `))
859 require.NoError(t, err)
860 require.NotNil(t, f)
861 sec := f.Section("author")
862 nameValue := sec.Key("NAME").String()
863 githubValue := sec.Key("GITHUB").String()
864 assert.Equal(t, "U", nameValue)
865 assert.Equal(t, "U", githubValue)
866 })
867 })
868
869 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) {
870 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) {
871 f, err := LoadSources(
872 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true},
873 []byte(`
874 [author]
875 NAME = U#n#k#n#w#o#n
876 GITHUB = U;n;k;n;w;o;n
877 `))
878 require.NoError(t, err)
879 require.NotNil(t, f)
880 sec := f.Section("author")
881 nameValue := sec.Key("NAME").String()
882 githubValue := sec.Key("GITHUB").String()
883 assert.Equal(t, "U#n#k#n#w#o#n", nameValue)
884 assert.Equal(t, "U;n;k;n;w;o;n", githubValue)
885 })
886 })
887 })
888
889 t.Run("with false `AllowPythonMultilineValues`", func(t *testing.T) {
890 t.Run("ignore nonexistent files", func(t *testing.T) {
891 f, err := LoadSources(LoadOptions{
892 AllowPythonMultilineValues: false,
893 Loose: true,
894 }, notFoundConf, minimalConf)
895 require.NoError(t, err)
896 require.NotNil(t, f)
897
898 t.Run("inverse case", func(t *testing.T) {
899 _, err = LoadSources(LoadOptions{
900 AllowPythonMultilineValues: false,
901 }, notFoundConf)
902 require.Error(t, err)
903 })
904 })
905
906 t.Run("insensitive to section and key names", func(t *testing.T) {
907 f, err := LoadSources(LoadOptions{
908 AllowPythonMultilineValues: false,
909 Insensitive: true,
910 }, minimalConf)
911 require.NoError(t, err)
912 require.NotNil(t, f)
913
914 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String())
915
916 t.Run("write out", func(t *testing.T) {
917 var buf bytes.Buffer
918 _, err := f.WriteTo(&buf)
919 require.NoError(t, err)
920 assert.Equal(t, `[author]
921 e-mail = u@gogs.io
922 `,
923 buf.String(),
924 )
925 })
926
927 t.Run("inverse case", func(t *testing.T) {
928 f, err := LoadSources(LoadOptions{
929 AllowPythonMultilineValues: false,
930 }, minimalConf)
931 require.NoError(t, err)
932 require.NotNil(t, f)
933
934 assert.Empty(t, f.Section("Author").Key("e-mail").String())
935 })
936 })
937
938 t.Run("ignore continuation lines", func(t *testing.T) {
939 f, err := LoadSources(LoadOptions{
940 AllowPythonMultilineValues: false,
941 IgnoreContinuation: true,
942 }, []byte(`
943 key1=a\b\
944 key2=c\d\
945 key3=value`))
946 require.NoError(t, err)
947 require.NotNil(t, f)
948
949 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String())
950 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String())
951 assert.Equal(t, "value", f.Section("").Key("key3").String())
952
953 t.Run("inverse case", func(t *testing.T) {
954 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
955 key1=a\b\
956 key2=c\d\`))
957 require.NoError(t, err)
958 require.NotNil(t, f)
959
960 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String())
961 })
962 })
963
964 t.Run("ignore inline comments", func(t *testing.T) {
965 f, err := LoadSources(LoadOptions{
966 AllowPythonMultilineValues: false,
967 IgnoreInlineComment: true,
968 }, []byte(`
969 key1=value ;comment
970 key2=value2 #comment2
971 key3=val#ue #comment3`))
972 require.NoError(t, err)
973 require.NotNil(t, f)
974
975 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String())
976 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String())
977 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String())
978
979 t.Run("inverse case", func(t *testing.T) {
980 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
981 key1=value ;comment
982 key2=value2 #comment2`))
983 require.NoError(t, err)
984 require.NotNil(t, f)
985
986 assert.Equal(t, `value`, f.Section("").Key("key1").String())
987 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment)
988 assert.Equal(t, `value2`, f.Section("").Key("key2").String())
989 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment)
990 })
991 })
992
993 t.Run("allow boolean type keys", func(t *testing.T) {
994 f, err := LoadSources(LoadOptions{
995 AllowPythonMultilineValues: false,
996 AllowBooleanKeys: true,
997 }, []byte(`
998 key1=hello
999 #key2
1000 key3`))
1001 require.NoError(t, err)
1002 require.NotNil(t, f)
1003
1004 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings())
1005 assert.True(t, f.Section("").Key("key3").MustBool(false))
1006
1007 t.Run("write out", func(t *testing.T) {
1008 var buf bytes.Buffer
1009 _, err := f.WriteTo(&buf)
1010 require.NoError(t, err)
1011 assert.Equal(t, `key1 = hello
1012 # key2
1013 key3
1014 `,
1015 buf.String(),
1016 )
1017 })
1018
1019 t.Run("inverse case", func(t *testing.T) {
1020 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1021 key1=hello
1022 #key2
1023 key3`))
1024 require.Error(t, err)
1025 })
1026 })
1027
1028 t.Run("allow shadow keys", func(t *testing.T) {
1029 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(`
1030 [remote "origin"]
1031 url = https://github.com/Antergone/test1.git
1032 url = https://github.com/Antergone/test2.git
1033 fetch = +refs/heads/*:refs/remotes/origin/*`))
1034 require.NoError(t, err)
1035 require.NotNil(t, f)
1036
1037 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String())
1038 assert.Equal(
1039 t,
1040 []string{
1041 "https://github.com/Antergone/test1.git",
1042 "https://github.com/Antergone/test2.git",
1043 },
1044 f.Section(`remote "origin"`).Key("url").ValueWithShadows(),
1045 )
1046 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String())
1047
1048 t.Run("write out", func(t *testing.T) {
1049 var buf bytes.Buffer
1050 _, err := f.WriteTo(&buf)
1051 require.NoError(t, err)
1052 assert.Equal(t, `[remote "origin"]
1053 url = https://github.com/Antergone/test1.git
1054 url = https://github.com/Antergone/test2.git
1055 fetch = +refs/heads/*:refs/remotes/origin/*
1056 `,
1057 buf.String(),
1058 )
1059 })
1060
1061 t.Run("inverse case", func(t *testing.T) {
1062 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1063 [remote "origin"]
1064 url = https://github.com/Antergone/test1.git
1065 url = https://github.com/Antergone/test2.git`))
1066 require.NoError(t, err)
1067 require.NotNil(t, f)
1068
1069 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String())
1070 })
1071 })
1072
1073 t.Run("unescape double quotes inside value", func(t *testing.T) {
1074 f, err := LoadSources(LoadOptions{
1075 AllowPythonMultilineValues: false,
1076 UnescapeValueDoubleQuotes: true,
1077 }, []byte(`
1078 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
1079 require.NoError(t, err)
1080 require.NotNil(t, f)
1081
1082 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String())
1083
1084 t.Run("inverse case", func(t *testing.T) {
1085 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1086 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
1087 require.NoError(t, err)
1088 require.NotNil(t, f)
1089
1090 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String())
1091 })
1092 })
1093
1094 t.Run("unescape comment symbols inside value", func(t *testing.T) {
1095 f, err := LoadSources(LoadOptions{
1096 AllowPythonMultilineValues: false,
1097 IgnoreInlineComment: true,
1098 UnescapeValueCommentSymbols: true,
1099 }, []byte(`
1100 key = test value <span style="color: %s\; background: %s">more text</span>
1101 `))
1102 require.NoError(t, err)
1103 require.NotNil(t, f)
1104
1105 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String())
1106 })
1107
1108 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) {
1109 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1110 [long]
1111 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
1112 foo
1113 bar
1114 foobar
1115 barfoo
1116 -----END RSA PRIVATE KEY-----
1117 `))
1118 require.Error(t, err)
1119 assert.Nil(t, f)
1120 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error())
1121 })
1122
1123 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) {
1124 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1125 [long]
1126 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
1127 1foo
1128 2bar
1129 3foobar
1130 4barfoo
1131 5foo
1132 6bar
1133 7foobar
1134 8barfoo
1135 9foo
1136 10bar
1137 11foobar
1138 12barfoo
1139 13foo
1140 14bar
1141 15foobar
1142 16barfoo
1143 17foo
1144 18bar
1145 19foobar
1146 20barfoo
1147 21foo
1148 22bar
1149 23foobar
1150 24barfoo
1151 25foo
1152 26bar
1153 27foobar
1154 28barfoo
1155 29foo
1156 30bar
1157 31foobar
1158 32barfoo
1159 33foo
1160 34bar
1161 35foobar
1162 36barfoo
1163 37foo
1164 38bar
1165 39foobar
1166 40barfoo
1167 41foo
1168 42bar
1169 43foobar
1170 44barfoo
1171 45foo
1172 46bar
1173 47foobar
1174 48barfoo
1175 49foo
1176 50bar
1177 51foobar
1178 52barfoo
1179 53foo
1180 54bar
1181 55foobar
1182 56barfoo
1183 57foo
1184 58bar
1185 59foobar
1186 60barfoo
1187 61foo
1188 62bar
1189 63foobar
1190 64barfoo
1191 65foo
1192 66bar
1193 67foobar
1194 68barfoo
1195 69foo
1196 70bar
1197 71foobar
1198 72barfoo
1199 73foo
1200 74bar
1201 75foobar
1202 76barfoo
1203 77foo
1204 78bar
1205 79foobar
1206 80barfoo
1207 81foo
1208 82bar
1209 83foobar
1210 84barfoo
1211 85foo
1212 86bar
1213 87foobar
1214 88barfoo
1215 89foo
1216 90bar
1217 91foobar
1218 92barfoo
1219 93foo
1220 94bar
1221 95foobar
1222 96barfoo
1223 -----END RSA PRIVATE KEY-----
1224 `))
1225 require.Error(t, err)
1226 assert.Nil(t, f)
1227 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error())
1228 })
1229
1230 t.Run("allow unparsable sections", func(t *testing.T) {
1231 f, err := LoadSources(LoadOptions{
1232 AllowPythonMultilineValues: false,
1233 Insensitive: true,
1234 UnparseableSections: []string{"core_lesson", "comments"},
1235 }, []byte(`
1236 Lesson_Location = 87
1237 Lesson_Status = C
1238 Score = 3
1239 Time = 00:02:30
1240
1241 [CORE_LESSON]
1242 my lesson state data – 1111111111111111111000000000000000001110000
1243 111111111111111111100000000000111000000000 – end my lesson state data
1244
1245 [COMMENTS]
1246 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
1247 `))
1248 require.NoError(t, err)
1249 require.NotNil(t, f)
1250
1251 assert.Equal(t, "3", f.Section("").Key("score").String())
1252 assert.Empty(t, f.Section("").Body())
1253 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000
1254 111111111111111111100000000000111000000000 – end my lesson state data`,
1255 f.Section("core_lesson").Body(),
1256 )
1257 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body())
1258
1259 t.Run("write out", func(t *testing.T) {
1260 var buf bytes.Buffer
1261 _, err := f.WriteTo(&buf)
1262 require.NoError(t, err)
1263 assert.Equal(t, `lesson_location = 87
1264 lesson_status = C
1265 score = 3
1266 time = 00:02:30
1267
1268 [core_lesson]
1269 my lesson state data – 1111111111111111111000000000000000001110000
1270 111111111111111111100000000000111000000000 – end my lesson state data
1271
1272 [comments]
1273 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
1274 `,
1275 buf.String(),
1276 )
1277 })
1278
1279 t.Run("inverse case", func(t *testing.T) {
1280 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1281 [CORE_LESSON]
1282 my lesson state data – 1111111111111111111000000000000000001110000
1283 111111111111111111100000000000111000000000 – end my lesson state data`))
1284 require.Error(t, err)
1285 })
1286 })
1287
1288 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) {
1289 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) {
1290 f, err := LoadSources(
1291 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false},
1292 []byte(`
1293 [author]
1294 NAME = U#n#k#n#w#o#n
1295 GITHUB = U;n;k;n;w;o;n
1296 `))
1297 require.NoError(t, err)
1298 require.NotNil(t, f)
1299 sec := f.Section("author")
1300 nameValue := sec.Key("NAME").String()
1301 githubValue := sec.Key("GITHUB").String()
1302 assert.Equal(t, "U", nameValue)
1303 assert.Equal(t, "U", githubValue)
1304 })
1305 })
1306
1307 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) {
1308 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) {
1309 f, err := LoadSources(
1310 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true},
1311 []byte(`
1312 [author]
1313 NAME = U#n#k#n#w#o#n
1314 GITHUB = U;n;k;n;w;o;n
1315 `))
1316 require.NoError(t, err)
1317 require.NotNil(t, f)
1318 sec := f.Section("author")
1319 nameValue := sec.Key("NAME").String()
1320 githubValue := sec.Key("GITHUB").String()
1321 assert.Equal(t, "U#n#k#n#w#o#n", nameValue)
1322 assert.Equal(t, "U;n;k;n;w;o;n", githubValue)
1323 })
1324 })
1325 })
1326
1327 t.Run("with `ChildSectionDelimiter` ':'", func(t *testing.T) {
1328 t.Run("get all keys of parent sections", func(t *testing.T) {
1329 f := Empty(LoadOptions{ChildSectionDelimiter: ":"})
1330 require.NotNil(t, f)
1331
1332 k, err := f.Section("package").NewKey("NAME", "ini")
1333 require.NoError(t, err)
1334 assert.NotNil(t, k)
1335 k, err = f.Section("package").NewKey("VERSION", "v1")
1336 require.NoError(t, err)
1337 assert.NotNil(t, k)
1338 k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
1339 require.NoError(t, err)
1340 assert.NotNil(t, k)
1341
1342 keys := f.Section("package:sub:sub2").ParentKeys()
1343 names := []string{"NAME", "VERSION", "IMPORT_PATH"}
1344 assert.Equal(t, len(names), len(keys))
1345 for i, name := range names {
1346 assert.Equal(t, name, keys[i].Name())
1347 }
1348 })
1349
1350 t.Run("getting and setting values", func(t *testing.T) {
1351 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, fullConf)
1352 require.NoError(t, err)
1353 require.NotNil(t, f)
1354
1355 t.Run("get parent-keys that are available to the child section", func(t *testing.T) {
1356 parentKeys := f.Section("package:sub").ParentKeys()
1357 assert.NotNil(t, parentKeys)
1358 for _, k := range parentKeys {
1359 assert.Equal(t, "CLONE_URL", k.Name())
1360 }
1361 })
1362
1363 t.Run("get parent section value", func(t *testing.T) {
1364 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:sub").Key("CLONE_URL").String())
1365 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:fake:sub").Key("CLONE_URL").String())
1366 })
1367 })
1368
1369 t.Run("get child sections by parent name", func(t *testing.T) {
1370 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, []byte(`
1371 [node]
1372 [node:biz1]
1373 [node:biz2]
1374 [node.biz3]
1375 [node.bizN]
1376 `))
1377 require.NoError(t, err)
1378 require.NotNil(t, f)
1379
1380 children := f.ChildSections("node")
1381 names := []string{"node:biz1", "node:biz2"}
1382 assert.Equal(t, len(names), len(children))
1383 for i, name := range names {
1384 assert.Equal(t, name, children[i].Name())
1385 }
1386 })
1387 })
1388
1389 t.Run("ShortCircuit", func(t *testing.T) {
1390 t.Run("load the first available configuration, ignore other configuration", func(t *testing.T) {
1391 f, err := LoadSources(LoadOptions{ShortCircuit: true}, minimalConf, []byte(`key1 = value1`))
1392 require.NotNil(t, f)
1393 require.NoError(t, err)
1394 var buf bytes.Buffer
1395 _, err = f.WriteTo(&buf)
1396 require.NoError(t, err)
1397 assert.Equal(t, `[author]
1398 E-MAIL = u@gogs.io
1399 `,
1400 buf.String(),
1401 )
1402 })
1403
1404 t.Run("return an error when fail to load", func(t *testing.T) {
1405 f, err := LoadSources(LoadOptions{ShortCircuit: true}, notFoundConf, minimalConf)
1406 assert.Nil(t, f)
1407 require.Error(t, err)
1408 })
1409
1410 t.Run("used with Loose to ignore errors that the file does not exist", func(t *testing.T) {
1411 f, err := LoadSources(LoadOptions{ShortCircuit: true, Loose: true}, notFoundConf, minimalConf)
1412 require.NotNil(t, f)
1413 require.NoError(t, err)
1414 var buf bytes.Buffer
1415 _, err = f.WriteTo(&buf)
1416 require.NoError(t, err)
1417 assert.Equal(t, `[author]
1418 E-MAIL = u@gogs.io
1419 `,
1420 buf.String(),
1421 )
1422 })
1423
1424 t.Run("ensure all sources are loaded without ShortCircuit", func(t *testing.T) {
1425 f, err := LoadSources(LoadOptions{ShortCircuit: false}, minimalConf, []byte(`key1 = value1`))
1426 require.NotNil(t, f)
1427 require.NoError(t, err)
1428 var buf bytes.Buffer
1429 _, err = f.WriteTo(&buf)
1430 require.NoError(t, err)
1431 assert.Equal(t, `key1 = value1
1432
1433 [author]
1434 E-MAIL = u@gogs.io
1435 `,
1436 buf.String(),
1437 )
1438 })
1439 })
1440 }
1441
1442 func Test_KeyValueDelimiters(t *testing.T) {
1443 t.Run("custom key-value delimiters", func(t *testing.T) {
1444 f, err := LoadSources(LoadOptions{
1445 KeyValueDelimiters: "?!",
1446 }, []byte(`
1447 [section]
1448 key1?value1
1449 key2!value2
1450 `))
1451 require.NoError(t, err)
1452 require.NotNil(t, f)
1453
1454 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1455 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
1456 })
1457 }
1458
1459 func Test_PreserveSurroundedQuote(t *testing.T) {
1460 t.Run("preserve surrounded quote test", func(t *testing.T) {
1461 f, err := LoadSources(LoadOptions{
1462 PreserveSurroundedQuote: true,
1463 }, []byte(`
1464 [section]
1465 key1 = "value1"
1466 key2 = value2
1467 `))
1468 require.NoError(t, err)
1469 require.NotNil(t, f)
1470
1471 assert.Equal(t, "\"value1\"", f.Section("section").Key("key1").String())
1472 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
1473 })
1474
1475 t.Run("preserve surrounded quote test inverse test", func(t *testing.T) {
1476 f, err := LoadSources(LoadOptions{
1477 PreserveSurroundedQuote: false,
1478 }, []byte(`
1479 [section]
1480 key1 = "value1"
1481 key2 = value2
1482 `))
1483 require.NoError(t, err)
1484 require.NotNil(t, f)
1485
1486 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1487 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
1488 })
1489 }
1490
1491 type testData struct {
1492 Value1 string `ini:"value1"`
1493 Value2 string `ini:"value2"`
1494 Value3 string `ini:"value3"`
1495 }
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547 func TestPythonMultiline_EOF(t *testing.T) {
1548 if runtime.GOOS == "windows" {
1549 t.Skip("Skipping testing on Windows")
1550 }
1551
1552 path := filepath.Join("testdata", "multiline_eof.ini")
1553 f, err := LoadSources(LoadOptions{
1554 AllowPythonMultilineValues: true,
1555 ReaderBufferSize: 64 * 1024,
1556 }, path)
1557 require.NoError(t, err)
1558 require.NotNil(t, f)
1559 assert.Len(t, f.Sections(), 1)
1560
1561 defaultSection := f.Section("")
1562 assert.NotNil(t, f.Section(""))
1563
1564 var testData testData
1565 err = defaultSection.MapTo(&testData)
1566 require.NoError(t, err)
1567 assert.Equal(t, "some text here\n\tsome more text here 2", testData.Value1)
1568 }
1569
1570 func Test_NestedValuesSpanningSections(t *testing.T) {
1571 t.Run("basic nested value", func(t *testing.T) {
1572 f, err := LoadSources(LoadOptions{
1573 AllowNestedValues: true,
1574 }, []byte(`
1575 [section]
1576 key1 = value1
1577 key2 =
1578 nested1 = nestedvalue1
1579 `))
1580 require.NoError(t, err)
1581 require.NotNil(t, f)
1582
1583 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1584 assert.Equal(t, "", f.Section("section").Key("key2").String())
1585 assert.Equal(t, []string{"nested1 = nestedvalue1"}, f.Section("section").Key("key2").NestedValues())
1586 })
1587
1588 t.Run("no nested values", func(t *testing.T) {
1589 f, err := LoadSources(LoadOptions{
1590 AllowNestedValues: true,
1591 }, []byte(`
1592 [section]
1593 key1 = value1
1594 key2 =
1595 `))
1596 require.NoError(t, err)
1597 require.NotNil(t, f)
1598
1599 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1600 assert.Equal(t, "", f.Section("section").Key("key2").String())
1601 })
1602
1603 t.Run("no nested values and following sections", func(t *testing.T) {
1604 f, err := LoadSources(LoadOptions{
1605 AllowNestedValues: true,
1606 }, []byte(`
1607 [section]
1608 key1 = value1
1609 key2 =
1610
1611 [section2]
1612 key3 = value3
1613 `))
1614 require.NoError(t, err)
1615 require.NotNil(t, f)
1616
1617 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1618 assert.Equal(t, "", f.Section("section").Key("key2").String())
1619 assert.Equal(t, "value3", f.Section("section2").Key("key3").String())
1620 })
1621
1622 t.Run("no nested values and following sections with indentation", func(t *testing.T) {
1623 f, err := LoadSources(LoadOptions{
1624 AllowNestedValues: true,
1625 }, []byte(`
1626 [section]
1627 key1 = value1
1628 key2 =
1629
1630 [section2]
1631 key3 = value3
1632 `))
1633 require.NoError(t, err)
1634 require.NotNil(t, f)
1635
1636 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1637 assert.Equal(t, "", f.Section("section").Key("key2").String())
1638 assert.Equal(t, "value3", f.Section("section2").Key("key3").String())
1639 })
1640 }
1641
View as plain text