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