1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ini
16
17 import (
18 "bytes"
19 "io/ioutil"
20 "runtime"
21 "sort"
22 "testing"
23
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
26 )
27
28 func TestEmpty(t *testing.T) {
29 f := Empty()
30 require.NotNil(t, f)
31
32
33 assert.Len(t, f.Sections(), 1)
34
35
36 assert.Len(t, f.Section("").Keys(), 0)
37 }
38
39 func TestFile_NewSection(t *testing.T) {
40 f := Empty()
41 require.NotNil(t, f)
42
43 sec, err := f.NewSection("author")
44 require.NoError(t, err)
45 require.NotNil(t, sec)
46 assert.Equal(t, "author", sec.Name())
47
48 assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
49
50 t.Run("with duplicated name", func(t *testing.T) {
51 sec, err := f.NewSection("author")
52 require.NoError(t, err)
53 require.NotNil(t, sec)
54
55
56 assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
57 })
58
59 t.Run("with empty string", func(t *testing.T) {
60 _, err := f.NewSection("")
61 require.Error(t, err)
62 })
63 }
64
65 func TestFile_NonUniqueSection(t *testing.T) {
66 t.Run("read and write non-unique sections", func(t *testing.T) {
67 f, err := LoadSources(LoadOptions{
68 AllowNonUniqueSections: true,
69 }, []byte(`[Interface]
70 Address = 192.168.2.1
71 PrivateKey = <server's privatekey>
72 ListenPort = 51820
73
74 [Peer]
75 PublicKey = <client's publickey>
76 AllowedIPs = 192.168.2.2/32
77
78 [Peer]
79 PublicKey = <client2's publickey>
80 AllowedIPs = 192.168.2.3/32`))
81 require.NoError(t, err)
82 require.NotNil(t, f)
83
84 sec, err := f.NewSection("Peer")
85 require.NoError(t, err)
86 require.NotNil(t, f)
87
88 _, _ = sec.NewKey("PublicKey", "<client3's publickey>")
89 _, _ = sec.NewKey("AllowedIPs", "192.168.2.4/32")
90
91 var buf bytes.Buffer
92 _, err = f.WriteTo(&buf)
93 require.NoError(t, err)
94 str := buf.String()
95 assert.Equal(t, `[Interface]
96 Address = 192.168.2.1
97 PrivateKey = <server's privatekey>
98 ListenPort = 51820
99
100 [Peer]
101 PublicKey = <client's publickey>
102 AllowedIPs = 192.168.2.2/32
103
104 [Peer]
105 PublicKey = <client2's publickey>
106 AllowedIPs = 192.168.2.3/32
107
108 [Peer]
109 PublicKey = <client3's publickey>
110 AllowedIPs = 192.168.2.4/32
111 `, str)
112 })
113
114 t.Run("delete non-unique section", func(t *testing.T) {
115 f, err := LoadSources(LoadOptions{
116 AllowNonUniqueSections: true,
117 }, []byte(`[Interface]
118 Address = 192.168.2.1
119 PrivateKey = <server's privatekey>
120 ListenPort = 51820
121
122 [Peer]
123 PublicKey = <client's publickey>
124 AllowedIPs = 192.168.2.2/32
125
126 [Peer]
127 PublicKey = <client2's publickey>
128 AllowedIPs = 192.168.2.3/32
129
130 [Peer]
131 PublicKey = <client3's publickey>
132 AllowedIPs = 192.168.2.4/32
133
134 `))
135 require.NoError(t, err)
136 require.NotNil(t, f)
137
138 err = f.DeleteSectionWithIndex("Peer", 1)
139 require.NoError(t, err)
140
141 var buf bytes.Buffer
142 _, err = f.WriteTo(&buf)
143 require.NoError(t, err)
144 str := buf.String()
145 assert.Equal(t, `[Interface]
146 Address = 192.168.2.1
147 PrivateKey = <server's privatekey>
148 ListenPort = 51820
149
150 [Peer]
151 PublicKey = <client's publickey>
152 AllowedIPs = 192.168.2.2/32
153
154 [Peer]
155 PublicKey = <client3's publickey>
156 AllowedIPs = 192.168.2.4/32
157 `, str)
158 })
159
160 t.Run("delete all sections", func(t *testing.T) {
161 f := Empty(LoadOptions{
162 AllowNonUniqueSections: true,
163 })
164 require.NotNil(t, f)
165
166 _ = f.NewSections("Interface", "Peer", "Peer")
167 assert.Equal(t, []string{DefaultSection, "Interface", "Peer", "Peer"}, f.SectionStrings())
168 f.DeleteSection("Peer")
169 assert.Equal(t, []string{DefaultSection, "Interface"}, f.SectionStrings())
170 })
171 }
172
173 func TestFile_NewRawSection(t *testing.T) {
174 f := Empty()
175 require.NotNil(t, f)
176
177 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
178 111111111111111111100000000000111000000000`)
179 require.NoError(t, err)
180 require.NotNil(t, sec)
181 assert.Equal(t, "comments", sec.Name())
182
183 assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
184 assert.Equal(t, `1111111111111111111000000000000000001110000
185 111111111111111111100000000000111000000000`, f.Section("comments").Body())
186
187 t.Run("with duplicated name", func(t *testing.T) {
188 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`)
189 require.NoError(t, err)
190 require.NotNil(t, sec)
191 assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
192
193
194 assert.Equal(t, `1111111111111111111000000000000000001110000`, f.Section("comments").Body())
195 })
196
197 t.Run("with empty string", func(t *testing.T) {
198 _, err := f.NewRawSection("", "")
199 require.Error(t, err)
200 })
201 }
202
203 func TestFile_NewSections(t *testing.T) {
204 f := Empty()
205 require.NotNil(t, f)
206
207 assert.NoError(t, f.NewSections("package", "author"))
208 assert.Equal(t, []string{DefaultSection, "package", "author"}, f.SectionStrings())
209
210 t.Run("with duplicated name", func(t *testing.T) {
211 assert.NoError(t, f.NewSections("author", "features"))
212
213
214 assert.Equal(t, []string{DefaultSection, "package", "author", "features"}, f.SectionStrings())
215 })
216
217 t.Run("with empty string", func(t *testing.T) {
218 assert.Error(t, f.NewSections("", ""))
219 })
220 }
221
222 func TestFile_GetSection(t *testing.T) {
223 f, err := Load(fullConf)
224 require.NoError(t, err)
225 require.NotNil(t, f)
226
227 sec, err := f.GetSection("author")
228 require.NoError(t, err)
229 require.NotNil(t, sec)
230 assert.Equal(t, "author", sec.Name())
231
232 t.Run("section not exists", func(t *testing.T) {
233 _, err := f.GetSection("404")
234 require.Error(t, err)
235 })
236 }
237
238 func TestFile_HasSection(t *testing.T) {
239 f, err := Load(fullConf)
240 require.NoError(t, err)
241 require.NotNil(t, f)
242
243 sec := f.HasSection("author")
244 assert.True(t, sec)
245
246 t.Run("section not exists", func(t *testing.T) {
247 nonexistent := f.HasSection("404")
248 assert.False(t, nonexistent)
249 })
250 }
251
252 func TestFile_Section(t *testing.T) {
253 t.Run("get a section", func(t *testing.T) {
254 f, err := Load(fullConf)
255 require.NoError(t, err)
256 require.NotNil(t, f)
257
258 sec := f.Section("author")
259 require.NotNil(t, sec)
260 assert.Equal(t, "author", sec.Name())
261
262 t.Run("section not exists", func(t *testing.T) {
263 sec := f.Section("404")
264 require.NotNil(t, sec)
265 assert.Equal(t, "404", sec.Name())
266 })
267 })
268
269 t.Run("get default section in lower case with insensitive load", func(t *testing.T) {
270 f, err := InsensitiveLoad([]byte(`
271 [default]
272 NAME = ini
273 VERSION = v1`))
274 require.NoError(t, err)
275 require.NotNil(t, f)
276
277 assert.Equal(t, "ini", f.Section("").Key("name").String())
278 assert.Equal(t, "v1", f.Section("").Key("version").String())
279 })
280
281 t.Run("get sections after deletion", func(t *testing.T) {
282 f, err := Load([]byte(`
283 [RANDOM]
284 `))
285 require.NoError(t, err)
286 require.NotNil(t, f)
287
288 sectionNames := f.SectionStrings()
289 sort.Strings(sectionNames)
290 assert.Equal(t, []string{DefaultSection, "RANDOM"}, sectionNames)
291
292 for _, currentSection := range sectionNames {
293 f.DeleteSection(currentSection)
294 }
295
296 for sectionParam, expectedSectionName := range map[string]string{
297 "": DefaultSection,
298 "RANDOM": "RANDOM",
299 } {
300 sec := f.Section(sectionParam)
301 require.NotNil(t, sec)
302 assert.Equal(t, expectedSectionName, sec.Name())
303 }
304 })
305
306 }
307
308 func TestFile_Sections(t *testing.T) {
309 f, err := Load(fullConf)
310 require.NoError(t, err)
311 require.NotNil(t, f)
312
313 secs := f.Sections()
314 names := []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}
315 assert.Len(t, secs, len(names))
316 for i, name := range names {
317 assert.Equal(t, name, secs[i].Name())
318 }
319 }
320
321 func TestFile_ChildSections(t *testing.T) {
322 f, err := Load([]byte(`
323 [node]
324 [node.biz1]
325 [node.biz2]
326 [node.biz3]
327 [node.bizN]
328 `))
329 require.NoError(t, err)
330 require.NotNil(t, f)
331
332 children := f.ChildSections("node")
333 names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"}
334 assert.Len(t, children, len(names))
335 for i, name := range names {
336 assert.Equal(t, name, children[i].Name())
337 }
338 }
339
340 func TestFile_SectionStrings(t *testing.T) {
341 f, err := Load(fullConf)
342 require.NoError(t, err)
343 require.NotNil(t, f)
344
345 assert.Equal(t, []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}, f.SectionStrings())
346 }
347
348 func TestFile_DeleteSection(t *testing.T) {
349 t.Run("delete a section", func(t *testing.T) {
350 f := Empty()
351 require.NotNil(t, f)
352
353 _ = f.NewSections("author", "package", "features")
354 f.DeleteSection("features")
355 f.DeleteSection("")
356 assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
357 })
358
359 t.Run("delete default section", func(t *testing.T) {
360 f := Empty()
361 require.NotNil(t, f)
362
363 f.Section("").Key("foo").SetValue("bar")
364 f.Section("section1").Key("key1").SetValue("value1")
365 f.DeleteSection("")
366 assert.Equal(t, []string{"section1"}, f.SectionStrings())
367
368 var buf bytes.Buffer
369 _, err := f.WriteTo(&buf)
370 require.NoError(t, err)
371
372 assert.Equal(t, `[section1]
373 key1 = value1
374 `, buf.String())
375 })
376
377 t.Run("delete a section with InsensitiveSections", func(t *testing.T) {
378 f := Empty(LoadOptions{InsensitiveSections: true})
379 require.NotNil(t, f)
380
381 _ = f.NewSections("author", "package", "features")
382 f.DeleteSection("FEATURES")
383 f.DeleteSection("")
384 assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
385 })
386 }
387
388 func TestFile_Append(t *testing.T) {
389 f := Empty()
390 require.NotNil(t, f)
391
392 assert.NoError(t, f.Append(minimalConf, []byte(`
393 [author]
394 NAME = Unknwon`)))
395
396 t.Run("with bad input", func(t *testing.T) {
397 assert.Error(t, f.Append(123))
398 assert.Error(t, f.Append(minimalConf, 123))
399 })
400 }
401
402 func TestFile_WriteTo(t *testing.T) {
403 if runtime.GOOS == "windows" {
404 t.Skip("Skipping testing on Windows")
405 }
406
407 t.Run("write content to somewhere", func(t *testing.T) {
408 f, err := Load(fullConf)
409 require.NoError(t, err)
410 require.NotNil(t, f)
411
412 f.Section("author").Comment = `Information about package author
413 # Bio can be written in multiple lines.`
414 f.Section("author").Key("NAME").Comment = "This is author name"
415 _, _ = f.Section("note").NewBooleanKey("boolean_key")
416 _, _ = f.Section("note").NewKey("more", "notes")
417
418 var buf bytes.Buffer
419 _, err = f.WriteTo(&buf)
420 require.NoError(t, err)
421
422 golden := "testdata/TestFile_WriteTo.golden"
423 if *update {
424 require.NoError(t, ioutil.WriteFile(golden, buf.Bytes(), 0644))
425 }
426
427 expected, err := ioutil.ReadFile(golden)
428 require.NoError(t, err)
429 assert.Equal(t, string(expected), buf.String())
430 })
431
432 t.Run("support multiline comments", func(t *testing.T) {
433 f, err := Load([]byte(`
434 #
435 # general.domain
436 #
437 # Domain name of XX system.
438 domain = mydomain.com
439 `))
440 require.NoError(t, err)
441
442 f.Section("").Key("test").Comment = "Multiline\nComment"
443
444 var buf bytes.Buffer
445 _, err = f.WriteTo(&buf)
446 require.NoError(t, err)
447
448 assert.Equal(t, `#
449 # general.domain
450 #
451 # Domain name of XX system.
452 domain = mydomain.com
453 ; Multiline
454 ; Comment
455 test =
456 `, buf.String())
457
458 })
459
460 t.Run("keep leading and trailing spaces in value", func(t *testing.T) {
461 f, _ := Load([]byte(`[foo]
462 bar1 = ' val ue1 '
463 bar2 = """ val ue2 """
464 bar3 = " val ue3 "
465 `))
466 require.NotNil(t, f)
467
468 var buf bytes.Buffer
469 _, err := f.WriteTo(&buf)
470 require.NoError(t, err)
471 assert.Equal(t, `[foo]
472 bar1 = " val ue1 "
473 bar2 = " val ue2 "
474 bar3 = " val ue3 "
475 `, buf.String())
476 })
477 }
478
479 func TestFile_SaveTo(t *testing.T) {
480 f, err := Load(fullConf)
481 require.NoError(t, err)
482 require.NotNil(t, f)
483
484 assert.NoError(t, f.SaveTo("testdata/conf_out.ini"))
485 assert.NoError(t, f.SaveToIndent("testdata/conf_out.ini", "\t"))
486 }
487
488 func TestFile_WriteToWithOutputDelimiter(t *testing.T) {
489 f, err := LoadSources(LoadOptions{
490 KeyValueDelimiterOnWrite: "->",
491 }, []byte(`[Others]
492 Cities = HangZhou|Boston
493 Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
494 Years = 1993,1994
495 Numbers = 10010,10086
496 Ages = 18,19
497 Populations = 12345678,98765432
498 Coordinates = 192.168,10.11
499 Flags = true,false
500 Note = Hello world!`))
501 require.NoError(t, err)
502 require.NotNil(t, f)
503
504 var actual bytes.Buffer
505 var expected = []byte(`[Others]
506 Cities -> HangZhou|Boston
507 Visits -> 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
508 Years -> 1993,1994
509 Numbers -> 10010,10086
510 Ages -> 18,19
511 Populations -> 12345678,98765432
512 Coordinates -> 192.168,10.11
513 Flags -> true,false
514 Note -> Hello world!
515 `)
516 _, err = f.WriteTo(&actual)
517 require.NoError(t, err)
518
519 assert.Equal(t, expected, actual.Bytes())
520 }
521
522
523 func TestReloadAfterShadowLoad(t *testing.T) {
524 f, err := ShadowLoad([]byte(`
525 [slice]
526 v = 1
527 v = 2
528 v = 3
529 `))
530 require.NoError(t, err)
531 require.NotNil(t, f)
532
533 assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
534
535 require.NoError(t, f.Reload())
536 assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
537 }
538
View as plain text