Codebase list golang-gopkg-ini.v1 / 168032d
Import upstream version 1.64.0 Debian Janitor 2 years ago
19 changed file(s) with 2070 addition(s) and 1948 deletion(s). Raw diff Collapse all Expand all
00 name: Go
11 on:
22 push:
3 branches: [master]
3 branches: [ main ]
4 paths:
5 - '**.go'
6 - 'go.mod'
7 - '.golangci.yml'
8 - '.github/workflows/go.yml'
49 pull_request:
10 paths:
11 - '**.go'
12 - 'go.mod'
13 - '.golangci.yml'
14 - '.github/workflows/go.yml'
515 env:
616 GOPROXY: "https://proxy.golang.org"
717
1020 name: Lint
1121 runs-on: ubuntu-latest
1222 steps:
13 - uses: actions/checkout@v2
14 - name: Init Go modules
15 run: go mod init gopkg.in/ini.v1
23 - name: Checkout code
24 uses: actions/checkout@v2
25 - name: Init Go Modules
26 run: |
27 go mod init github.com/go-ini/ini
28 go mod tidy
1629 - name: Run golangci-lint
17 uses: actions-contrib/golangci-lint@v1
30 uses: golangci/golangci-lint-action@v2
31 with:
32 version: latest
33 args: --timeout=30m
34 skip-pkg-cache: true # Wrokaround of the "tar" problem: https://github.com/golangci/golangci-lint-action/issues/244
1835
1936 test:
2037 name: Test
2138 strategy:
2239 matrix:
23 go-version: [1.13.x, 1.14.x, 1.15.x]
24 platform: [ubuntu-latest, macos-latest, windows-latest]
40 go-version: [ 1.15.x, 1.16.x, 1.17.x ]
41 platform: [ ubuntu-latest, macos-latest, windows-latest ]
2542 runs-on: ${{ matrix.platform }}
2643 steps:
2744 - name: Install Go
28 uses: actions/setup-go@v1
45 uses: actions/setup-go@v2
2946 with:
3047 go-version: ${{ matrix.go-version }}
3148 - name: Checkout code
3249 uses: actions/checkout@v2
33 - name: Run unit tests
50 - name: Run tests with coverage
3451 run: |
35 go mod init gopkg.in/ini.v1
52 go mod init github.com/go-ini/ini
53 go mod tidy
3654 go test -v -race -coverprofile=coverage -covermode=atomic ./...
3755 - name: Upload coverage report to Codecov
38 uses: codecov/codecov-action@v1.0.6
56 uses: codecov/codecov-action@v1.5.0
3957 with:
4058 file: ./coverage
4159 flags: unittests
42 - name: Cache downloaded modules
43 uses: actions/cache@v1
44 with:
45 path: ~/go/pkg/mod
46 key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
47 restore-keys: |
48 ${{ runner.os }}-go-
00 name: LSIF
1 on: [push]
1 on:
2 push:
3 paths:
4 - '**.go'
5 - 'go.mod'
6 - '.github/workflows/lsif.yml'
7 env:
8 GOPROXY: "https://proxy.golang.org"
9
210 jobs:
3 build:
11 lsif-go:
12 if: github.repository == 'go-ini/ini'
413 runs-on: ubuntu-latest
514 steps:
6 - uses: actions/checkout@v1
15 - uses: actions/checkout@v2
716 - name: Generate LSIF data
817 uses: sourcegraph/lsif-go-action@master
18 - name: Upload LSIF data to sourcegraph.com
19 continue-on-error: true
20 uses: docker://sourcegraph/src-cli:latest
921 with:
10 verbose: 'true'
11 - name: Upload LSIF data
12 uses: sourcegraph/lsif-upload-action@master
22 args: lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}
23 - name: Upload LSIF data to sourcegraph.unknwon.cn
1324 continue-on-error: true
25 uses: docker://sourcegraph/src-cli:latest
1426 with:
15 endpoint: https://sourcegraph.com
16 github_token: ${{ secrets.GITHUB_TOKEN }}
27 args: -endpoint=https://sourcegraph.unknwon.cn lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}
0 linters-settings:
1 nakedret:
2 max-func-lines: 0 # Disallow any unnamed return statement
3
4 linters:
5 enable:
6 - deadcode
7 - errcheck
8 - gosimple
9 - govet
10 - ineffassign
11 - staticcheck
12 - structcheck
13 - typecheck
14 - unused
15 - varcheck
16 - nakedret
17 - gofmt
18 - rowserrcheck
19 - unconvert
20 - goimports
2323
2424 ## Installation
2525
26 The minimum requirement of Go is **1.6**.
26 The minimum requirement of Go is **1.12**.
2727
2828 ```sh
2929 $ go get gopkg.in/ini.v1
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "testing"
18
19 "gopkg.in/ini.v1"
2018 )
2119
22 func newTestFile(block bool) *ini.File {
23 c, _ := ini.Load([]byte(confData))
20 func newTestFile(block bool) *File {
21 c, _ := Load([]byte(confData))
2422 c.BlockMode = block
2523 return c
2624 }
55 threshold: 1%
66
77 comment:
8 layout: 'diff, files'
8 layout: 'diff'
167167 func (f *File) Section(name string) *Section {
168168 sec, err := f.GetSection(name)
169169 if err != nil {
170 // Note: It's OK here because the only possible error is empty section name,
171 // but if it's empty, this piece of code won't be executed.
170 if name == "" {
171 name = DefaultSection
172 }
172173 sec, _ = f.NewSection(name)
173174 return sec
174175 }
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "bytes"
1818 "io/ioutil"
1919 "runtime"
20 "sort"
2021 "testing"
2122
22 . "github.com/smartystreets/goconvey/convey"
23 "gopkg.in/ini.v1"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
2425 )
2526
2627 func TestEmpty(t *testing.T) {
27 Convey("Create an empty object", t, func() {
28 f := ini.Empty()
29 So(f, ShouldNotBeNil)
30
31 // Should only have the default section
32 So(len(f.Sections()), ShouldEqual, 1)
33
34 // Default section should not contain any key
35 So(len(f.Section("").Keys()), ShouldBeZeroValue)
36 })
28 f := Empty()
29 require.NotNil(t, f)
30
31 // Should only have the default section
32 assert.Len(t, f.Sections(), 1)
33
34 // Default section should not contain any key
35 assert.Len(t, f.Section("").Keys(), 0)
3736 }
3837
3938 func TestFile_NewSection(t *testing.T) {
40 Convey("Create a new section", t, func() {
41 f := ini.Empty()
42 So(f, ShouldNotBeNil)
43
39 f := Empty()
40 require.NotNil(t, f)
41
42 sec, err := f.NewSection("author")
43 require.NoError(t, err)
44 require.NotNil(t, sec)
45 assert.Equal(t, "author", sec.Name())
46
47 assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
48
49 t.Run("with duplicated name", func(t *testing.T) {
4450 sec, err := f.NewSection("author")
45 So(err, ShouldBeNil)
46 So(sec, ShouldNotBeNil)
47 So(sec.Name(), ShouldEqual, "author")
48
49 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "author"})
50
51 Convey("With duplicated name", func() {
52 sec, err := f.NewSection("author")
53 So(err, ShouldBeNil)
54 So(sec, ShouldNotBeNil)
55
56 // Does nothing if section already exists
57 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "author"})
58 })
59
60 Convey("With empty string", func() {
61 _, err := f.NewSection("")
62 So(err, ShouldNotBeNil)
63 })
51 require.NoError(t, err)
52 require.NotNil(t, sec)
53
54 // Does nothing if section already exists
55 assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
56 })
57
58 t.Run("with empty string", func(t *testing.T) {
59 _, err := f.NewSection("")
60 require.Error(t, err)
6461 })
6562 }
6663
6764 func TestFile_NonUniqueSection(t *testing.T) {
68 Convey("Read and write non-unique sections", t, func() {
69 f, err := ini.LoadSources(ini.LoadOptions{
65 t.Run("read and write non-unique sections", func(t *testing.T) {
66 f, err := LoadSources(LoadOptions{
7067 AllowNonUniqueSections: true,
7168 }, []byte(`[Interface]
7269 Address = 192.168.2.1
8077 [Peer]
8178 PublicKey = <client2's publickey>
8279 AllowedIPs = 192.168.2.3/32`))
83 So(err, ShouldBeNil)
84 So(f, ShouldNotBeNil)
80 require.NoError(t, err)
81 require.NotNil(t, f)
8582
8683 sec, err := f.NewSection("Peer")
87 So(err, ShouldBeNil)
88 So(f, ShouldNotBeNil)
84 require.NoError(t, err)
85 require.NotNil(t, f)
8986
9087 _, _ = sec.NewKey("PublicKey", "<client3's publickey>")
9188 _, _ = sec.NewKey("AllowedIPs", "192.168.2.4/32")
9289
9390 var buf bytes.Buffer
9491 _, err = f.WriteTo(&buf)
95 So(err, ShouldBeNil)
92 require.NoError(t, err)
9693 str := buf.String()
97 So(str, ShouldEqual, `[Interface]
94 assert.Equal(t, `[Interface]
9895 Address = 192.168.2.1
9996 PrivateKey = <server's privatekey>
10097 ListenPort = 51820
111108 PublicKey = <client3's publickey>
112109 AllowedIPs = 192.168.2.4/32
113110
114 `)
115 })
116
117 Convey("Delete non-unique section", t, func() {
118 f, err := ini.LoadSources(ini.LoadOptions{
111 `, str)
112 })
113
114 t.Run("delete non-unique section", func(t *testing.T) {
115 f, err := LoadSources(LoadOptions{
119116 AllowNonUniqueSections: true,
120117 }, []byte(`[Interface]
121118 Address = 192.168.2.1
135132 AllowedIPs = 192.168.2.4/32
136133
137134 `))
138 So(err, ShouldBeNil)
139 So(f, ShouldNotBeNil)
135 require.NoError(t, err)
136 require.NotNil(t, f)
140137
141138 err = f.DeleteSectionWithIndex("Peer", 1)
142 So(err, ShouldBeNil)
139 require.NoError(t, err)
143140
144141 var buf bytes.Buffer
145142 _, err = f.WriteTo(&buf)
146 So(err, ShouldBeNil)
143 require.NoError(t, err)
147144 str := buf.String()
148 So(str, ShouldEqual, `[Interface]
145 assert.Equal(t, `[Interface]
149146 Address = 192.168.2.1
150147 PrivateKey = <server's privatekey>
151148 ListenPort = 51820
158155 PublicKey = <client3's publickey>
159156 AllowedIPs = 192.168.2.4/32
160157
161 `)
162 })
163
164 Convey("Delete all sections", t, func() {
165 f := ini.Empty(ini.LoadOptions{
158 `, str)
159 })
160
161 t.Run("delete all sections", func(t *testing.T) {
162 f := Empty(LoadOptions{
166163 AllowNonUniqueSections: true,
167164 })
168 So(f, ShouldNotBeNil)
165 require.NotNil(t, f)
169166
170167 _ = f.NewSections("Interface", "Peer", "Peer")
171 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "Interface", "Peer", "Peer"})
168 assert.Equal(t, []string{DefaultSection, "Interface", "Peer", "Peer"}, f.SectionStrings())
172169 f.DeleteSection("Peer")
173 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "Interface"})
170 assert.Equal(t, []string{DefaultSection, "Interface"}, f.SectionStrings())
174171 })
175172 }
176173
177174 func TestFile_NewRawSection(t *testing.T) {
178 Convey("Create a new raw section", t, func() {
179 f := ini.Empty()
180 So(f, ShouldNotBeNil)
181
182 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
175 f := Empty()
176 require.NotNil(t, f)
177
178 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
183179 111111111111111111100000000000111000000000`)
184 So(err, ShouldBeNil)
185 So(sec, ShouldNotBeNil)
186 So(sec.Name(), ShouldEqual, "comments")
187
188 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "comments"})
189 So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000
190 111111111111111111100000000000111000000000`)
191
192 Convey("With duplicated name", func() {
193 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`)
194 So(err, ShouldBeNil)
195 So(sec, ShouldNotBeNil)
196 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "comments"})
197
198 // Overwrite previous existed section
199 So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000`)
180 require.NoError(t, err)
181 require.NotNil(t, sec)
182 assert.Equal(t, "comments", sec.Name())
183
184 assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
185 assert.Equal(t, `1111111111111111111000000000000000001110000
186 111111111111111111100000000000111000000000`, f.Section("comments").Body())
187
188 t.Run("with duplicated name", func(t *testing.T) {
189 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`)
190 require.NoError(t, err)
191 require.NotNil(t, sec)
192 assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
193
194 // Overwrite previous existed section
195 assert.Equal(t, `1111111111111111111000000000000000001110000`, f.Section("comments").Body())
196 })
197
198 t.Run("with empty string", func(t *testing.T) {
199 _, err := f.NewRawSection("", "")
200 require.Error(t, err)
201 })
202 }
203
204 func TestFile_NewSections(t *testing.T) {
205 f := Empty()
206 require.NotNil(t, f)
207
208 assert.NoError(t, f.NewSections("package", "author"))
209 assert.Equal(t, []string{DefaultSection, "package", "author"}, f.SectionStrings())
210
211 t.Run("with duplicated name", func(t *testing.T) {
212 assert.NoError(t, f.NewSections("author", "features"))
213
214 // Ignore section already exists
215 assert.Equal(t, []string{DefaultSection, "package", "author", "features"}, f.SectionStrings())
216 })
217
218 t.Run("with empty string", func(t *testing.T) {
219 assert.Error(t, f.NewSections("", ""))
220 })
221 }
222
223 func TestFile_GetSection(t *testing.T) {
224 f, err := Load(fullConf)
225 require.NoError(t, err)
226 require.NotNil(t, f)
227
228 sec, err := f.GetSection("author")
229 require.NoError(t, err)
230 require.NotNil(t, sec)
231 assert.Equal(t, "author", sec.Name())
232
233 t.Run("section not exists", func(t *testing.T) {
234 _, err := f.GetSection("404")
235 require.Error(t, err)
236 })
237 }
238
239 func TestFile_Section(t *testing.T) {
240 t.Run("get a section", func(t *testing.T) {
241 f, err := Load(fullConf)
242 require.NoError(t, err)
243 require.NotNil(t, f)
244
245 sec := f.Section("author")
246 require.NotNil(t, sec)
247 assert.Equal(t, "author", sec.Name())
248
249 t.Run("section not exists", func(t *testing.T) {
250 sec := f.Section("404")
251 require.NotNil(t, sec)
252 assert.Equal(t, "404", sec.Name())
200253 })
201
202 Convey("With empty string", func() {
203 _, err := f.NewRawSection("", "")
204 So(err, ShouldNotBeNil)
205 })
206 })
207 }
208
209 func TestFile_NewSections(t *testing.T) {
210 Convey("Create new sections", t, func() {
211 f := ini.Empty()
212 So(f, ShouldNotBeNil)
213
214 So(f.NewSections("package", "author"), ShouldBeNil)
215 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "package", "author"})
216
217 Convey("With duplicated name", func() {
218 So(f.NewSections("author", "features"), ShouldBeNil)
219
220 // Ignore section already exists
221 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "package", "author", "features"})
222 })
223
224 Convey("With empty string", func() {
225 So(f.NewSections("", ""), ShouldNotBeNil)
226 })
227 })
228 }
229
230 func TestFile_GetSection(t *testing.T) {
231 Convey("Get a section", t, func() {
232 f, err := ini.Load(fullConf)
233 So(err, ShouldBeNil)
234 So(f, ShouldNotBeNil)
235
236 sec, err := f.GetSection("author")
237 So(err, ShouldBeNil)
238 So(sec, ShouldNotBeNil)
239 So(sec.Name(), ShouldEqual, "author")
240
241 Convey("Section not exists", func() {
242 _, err := f.GetSection("404")
243 So(err, ShouldNotBeNil)
244 })
245 })
246 }
247
248 func TestFile_Section(t *testing.T) {
249 Convey("Get a section", t, func() {
250 f, err := ini.Load(fullConf)
251 So(err, ShouldBeNil)
252 So(f, ShouldNotBeNil)
253
254 sec := f.Section("author")
255 So(sec, ShouldNotBeNil)
256 So(sec.Name(), ShouldEqual, "author")
257
258 Convey("Section not exists", func() {
259 sec := f.Section("404")
260 So(sec, ShouldNotBeNil)
261 So(sec.Name(), ShouldEqual, "404")
262 })
263 })
264
265 Convey("Get default section in lower case with insensitive load", t, func() {
266 f, err := ini.InsensitiveLoad([]byte(`
254 })
255
256 t.Run("get default section in lower case with insensitive load", func(t *testing.T) {
257 f, err := InsensitiveLoad([]byte(`
267258 [default]
268259 NAME = ini
269260 VERSION = v1`))
270 So(err, ShouldBeNil)
271 So(f, ShouldNotBeNil)
272
273 So(f.Section("").Key("name").String(), ShouldEqual, "ini")
274 So(f.Section("").Key("version").String(), ShouldEqual, "v1")
275 })
261 require.NoError(t, err)
262 require.NotNil(t, f)
263
264 assert.Equal(t, "ini", f.Section("").Key("name").String())
265 assert.Equal(t, "v1", f.Section("").Key("version").String())
266 })
267
268 t.Run("get sections after deletion", func(t *testing.T) {
269 f, err := Load([]byte(`
270 [RANDOM]
271 `))
272 require.NoError(t, err)
273 require.NotNil(t, f)
274
275 sectionNames := f.SectionStrings()
276 sort.Strings(sectionNames)
277 assert.Equal(t, []string{DefaultSection, "RANDOM"}, sectionNames)
278
279 for _, currentSection := range sectionNames {
280 f.DeleteSection(currentSection)
281 }
282
283 for sectionParam, expectedSectionName := range map[string]string{
284 "": DefaultSection,
285 "RANDOM": "RANDOM",
286 } {
287 sec := f.Section(sectionParam)
288 require.NotNil(t, sec)
289 assert.Equal(t, expectedSectionName, sec.Name())
290 }
291 })
292
276293 }
277294
278295 func TestFile_Sections(t *testing.T) {
279 Convey("Get all sections", t, func() {
280 f, err := ini.Load(fullConf)
281 So(err, ShouldBeNil)
282 So(f, ShouldNotBeNil)
283
284 secs := f.Sections()
285 names := []string{ini.DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}
286 So(len(secs), ShouldEqual, len(names))
287 for i, name := range names {
288 So(secs[i].Name(), ShouldEqual, name)
289 }
290 })
296 f, err := Load(fullConf)
297 require.NoError(t, err)
298 require.NotNil(t, f)
299
300 secs := f.Sections()
301 names := []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}
302 assert.Len(t, secs, len(names))
303 for i, name := range names {
304 assert.Equal(t, name, secs[i].Name())
305 }
291306 }
292307
293308 func TestFile_ChildSections(t *testing.T) {
294 Convey("Get child sections by parent name", t, func() {
295 f, err := ini.Load([]byte(`
309 f, err := Load([]byte(`
296310 [node]
297311 [node.biz1]
298312 [node.biz2]
299313 [node.biz3]
300314 [node.bizN]
301315 `))
302 So(err, ShouldBeNil)
303 So(f, ShouldNotBeNil)
304
305 children := f.ChildSections("node")
306 names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"}
307 So(len(children), ShouldEqual, len(names))
308 for i, name := range names {
309 So(children[i].Name(), ShouldEqual, name)
310 }
311 })
316 require.NoError(t, err)
317 require.NotNil(t, f)
318
319 children := f.ChildSections("node")
320 names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"}
321 assert.Len(t, children, len(names))
322 for i, name := range names {
323 assert.Equal(t, name, children[i].Name())
324 }
312325 }
313326
314327 func TestFile_SectionStrings(t *testing.T) {
315 Convey("Get all section names", t, func() {
316 f, err := ini.Load(fullConf)
317 So(err, ShouldBeNil)
318 So(f, ShouldNotBeNil)
319
320 So(f.SectionStrings(), ShouldResemble, []string{ini.DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"})
321 })
328 f, err := Load(fullConf)
329 require.NoError(t, err)
330 require.NotNil(t, f)
331
332 assert.Equal(t, []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}, f.SectionStrings())
322333 }
323334
324335 func TestFile_DeleteSection(t *testing.T) {
325 Convey("Delete a section", t, func() {
326 f := ini.Empty()
327 So(f, ShouldNotBeNil)
336 t.Run("delete a section", func(t *testing.T) {
337 f := Empty()
338 require.NotNil(t, f)
328339
329340 _ = f.NewSections("author", "package", "features")
330341 f.DeleteSection("features")
331342 f.DeleteSection("")
332 So(f.SectionStrings(), ShouldResemble, []string{"author", "package"})
333 })
334
335 Convey("Delete default section", t, func() {
336 f := ini.Empty()
337 So(f, ShouldNotBeNil)
343 assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
344 })
345
346 t.Run("delete default section", func(t *testing.T) {
347 f := Empty()
348 require.NotNil(t, f)
338349
339350 f.Section("").Key("foo").SetValue("bar")
340351 f.Section("section1").Key("key1").SetValue("value1")
341352 f.DeleteSection("")
342 So(f.SectionStrings(), ShouldResemble, []string{"section1"})
353 assert.Equal(t, []string{"section1"}, f.SectionStrings())
343354
344355 var buf bytes.Buffer
345356 _, err := f.WriteTo(&buf)
346 So(err, ShouldBeNil)
347
348 So(buf.String(), ShouldEqual, `[section1]
357 require.NoError(t, err)
358
359 assert.Equal(t, `[section1]
349360 key1 = value1
350361
351 `)
352 })
353
354 Convey("Delete a section with InsensitiveSections", t, func() {
355 f := ini.Empty(ini.LoadOptions{InsensitiveSections: true})
356 So(f, ShouldNotBeNil)
362 `, buf.String())
363 })
364
365 t.Run("delete a section with InsensitiveSections", func(t *testing.T) {
366 f := Empty(LoadOptions{InsensitiveSections: true})
367 require.NotNil(t, f)
357368
358369 _ = f.NewSections("author", "package", "features")
359370 f.DeleteSection("FEATURES")
360371 f.DeleteSection("")
361 So(f.SectionStrings(), ShouldResemble, []string{"author", "package"})
372 assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
362373 })
363374 }
364375
365376 func TestFile_Append(t *testing.T) {
366 Convey("Append a data source", t, func() {
367 f := ini.Empty()
368 So(f, ShouldNotBeNil)
369
370 So(f.Append(minimalConf, []byte(`
377 f := Empty()
378 require.NotNil(t, f)
379
380 assert.NoError(t, f.Append(minimalConf, []byte(`
371381 [author]
372 NAME = Unknwon`)), ShouldBeNil)
373
374 Convey("With bad input", func() {
375 So(f.Append(123), ShouldNotBeNil)
376 So(f.Append(minimalConf, 123), ShouldNotBeNil)
377 })
382 NAME = Unknwon`)))
383
384 t.Run("with bad input", func(t *testing.T) {
385 assert.Error(t, f.Append(123))
386 assert.Error(t, f.Append(minimalConf, 123))
378387 })
379388 }
380389
383392 t.Skip("Skipping testing on Windows")
384393 }
385394
386 Convey("Write content to somewhere", t, func() {
387 f, err := ini.Load(fullConf)
388 So(err, ShouldBeNil)
389 So(f, ShouldNotBeNil)
395 t.Run("write content to somewhere", func(t *testing.T) {
396 f, err := Load(fullConf)
397 require.NoError(t, err)
398 require.NotNil(t, f)
390399
391400 f.Section("author").Comment = `Information about package author
392401 # Bio can be written in multiple lines.`
396405
397406 var buf bytes.Buffer
398407 _, err = f.WriteTo(&buf)
399 So(err, ShouldBeNil)
408 require.NoError(t, err)
400409
401410 golden := "testdata/TestFile_WriteTo.golden"
402411 if *update {
403 So(ioutil.WriteFile(golden, buf.Bytes(), 0644), ShouldBeNil)
412 require.NoError(t, ioutil.WriteFile(golden, buf.Bytes(), 0644))
404413 }
405414
406415 expected, err := ioutil.ReadFile(golden)
407 So(err, ShouldBeNil)
408 So(buf.String(), ShouldEqual, string(expected))
409 })
410
411 Convey("Support multiline comments", t, func() {
412 f, err := ini.Load([]byte(`
416 require.NoError(t, err)
417 assert.Equal(t, string(expected), buf.String())
418 })
419
420 t.Run("support multiline comments", func(t *testing.T) {
421 f, err := Load([]byte(`
413422 #
414423 # general.domain
415424 #
416425 # Domain name of XX system.
417426 domain = mydomain.com
418427 `))
419 So(err, ShouldBeNil)
428 require.NoError(t, err)
420429
421430 f.Section("").Key("test").Comment = "Multiline\nComment"
422431
423432 var buf bytes.Buffer
424433 _, err = f.WriteTo(&buf)
425 So(err, ShouldBeNil)
426
427 So(buf.String(), ShouldEqual, `#
434 require.NoError(t, err)
435
436 assert.Equal(t, `#
428437 # general.domain
429438 #
430439 # Domain name of XX system.
433442 ; Comment
434443 test =
435444
436 `)
437
438 })
439
440 Convey("Keep leading and trailing spaces in value", t, func() {
441 f, _ := ini.Load([]byte(`[foo]
445 `, buf.String())
446
447 })
448
449 t.Run("keep leading and trailing spaces in value", func(t *testing.T) {
450 f, _ := Load([]byte(`[foo]
442451 bar1 = ' val ue1 '
443452 bar2 = """ val ue2 """
444453 bar3 = " val ue3 "
445454 `))
446 So(f, ShouldNotBeNil)
455 require.NotNil(t, f)
447456
448457 var buf bytes.Buffer
449458 _, err := f.WriteTo(&buf)
450 So(err, ShouldBeNil)
451 So(buf.String(), ShouldEqual, `[foo]
459 require.NoError(t, err)
460 assert.Equal(t, `[foo]
452461 bar1 = " val ue1 "
453462 bar2 = " val ue2 "
454463 bar3 = " val ue3 "
455464
456 `)
465 `, buf.String())
457466 })
458467 }
459468
460469 func TestFile_SaveTo(t *testing.T) {
461 Convey("Write content to somewhere", t, func() {
462 f, err := ini.Load(fullConf)
463 So(err, ShouldBeNil)
464 So(f, ShouldNotBeNil)
465
466 So(f.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
467 So(f.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil)
468 })
470 f, err := Load(fullConf)
471 require.NoError(t, err)
472 require.NotNil(t, f)
473
474 assert.NoError(t, f.SaveTo("testdata/conf_out.ini"))
475 assert.NoError(t, f.SaveToIndent("testdata/conf_out.ini", "\t"))
469476 }
470477
471478 func TestFile_WriteToWithOutputDelimiter(t *testing.T) {
472 Convey("Write content to somewhere using a custom output delimiter", t, func() {
473 f, err := ini.LoadSources(ini.LoadOptions{
474 KeyValueDelimiterOnWrite: "->",
475 }, []byte(`[Others]
479 f, err := LoadSources(LoadOptions{
480 KeyValueDelimiterOnWrite: "->",
481 }, []byte(`[Others]
476482 Cities = HangZhou|Boston
477483 Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
478484 Years = 1993,1994
482488 Coordinates = 192.168,10.11
483489 Flags = true,false
484490 Note = Hello world!`))
485 So(err, ShouldBeNil)
486 So(f, ShouldNotBeNil)
487
488 var actual bytes.Buffer
489 var expected = []byte(`[Others]
491 require.NoError(t, err)
492 require.NotNil(t, f)
493
494 var actual bytes.Buffer
495 var expected = []byte(`[Others]
490496 Cities -> HangZhou|Boston
491497 Visits -> 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
492498 Years -> 1993,1994
498504 Note -> Hello world!
499505
500506 `)
501 _, err = f.WriteTo(&actual)
502 So(err, ShouldBeNil)
503
504 So(bytes.Equal(expected, actual.Bytes()), ShouldBeTrue)
505 })
507 _, err = f.WriteTo(&actual)
508 require.NoError(t, err)
509
510 assert.Equal(t, expected, actual.Bytes())
506511 }
507512
508513 // Inspired by https://github.com/go-ini/ini/issues/207
509514 func TestReloadAfterShadowLoad(t *testing.T) {
510 Convey("Reload file after ShadowLoad", t, func() {
511 f, err := ini.ShadowLoad([]byte(`
515 f, err := ShadowLoad([]byte(`
512516 [slice]
513517 v = 1
514518 v = 2
515519 v = 3
516520 `))
517 So(err, ShouldBeNil)
518 So(f, ShouldNotBeNil)
519
520 So(f.Section("slice").Key("v").ValueWithShadows(), ShouldResemble, []string{"1", "2", "3"})
521
522 So(f.Reload(), ShouldBeNil)
523 So(f.Section("slice").Key("v").ValueWithShadows(), ShouldResemble, []string{"1", "2", "3"})
524 })
525 }
521 require.NoError(t, err)
522 require.NotNil(t, f)
523
524 assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
525
526 require.NoError(t, f.Reload())
527 assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
528 }
1616 import (
1717 "testing"
1818
19 . "github.com/smartystreets/goconvey/convey"
19 "github.com/stretchr/testify/assert"
2020 )
2121
22 func Test_isSlice(t *testing.T) {
23 Convey("Check if a string is in the slice", t, func() {
24 ss := []string{"a", "b", "c"}
25 So(inSlice("a", ss), ShouldBeTrue)
26 So(inSlice("d", ss), ShouldBeFalse)
27 })
22 func TestIsInSlice(t *testing.T) {
23 ss := []string{"a", "b", "c"}
24 assert.True(t, inSlice("a", ss))
25 assert.False(t, inSlice("d", ss))
2826 }
0 // +build go1.6
1
20 // Copyright 2014 Unknwon
31 //
42 // Licensed under the Apache License, Version 2.0 (the "License"): you may
124122 ReaderBufferSize int
125123 // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
126124 AllowNonUniqueSections bool
125 // AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
126 AllowDuplicateShadowValues bool
127127 }
128128
129129 // DebugFunc is the type of function called to log parse events.
+0
-65
ini_python_multiline_test.go less more
0 package ini_test
1
2 import (
3 "path/filepath"
4 "runtime"
5 "testing"
6
7 . "github.com/smartystreets/goconvey/convey"
8 "gopkg.in/ini.v1"
9 )
10
11 type testData struct {
12 Value1 string `ini:"value1"`
13 Value2 string `ini:"value2"`
14 Value3 string `ini:"value3"`
15 }
16
17 func TestMultiline(t *testing.T) {
18 if runtime.GOOS == "windows" {
19 t.Skip("Skipping testing on Windows")
20 }
21
22 Convey("Parse Python-style multiline values", t, func() {
23 path := filepath.Join("testdata", "multiline.ini")
24 f, err := ini.LoadSources(ini.LoadOptions{
25 AllowPythonMultilineValues: true,
26 ReaderBufferSize: 64 * 1024,
27 }, path)
28 So(err, ShouldBeNil)
29 So(f, ShouldNotBeNil)
30 So(len(f.Sections()), ShouldEqual, 1)
31
32 defaultSection := f.Section("")
33 So(f.Section(""), ShouldNotBeNil)
34
35 var testData testData
36 err = defaultSection.MapTo(&testData)
37 So(err, ShouldBeNil)
38 So(testData.Value1, ShouldEqual, "some text here\nsome more text here\n\nthere is an empty line above and below\n")
39 So(testData.Value2, ShouldEqual, "there is an empty line above\nthat is not indented so it should not be part\nof the value")
40 So(testData.Value3, ShouldEqual, `.
41
42 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.
43
44 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.
45
46 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.
47
48 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.
49
50 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.
51
52 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.
53
54 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.
55
56 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.
57
58 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.
59
60 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.
61
62 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.`)
63 })
64 }
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "bytes"
1818 "flag"
1919 "io/ioutil"
20 "path/filepath"
21 "runtime"
2022 "testing"
2123
22 . "github.com/smartystreets/goconvey/convey"
23 "gopkg.in/ini.v1"
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
2426 )
2527
2628 const (
5052 var update = flag.Bool("update", false, "Update .golden files")
5153
5254 func TestLoad(t *testing.T) {
53 Convey("Load from good data sources", t, func() {
54 f, err := ini.Load(
55 t.Run("load from good data sources", func(t *testing.T) {
56 f, err := Load(
5557 "testdata/minimal.ini",
5658 []byte("NAME = ini\nIMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s"),
5759 bytes.NewReader([]byte(`VERSION = v1`)),
5860 ioutil.NopCloser(bytes.NewReader([]byte("[author]\nNAME = Unknwon"))),
5961 )
60 So(err, ShouldBeNil)
61 So(f, ShouldNotBeNil)
62 require.NoError(t, err)
63 require.NotNil(t, f)
6264
6365 // Validate values make sure all sources are loaded correctly
6466 sec := f.Section("")
65 So(sec.Key("NAME").String(), ShouldEqual, "ini")
66 So(sec.Key("VERSION").String(), ShouldEqual, "v1")
67 So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
67 assert.Equal(t, "ini", sec.Key("NAME").String())
68 assert.Equal(t, "v1", sec.Key("VERSION").String())
69 assert.Equal(t, "gopkg.in/ini.v1", sec.Key("IMPORT_PATH").String())
6870
6971 sec = f.Section("author")
70 So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
71 So(sec.Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
72 assert.Equal(t, "Unknwon", sec.Key("NAME").String())
73 assert.Equal(t, "u@gogs.io", sec.Key("E-MAIL").String())
7274 })
7375
74 Convey("Load from bad data sources", t, func() {
75 Convey("Invalid input", func() {
76 _, err := ini.Load(notFoundConf)
77 So(err, ShouldNotBeNil)
78 })
79
80 Convey("Unsupported type", func() {
81 _, err := ini.Load(123)
82 So(err, ShouldNotBeNil)
76 t.Run("load from bad data sources", func(t *testing.T) {
77 t.Run("invalid input", func(t *testing.T) {
78 _, err := Load(notFoundConf)
79 require.Error(t, err)
80 })
81
82 t.Run("unsupported type", func(t *testing.T) {
83 _, err := Load(123)
84 require.Error(t, err)
8385 })
8486 })
8587
86 Convey("Can't properly parse INI files containing `#` or `;` in value", t, func() {
87 f, err := ini.Load([]byte(`
88 t.Run("cannot properly parse INI files containing `#` or `;` in value", func(t *testing.T) {
89 f, err := Load([]byte(`
8890 [author]
8991 NAME = U#n#k#n#w#o#n
9092 GITHUB = U;n;k;n;w;o;n
9193 `))
92 So(err, ShouldBeNil)
93 So(f, ShouldNotBeNil)
94 require.NoError(t, err)
95 require.NotNil(t, f)
9496
9597 sec := f.Section("author")
9698 nameValue := sec.Key("NAME").String()
9799 githubValue := sec.Key("GITHUB").String()
98 So(nameValue, ShouldEqual, "U")
99 So(githubValue, ShouldEqual, "U")
100 assert.Equal(t, "U", nameValue)
101 assert.Equal(t, "U", githubValue)
100102 })
101103
102 Convey("Can't parse small python-compatible INI files", t, func() {
103 f, err := ini.Load([]byte(`
104 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) {
105 f, err := Load([]byte(`
104106 [long]
105107 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
106108 foo
109111 barfoo
110112 -----END RSA PRIVATE KEY-----
111113 `))
112 So(err, ShouldNotBeNil)
113 So(f, ShouldBeNil)
114 So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n")
114 require.Error(t, err)
115 assert.Nil(t, f)
116 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error())
115117 })
116118
117 Convey("Can't parse big python-compatible INI files", t, func() {
118 f, err := ini.Load([]byte(`
119 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) {
120 f, err := Load([]byte(`
119121 [long]
120122 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
121123 1foo
216218 96barfoo
217219 -----END RSA PRIVATE KEY-----
218220 `))
219 So(err, ShouldNotBeNil)
220 So(f, ShouldBeNil)
221 So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n")
221 require.Error(t, err)
222 assert.Nil(t, f)
223 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error())
222224 })
223225 }
224226
225227 func TestLooseLoad(t *testing.T) {
226 Convey("Load from data sources with option `Loose` true", t, func() {
227 f, err := ini.LoadSources(ini.LoadOptions{Loose: true}, notFoundConf, minimalConf)
228 So(err, ShouldBeNil)
229 So(f, ShouldNotBeNil)
230
231 Convey("Inverse case", func() {
232 _, err = ini.Load(notFoundConf)
233 So(err, ShouldNotBeNil)
234 })
228 f, err := LoadSources(LoadOptions{Loose: true}, notFoundConf, minimalConf)
229 require.NoError(t, err)
230 require.NotNil(t, f)
231
232 t.Run("inverse case", func(t *testing.T) {
233 _, err = Load(notFoundConf)
234 require.Error(t, err)
235235 })
236236 }
237237
238238 func TestInsensitiveLoad(t *testing.T) {
239 Convey("Insensitive to section and key names", t, func() {
240 f, err := ini.InsensitiveLoad(minimalConf)
241 So(err, ShouldBeNil)
242 So(f, ShouldNotBeNil)
243
244 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
245
246 Convey("Write out", func() {
239 t.Run("insensitive to section and key names", func(t *testing.T) {
240 f, err := InsensitiveLoad(minimalConf)
241 require.NoError(t, err)
242 require.NotNil(t, f)
243
244 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String())
245
246 t.Run("write out", func(t *testing.T) {
247247 var buf bytes.Buffer
248248 _, err := f.WriteTo(&buf)
249 So(err, ShouldBeNil)
250 So(buf.String(), ShouldEqual, `[author]
249 require.NoError(t, err)
250 assert.Equal(t, `[author]
251251 e-mail = u@gogs.io
252252
253 `)
254 })
255
256 Convey("Inverse case", func() {
257 f, err := ini.Load(minimalConf)
258 So(err, ShouldBeNil)
259 So(f, ShouldNotBeNil)
260
261 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
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())
262264 })
263265 })
264266
265267 // Ref: https://github.com/go-ini/ini/issues/198
266 Convey("Insensitive load with default section", t, func() {
267 f, err := ini.InsensitiveLoad([]byte(`
268 t.Run("insensitive load with default section", func(t *testing.T) {
269 f, err := InsensitiveLoad([]byte(`
268270 user = unknwon
269271 [profile]
270272 email = unknwon@local
271273 `))
272 So(err, ShouldBeNil)
273 So(f, ShouldNotBeNil)
274
275 So(f.Section(ini.DefaultSection).Key("user").String(), ShouldEqual, "unknwon")
274 require.NoError(t, err)
275 require.NotNil(t, f)
276
277 assert.Equal(t, "unknwon", f.Section(DefaultSection).Key("user").String())
276278 })
277279 }
278280
279281 func TestLoadSources(t *testing.T) {
280 Convey("Load from data sources with options", t, func() {
281 Convey("with true `AllowPythonMultilineValues`", func() {
282 Convey("Ignore nonexistent files", func() {
283 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Loose: true}, notFoundConf, minimalConf)
284 So(err, ShouldBeNil)
285 So(f, ShouldNotBeNil)
286
287 Convey("Inverse case", func() {
288 _, err = ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, notFoundConf)
289 So(err, ShouldNotBeNil)
290 })
291 })
292
293 Convey("Insensitive to section and key names", func() {
294 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Insensitive: true}, minimalConf)
295 So(err, ShouldBeNil)
296 So(f, ShouldNotBeNil)
297
298 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
299
300 Convey("Write out", func() {
301 var buf bytes.Buffer
302 _, err := f.WriteTo(&buf)
303 So(err, ShouldBeNil)
304 So(buf.String(), ShouldEqual, `[author]
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]
305306 e-mail = u@gogs.io
306307
307 `)
308 })
309
310 Convey("Inverse case", func() {
311 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, minimalConf)
312 So(err, ShouldBeNil)
313 So(f, ShouldNotBeNil)
314
315 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
316 })
317 })
318
319 Convey("Insensitive to sections and sensitive to key names", func() {
320 f, err := ini.LoadSources(ini.LoadOptions{InsensitiveSections: true}, minimalConf)
321 So(err, ShouldBeNil)
322 So(f, ShouldNotBeNil)
323
324 So(f.Section("Author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
325
326 Convey("Write out", func() {
327 var buf bytes.Buffer
328 _, err := f.WriteTo(&buf)
329 So(err, ShouldBeNil)
330 So(buf.String(), ShouldEqual, `[author]
308 `,
309 buf.String(),
310 )
311 })
312
313 t.Run("inverse case", func(t *testing.T) {
314 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, minimalConf)
315 require.NoError(t, err)
316 require.NotNil(t, f)
317
318 assert.Empty(t, f.Section("Author").Key("e-mail").String())
319 })
320 })
321
322 t.Run("insensitive to sections and sensitive to key names", func(t *testing.T) {
323 f, err := LoadSources(LoadOptions{InsensitiveSections: true}, minimalConf)
324 require.NoError(t, err)
325 require.NotNil(t, f)
326
327 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("E-MAIL").String())
328
329 t.Run("write out", func(t *testing.T) {
330 var buf bytes.Buffer
331 _, err := f.WriteTo(&buf)
332 require.NoError(t, err)
333 assert.Equal(t, `[author]
331334 E-MAIL = u@gogs.io
332335
333 `)
334 })
335
336 Convey("Inverse case", func() {
337 f, err := ini.LoadSources(ini.LoadOptions{}, minimalConf)
338 So(err, ShouldBeNil)
339 So(f, ShouldNotBeNil)
340
341 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
342 })
343 })
344
345 Convey("Sensitive to sections and insensitive to key names", func() {
346 f, err := ini.LoadSources(ini.LoadOptions{InsensitiveKeys: true}, minimalConf)
347 So(err, ShouldBeNil)
348 So(f, ShouldNotBeNil)
349
350 So(f.Section("author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
351
352 Convey("Write out", func() {
353 var buf bytes.Buffer
354 _, err := f.WriteTo(&buf)
355 So(err, ShouldBeNil)
356 So(buf.String(), ShouldEqual, `[author]
336 `,
337 buf.String(),
338 )
339 })
340
341 t.Run("inverse case", func(t *testing.T) {
342 f, err := LoadSources(LoadOptions{}, minimalConf)
343 require.NoError(t, err)
344 require.NotNil(t, f)
345
346 assert.Empty(t, f.Section("Author").Key("e-mail").String())
347 })
348 })
349
350 t.Run("sensitive to sections and insensitive to key names", func(t *testing.T) {
351 f, err := LoadSources(LoadOptions{InsensitiveKeys: true}, minimalConf)
352 require.NoError(t, err)
353 require.NotNil(t, f)
354
355 assert.Equal(t, "u@gogs.io", f.Section("author").Key("e-mail").String())
356
357 t.Run("write out", func(t *testing.T) {
358 var buf bytes.Buffer
359 _, err := f.WriteTo(&buf)
360 require.NoError(t, err)
361 assert.Equal(t, `[author]
357362 e-mail = u@gogs.io
358363
359 `)
360 })
361
362 Convey("Inverse case", func() {
363 f, err := ini.LoadSources(ini.LoadOptions{}, minimalConf)
364 So(err, ShouldBeNil)
365 So(f, ShouldNotBeNil)
366
367 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
368 })
369 })
370
371 Convey("Ignore continuation lines", func() {
372 f, err := ini.LoadSources(ini.LoadOptions{
373 AllowPythonMultilineValues: true,
374 IgnoreContinuation: true,
375 }, []byte(`
364 `,
365 buf.String(),
366 )
367 })
368
369 t.Run("inverse case", func(t *testing.T) {
370 f, err := LoadSources(LoadOptions{}, minimalConf)
371 require.NoError(t, err)
372 require.NotNil(t, f)
373
374 assert.Empty(t, f.Section("Author").Key("e-mail").String())
375 })
376 })
377
378 t.Run("ignore continuation lines", func(t *testing.T) {
379 f, err := LoadSources(LoadOptions{
380 AllowPythonMultilineValues: true,
381 IgnoreContinuation: true,
382 }, []byte(`
376383 key1=a\b\
377384 key2=c\d\
378385 key3=value`))
379 So(err, ShouldBeNil)
380 So(f, ShouldNotBeNil)
381
382 So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
383 So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
384 So(f.Section("").Key("key3").String(), ShouldEqual, "value")
385
386 Convey("Inverse case", func() {
387 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
386 require.NoError(t, err)
387 require.NotNil(t, f)
388
389 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String())
390 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String())
391 assert.Equal(t, "value", f.Section("").Key("key3").String())
392
393 t.Run("inverse case", func(t *testing.T) {
394 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
388395 key1=a\b\
389396 key2=c\d\`))
390 So(err, ShouldBeNil)
391 So(f, ShouldNotBeNil)
392
393 So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`)
394 })
395 })
396
397 Convey("Ignore inline comments", func() {
398 f, err := ini.LoadSources(ini.LoadOptions{
399 AllowPythonMultilineValues: true,
400 IgnoreInlineComment: true,
401 }, []byte(`
397 require.NoError(t, err)
398 require.NotNil(t, f)
399
400 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String())
401 })
402 })
403
404 t.Run("ignore inline comments", func(t *testing.T) {
405 f, err := LoadSources(LoadOptions{
406 AllowPythonMultilineValues: true,
407 IgnoreInlineComment: true,
408 }, []byte(`
402409 key1=value ;comment
403410 key2=value2 #comment2
404411 key3=val#ue #comment3`))
405 So(err, ShouldBeNil)
406 So(f, ShouldNotBeNil)
407
408 So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
409 So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`)
410 So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`)
411
412 Convey("Inverse case", func() {
413 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
412 require.NoError(t, err)
413 require.NotNil(t, f)
414
415 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String())
416 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String())
417 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String())
418
419 t.Run("inverse case", func(t *testing.T) {
420 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
414421 key1=value ;comment
415422 key2=value2 #comment2`))
416 So(err, ShouldBeNil)
417 So(f, ShouldNotBeNil)
418
419 So(f.Section("").Key("key1").String(), ShouldEqual, `value`)
420 So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`)
421 So(f.Section("").Key("key2").String(), ShouldEqual, `value2`)
422 So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`)
423 })
424 })
425
426 Convey("Skip unrecognizable lines", func() {
427 f, err := ini.LoadSources(ini.LoadOptions{
428 SkipUnrecognizableLines: true,
429 }, []byte(`
423 require.NoError(t, err)
424 require.NotNil(t, f)
425
426 assert.Equal(t, `value`, f.Section("").Key("key1").String())
427 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment)
428 assert.Equal(t, `value2`, f.Section("").Key("key2").String())
429 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment)
430 })
431 })
432
433 t.Run("skip unrecognizable lines", func(t *testing.T) {
434 f, err := LoadSources(LoadOptions{
435 SkipUnrecognizableLines: true,
436 }, []byte(`
430437 GenerationDepth: 13
431438
432439 BiomeRarityScale: 100
438445 BiomeGroup(NormalBiomes, 3, 99, RoofedForestEnchanted, ForestSakura, FloatingJungle
439446 BiomeGroup(IceBiomes, 4, 85, Ice Plains)
440447 `))
441 So(err, ShouldBeNil)
442 So(f, ShouldNotBeNil)
443
444 So(f.Section("").Key("GenerationDepth").String(), ShouldEqual, "13")
445 So(f.Section("").Key("BiomeRarityScale").String(), ShouldEqual, "100")
446 So(f.Section("").HasKey("BiomeGroup"), ShouldBeFalse)
447 })
448
449 Convey("Allow boolean type keys", func() {
450 f, err := ini.LoadSources(ini.LoadOptions{
451 AllowPythonMultilineValues: true,
452 AllowBooleanKeys: true,
453 }, []byte(`
448 require.NoError(t, err)
449 require.NotNil(t, f)
450
451 assert.Equal(t, "13", f.Section("").Key("GenerationDepth").String())
452 assert.Equal(t, "100", f.Section("").Key("BiomeRarityScale").String())
453 assert.False(t, f.Section("").HasKey("BiomeGroup"))
454 })
455
456 t.Run("allow boolean type keys", func(t *testing.T) {
457 f, err := LoadSources(LoadOptions{
458 AllowPythonMultilineValues: true,
459 AllowBooleanKeys: true,
460 }, []byte(`
454461 key1=hello
455462 #key2
456463 key3`))
457 So(err, ShouldBeNil)
458 So(f, ShouldNotBeNil)
459
460 So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"})
461 So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue)
462
463 Convey("Write out", func() {
464 var buf bytes.Buffer
465 _, err := f.WriteTo(&buf)
466 So(err, ShouldBeNil)
467 So(buf.String(), ShouldEqual, `key1 = hello
464 require.NoError(t, err)
465 require.NotNil(t, f)
466
467 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings())
468 assert.True(t, f.Section("").Key("key3").MustBool(false))
469
470 t.Run("write out", func(t *testing.T) {
471 var buf bytes.Buffer
472 _, err := f.WriteTo(&buf)
473 require.NoError(t, err)
474 assert.Equal(t, `key1 = hello
468475 # key2
469476 key3
470 `)
471 })
472
473 Convey("Inverse case", func() {
474 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
477 `,
478 buf.String(),
479 )
480 })
481
482 t.Run("inverse case", func(t *testing.T) {
483 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
475484 key1=hello
476485 #key2
477486 key3`))
478 So(err, ShouldNotBeNil)
479 })
480 })
481
482 Convey("Allow shadow keys", func() {
483 f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(`
487 require.Error(t, err)
488 })
489 })
490
491 t.Run("allow shadow keys", func(t *testing.T) {
492 f, err := LoadSources(LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(`
484493 [remote "origin"]
485494 url = https://github.com/Antergone/test1.git
486495 url = https://github.com/Antergone/test2.git
487496 fetch = +refs/heads/*:refs/remotes/origin/*`))
488 So(err, ShouldBeNil)
489 So(f, ShouldNotBeNil)
490
491 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
492 So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{
497 require.NoError(t, err)
498 require.NotNil(t, f)
499
500 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String())
501 assert.Equal(
502 t,
503 []string{
493504 "https://github.com/Antergone/test1.git",
494505 "https://github.com/Antergone/test2.git",
495 })
496 So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*")
497
498 Convey("Write out", func() {
499 var buf bytes.Buffer
500 _, err := f.WriteTo(&buf)
501 So(err, ShouldBeNil)
502 So(buf.String(), ShouldEqual, `[remote "origin"]
506 },
507 f.Section(`remote "origin"`).Key("url").ValueWithShadows(),
508 )
509 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String())
510
511 t.Run("write out", func(t *testing.T) {
512 var buf bytes.Buffer
513 _, err := f.WriteTo(&buf)
514 require.NoError(t, err)
515 assert.Equal(t, `[remote "origin"]
503516 url = https://github.com/Antergone/test1.git
504517 url = https://github.com/Antergone/test2.git
505518 fetch = +refs/heads/*:refs/remotes/origin/*
506519
507 `)
508 })
509
510 Convey("Inverse case", func() {
511 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
520 `,
521 buf.String(),
522 )
523 })
524
525 t.Run("inverse case", func(t *testing.T) {
526 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
512527 [remote "origin"]
513528 url = https://github.com/Antergone/test1.git
514529 url = https://github.com/Antergone/test2.git`))
515 So(err, ShouldBeNil)
516 So(f, ShouldNotBeNil)
517
518 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
519 })
520 })
521
522 Convey("Unescape double quotes inside value", func() {
523 f, err := ini.LoadSources(ini.LoadOptions{
524 AllowPythonMultilineValues: true,
525 UnescapeValueDoubleQuotes: true,
526 }, []byte(`
530 require.NoError(t, err)
531 require.NotNil(t, f)
532
533 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String())
534 })
535 })
536
537 t.Run("unescape double quotes inside value", func(t *testing.T) {
538 f, err := LoadSources(LoadOptions{
539 AllowPythonMultilineValues: true,
540 UnescapeValueDoubleQuotes: true,
541 }, []byte(`
527542 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
528 So(err, ShouldBeNil)
529 So(f, ShouldNotBeNil)
530
531 So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
532
533 Convey("Inverse case", func() {
534 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
543 require.NoError(t, err)
544 require.NotNil(t, f)
545
546 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String())
547
548 t.Run("inverse case", func(t *testing.T) {
549 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
535550 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
536 So(err, ShouldBeNil)
537 So(f, ShouldNotBeNil)
538
539 So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`)
540 })
541 })
542
543 Convey("Unescape comment symbols inside value", func() {
544 f, err := ini.LoadSources(ini.LoadOptions{
545 AllowPythonMultilineValues: true,
546 IgnoreInlineComment: true,
547 UnescapeValueCommentSymbols: true,
548 }, []byte(`
551 require.NoError(t, err)
552 require.NotNil(t, f)
553
554 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String())
555 })
556 })
557
558 t.Run("unescape comment symbols inside value", func(t *testing.T) {
559 f, err := LoadSources(LoadOptions{
560 AllowPythonMultilineValues: true,
561 IgnoreInlineComment: true,
562 UnescapeValueCommentSymbols: true,
563 }, []byte(`
549564 key = test value <span style="color: %s\; background: %s">more text</span>
550565 `))
551 So(err, ShouldBeNil)
552 So(f, ShouldNotBeNil)
553
554 So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`)
555 })
556
557 Convey("Can parse small python-compatible INI files", func() {
558 f, err := ini.LoadSources(ini.LoadOptions{
559 AllowPythonMultilineValues: true,
560 Insensitive: true,
561 UnparseableSections: []string{"core_lesson", "comments"},
562 }, []byte(`
566 require.NoError(t, err)
567 require.NotNil(t, f)
568
569 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String())
570 })
571
572 t.Run("can parse small python-compatible INI files", func(t *testing.T) {
573 f, err := LoadSources(LoadOptions{
574 AllowPythonMultilineValues: true,
575 Insensitive: true,
576 UnparseableSections: []string{"core_lesson", "comments"},
577 }, []byte(`
563578 [long]
564579 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
565580 foo
572587 second
573588 third
574589 `))
575 So(err, ShouldBeNil)
576 So(f, ShouldNotBeNil)
577
578 So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----")
579 So(f.Section("long").Key("multiline_list").String(), ShouldEqual, "\nfirst\nsecond\nthird")
580 })
581
582 Convey("Can parse big python-compatible INI files", func() {
583 f, err := ini.LoadSources(ini.LoadOptions{
584 AllowPythonMultilineValues: true,
585 Insensitive: true,
586 UnparseableSections: []string{"core_lesson", "comments"},
587 }, []byte(`
590 require.NoError(t, err)
591 require.NotNil(t, f)
592
593 assert.Equal(t, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----", f.Section("long").Key("long_rsa_private_key").String())
594 assert.Equal(t, "\nfirst\nsecond\nthird", f.Section("long").Key("multiline_list").String())
595 })
596
597 t.Run("can parse big python-compatible INI files", func(t *testing.T) {
598 f, err := LoadSources(LoadOptions{
599 AllowPythonMultilineValues: true,
600 Insensitive: true,
601 UnparseableSections: []string{"core_lesson", "comments"},
602 }, []byte(`
588603 [long]
589604 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
590605 1foo
685700 96barfoo
686701 -----END RSA PRIVATE KEY-----
687702 `))
688 So(err, ShouldBeNil)
689 So(f, ShouldNotBeNil)
690
691 So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, `-----BEGIN RSA PRIVATE KEY-----
703 require.NoError(t, err)
704 require.NotNil(t, f)
705
706 assert.Equal(t, `-----BEGIN RSA PRIVATE KEY-----
692707 1foo
693708 2bar
694709 3foobar
785800 94bar
786801 95foobar
787802 96barfoo
788 -----END RSA PRIVATE KEY-----`)
789 })
790
791 Convey("Allow unparsable sections", func() {
792 f, err := ini.LoadSources(ini.LoadOptions{
793 AllowPythonMultilineValues: true,
794 Insensitive: true,
795 UnparseableSections: []string{"core_lesson", "comments"},
796 }, []byte(`
803 -----END RSA PRIVATE KEY-----`,
804 f.Section("long").Key("long_rsa_private_key").String(),
805 )
806 })
807
808 t.Run("allow unparsable sections", func(t *testing.T) {
809 f, err := LoadSources(LoadOptions{
810 AllowPythonMultilineValues: true,
811 Insensitive: true,
812 UnparseableSections: []string{"core_lesson", "comments"},
813 }, []byte(`
797814 Lesson_Location = 87
798815 Lesson_Status = C
799816 Score = 3
805822
806823 [COMMENTS]
807824 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
808 So(err, ShouldBeNil)
809 So(f, ShouldNotBeNil)
810
811 So(f.Section("").Key("score").String(), ShouldEqual, "3")
812 So(f.Section("").Body(), ShouldBeEmpty)
813 So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000
814 111111111111111111100000000000111000000000 – end my lesson state data`)
815 So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)
816
817 Convey("Write out", func() {
818 var buf bytes.Buffer
819 _, err := f.WriteTo(&buf)
820 So(err, ShouldBeNil)
821 So(buf.String(), ShouldEqual, `lesson_location = 87
825 require.NoError(t, err)
826 require.NotNil(t, f)
827
828 assert.Equal(t, "3", f.Section("").Key("score").String())
829 assert.Empty(t, f.Section("").Body())
830 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000
831 111111111111111111100000000000111000000000 – end my lesson state data`,
832 f.Section("core_lesson").Body(),
833 )
834 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body())
835
836 t.Run("write out", func(t *testing.T) {
837 var buf bytes.Buffer
838 _, err := f.WriteTo(&buf)
839 require.NoError(t, err)
840 assert.Equal(t, `lesson_location = 87
822841 lesson_status = C
823842 score = 3
824843 time = 00:02:30
829848
830849 [comments]
831850 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
832 `)
833 })
834
835 Convey("Inverse case", func() {
836 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
851 `,
852 buf.String(),
853 )
854 })
855
856 t.Run("inverse case", func(t *testing.T) {
857 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(`
837858 [CORE_LESSON]
838859 my lesson state data – 1111111111111111111000000000000000001110000
839860 111111111111111111100000000000111000000000 – end my lesson state data`))
840 So(err, ShouldNotBeNil)
841 })
842 })
843
844 Convey("And false `SpaceBeforeInlineComment`", func() {
845 Convey("Can't parse INI files containing `#` or `;` in value", func() {
846 f, err := ini.LoadSources(
847 ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false},
848 []byte(`
861 require.Error(t, err)
862 })
863 })
864
865 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) {
866 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) {
867 f, err := LoadSources(
868 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false},
869 []byte(`
849870 [author]
850871 NAME = U#n#k#n#w#o#n
851872 GITHUB = U;n;k;n;w;o;n
852873 `))
853 So(err, ShouldBeNil)
854 So(f, ShouldNotBeNil)
855 sec := f.Section("author")
856 nameValue := sec.Key("NAME").String()
857 githubValue := sec.Key("GITHUB").String()
858 So(nameValue, ShouldEqual, "U")
859 So(githubValue, ShouldEqual, "U")
860 })
861 })
862
863 Convey("And true `SpaceBeforeInlineComment`", func() {
864 Convey("Can parse INI files containing `#` or `;` in value", func() {
865 f, err := ini.LoadSources(
866 ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true},
867 []byte(`
874 require.NoError(t, err)
875 require.NotNil(t, f)
876 sec := f.Section("author")
877 nameValue := sec.Key("NAME").String()
878 githubValue := sec.Key("GITHUB").String()
879 assert.Equal(t, "U", nameValue)
880 assert.Equal(t, "U", githubValue)
881 })
882 })
883
884 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) {
885 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) {
886 f, err := LoadSources(
887 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true},
888 []byte(`
868889 [author]
869890 NAME = U#n#k#n#w#o#n
870891 GITHUB = U;n;k;n;w;o;n
871892 `))
872 So(err, ShouldBeNil)
873 So(f, ShouldNotBeNil)
874 sec := f.Section("author")
875 nameValue := sec.Key("NAME").String()
876 githubValue := sec.Key("GITHUB").String()
877 So(nameValue, ShouldEqual, "U#n#k#n#w#o#n")
878 So(githubValue, ShouldEqual, "U;n;k;n;w;o;n")
879 })
880 })
881 })
882
883 Convey("with false `AllowPythonMultilineValues`", func() {
884 Convey("Ignore nonexistent files", func() {
885 f, err := ini.LoadSources(ini.LoadOptions{
893 require.NoError(t, err)
894 require.NotNil(t, f)
895 sec := f.Section("author")
896 nameValue := sec.Key("NAME").String()
897 githubValue := sec.Key("GITHUB").String()
898 assert.Equal(t, "U#n#k#n#w#o#n", nameValue)
899 assert.Equal(t, "U;n;k;n;w;o;n", githubValue)
900 })
901 })
902 })
903
904 t.Run("with false `AllowPythonMultilineValues`", func(t *testing.T) {
905 t.Run("ignore nonexistent files", func(t *testing.T) {
906 f, err := LoadSources(LoadOptions{
907 AllowPythonMultilineValues: false,
908 Loose: true,
909 }, notFoundConf, minimalConf)
910 require.NoError(t, err)
911 require.NotNil(t, f)
912
913 t.Run("inverse case", func(t *testing.T) {
914 _, err = LoadSources(LoadOptions{
886915 AllowPythonMultilineValues: false,
887 Loose: true,
888 }, notFoundConf, minimalConf)
889 So(err, ShouldBeNil)
890 So(f, ShouldNotBeNil)
891
892 Convey("Inverse case", func() {
893 _, err = ini.LoadSources(ini.LoadOptions{
894 AllowPythonMultilineValues: false,
895 }, notFoundConf)
896 So(err, ShouldNotBeNil)
897 })
898 })
899
900 Convey("Insensitive to section and key names", func() {
901 f, err := ini.LoadSources(ini.LoadOptions{
916 }, notFoundConf)
917 require.Error(t, err)
918 })
919 })
920
921 t.Run("insensitive to section and key names", func(t *testing.T) {
922 f, err := LoadSources(LoadOptions{
923 AllowPythonMultilineValues: false,
924 Insensitive: true,
925 }, minimalConf)
926 require.NoError(t, err)
927 require.NotNil(t, f)
928
929 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String())
930
931 t.Run("write out", func(t *testing.T) {
932 var buf bytes.Buffer
933 _, err := f.WriteTo(&buf)
934 require.NoError(t, err)
935 assert.Equal(t, `[author]
936 e-mail = u@gogs.io
937
938 `,
939 buf.String(),
940 )
941 })
942
943 t.Run("inverse case", func(t *testing.T) {
944 f, err := LoadSources(LoadOptions{
902945 AllowPythonMultilineValues: false,
903 Insensitive: true,
904946 }, minimalConf)
905 So(err, ShouldBeNil)
906 So(f, ShouldNotBeNil)
907
908 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
909
910 Convey("Write out", func() {
911 var buf bytes.Buffer
912 _, err := f.WriteTo(&buf)
913 So(err, ShouldBeNil)
914 So(buf.String(), ShouldEqual, `[author]
915 e-mail = u@gogs.io
916
917 `)
918 })
919
920 Convey("Inverse case", func() {
921 f, err := ini.LoadSources(ini.LoadOptions{
922 AllowPythonMultilineValues: false,
923 }, minimalConf)
924 So(err, ShouldBeNil)
925 So(f, ShouldNotBeNil)
926
927 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
928 })
929 })
930
931 Convey("Ignore continuation lines", func() {
932 f, err := ini.LoadSources(ini.LoadOptions{
933 AllowPythonMultilineValues: false,
934 IgnoreContinuation: true,
935 }, []byte(`
947 require.NoError(t, err)
948 require.NotNil(t, f)
949
950 assert.Empty(t, f.Section("Author").Key("e-mail").String())
951 })
952 })
953
954 t.Run("ignore continuation lines", func(t *testing.T) {
955 f, err := LoadSources(LoadOptions{
956 AllowPythonMultilineValues: false,
957 IgnoreContinuation: true,
958 }, []byte(`
936959 key1=a\b\
937960 key2=c\d\
938961 key3=value`))
939 So(err, ShouldBeNil)
940 So(f, ShouldNotBeNil)
941
942 So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
943 So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
944 So(f.Section("").Key("key3").String(), ShouldEqual, "value")
945
946 Convey("Inverse case", func() {
947 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
962 require.NoError(t, err)
963 require.NotNil(t, f)
964
965 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String())
966 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String())
967 assert.Equal(t, "value", f.Section("").Key("key3").String())
968
969 t.Run("inverse case", func(t *testing.T) {
970 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
948971 key1=a\b\
949972 key2=c\d\`))
950 So(err, ShouldBeNil)
951 So(f, ShouldNotBeNil)
952
953 So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`)
954 })
955 })
956
957 Convey("Ignore inline comments", func() {
958 f, err := ini.LoadSources(ini.LoadOptions{
959 AllowPythonMultilineValues: false,
960 IgnoreInlineComment: true,
961 }, []byte(`
973 require.NoError(t, err)
974 require.NotNil(t, f)
975
976 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String())
977 })
978 })
979
980 t.Run("ignore inline comments", func(t *testing.T) {
981 f, err := LoadSources(LoadOptions{
982 AllowPythonMultilineValues: false,
983 IgnoreInlineComment: true,
984 }, []byte(`
962985 key1=value ;comment
963986 key2=value2 #comment2
964987 key3=val#ue #comment3`))
965 So(err, ShouldBeNil)
966 So(f, ShouldNotBeNil)
967
968 So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
969 So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`)
970 So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`)
971
972 Convey("Inverse case", func() {
973 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
988 require.NoError(t, err)
989 require.NotNil(t, f)
990
991 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String())
992 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String())
993 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String())
994
995 t.Run("inverse case", func(t *testing.T) {
996 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
974997 key1=value ;comment
975998 key2=value2 #comment2`))
976 So(err, ShouldBeNil)
977 So(f, ShouldNotBeNil)
978
979 So(f.Section("").Key("key1").String(), ShouldEqual, `value`)
980 So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`)
981 So(f.Section("").Key("key2").String(), ShouldEqual, `value2`)
982 So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`)
983 })
984 })
985
986 Convey("Allow boolean type keys", func() {
987 f, err := ini.LoadSources(ini.LoadOptions{
988 AllowPythonMultilineValues: false,
989 AllowBooleanKeys: true,
990 }, []byte(`
999 require.NoError(t, err)
1000 require.NotNil(t, f)
1001
1002 assert.Equal(t, `value`, f.Section("").Key("key1").String())
1003 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment)
1004 assert.Equal(t, `value2`, f.Section("").Key("key2").String())
1005 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment)
1006 })
1007 })
1008
1009 t.Run("allow boolean type keys", func(t *testing.T) {
1010 f, err := LoadSources(LoadOptions{
1011 AllowPythonMultilineValues: false,
1012 AllowBooleanKeys: true,
1013 }, []byte(`
9911014 key1=hello
9921015 #key2
9931016 key3`))
994 So(err, ShouldBeNil)
995 So(f, ShouldNotBeNil)
996
997 So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"})
998 So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue)
999
1000 Convey("Write out", func() {
1001 var buf bytes.Buffer
1002 _, err := f.WriteTo(&buf)
1003 So(err, ShouldBeNil)
1004 So(buf.String(), ShouldEqual, `key1 = hello
1017 require.NoError(t, err)
1018 require.NotNil(t, f)
1019
1020 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings())
1021 assert.True(t, f.Section("").Key("key3").MustBool(false))
1022
1023 t.Run("write out", func(t *testing.T) {
1024 var buf bytes.Buffer
1025 _, err := f.WriteTo(&buf)
1026 require.NoError(t, err)
1027 assert.Equal(t, `key1 = hello
10051028 # key2
10061029 key3
1007 `)
1008 })
1009
1010 Convey("Inverse case", func() {
1011 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1030 `,
1031 buf.String(),
1032 )
1033 })
1034
1035 t.Run("inverse case", func(t *testing.T) {
1036 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
10121037 key1=hello
10131038 #key2
10141039 key3`))
1015 So(err, ShouldNotBeNil)
1016 })
1017 })
1018
1019 Convey("Allow shadow keys", func() {
1020 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(`
1040 require.Error(t, err)
1041 })
1042 })
1043
1044 t.Run("allow shadow keys", func(t *testing.T) {
1045 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(`
10211046 [remote "origin"]
10221047 url = https://github.com/Antergone/test1.git
10231048 url = https://github.com/Antergone/test2.git
10241049 fetch = +refs/heads/*:refs/remotes/origin/*`))
1025 So(err, ShouldBeNil)
1026 So(f, ShouldNotBeNil)
1027
1028 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
1029 So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{
1050 require.NoError(t, err)
1051 require.NotNil(t, f)
1052
1053 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String())
1054 assert.Equal(
1055 t,
1056 []string{
10301057 "https://github.com/Antergone/test1.git",
10311058 "https://github.com/Antergone/test2.git",
1032 })
1033 So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*")
1034
1035 Convey("Write out", func() {
1036 var buf bytes.Buffer
1037 _, err := f.WriteTo(&buf)
1038 So(err, ShouldBeNil)
1039 So(buf.String(), ShouldEqual, `[remote "origin"]
1059 },
1060 f.Section(`remote "origin"`).Key("url").ValueWithShadows(),
1061 )
1062 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String())
1063
1064 t.Run("write out", func(t *testing.T) {
1065 var buf bytes.Buffer
1066 _, err := f.WriteTo(&buf)
1067 require.NoError(t, err)
1068 assert.Equal(t, `[remote "origin"]
10401069 url = https://github.com/Antergone/test1.git
10411070 url = https://github.com/Antergone/test2.git
10421071 fetch = +refs/heads/*:refs/remotes/origin/*
10431072
1044 `)
1045 })
1046
1047 Convey("Inverse case", func() {
1048 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1073 `,
1074 buf.String(),
1075 )
1076 })
1077
1078 t.Run("inverse case", func(t *testing.T) {
1079 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
10491080 [remote "origin"]
10501081 url = https://github.com/Antergone/test1.git
10511082 url = https://github.com/Antergone/test2.git`))
1052 So(err, ShouldBeNil)
1053 So(f, ShouldNotBeNil)
1054
1055 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
1056 })
1057 })
1058
1059 Convey("Unescape double quotes inside value", func() {
1060 f, err := ini.LoadSources(ini.LoadOptions{
1061 AllowPythonMultilineValues: false,
1062 UnescapeValueDoubleQuotes: true,
1063 }, []byte(`
1083 require.NoError(t, err)
1084 require.NotNil(t, f)
1085
1086 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String())
1087 })
1088 })
1089
1090 t.Run("unescape double quotes inside value", func(t *testing.T) {
1091 f, err := LoadSources(LoadOptions{
1092 AllowPythonMultilineValues: false,
1093 UnescapeValueDoubleQuotes: true,
1094 }, []byte(`
10641095 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
1065 So(err, ShouldBeNil)
1066 So(f, ShouldNotBeNil)
1067
1068 So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
1069
1070 Convey("Inverse case", func() {
1071 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1096 require.NoError(t, err)
1097 require.NotNil(t, f)
1098
1099 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String())
1100
1101 t.Run("inverse case", func(t *testing.T) {
1102 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
10721103 create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
1073 So(err, ShouldBeNil)
1074 So(f, ShouldNotBeNil)
1075
1076 So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`)
1077 })
1078 })
1079
1080 Convey("Unescape comment symbols inside value", func() {
1081 f, err := ini.LoadSources(ini.LoadOptions{
1082 AllowPythonMultilineValues: false,
1083 IgnoreInlineComment: true,
1084 UnescapeValueCommentSymbols: true,
1085 }, []byte(`
1104 require.NoError(t, err)
1105 require.NotNil(t, f)
1106
1107 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String())
1108 })
1109 })
1110
1111 t.Run("unescape comment symbols inside value", func(t *testing.T) {
1112 f, err := LoadSources(LoadOptions{
1113 AllowPythonMultilineValues: false,
1114 IgnoreInlineComment: true,
1115 UnescapeValueCommentSymbols: true,
1116 }, []byte(`
10861117 key = test value <span style="color: %s\; background: %s">more text</span>
10871118 `))
1088 So(err, ShouldBeNil)
1089 So(f, ShouldNotBeNil)
1090
1091 So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`)
1092 })
1093
1094 Convey("Can't parse small python-compatible INI files", func() {
1095 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1119 require.NoError(t, err)
1120 require.NotNil(t, f)
1121
1122 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String())
1123 })
1124
1125 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) {
1126 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
10961127 [long]
10971128 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
10981129 foo
11011132 barfoo
11021133 -----END RSA PRIVATE KEY-----
11031134 `))
1104 So(err, ShouldNotBeNil)
1105 So(f, ShouldBeNil)
1106 So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n")
1107 })
1108
1109 Convey("Can't parse big python-compatible INI files", func() {
1110 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1135 require.Error(t, err)
1136 assert.Nil(t, f)
1137 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error())
1138 })
1139
1140 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) {
1141 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
11111142 [long]
11121143 long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
11131144 1foo
12081239 96barfoo
12091240 -----END RSA PRIVATE KEY-----
12101241 `))
1211 So(err, ShouldNotBeNil)
1212 So(f, ShouldBeNil)
1213 So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n")
1214 })
1215
1216 Convey("Allow unparsable sections", func() {
1217 f, err := ini.LoadSources(ini.LoadOptions{
1218 AllowPythonMultilineValues: false,
1219 Insensitive: true,
1220 UnparseableSections: []string{"core_lesson", "comments"},
1221 }, []byte(`
1242 require.Error(t, err)
1243 assert.Nil(t, f)
1244 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error())
1245 })
1246
1247 t.Run("allow unparsable sections", func(t *testing.T) {
1248 f, err := LoadSources(LoadOptions{
1249 AllowPythonMultilineValues: false,
1250 Insensitive: true,
1251 UnparseableSections: []string{"core_lesson", "comments"},
1252 }, []byte(`
12221253 Lesson_Location = 87
12231254 Lesson_Status = C
12241255 Score = 3
12301261
12311262 [COMMENTS]
12321263 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
1233 So(err, ShouldBeNil)
1234 So(f, ShouldNotBeNil)
1235
1236 So(f.Section("").Key("score").String(), ShouldEqual, "3")
1237 So(f.Section("").Body(), ShouldBeEmpty)
1238 So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000
1239 111111111111111111100000000000111000000000 – end my lesson state data`)
1240 So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)
1241
1242 Convey("Write out", func() {
1243 var buf bytes.Buffer
1244 _, err := f.WriteTo(&buf)
1245 So(err, ShouldBeNil)
1246 So(buf.String(), ShouldEqual, `lesson_location = 87
1264 require.NoError(t, err)
1265 require.NotNil(t, f)
1266
1267 assert.Equal(t, "3", f.Section("").Key("score").String())
1268 assert.Empty(t, f.Section("").Body())
1269 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000
1270 111111111111111111100000000000111000000000 – end my lesson state data`,
1271 f.Section("core_lesson").Body(),
1272 )
1273 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body())
1274
1275 t.Run("write out", func(t *testing.T) {
1276 var buf bytes.Buffer
1277 _, err := f.WriteTo(&buf)
1278 require.NoError(t, err)
1279 assert.Equal(t, `lesson_location = 87
12471280 lesson_status = C
12481281 score = 3
12491282 time = 00:02:30
12541287
12551288 [comments]
12561289 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
1257 `)
1258 })
1259
1260 Convey("Inverse case", func() {
1261 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
1290 `,
1291 buf.String(),
1292 )
1293 })
1294
1295 t.Run("inverse case", func(t *testing.T) {
1296 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(`
12621297 [CORE_LESSON]
12631298 my lesson state data – 1111111111111111111000000000000000001110000
12641299 111111111111111111100000000000111000000000 – end my lesson state data`))
1265 So(err, ShouldNotBeNil)
1266 })
1267 })
1268
1269 Convey("And false `SpaceBeforeInlineComment`", func() {
1270 Convey("Can't parse INI files containing `#` or `;` in value", func() {
1271 f, err := ini.LoadSources(
1272 ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false},
1273 []byte(`
1300 require.Error(t, err)
1301 })
1302 })
1303
1304 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) {
1305 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) {
1306 f, err := LoadSources(
1307 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false},
1308 []byte(`
12741309 [author]
12751310 NAME = U#n#k#n#w#o#n
12761311 GITHUB = U;n;k;n;w;o;n
12771312 `))
1278 So(err, ShouldBeNil)
1279 So(f, ShouldNotBeNil)
1280 sec := f.Section("author")
1281 nameValue := sec.Key("NAME").String()
1282 githubValue := sec.Key("GITHUB").String()
1283 So(nameValue, ShouldEqual, "U")
1284 So(githubValue, ShouldEqual, "U")
1285 })
1286 })
1287
1288 Convey("And true `SpaceBeforeInlineComment`", func() {
1289 Convey("Can parse INI files containing `#` or `;` in value", func() {
1290 f, err := ini.LoadSources(
1291 ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true},
1292 []byte(`
1313 require.NoError(t, err)
1314 require.NotNil(t, f)
1315 sec := f.Section("author")
1316 nameValue := sec.Key("NAME").String()
1317 githubValue := sec.Key("GITHUB").String()
1318 assert.Equal(t, "U", nameValue)
1319 assert.Equal(t, "U", githubValue)
1320 })
1321 })
1322
1323 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) {
1324 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) {
1325 f, err := LoadSources(
1326 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true},
1327 []byte(`
12931328 [author]
12941329 NAME = U#n#k#n#w#o#n
12951330 GITHUB = U;n;k;n;w;o;n
12961331 `))
1297 So(err, ShouldBeNil)
1298 So(f, ShouldNotBeNil)
1299 sec := f.Section("author")
1300 nameValue := sec.Key("NAME").String()
1301 githubValue := sec.Key("GITHUB").String()
1302 So(nameValue, ShouldEqual, "U#n#k#n#w#o#n")
1303 So(githubValue, ShouldEqual, "U;n;k;n;w;o;n")
1304 })
1305 })
1306 })
1307
1308 Convey("with `ChildSectionDelimiter` ':'", func() {
1309 Convey("Get all keys of parent sections", func() {
1310 f := ini.Empty(ini.LoadOptions{ChildSectionDelimiter: ":"})
1311 So(f, ShouldNotBeNil)
1312
1313 k, err := f.Section("package").NewKey("NAME", "ini")
1314 So(err, ShouldBeNil)
1315 So(k, ShouldNotBeNil)
1316 k, err = f.Section("package").NewKey("VERSION", "v1")
1317 So(err, ShouldBeNil)
1318 So(k, ShouldNotBeNil)
1319 k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
1320 So(err, ShouldBeNil)
1321 So(k, ShouldNotBeNil)
1322
1323 keys := f.Section("package:sub:sub2").ParentKeys()
1324 names := []string{"NAME", "VERSION", "IMPORT_PATH"}
1325 So(len(keys), ShouldEqual, len(names))
1326 for i, name := range names {
1327 So(keys[i].Name(), ShouldEqual, name)
1332 require.NoError(t, err)
1333 require.NotNil(t, f)
1334 sec := f.Section("author")
1335 nameValue := sec.Key("NAME").String()
1336 githubValue := sec.Key("GITHUB").String()
1337 assert.Equal(t, "U#n#k#n#w#o#n", nameValue)
1338 assert.Equal(t, "U;n;k;n;w;o;n", githubValue)
1339 })
1340 })
1341 })
1342
1343 t.Run("with `ChildSectionDelimiter` ':'", func(t *testing.T) {
1344 t.Run("get all keys of parent sections", func(t *testing.T) {
1345 f := Empty(LoadOptions{ChildSectionDelimiter: ":"})
1346 require.NotNil(t, f)
1347
1348 k, err := f.Section("package").NewKey("NAME", "ini")
1349 require.NoError(t, err)
1350 assert.NotNil(t, k)
1351 k, err = f.Section("package").NewKey("VERSION", "v1")
1352 require.NoError(t, err)
1353 assert.NotNil(t, k)
1354 k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
1355 require.NoError(t, err)
1356 assert.NotNil(t, k)
1357
1358 keys := f.Section("package:sub:sub2").ParentKeys()
1359 names := []string{"NAME", "VERSION", "IMPORT_PATH"}
1360 assert.Equal(t, len(names), len(keys))
1361 for i, name := range names {
1362 assert.Equal(t, name, keys[i].Name())
1363 }
1364 })
1365
1366 t.Run("getting and setting values", func(t *testing.T) {
1367 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, fullConf)
1368 require.NoError(t, err)
1369 require.NotNil(t, f)
1370
1371 t.Run("get parent-keys that are available to the child section", func(t *testing.T) {
1372 parentKeys := f.Section("package:sub").ParentKeys()
1373 assert.NotNil(t, parentKeys)
1374 for _, k := range parentKeys {
1375 assert.Equal(t, "CLONE_URL", k.Name())
13281376 }
13291377 })
13301378
1331 Convey("Getting and setting values", func() {
1332 f, err := ini.LoadSources(ini.LoadOptions{ChildSectionDelimiter: ":"}, fullConf)
1333 So(err, ShouldBeNil)
1334 So(f, ShouldNotBeNil)
1335
1336 Convey("Get parent-keys that are available to the child section", func() {
1337 parentKeys := f.Section("package:sub").ParentKeys()
1338 So(parentKeys, ShouldNotBeNil)
1339 for _, k := range parentKeys {
1340 So(k.Name(), ShouldEqual, "CLONE_URL")
1341 }
1342 })
1343
1344 Convey("Get parent section value", func() {
1345 So(f.Section("package:sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
1346 So(f.Section("package:fake:sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
1347 })
1348 })
1349
1350 Convey("Get child sections by parent name", func() {
1351 f, err := ini.LoadSources(ini.LoadOptions{ChildSectionDelimiter: ":"}, []byte(`
1379 t.Run("get parent section value", func(t *testing.T) {
1380 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:sub").Key("CLONE_URL").String())
1381 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:fake:sub").Key("CLONE_URL").String())
1382 })
1383 })
1384
1385 t.Run("get child sections by parent name", func(t *testing.T) {
1386 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, []byte(`
13521387 [node]
13531388 [node:biz1]
13541389 [node:biz2]
13551390 [node.biz3]
13561391 [node.bizN]
13571392 `))
1358 So(err, ShouldBeNil)
1359 So(f, ShouldNotBeNil)
1360
1361 children := f.ChildSections("node")
1362 names := []string{"node:biz1", "node:biz2"}
1363 So(len(children), ShouldEqual, len(names))
1364 for i, name := range names {
1365 So(children[i].Name(), ShouldEqual, name)
1366 }
1367 })
1368 })
1369
1370 Convey("ShortCircuit", func() {
1371 Convey("Load the first available configuration, ignore other configuration", func() {
1372 f, err := ini.LoadSources(ini.LoadOptions{ShortCircuit: true}, minimalConf, []byte(`key1 = value1`))
1373 So(f, ShouldNotBeNil)
1374 So(err, ShouldBeNil)
1375 var buf bytes.Buffer
1376 _, err = f.WriteTo(&buf)
1377 So(err, ShouldBeNil)
1378 So(buf.String(), ShouldEqual, `[author]
1393 require.NoError(t, err)
1394 require.NotNil(t, f)
1395
1396 children := f.ChildSections("node")
1397 names := []string{"node:biz1", "node:biz2"}
1398 assert.Equal(t, len(names), len(children))
1399 for i, name := range names {
1400 assert.Equal(t, name, children[i].Name())
1401 }
1402 })
1403 })
1404
1405 t.Run("ShortCircuit", func(t *testing.T) {
1406 t.Run("load the first available configuration, ignore other configuration", func(t *testing.T) {
1407 f, err := LoadSources(LoadOptions{ShortCircuit: true}, minimalConf, []byte(`key1 = value1`))
1408 require.NotNil(t, f)
1409 require.NoError(t, err)
1410 var buf bytes.Buffer
1411 _, err = f.WriteTo(&buf)
1412 require.NoError(t, err)
1413 assert.Equal(t, `[author]
13791414 E-MAIL = u@gogs.io
13801415
1381 `)
1382 })
1383
1384 Convey("Return an error when fail to load", func() {
1385 f, err := ini.LoadSources(ini.LoadOptions{ShortCircuit: true}, notFoundConf, minimalConf)
1386 So(f, ShouldBeNil)
1387 So(err, ShouldNotBeNil)
1388 })
1389
1390 Convey("Used with Loose to ignore errors that the file does not exist", func() {
1391 f, err := ini.LoadSources(ini.LoadOptions{ShortCircuit: true, Loose: true}, notFoundConf, minimalConf)
1392 So(f, ShouldNotBeNil)
1393 So(err, ShouldBeNil)
1394 var buf bytes.Buffer
1395 _, err = f.WriteTo(&buf)
1396 So(err, ShouldBeNil)
1397 So(buf.String(), ShouldEqual, `[author]
1416 `,
1417 buf.String(),
1418 )
1419 })
1420
1421 t.Run("return an error when fail to load", func(t *testing.T) {
1422 f, err := LoadSources(LoadOptions{ShortCircuit: true}, notFoundConf, minimalConf)
1423 assert.Nil(t, f)
1424 require.Error(t, err)
1425 })
1426
1427 t.Run("used with Loose to ignore errors that the file does not exist", func(t *testing.T) {
1428 f, err := LoadSources(LoadOptions{ShortCircuit: true, Loose: true}, notFoundConf, minimalConf)
1429 require.NotNil(t, f)
1430 require.NoError(t, err)
1431 var buf bytes.Buffer
1432 _, err = f.WriteTo(&buf)
1433 require.NoError(t, err)
1434 assert.Equal(t, `[author]
13981435 E-MAIL = u@gogs.io
13991436
1400 `)
1401 })
1402
1403 Convey("Ensure all sources are loaded without ShortCircuit", func() {
1404 f, err := ini.LoadSources(ini.LoadOptions{ShortCircuit: false}, minimalConf, []byte(`key1 = value1`))
1405 So(f, ShouldNotBeNil)
1406 So(err, ShouldBeNil)
1407 var buf bytes.Buffer
1408 _, err = f.WriteTo(&buf)
1409 So(err, ShouldBeNil)
1410 So(buf.String(), ShouldEqual, `key1 = value1
1437 `,
1438 buf.String(),
1439 )
1440 })
1441
1442 t.Run("ensure all sources are loaded without ShortCircuit", func(t *testing.T) {
1443 f, err := LoadSources(LoadOptions{ShortCircuit: false}, minimalConf, []byte(`key1 = value1`))
1444 require.NotNil(t, f)
1445 require.NoError(t, err)
1446 var buf bytes.Buffer
1447 _, err = f.WriteTo(&buf)
1448 require.NoError(t, err)
1449 assert.Equal(t, `key1 = value1
14111450
14121451 [author]
14131452 E-MAIL = u@gogs.io
14141453
1415 `)
1416 })
1454 `,
1455 buf.String(),
1456 )
14171457 })
14181458 })
14191459 }
14201460
14211461 func Test_KeyValueDelimiters(t *testing.T) {
1422 Convey("Custom key-value delimiters", t, func() {
1423 f, err := ini.LoadSources(ini.LoadOptions{
1462 t.Run("custom key-value delimiters", func(t *testing.T) {
1463 f, err := LoadSources(LoadOptions{
14241464 KeyValueDelimiters: "?!",
14251465 }, []byte(`
14261466 [section]
14271467 key1?value1
14281468 key2!value2
14291469 `))
1430 So(err, ShouldBeNil)
1431 So(f, ShouldNotBeNil)
1432
1433 So(f.Section("section").Key("key1").String(), ShouldEqual, "value1")
1434 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
1470 require.NoError(t, err)
1471 require.NotNil(t, f)
1472
1473 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1474 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
14351475 })
14361476 }
14371477
14381478 func Test_PreserveSurroundedQuote(t *testing.T) {
1439 Convey("Preserve surrounded quote test", t, func() {
1440 f, err := ini.LoadSources(ini.LoadOptions{
1479 t.Run("preserve surrounded quote test", func(t *testing.T) {
1480 f, err := LoadSources(LoadOptions{
14411481 PreserveSurroundedQuote: true,
14421482 }, []byte(`
14431483 [section]
14441484 key1 = "value1"
14451485 key2 = value2
14461486 `))
1447 So(err, ShouldBeNil)
1448 So(f, ShouldNotBeNil)
1449
1450 So(f.Section("section").Key("key1").String(), ShouldEqual, "\"value1\"")
1451 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
1487 require.NoError(t, err)
1488 require.NotNil(t, f)
1489
1490 assert.Equal(t, "\"value1\"", f.Section("section").Key("key1").String())
1491 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
14521492 })
14531493
1454 Convey("Preserve surrounded quote test inverse test", t, func() {
1455 f, err := ini.LoadSources(ini.LoadOptions{
1494 t.Run("preserve surrounded quote test inverse test", func(t *testing.T) {
1495 f, err := LoadSources(LoadOptions{
14561496 PreserveSurroundedQuote: false,
14571497 }, []byte(`
14581498 [section]
14591499 key1 = "value1"
14601500 key2 = value2
14611501 `))
1462 So(err, ShouldBeNil)
1463 So(f, ShouldNotBeNil)
1464
1465 So(f.Section("section").Key("key1").String(), ShouldEqual, "value1")
1466 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
1502 require.NoError(t, err)
1503 require.NotNil(t, f)
1504
1505 assert.Equal(t, "value1", f.Section("section").Key("key1").String())
1506 assert.Equal(t, "value2", f.Section("section").Key("key2").String())
14671507 })
14681508 }
1509
1510 type testData struct {
1511 Value1 string `ini:"value1"`
1512 Value2 string `ini:"value2"`
1513 Value3 string `ini:"value3"`
1514 }
1515
1516 func TestPythonMultiline(t *testing.T) {
1517 if runtime.GOOS == "windows" {
1518 t.Skip("Skipping testing on Windows")
1519 }
1520
1521 path := filepath.Join("testdata", "multiline.ini")
1522 f, err := LoadSources(LoadOptions{
1523 AllowPythonMultilineValues: true,
1524 ReaderBufferSize: 64 * 1024,
1525 }, path)
1526 require.NoError(t, err)
1527 require.NotNil(t, f)
1528 assert.Len(t, f.Sections(), 1)
1529
1530 defaultSection := f.Section("")
1531 assert.NotNil(t, f.Section(""))
1532
1533 var testData testData
1534 err = defaultSection.MapTo(&testData)
1535 require.NoError(t, err)
1536 assert.Equal(t, "some text here\nsome more text here\n\nthere is an empty line above and below\n", testData.Value1)
1537 assert.Equal(t, "there is an empty line above\nthat is not indented so it should not be part\nof the value", testData.Value2)
1538 assert.Equal(t, `.
1539
1540 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.
1541
1542 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.
1543
1544 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.
1545
1546 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.
1547
1548 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.
1549
1550 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.
1551
1552 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.
1553
1554 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.
1555
1556 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.
1557
1558 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.
1559
1560 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.`,
1561 testData.Value3,
1562 )
1563 }
5353 return errors.New("cannot add shadow to auto-increment or boolean key")
5454 }
5555
56 // Deduplicate shadows based on their values.
57 if k.value == val {
58 return nil
59 }
60 for i := range k.shadows {
61 if k.shadows[i].value == val {
56 if !k.s.f.options.AllowDuplicateShadowValues {
57 // Deduplicate shadows based on their values.
58 if k.value == val {
6259 return nil
60 }
61 for i := range k.shadows {
62 if k.shadows[i].value == val {
63 return nil
64 }
6365 }
6466 }
6567
780782 return vals, err
781783 }
782784
783
784785 type Parser func(str string) (interface{}, error)
785
786786
787787 // parseTimesFormat transforms strings to times in given format.
788788 func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
799799 }
800800 return vals, err
801801 }
802
803802
804803 // doParse transforms strings to different types
805804 func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) {
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "bytes"
2121 "testing"
2222 "time"
2323
24 . "github.com/smartystreets/goconvey/convey"
25 "gopkg.in/ini.v1"
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
2626 )
2727
2828 func TestKey_AddShadow(t *testing.T) {
29 Convey("Add shadow to a key", t, func() {
30 f, err := ini.ShadowLoad([]byte(`
29 t.Run("add shadow to a key", func(t *testing.T) {
30 f, err := ShadowLoad([]byte(`
3131 [notes]
3232 -: note1`))
33 So(err, ShouldBeNil)
34 So(f, ShouldNotBeNil)
33 require.NoError(t, err)
34 require.NotNil(t, f)
3535
3636 k, err := f.Section("").NewKey("NAME", "ini")
37 So(err, ShouldBeNil)
38 So(k, ShouldNotBeNil)
39
40 So(k.AddShadow("ini.v1"), ShouldBeNil)
41 So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"})
42
43 Convey("Add shadow to boolean key", func() {
37 require.NoError(t, err)
38 require.NotNil(t, k)
39
40 assert.NoError(t, k.AddShadow("ini.v1"))
41 assert.Equal(t, []string{"ini", "ini.v1"}, k.ValueWithShadows())
42
43 t.Run("add shadow to boolean key", func(t *testing.T) {
4444 k, err := f.Section("").NewBooleanKey("published")
45 So(err, ShouldBeNil)
46 So(k, ShouldNotBeNil)
47 So(k.AddShadow("beta"), ShouldNotBeNil)
48 })
49
50 Convey("Add shadow to auto-increment key", func() {
51 So(f.Section("notes").Key("#1").AddShadow("beta"), ShouldNotBeNil)
52 })
53 })
54
55 Convey("Shadow is not allowed", t, func() {
56 f := ini.Empty()
57 So(f, ShouldNotBeNil)
45 require.NoError(t, err)
46 require.NotNil(t, k)
47 assert.Error(t, k.AddShadow("beta"))
48 })
49
50 t.Run("add shadow to auto-increment key", func(t *testing.T) {
51 assert.Error(t, f.Section("notes").Key("#1").AddShadow("beta"))
52 })
53
54 t.Run("deduplicate an existing value", func(t *testing.T) {
55 k := f.Section("").Key("NAME")
56 assert.NoError(t, k.AddShadow("ini"))
57 assert.Equal(t, []string{"ini", "ini.v1"}, k.ValueWithShadows())
58 })
59 })
60
61 t.Run("allow duplicate shadowed values", func(t *testing.T) {
62 f := Empty(LoadOptions{
63 AllowShadows: true,
64 AllowDuplicateShadowValues: true,
65 })
66 require.NotNil(t, f)
5867
5968 k, err := f.Section("").NewKey("NAME", "ini")
60 So(err, ShouldBeNil)
61 So(k, ShouldNotBeNil)
62
63 So(k.AddShadow("ini.v1"), ShouldNotBeNil)
69 require.NoError(t, err)
70 require.NotNil(t, k)
71
72 assert.NoError(t, k.AddShadow("ini.v1"))
73 assert.NoError(t, k.AddShadow("ini"))
74 assert.NoError(t, k.AddShadow("ini"))
75 assert.Equal(t, []string{"ini", "ini.v1", "ini", "ini"}, k.ValueWithShadows())
76 })
77
78 t.Run("shadow is not allowed", func(t *testing.T) {
79 f := Empty()
80 require.NotNil(t, f)
81
82 k, err := f.Section("").NewKey("NAME", "ini")
83 require.NoError(t, err)
84 require.NotNil(t, k)
85
86 assert.Error(t, k.AddShadow("ini.v1"))
6487 })
6588 }
6689
6790 // Helpers for slice tests.
68 func float64sEqual(values []float64, expected ...float64) {
69 So(values, ShouldHaveLength, len(expected))
91 func float64sEqual(t *testing.T, values []float64, expected ...float64) {
92 t.Helper()
93
94 assert.Len(t, values, len(expected))
7095 for i, v := range expected {
71 So(values[i], ShouldEqual, v)
96 assert.Equal(t, v, values[i])
7297 }
7398 }
7499
75 func intsEqual(values []int, expected ...int) {
76 So(values, ShouldHaveLength, len(expected))
100 func intsEqual(t *testing.T, values []int, expected ...int) {
101 t.Helper()
102
103 assert.Len(t, values, len(expected))
77104 for i, v := range expected {
78 So(values[i], ShouldEqual, v)
105 assert.Equal(t, v, values[i])
79106 }
80107 }
81108
82 func int64sEqual(values []int64, expected ...int64) {
83 So(values, ShouldHaveLength, len(expected))
109 func int64sEqual(t *testing.T, values []int64, expected ...int64) {
110 t.Helper()
111
112 assert.Len(t, values, len(expected))
84113 for i, v := range expected {
85 So(values[i], ShouldEqual, v)
114 assert.Equal(t, v, values[i])
86115 }
87116 }
88117
89 func uintsEqual(values []uint, expected ...uint) {
90 So(values, ShouldHaveLength, len(expected))
118 func uintsEqual(t *testing.T, values []uint, expected ...uint) {
119 t.Helper()
120
121 assert.Len(t, values, len(expected))
91122 for i, v := range expected {
92 So(values[i], ShouldEqual, v)
123 assert.Equal(t, v, values[i])
93124 }
94125 }
95126
96 func uint64sEqual(values []uint64, expected ...uint64) {
97 So(values, ShouldHaveLength, len(expected))
127 func uint64sEqual(t *testing.T, values []uint64, expected ...uint64) {
128 t.Helper()
129
130 assert.Len(t, values, len(expected))
98131 for i, v := range expected {
99 So(values[i], ShouldEqual, v)
132 assert.Equal(t, v, values[i])
100133 }
101134 }
102135
103 func boolsEqual(values []bool, expected ...bool) {
104 So(values, ShouldHaveLength, len(expected))
136 func boolsEqual(t *testing.T, values []bool, expected ...bool) {
137 t.Helper()
138
139 assert.Len(t, values, len(expected))
105140 for i, v := range expected {
106 So(values[i], ShouldEqual, v)
141 assert.Equal(t, v, values[i])
107142 }
108143 }
109144
110 func timesEqual(values []time.Time, expected ...time.Time) {
111 So(values, ShouldHaveLength, len(expected))
145 func timesEqual(t *testing.T, values []time.Time, expected ...time.Time) {
146 t.Helper()
147
148 assert.Len(t, values, len(expected))
112149 for i, v := range expected {
113 So(values[i].String(), ShouldEqual, v.String())
150 assert.Equal(t, v.String(), values[i].String())
114151 }
115152 }
116153
117154 func TestKey_Helpers(t *testing.T) {
118 Convey("Getting and setting values", t, func() {
119 f, err := ini.Load(fullConf)
120 So(err, ShouldBeNil)
121 So(f, ShouldNotBeNil)
122
123 Convey("Get string representation", func() {
155 t.Run("getting and setting values", func(t *testing.T) {
156 f, err := Load(fullConf)
157 require.NoError(t, err)
158 require.NotNil(t, f)
159
160 t.Run("get string representation", func(t *testing.T) {
124161 sec := f.Section("")
125 So(sec, ShouldNotBeNil)
126 So(sec.Key("NAME").Value(), ShouldEqual, "ini")
127 So(sec.Key("NAME").String(), ShouldEqual, "ini")
128 So(sec.Key("NAME").Validate(func(in string) string {
162 require.NotNil(t, sec)
163 assert.Equal(t, "ini", sec.Key("NAME").Value())
164 assert.Equal(t, "ini", sec.Key("NAME").String())
165 assert.Equal(t, "ini", sec.Key("NAME").Validate(func(in string) string {
129166 return in
130 }), ShouldEqual, "ini")
131 So(sec.Key("NAME").Comment, ShouldEqual, "; Package name")
132 So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
133
134 Convey("With ValueMapper", func() {
167 }))
168 assert.Equal(t, "; Package name", sec.Key("NAME").Comment)
169 assert.Equal(t, "gopkg.in/ini.v1", sec.Key("IMPORT_PATH").String())
170
171 t.Run("with ValueMapper", func(t *testing.T) {
135172 f.ValueMapper = func(in string) string {
136173 if in == "gopkg.in/%(NAME)s.%(VERSION)s" {
137174 return "github.com/go-ini/ini"
138175 }
139176 return in
140177 }
141 So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "github.com/go-ini/ini")
178 assert.Equal(t, "github.com/go-ini/ini", sec.Key("IMPORT_PATH").String())
142179 })
143180 })
144181
145 Convey("Get values in non-default section", func() {
182 t.Run("get values in non-default section", func(t *testing.T) {
146183 sec := f.Section("author")
147 So(sec, ShouldNotBeNil)
148 So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
149 So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon")
184 require.NotNil(t, sec)
185 assert.Equal(t, "Unknwon", sec.Key("NAME").String())
186 assert.Equal(t, "https://github.com/Unknwon", sec.Key("GITHUB").String())
150187
151188 sec = f.Section("package")
152 So(sec, ShouldNotBeNil)
153 So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
154 })
155
156 Convey("Get auto-increment key names", func() {
189 require.NotNil(t, sec)
190 assert.Equal(t, "https://gopkg.in/ini.v1", sec.Key("CLONE_URL").String())
191 })
192
193 t.Run("get auto-increment key names", func(t *testing.T) {
157194 keys := f.Section("features").Keys()
158195 for i, k := range keys {
159 So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1))
196 assert.Equal(t, fmt.Sprintf("#%d", i+1), k.Name())
160197 }
161198 })
162199
163 Convey("Get parent-keys that are available to the child section", func() {
200 t.Run("get parent-keys that are available to the child section", func(t *testing.T) {
164201 parentKeys := f.Section("package.sub").ParentKeys()
165202 for _, k := range parentKeys {
166 So(k.Name(), ShouldEqual, "CLONE_URL")
203 assert.Equal(t, "CLONE_URL", k.Name())
167204 }
168205 })
169206
170 Convey("Get overwrite value", func() {
171 So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
172 })
173
174 Convey("Get sections", func() {
207 t.Run("get overwrite value", func(t *testing.T) {
208 assert.Equal(t, "u@gogs.io", f.Section("author").Key("E-MAIL").String())
209 })
210
211 t.Run("get sections", func(t *testing.T) {
175212 sections := f.Sections()
176 for i, name := range []string{ini.DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} {
177 So(sections[i].Name(), ShouldEqual, name)
213 for i, name := range []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} {
214 assert.Equal(t, name, sections[i].Name())
178215 }
179216 })
180217
181 Convey("Get parent section value", func() {
182 So(f.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
183 So(f.Section("package.fake.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
184 })
185
186 Convey("Get multiple line value", func() {
218 t.Run("get parent section value", func(t *testing.T) {
219 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package.sub").Key("CLONE_URL").String())
220 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package.fake.sub").Key("CLONE_URL").String())
221 })
222
223 t.Run("get multiple line value", func(t *testing.T) {
187224 if runtime.GOOS == "windows" {
188225 t.Skip("Skipping testing on Windows")
189226 }
190227
191 So(f.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n")
192 })
193
194 Convey("Get values with type", func() {
228 assert.Equal(t, "Gopher.\nCoding addict.\nGood man.\n", f.Section("author").Key("BIO").String())
229 })
230
231 t.Run("get values with type", func(t *testing.T) {
195232 sec := f.Section("types")
196233 v1, err := sec.Key("BOOL").Bool()
197 So(err, ShouldBeNil)
198 So(v1, ShouldBeTrue)
234 require.NoError(t, err)
235 assert.True(t, v1)
199236
200237 v1, err = sec.Key("BOOL_FALSE").Bool()
201 So(err, ShouldBeNil)
202 So(v1, ShouldBeFalse)
238 require.NoError(t, err)
239 assert.False(t, v1)
203240
204241 v2, err := sec.Key("FLOAT64").Float64()
205 So(err, ShouldBeNil)
206 So(v2, ShouldEqual, 1.25)
242 require.NoError(t, err)
243 assert.Equal(t, 1.25, v2)
207244
208245 v3, err := sec.Key("INT").Int()
209 So(err, ShouldBeNil)
210 So(v3, ShouldEqual, 10)
246 require.NoError(t, err)
247 assert.Equal(t, 10, v3)
211248
212249 v4, err := sec.Key("INT").Int64()
213 So(err, ShouldBeNil)
214 So(v4, ShouldEqual, 10)
250 require.NoError(t, err)
251 assert.Equal(t, int64(10), v4)
215252
216253 v5, err := sec.Key("UINT").Uint()
217 So(err, ShouldBeNil)
218 So(v5, ShouldEqual, 3)
254 require.NoError(t, err)
255 assert.Equal(t, uint(3), v5)
219256
220257 v6, err := sec.Key("UINT").Uint64()
221 So(err, ShouldBeNil)
222 So(v6, ShouldEqual, 3)
223
224 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
225 So(err, ShouldBeNil)
258 require.NoError(t, err)
259 assert.Equal(t, uint64(3), v6)
260
261 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
262 require.NoError(t, err)
226263 v7, err := sec.Key("TIME").Time()
227 So(err, ShouldBeNil)
228 So(v7.String(), ShouldEqual, t.String())
264 require.NoError(t, err)
265 assert.Equal(t, ti.String(), v7.String())
229266
230267 v8, err := sec.Key("HEX_NUMBER").Int()
231 So(err, ShouldBeNil)
232 So(v8, ShouldEqual, 0x3000)
233
234 Convey("Must get values with type", func() {
235 So(sec.Key("STRING").MustString("404"), ShouldEqual, "str")
236 So(sec.Key("BOOL").MustBool(), ShouldBeTrue)
237 So(sec.Key("FLOAT64").MustFloat64(), ShouldEqual, 1.25)
238 So(sec.Key("INT").MustInt(), ShouldEqual, 10)
239 So(sec.Key("INT").MustInt64(), ShouldEqual, 10)
240 So(sec.Key("UINT").MustUint(), ShouldEqual, 3)
241 So(sec.Key("UINT").MustUint64(), ShouldEqual, 3)
242 So(sec.Key("TIME").MustTime().String(), ShouldEqual, t.String())
243 So(sec.Key("HEX_NUMBER").MustInt(), ShouldEqual, 0x3000)
268 require.NoError(t, err)
269 assert.Equal(t, 0x3000, v8)
270
271 t.Run("must get values with type", func(t *testing.T) {
272 assert.Equal(t, "str", sec.Key("STRING").MustString("404"))
273 assert.True(t, sec.Key("BOOL").MustBool())
274 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").MustFloat64())
275 assert.Equal(t, int(10), sec.Key("INT").MustInt())
276 assert.Equal(t, int64(10), sec.Key("INT").MustInt64())
277 assert.Equal(t, uint(3), sec.Key("UINT").MustUint())
278 assert.Equal(t, uint64(3), sec.Key("UINT").MustUint64())
279 assert.Equal(t, ti.String(), sec.Key("TIME").MustTime().String())
280 assert.Equal(t, 0x3000, sec.Key("HEX_NUMBER").MustInt())
244281
245282 dur, err := time.ParseDuration("2h45m")
246 So(err, ShouldBeNil)
247 So(sec.Key("DURATION").MustDuration().Seconds(), ShouldEqual, dur.Seconds())
248
249 Convey("Must get values with default value", func() {
250 So(sec.Key("STRING_404").MustString("404"), ShouldEqual, "404")
251 So(sec.Key("BOOL_404").MustBool(true), ShouldBeTrue)
252 So(sec.Key("FLOAT64_404").MustFloat64(2.5), ShouldEqual, 2.5)
253 So(sec.Key("INT_404").MustInt(15), ShouldEqual, 15)
254 So(sec.Key("INT64_404").MustInt64(15), ShouldEqual, 15)
255 So(sec.Key("UINT_404").MustUint(6), ShouldEqual, 6)
256 So(sec.Key("UINT64_404").MustUint64(6), ShouldEqual, 6)
257 So(sec.Key("HEX_NUMBER_404").MustInt(0x3001), ShouldEqual, 0x3001)
258
259 t, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z")
260 So(err, ShouldBeNil)
261 So(sec.Key("TIME_404").MustTime(t).String(), ShouldEqual, t.String())
262
263 So(sec.Key("DURATION_404").MustDuration(dur).Seconds(), ShouldEqual, dur.Seconds())
264
265 Convey("Must should set default as key value", func() {
266 So(sec.Key("STRING_404").String(), ShouldEqual, "404")
267 So(sec.Key("BOOL_404").String(), ShouldEqual, "true")
268 So(sec.Key("FLOAT64_404").String(), ShouldEqual, "2.5")
269 So(sec.Key("INT_404").String(), ShouldEqual, "15")
270 So(sec.Key("INT64_404").String(), ShouldEqual, "15")
271 So(sec.Key("UINT_404").String(), ShouldEqual, "6")
272 So(sec.Key("UINT64_404").String(), ShouldEqual, "6")
273 So(sec.Key("TIME_404").String(), ShouldEqual, "2014-01-01T20:17:05Z")
274 So(sec.Key("DURATION_404").String(), ShouldEqual, "2h45m0s")
275 So(sec.Key("HEX_NUMBER_404").String(), ShouldEqual, "12289")
283 require.NoError(t, err)
284 assert.Equal(t, dur.Seconds(), sec.Key("DURATION").MustDuration().Seconds())
285
286 t.Run("must get values with default value", func(t *testing.T) {
287 assert.Equal(t, "404", sec.Key("STRING_404").MustString("404"))
288 assert.True(t, sec.Key("BOOL_404").MustBool(true))
289 assert.Equal(t, float64(2.5), sec.Key("FLOAT64_404").MustFloat64(2.5))
290 assert.Equal(t, int(15), sec.Key("INT_404").MustInt(15))
291 assert.Equal(t, int64(15), sec.Key("INT64_404").MustInt64(15))
292 assert.Equal(t, uint(6), sec.Key("UINT_404").MustUint(6))
293 assert.Equal(t, uint64(6), sec.Key("UINT64_404").MustUint64(6))
294 assert.Equal(t, 0x3001, sec.Key("HEX_NUMBER_404").MustInt(0x3001))
295
296 ti, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z")
297 require.NoError(t, err)
298 assert.Equal(t, ti.String(), sec.Key("TIME_404").MustTime(ti).String())
299
300 assert.Equal(t, dur.Seconds(), sec.Key("DURATION_404").MustDuration(dur).Seconds())
301
302 t.Run("must should set default as key value", func(t *testing.T) {
303 assert.Equal(t, "404", sec.Key("STRING_404").String())
304 assert.Equal(t, "true", sec.Key("BOOL_404").String())
305 assert.Equal(t, "2.5", sec.Key("FLOAT64_404").String())
306 assert.Equal(t, "15", sec.Key("INT_404").String())
307 assert.Equal(t, "15", sec.Key("INT64_404").String())
308 assert.Equal(t, "6", sec.Key("UINT_404").String())
309 assert.Equal(t, "6", sec.Key("UINT64_404").String())
310 assert.Equal(t, "2014-01-01T20:17:05Z", sec.Key("TIME_404").String())
311 assert.Equal(t, "2h45m0s", sec.Key("DURATION_404").String())
312 assert.Equal(t, "12289", sec.Key("HEX_NUMBER_404").String())
276313 })
277314 })
278315 })
279316 })
280317
281 Convey("Get value with candidates", func() {
318 t.Run("get value with candidates", func(t *testing.T) {
282319 sec := f.Section("types")
283 So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str")
284 So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
285 So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10)
286 So(sec.Key("INT").InInt64(0, []int64{10, 20, 30}), ShouldEqual, 10)
287 So(sec.Key("UINT").InUint(0, []uint{3, 6, 9}), ShouldEqual, 3)
288 So(sec.Key("UINT").InUint64(0, []uint64{3, 6, 9}), ShouldEqual, 3)
320 assert.Equal(t, "str", sec.Key("STRING").In("", []string{"str", "arr", "types"}))
321 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}))
322 assert.Equal(t, int(10), sec.Key("INT").InInt(0, []int{10, 20, 30}))
323 assert.Equal(t, int64(10), sec.Key("INT").InInt64(0, []int64{10, 20, 30}))
324 assert.Equal(t, uint(3), sec.Key("UINT").InUint(0, []uint{3, 6, 9}))
325 assert.Equal(t, uint64(3), sec.Key("UINT").InUint64(0, []uint64{3, 6, 9}))
289326
290327 zt, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
291 So(err, ShouldBeNil)
292 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
293 So(err, ShouldBeNil)
294 So(sec.Key("TIME").InTime(zt, []time.Time{t, time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String())
295
296 Convey("Get value with candidates and default value", func() {
297 So(sec.Key("STRING_404").In("str", []string{"str", "arr", "types"}), ShouldEqual, "str")
298 So(sec.Key("FLOAT64_404").InFloat64(1.25, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
299 So(sec.Key("INT_404").InInt(10, []int{10, 20, 30}), ShouldEqual, 10)
300 So(sec.Key("INT64_404").InInt64(10, []int64{10, 20, 30}), ShouldEqual, 10)
301 So(sec.Key("UINT_404").InUint(3, []uint{3, 6, 9}), ShouldEqual, 3)
302 So(sec.Key("UINT_404").InUint64(3, []uint64{3, 6, 9}), ShouldEqual, 3)
303 So(sec.Key("TIME_404").InTime(t, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String())
328 require.NoError(t, err)
329 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
330 require.NoError(t, err)
331 assert.Equal(t, ti.String(), sec.Key("TIME").InTime(zt, []time.Time{ti, time.Now(), time.Now().Add(1 * time.Second)}).String())
332
333 t.Run("get value with candidates and default value", func(t *testing.T) {
334 assert.Equal(t, "str", sec.Key("STRING_404_2").In("str", []string{"str", "arr", "types"}))
335 assert.Equal(t, float64(1.25), sec.Key("FLOAT64_404_2").InFloat64(1.25, []float64{1.25, 2.5, 3.75}))
336 assert.Equal(t, int(10), sec.Key("INT_404_2").InInt(10, []int{10, 20, 30}))
337 assert.Equal(t, int64(10), sec.Key("INT64_404_2").InInt64(10, []int64{10, 20, 30}))
338 assert.Equal(t, uint(3), sec.Key("UINT_404_2").InUint(3, []uint{3, 6, 9}))
339 assert.Equal(t, uint64(3), sec.Key("UINT_404_2").InUint64(3, []uint64{3, 6, 9}))
340 assert.Equal(t, ti.String(), sec.Key("TIME_404_2").InTime(ti, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String())
304341 })
305342 })
306343
307 Convey("Get values in range", func() {
344 t.Run("get values in range", func(t *testing.T) {
308345 sec := f.Section("types")
309 So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25)
310 So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10)
311 So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10)
346 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").RangeFloat64(0, 1, 2))
347 assert.Equal(t, int(10), sec.Key("INT").RangeInt(0, 10, 20))
348 assert.Equal(t, int64(10), sec.Key("INT").RangeInt64(0, 10, 20))
312349
313350 minT, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
314 So(err, ShouldBeNil)
351 require.NoError(t, err)
315352 midT, err := time.Parse(time.RFC3339, "2013-01-01T01:00:00Z")
316 So(err, ShouldBeNil)
353 require.NoError(t, err)
317354 maxT, err := time.Parse(time.RFC3339, "9999-01-01T01:00:00Z")
318 So(err, ShouldBeNil)
319 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
320 So(err, ShouldBeNil)
321 So(sec.Key("TIME").RangeTime(t, minT, maxT).String(), ShouldEqual, t.String())
322
323 Convey("Get value in range with default value", func() {
324 So(sec.Key("FLOAT64").RangeFloat64(5, 0, 1), ShouldEqual, 5)
325 So(sec.Key("INT").RangeInt(7, 0, 5), ShouldEqual, 7)
326 So(sec.Key("INT").RangeInt64(7, 0, 5), ShouldEqual, 7)
327 So(sec.Key("TIME").RangeTime(t, minT, midT).String(), ShouldEqual, t.String())
355 require.NoError(t, err)
356 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
357 require.NoError(t, err)
358 assert.Equal(t, ti.String(), sec.Key("TIME").RangeTime(ti, minT, maxT).String())
359
360 t.Run("get value in range with default value", func(t *testing.T) {
361 assert.Equal(t, float64(5), sec.Key("FLOAT64").RangeFloat64(5, 0, 1))
362 assert.Equal(t, 7, sec.Key("INT").RangeInt(7, 0, 5))
363 assert.Equal(t, int64(7), sec.Key("INT").RangeInt64(7, 0, 5))
364 assert.Equal(t, ti.String(), sec.Key("TIME").RangeTime(ti, minT, midT).String())
328365 })
329366 })
330367
331 Convey("Get values into slice", func() {
368 t.Run("get values into slice", func(t *testing.T) {
332369 sec := f.Section("array")
333 So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de")
334 So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0)
370 assert.Equal(t, "en,zh,de", strings.Join(sec.Key("STRINGS").Strings(","), ","))
371 assert.Equal(t, 0, len(sec.Key("STRINGS_404").Strings(",")))
335372
336373 vals1 := sec.Key("FLOAT64S").Float64s(",")
337 float64sEqual(vals1, 1.1, 2.2, 3.3)
374 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
338375
339376 vals2 := sec.Key("INTS").Ints(",")
340 intsEqual(vals2, 1, 2, 3)
377 intsEqual(t, vals2, 1, 2, 3)
341378
342379 vals3 := sec.Key("INTS").Int64s(",")
343 int64sEqual(vals3, 1, 2, 3)
380 int64sEqual(t, vals3, 1, 2, 3)
344381
345382 vals4 := sec.Key("UINTS").Uints(",")
346 uintsEqual(vals4, 1, 2, 3)
383 uintsEqual(t, vals4, 1, 2, 3)
347384
348385 vals5 := sec.Key("UINTS").Uint64s(",")
349 uint64sEqual(vals5, 1, 2, 3)
386 uint64sEqual(t, vals5, 1, 2, 3)
350387
351388 vals6 := sec.Key("BOOLS").Bools(",")
352 boolsEqual(vals6, true, false, false)
353
354 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
355 So(err, ShouldBeNil)
389 boolsEqual(t, vals6, true, false, false)
390
391 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
392 require.NoError(t, err)
356393 vals7 := sec.Key("TIMES").Times(",")
357 timesEqual(vals7, t, t, t)
358 })
359
360 Convey("Test string slice escapes", func() {
394 timesEqual(t, vals7, ti, ti, ti)
395 })
396
397 t.Run("test string slice escapes", func(t *testing.T) {
361398 sec := f.Section("string escapes")
362 So(sec.Key("key1").Strings(","), ShouldResemble, []string{"value1", "value2", "value3"})
363 So(sec.Key("key2").Strings(","), ShouldResemble, []string{"value1, value2"})
364 So(sec.Key("key3").Strings(","), ShouldResemble, []string{`val\ue1`, "value2"})
365 So(sec.Key("key4").Strings(","), ShouldResemble, []string{`value1\`, `value\\2`})
366 So(sec.Key("key5").Strings(",,"), ShouldResemble, []string{"value1,, value2"})
367 So(sec.Key("key6").Strings(" "), ShouldResemble, []string{"aaa", "bbb and space", "ccc"})
368 })
369
370 Convey("Get valid values into slice", func() {
399 assert.Equal(t, []string{"value1", "value2", "value3"}, sec.Key("key1").Strings(","))
400 assert.Equal(t, []string{"value1, value2"}, sec.Key("key2").Strings(","))
401 assert.Equal(t, []string{`val\ue1`, "value2"}, sec.Key("key3").Strings(","))
402 assert.Equal(t, []string{`value1\`, `value\\2`}, sec.Key("key4").Strings(","))
403 assert.Equal(t, []string{"value1,, value2"}, sec.Key("key5").Strings(",,"))
404 assert.Equal(t, []string{"aaa", "bbb and space", "ccc"}, sec.Key("key6").Strings(" "))
405 })
406
407 t.Run("get valid values into slice", func(t *testing.T) {
371408 sec := f.Section("array")
372409 vals1 := sec.Key("FLOAT64S").ValidFloat64s(",")
373 float64sEqual(vals1, 1.1, 2.2, 3.3)
410 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
374411
375412 vals2 := sec.Key("INTS").ValidInts(",")
376 intsEqual(vals2, 1, 2, 3)
413 intsEqual(t, vals2, 1, 2, 3)
377414
378415 vals3 := sec.Key("INTS").ValidInt64s(",")
379 int64sEqual(vals3, 1, 2, 3)
416 int64sEqual(t, vals3, 1, 2, 3)
380417
381418 vals4 := sec.Key("UINTS").ValidUints(",")
382 uintsEqual(vals4, 1, 2, 3)
419 uintsEqual(t, vals4, 1, 2, 3)
383420
384421 vals5 := sec.Key("UINTS").ValidUint64s(",")
385 uint64sEqual(vals5, 1, 2, 3)
422 uint64sEqual(t, vals5, 1, 2, 3)
386423
387424 vals6 := sec.Key("BOOLS").ValidBools(",")
388 boolsEqual(vals6, true, false, false)
389
390 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
391 So(err, ShouldBeNil)
425 boolsEqual(t, vals6, true, false, false)
426
427 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
428 require.NoError(t, err)
392429 vals7 := sec.Key("TIMES").ValidTimes(",")
393 timesEqual(vals7, t, t, t)
394 })
395
396 Convey("Get values one type into slice of another type", func() {
430 timesEqual(t, vals7, ti, ti, ti)
431 })
432
433 t.Run("get values one type into slice of another type", func(t *testing.T) {
397434 sec := f.Section("array")
398435 vals1 := sec.Key("STRINGS").ValidFloat64s(",")
399 So(vals1, ShouldBeEmpty)
436 assert.Empty(t, vals1)
400437
401438 vals2 := sec.Key("STRINGS").ValidInts(",")
402 So(vals2, ShouldBeEmpty)
439 assert.Empty(t, vals2)
403440
404441 vals3 := sec.Key("STRINGS").ValidInt64s(",")
405 So(vals3, ShouldBeEmpty)
442 assert.Empty(t, vals3)
406443
407444 vals4 := sec.Key("STRINGS").ValidUints(",")
408 So(vals4, ShouldBeEmpty)
445 assert.Empty(t, vals4)
409446
410447 vals5 := sec.Key("STRINGS").ValidUint64s(",")
411 So(vals5, ShouldBeEmpty)
448 assert.Empty(t, vals5)
412449
413450 vals6 := sec.Key("STRINGS").ValidBools(",")
414 So(vals6, ShouldBeEmpty)
451 assert.Empty(t, vals6)
415452
416453 vals7 := sec.Key("STRINGS").ValidTimes(",")
417 So(vals7, ShouldBeEmpty)
418 })
419
420 Convey("Get valid values into slice without errors", func() {
454 assert.Empty(t, vals7)
455 })
456
457 t.Run("get valid values into slice without errors", func(t *testing.T) {
421458 sec := f.Section("array")
422459 vals1, err := sec.Key("FLOAT64S").StrictFloat64s(",")
423 So(err, ShouldBeNil)
424 float64sEqual(vals1, 1.1, 2.2, 3.3)
460 require.NoError(t, err)
461 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
425462
426463 vals2, err := sec.Key("INTS").StrictInts(",")
427 So(err, ShouldBeNil)
428 intsEqual(vals2, 1, 2, 3)
464 require.NoError(t, err)
465 intsEqual(t, vals2, 1, 2, 3)
429466
430467 vals3, err := sec.Key("INTS").StrictInt64s(",")
431 So(err, ShouldBeNil)
432 int64sEqual(vals3, 1, 2, 3)
468 require.NoError(t, err)
469 int64sEqual(t, vals3, 1, 2, 3)
433470
434471 vals4, err := sec.Key("UINTS").StrictUints(",")
435 So(err, ShouldBeNil)
436 uintsEqual(vals4, 1, 2, 3)
472 require.NoError(t, err)
473 uintsEqual(t, vals4, 1, 2, 3)
437474
438475 vals5, err := sec.Key("UINTS").StrictUint64s(",")
439 So(err, ShouldBeNil)
440 uint64sEqual(vals5, 1, 2, 3)
476 require.NoError(t, err)
477 uint64sEqual(t, vals5, 1, 2, 3)
441478
442479 vals6, err := sec.Key("BOOLS").StrictBools(",")
443 So(err, ShouldBeNil)
444 boolsEqual(vals6, true, false, false)
445
446 t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
447 So(err, ShouldBeNil)
480 require.NoError(t, err)
481 boolsEqual(t, vals6, true, false, false)
482
483 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
484 require.NoError(t, err)
448485 vals7, err := sec.Key("TIMES").StrictTimes(",")
449 So(err, ShouldBeNil)
450 timesEqual(vals7, t, t, t)
451 })
452
453 Convey("Get invalid values into slice", func() {
486 require.NoError(t, err)
487 timesEqual(t, vals7, ti, ti, ti)
488 })
489
490 t.Run("get invalid values into slice", func(t *testing.T) {
454491 sec := f.Section("array")
455492 vals1, err := sec.Key("STRINGS").StrictFloat64s(",")
456 So(vals1, ShouldBeEmpty)
457 So(err, ShouldNotBeNil)
493 assert.Empty(t, vals1)
494 assert.Error(t, err)
458495
459496 vals2, err := sec.Key("STRINGS").StrictInts(",")
460 So(vals2, ShouldBeEmpty)
461 So(err, ShouldNotBeNil)
497 assert.Empty(t, vals2)
498 assert.Error(t, err)
462499
463500 vals3, err := sec.Key("STRINGS").StrictInt64s(",")
464 So(vals3, ShouldBeEmpty)
465 So(err, ShouldNotBeNil)
501 assert.Empty(t, vals3)
502 assert.Error(t, err)
466503
467504 vals4, err := sec.Key("STRINGS").StrictUints(",")
468 So(vals4, ShouldBeEmpty)
469 So(err, ShouldNotBeNil)
505 assert.Empty(t, vals4)
506 assert.Error(t, err)
470507
471508 vals5, err := sec.Key("STRINGS").StrictUint64s(",")
472 So(vals5, ShouldBeEmpty)
473 So(err, ShouldNotBeNil)
509 assert.Empty(t, vals5)
510 assert.Error(t, err)
474511
475512 vals6, err := sec.Key("STRINGS").StrictBools(",")
476 So(vals6, ShouldBeEmpty)
477 So(err, ShouldNotBeNil)
513 assert.Empty(t, vals6)
514 assert.Error(t, err)
478515
479516 vals7, err := sec.Key("STRINGS").StrictTimes(",")
480 So(vals7, ShouldBeEmpty)
481 So(err, ShouldNotBeNil)
517 assert.Empty(t, vals7)
518 assert.Error(t, err)
482519 })
483520 })
484521 }
485522
486523 func TestKey_StringsWithShadows(t *testing.T) {
487 Convey("Get strings of shadows of a key", t, func() {
488 f, err := ini.ShadowLoad([]byte(""))
489 So(err, ShouldBeNil)
490 So(f, ShouldNotBeNil)
524 t.Run("get strings of shadows of a key", func(t *testing.T) {
525 f, err := ShadowLoad([]byte(""))
526 require.NoError(t, err)
527 require.NotNil(t, f)
491528
492529 k, err := f.Section("").NewKey("NUMS", "1,2")
493 So(err, ShouldBeNil)
494 So(k, ShouldNotBeNil)
530 require.NoError(t, err)
531 require.NotNil(t, k)
495532 k, err = f.Section("").NewKey("NUMS", "4,5,6")
496 So(err, ShouldBeNil)
497 So(k, ShouldNotBeNil)
498
499 So(k.StringsWithShadows(","), ShouldResemble, []string{"1", "2", "4", "5", "6"})
533 require.NoError(t, err)
534 require.NotNil(t, k)
535
536 assert.Equal(t, []string{"1", "2", "4", "5", "6"}, k.StringsWithShadows(","))
500537 })
501538 }
502539
503540 func TestKey_SetValue(t *testing.T) {
504 Convey("Set value of key", t, func() {
505 f := ini.Empty()
506 So(f, ShouldNotBeNil)
541 t.Run("set value of key", func(t *testing.T) {
542 f := Empty()
543 require.NotNil(t, f)
507544
508545 k, err := f.Section("").NewKey("NAME", "ini")
509 So(err, ShouldBeNil)
510 So(k, ShouldNotBeNil)
511 So(k.Value(), ShouldEqual, "ini")
546 require.NoError(t, err)
547 require.NotNil(t, k)
548 assert.Equal(t, "ini", k.Value())
512549
513550 k.SetValue("ini.v1")
514 So(k.Value(), ShouldEqual, "ini.v1")
551 assert.Equal(t, "ini.v1", k.Value())
515552 })
516553 }
517554
518555 func TestKey_NestedValues(t *testing.T) {
519 Convey("Read and write nested values", t, func() {
520 f, err := ini.LoadSources(ini.LoadOptions{
556 t.Run("read and write nested values", func(t *testing.T) {
557 f, err := LoadSources(LoadOptions{
521558 AllowNestedValues: true,
522559 }, []byte(`
523560 aws_access_key_id = foo
526563 s3 =
527564 max_concurrent_requests=10
528565 max_queue_size=1000`))
529 So(err, ShouldBeNil)
530 So(f, ShouldNotBeNil)
531
532 So(f.Section("").Key("s3").NestedValues(), ShouldResemble, []string{"max_concurrent_requests=10", "max_queue_size=1000"})
566 require.NoError(t, err)
567 require.NotNil(t, f)
568
569 assert.Equal(t, []string{"max_concurrent_requests=10", "max_queue_size=1000"}, f.Section("").Key("s3").NestedValues())
533570
534571 var buf bytes.Buffer
535572 _, err = f.WriteTo(&buf)
536 So(err, ShouldBeNil)
537 So(buf.String(), ShouldEqual, `aws_access_key_id = foo
573 require.NoError(t, err)
574 assert.Equal(t, `aws_access_key_id = foo
538575 aws_secret_access_key = bar
539576 region = us-west-2
540577 s3 =
541578 max_concurrent_requests=10
542579 max_queue_size=1000
543580
544 `)
581 `,
582 buf.String(),
583 )
545584 })
546585 }
547586
548587 func TestRecursiveValues(t *testing.T) {
549 Convey("Recursive values should not reflect on same key", t, func() {
550 f, err := ini.Load([]byte(`
588 t.Run("recursive values should not reflect on same key", func(t *testing.T) {
589 f, err := Load([]byte(`
551590 NAME = ini
552591 expires = yes
553592 [package]
554593 NAME = %(NAME)s
555594 expires = %(expires)s`))
556 So(err, ShouldBeNil)
557 So(f, ShouldNotBeNil)
558
559 So(f.Section("package").Key("NAME").String(), ShouldEqual, "ini")
560 So(f.Section("package").Key("expires").String(), ShouldEqual, "yes")
561 })
562
563 Convey("Recursive value with no target found", t, func() {
564 f, err := ini.Load([]byte(`
595 require.NoError(t, err)
596 require.NotNil(t, f)
597
598 assert.Equal(t, "ini", f.Section("package").Key("NAME").String())
599 assert.Equal(t, "yes", f.Section("package").Key("expires").String())
600 })
601
602 t.Run("recursive value with no target found", func(t *testing.T) {
603 f, err := Load([]byte(`
565604 [foo]
566605 bar = %(missing)s
567606 `))
568 So(err, ShouldBeNil)
569 So(f, ShouldNotBeNil)
570
571 So(f.Section("foo").Key("bar").String(), ShouldEqual, "%(missing)s")
572 })
573 }
607 require.NoError(t, err)
608 require.NotNil(t, f)
609
610 assert.Equal(t, "%(missing)s", f.Section("foo").Key("bar").String())
611 })
612 }
130130 // Check if key name surrounded by quotes.
131131 var keyQuote string
132132 if line[0] == '"' {
133 if len(line) > 6 && string(line[0:3]) == `"""` {
133 if len(line) > 6 && line[0:3] == `"""` {
134134 keyQuote = `"""`
135135 } else {
136136 keyQuote = `"`
231231 }
232232
233233 var valQuote string
234 if len(line) > 3 && string(line[0:3]) == `"""` {
234 if len(line) > 3 && line[0:3] == `"""` {
235235 valQuote = `"""`
236236 } else if line[0] == '`' {
237237 valQuote = "`"
288288 hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
289289 line = line[1 : len(line)-1]
290290 } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
291 if strings.Contains(line, `\;`) {
292 line = strings.Replace(line, `\;`, ";", -1)
293 }
294 if strings.Contains(line, `\#`) {
295 line = strings.Replace(line, `\#`, "#", -1)
296 }
291 line = strings.ReplaceAll(line, `\;`, ";")
292 line = strings.ReplaceAll(line, `\#`, "#")
297293 } else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
298294 return p.readPythonMultilines(line, bufferSize)
299295 }
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "testing"
1818
19 . "github.com/smartystreets/goconvey/convey"
20 "gopkg.in/ini.v1"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
2121 )
2222
2323 func TestBOM(t *testing.T) {
24 Convey("Test handling BOM", t, func() {
25 Convey("UTF-8-BOM", func() {
26 f, err := ini.Load("testdata/UTF-8-BOM.ini")
27 So(err, ShouldBeNil)
28 So(f, ShouldNotBeNil)
24 t.Run("test handling BOM", func(t *testing.T) {
25 t.Run("UTF-8-BOM", func(t *testing.T) {
26 f, err := Load("testdata/UTF-8-BOM.ini")
27 require.NoError(t, err)
28 require.NotNil(t, f)
2929
30 So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "example@email.com")
30 assert.Equal(t, "example@email.com", f.Section("author").Key("E-MAIL").String())
3131 })
3232
33 Convey("UTF-16-LE-BOM", func() {
34 f, err := ini.Load("testdata/UTF-16-LE-BOM.ini")
35 So(err, ShouldBeNil)
36 So(f, ShouldNotBeNil)
33 t.Run("UTF-16-LE-BOM", func(t *testing.T) {
34 f, err := Load("testdata/UTF-16-LE-BOM.ini")
35 require.NoError(t, err)
36 require.NotNil(t, f)
3737 })
3838
39 Convey("UTF-16-BE-BOM", func() {
39 t.Run("UTF-16-BE-BOM", func(t *testing.T) {
4040 })
4141 })
4242 }
4343
4444 func TestBadLoad(t *testing.T) {
45 Convey("Load with bad data", t, func() {
46 Convey("Bad section name", func() {
47 _, err := ini.Load([]byte("[]"))
48 So(err, ShouldNotBeNil)
45 t.Run("load with bad data", func(t *testing.T) {
46 t.Run("bad section name", func(t *testing.T) {
47 _, err := Load([]byte("[]"))
48 require.Error(t, err)
4949
50 _, err = ini.Load([]byte("["))
51 So(err, ShouldNotBeNil)
50 _, err = Load([]byte("["))
51 require.Error(t, err)
5252 })
5353
54 Convey("Bad keys", func() {
55 _, err := ini.Load([]byte(`"""name`))
56 So(err, ShouldNotBeNil)
54 t.Run("bad keys", func(t *testing.T) {
55 _, err := Load([]byte(`"""name`))
56 require.Error(t, err)
5757
58 _, err = ini.Load([]byte(`"""name"""`))
59 So(err, ShouldNotBeNil)
58 _, err = Load([]byte(`"""name"""`))
59 require.Error(t, err)
6060
61 _, err = ini.Load([]byte(`""=1`))
62 So(err, ShouldNotBeNil)
61 _, err = Load([]byte(`""=1`))
62 require.Error(t, err)
6363
64 _, err = ini.Load([]byte(`=`))
65 So(err, ShouldNotBeNil)
64 _, err = Load([]byte(`=`))
65 require.Error(t, err)
6666
67 _, err = ini.Load([]byte(`name`))
68 So(err, ShouldNotBeNil)
67 _, err = Load([]byte(`name`))
68 require.Error(t, err)
6969 })
7070
71 Convey("Bad values", func() {
72 _, err := ini.Load([]byte(`name="""Unknwon`))
73 So(err, ShouldNotBeNil)
71 t.Run("bad values", func(t *testing.T) {
72 _, err := Load([]byte(`name="""Unknwon`))
73 require.Error(t, err)
7474 })
7575 })
7676 }
216216 defer s.f.lock.RUnlock()
217217 }
218218
219 hash := map[string]string{}
219 hash := make(map[string]string, len(s.keysHash))
220220 for key, value := range s.keysHash {
221221 hash[key] = value
222222 }
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "testing"
1818
19 . "github.com/smartystreets/goconvey/convey"
20 "gopkg.in/ini.v1"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
2121 )
2222
2323 func TestSection_SetBody(t *testing.T) {
24 Convey("Set body of raw section", t, func() {
25 f := ini.Empty()
26 So(f, ShouldNotBeNil)
24 t.Run("set body of raw section", func(t *testing.T) {
25 f := Empty()
26 require.NotNil(t, f)
2727
2828 sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
2929 111111111111111111100000000000111000000000`)
30 So(err, ShouldBeNil)
31 So(sec, ShouldNotBeNil)
32 So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000
33 111111111111111111100000000000111000000000`)
30 require.NoError(t, err)
31 require.NotNil(t, sec)
32 assert.Equal(t, `1111111111111111111000000000000000001110000
33 111111111111111111100000000000111000000000`, sec.Body())
3434
3535 sec.SetBody("1111111111111111111000000000000000001110000")
36 So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000`)
37
38 Convey("Set for non-raw section", func() {
36 assert.Equal(t, `1111111111111111111000000000000000001110000`, sec.Body())
37
38 t.Run("set for non-raw section", func(t *testing.T) {
3939 sec, err := f.NewSection("author")
40 So(err, ShouldBeNil)
41 So(sec, ShouldNotBeNil)
42 So(sec.Body(), ShouldBeEmpty)
40 require.NoError(t, err)
41 require.NotNil(t, sec)
42 assert.Empty(t, sec.Body())
4343
4444 sec.SetBody("1111111111111111111000000000000000001110000")
45 So(sec.Body(), ShouldBeEmpty)
45 assert.Empty(t, sec.Body())
4646 })
4747 })
4848 }
4949
5050 func TestSection_NewKey(t *testing.T) {
51 Convey("Create a new key", t, func() {
52 f := ini.Empty()
53 So(f, ShouldNotBeNil)
54
55 k, err := f.Section("").NewKey("NAME", "ini")
56 So(err, ShouldBeNil)
57 So(k, ShouldNotBeNil)
58 So(k.Name(), ShouldEqual, "NAME")
59 So(k.Value(), ShouldEqual, "ini")
60
61 Convey("With duplicated name", func() {
51 t.Run("create a new key", func(t *testing.T) {
52 f := Empty()
53 require.NotNil(t, f)
54
55 k, err := f.Section("").NewKey("NAME", "ini")
56 require.NoError(t, err)
57 require.NotNil(t, k)
58 assert.Equal(t, "NAME", k.Name())
59 assert.Equal(t, "ini", k.Value())
60
61 t.Run("with duplicated name", func(t *testing.T) {
6262 k, err := f.Section("").NewKey("NAME", "ini.v1")
63 So(err, ShouldBeNil)
64 So(k, ShouldNotBeNil)
63 require.NoError(t, err)
64 require.NotNil(t, k)
6565
6666 // Overwrite previous existed key
67 So(k.Value(), ShouldEqual, "ini.v1")
68 })
69
70 Convey("With empty string", func() {
67 assert.Equal(t, "ini.v1", k.Value())
68 })
69
70 t.Run("with empty string", func(t *testing.T) {
7171 _, err := f.Section("").NewKey("", "")
72 So(err, ShouldNotBeNil)
73 })
74 })
75
76 Convey("Create keys with same name and allow shadow", t, func() {
77 f, err := ini.ShadowLoad([]byte(""))
78 So(err, ShouldBeNil)
79 So(f, ShouldNotBeNil)
80
81 k, err := f.Section("").NewKey("NAME", "ini")
82 So(err, ShouldBeNil)
83 So(k, ShouldNotBeNil)
72 require.Error(t, err)
73 })
74 })
75
76 t.Run("create keys with same name and allow shadow", func(t *testing.T) {
77 f, err := ShadowLoad([]byte(""))
78 require.NoError(t, err)
79 require.NotNil(t, f)
80
81 k, err := f.Section("").NewKey("NAME", "ini")
82 require.NoError(t, err)
83 require.NotNil(t, k)
8484 k, err = f.Section("").NewKey("NAME", "ini.v1")
85 So(err, ShouldBeNil)
86 So(k, ShouldNotBeNil)
87
88 So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"})
85 require.NoError(t, err)
86 require.NotNil(t, k)
87
88 assert.Equal(t, []string{"ini", "ini.v1"}, k.ValueWithShadows())
8989 })
9090 }
9191
9292 func TestSection_NewBooleanKey(t *testing.T) {
93 Convey("Create a new boolean key", t, func() {
94 f := ini.Empty()
95 So(f, ShouldNotBeNil)
93 t.Run("create a new boolean key", func(t *testing.T) {
94 f := Empty()
95 require.NotNil(t, f)
9696
9797 k, err := f.Section("").NewBooleanKey("start-ssh-server")
98 So(err, ShouldBeNil)
99 So(k, ShouldNotBeNil)
100 So(k.Name(), ShouldEqual, "start-ssh-server")
101 So(k.Value(), ShouldEqual, "true")
102
103 Convey("With empty string", func() {
98 require.NoError(t, err)
99 require.NotNil(t, k)
100 assert.Equal(t, "start-ssh-server", k.Name())
101 assert.Equal(t, "true", k.Value())
102
103 t.Run("with empty string", func(t *testing.T) {
104104 _, err := f.Section("").NewBooleanKey("")
105 So(err, ShouldNotBeNil)
105 require.Error(t, err)
106106 })
107107 })
108108 }
109109
110110 func TestSection_GetKey(t *testing.T) {
111 Convey("Get a key", t, func() {
112 f := ini.Empty()
113 So(f, ShouldNotBeNil)
114
115 k, err := f.Section("").NewKey("NAME", "ini")
116 So(err, ShouldBeNil)
117 So(k, ShouldNotBeNil)
111 t.Run("get a key", func(t *testing.T) {
112 f := Empty()
113 require.NotNil(t, f)
114
115 k, err := f.Section("").NewKey("NAME", "ini")
116 require.NoError(t, err)
117 require.NotNil(t, k)
118118
119119 k, err = f.Section("").GetKey("NAME")
120 So(err, ShouldBeNil)
121 So(k, ShouldNotBeNil)
122 So(k.Name(), ShouldEqual, "NAME")
123 So(k.Value(), ShouldEqual, "ini")
124
125 Convey("Key not exists", func() {
120 require.NoError(t, err)
121 require.NotNil(t, k)
122 assert.Equal(t, "NAME", k.Name())
123 assert.Equal(t, "ini", k.Value())
124
125 t.Run("key not exists", func(t *testing.T) {
126126 _, err := f.Section("").GetKey("404")
127 So(err, ShouldNotBeNil)
128 })
129
130 Convey("Key exists in parent section", func() {
127 require.Error(t, err)
128 })
129
130 t.Run("key exists in parent section", func(t *testing.T) {
131131 k, err := f.Section("parent").NewKey("AGE", "18")
132 So(err, ShouldBeNil)
133 So(k, ShouldNotBeNil)
132 require.NoError(t, err)
133 require.NotNil(t, k)
134134
135135 k, err = f.Section("parent.child.son").GetKey("AGE")
136 So(err, ShouldBeNil)
137 So(k, ShouldNotBeNil)
138 So(k.Value(), ShouldEqual, "18")
136 require.NoError(t, err)
137 require.NotNil(t, k)
138 assert.Equal(t, "18", k.Value())
139139 })
140140 })
141141 }
142142
143143 func TestSection_HasKey(t *testing.T) {
144 Convey("Check if a key exists", t, func() {
145 f := ini.Empty()
146 So(f, ShouldNotBeNil)
147
148 k, err := f.Section("").NewKey("NAME", "ini")
149 So(err, ShouldBeNil)
150 So(k, ShouldNotBeNil)
151
152 So(f.Section("").HasKey("NAME"), ShouldBeTrue)
153 So(f.Section("").HasKey("NAME"), ShouldBeTrue)
154 So(f.Section("").HasKey("404"), ShouldBeFalse)
155 So(f.Section("").HasKey("404"), ShouldBeFalse)
144 t.Run("check if a key exists", func(t *testing.T) {
145 f := Empty()
146 require.NotNil(t, f)
147
148 k, err := f.Section("").NewKey("NAME", "ini")
149 require.NoError(t, err)
150 require.NotNil(t, k)
151
152 assert.True(t, f.Section("").HasKey("NAME"))
153 assert.True(t, f.Section("").HasKey("NAME"))
154 assert.False(t, f.Section("").HasKey("404"))
155 assert.False(t, f.Section("").HasKey("404"))
156156 })
157157 }
158158
159159 func TestSection_HasValue(t *testing.T) {
160 Convey("Check if contains a value in any key", t, func() {
161 f := ini.Empty()
162 So(f, ShouldNotBeNil)
163
164 k, err := f.Section("").NewKey("NAME", "ini")
165 So(err, ShouldBeNil)
166 So(k, ShouldNotBeNil)
167
168 So(f.Section("").HasValue("ini"), ShouldBeTrue)
169 So(f.Section("").HasValue("404"), ShouldBeFalse)
160 t.Run("check if contains a value in any key", func(t *testing.T) {
161 f := Empty()
162 require.NotNil(t, f)
163
164 k, err := f.Section("").NewKey("NAME", "ini")
165 require.NoError(t, err)
166 require.NotNil(t, k)
167
168 assert.True(t, f.Section("").HasValue("ini"))
169 assert.False(t, f.Section("").HasValue("404"))
170170 })
171171 }
172172
173173 func TestSection_Key(t *testing.T) {
174 Convey("Get a key", t, func() {
175 f := ini.Empty()
176 So(f, ShouldNotBeNil)
177
178 k, err := f.Section("").NewKey("NAME", "ini")
179 So(err, ShouldBeNil)
180 So(k, ShouldNotBeNil)
174 t.Run("get a key", func(t *testing.T) {
175 f := Empty()
176 require.NotNil(t, f)
177
178 k, err := f.Section("").NewKey("NAME", "ini")
179 require.NoError(t, err)
180 require.NotNil(t, k)
181181
182182 k = f.Section("").Key("NAME")
183 So(k, ShouldNotBeNil)
184 So(k.Name(), ShouldEqual, "NAME")
185 So(k.Value(), ShouldEqual, "ini")
186
187 Convey("Key not exists", func() {
183 require.NotNil(t, k)
184 assert.Equal(t, "NAME", k.Name())
185 assert.Equal(t, "ini", k.Value())
186
187 t.Run("key not exists", func(t *testing.T) {
188188 k := f.Section("").Key("404")
189 So(k, ShouldNotBeNil)
190 So(k.Name(), ShouldEqual, "404")
191 })
192
193 Convey("Key exists in parent section", func() {
189 require.NotNil(t, k)
190 assert.Equal(t, "404", k.Name())
191 })
192
193 t.Run("key exists in parent section", func(t *testing.T) {
194194 k, err := f.Section("parent").NewKey("AGE", "18")
195 So(err, ShouldBeNil)
196 So(k, ShouldNotBeNil)
195 require.NoError(t, err)
196 require.NotNil(t, k)
197197
198198 k = f.Section("parent.child.son").Key("AGE")
199 So(k, ShouldNotBeNil)
200 So(k.Value(), ShouldEqual, "18")
199 require.NotNil(t, k)
200 assert.Equal(t, "18", k.Value())
201201 })
202202 })
203203 }
204204
205205 func TestSection_Keys(t *testing.T) {
206 Convey("Get all keys in a section", t, func() {
207 f := ini.Empty()
208 So(f, ShouldNotBeNil)
209
210 k, err := f.Section("").NewKey("NAME", "ini")
211 So(err, ShouldBeNil)
212 So(k, ShouldNotBeNil)
206 t.Run("get all keys in a section", func(t *testing.T) {
207 f := Empty()
208 require.NotNil(t, f)
209
210 k, err := f.Section("").NewKey("NAME", "ini")
211 require.NoError(t, err)
212 require.NotNil(t, k)
213213 k, err = f.Section("").NewKey("VERSION", "v1")
214 So(err, ShouldBeNil)
215 So(k, ShouldNotBeNil)
214 require.NoError(t, err)
215 require.NotNil(t, k)
216216 k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
217 So(err, ShouldBeNil)
218 So(k, ShouldNotBeNil)
217 require.NoError(t, err)
218 require.NotNil(t, k)
219219
220220 keys := f.Section("").Keys()
221221 names := []string{"NAME", "VERSION", "IMPORT_PATH"}
222 So(len(keys), ShouldEqual, len(names))
222 assert.Equal(t, len(names), len(keys))
223223 for i, name := range names {
224 So(keys[i].Name(), ShouldEqual, name)
224 assert.Equal(t, name, keys[i].Name())
225225 }
226226 })
227227 }
228228
229229 func TestSection_ParentKeys(t *testing.T) {
230 Convey("Get all keys of parent sections", t, func() {
231 f := ini.Empty()
232 So(f, ShouldNotBeNil)
230 t.Run("get all keys of parent sections", func(t *testing.T) {
231 f := Empty()
232 require.NotNil(t, f)
233233
234234 k, err := f.Section("package").NewKey("NAME", "ini")
235 So(err, ShouldBeNil)
236 So(k, ShouldNotBeNil)
235 require.NoError(t, err)
236 require.NotNil(t, k)
237237 k, err = f.Section("package").NewKey("VERSION", "v1")
238 So(err, ShouldBeNil)
239 So(k, ShouldNotBeNil)
238 require.NoError(t, err)
239 require.NotNil(t, k)
240240 k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
241 So(err, ShouldBeNil)
242 So(k, ShouldNotBeNil)
241 require.NoError(t, err)
242 require.NotNil(t, k)
243243
244244 keys := f.Section("package.sub.sub2").ParentKeys()
245245 names := []string{"NAME", "VERSION", "IMPORT_PATH"}
246 So(len(keys), ShouldEqual, len(names))
246 assert.Equal(t, len(names), len(keys))
247247 for i, name := range names {
248 So(keys[i].Name(), ShouldEqual, name)
248 assert.Equal(t, name, keys[i].Name())
249249 }
250250 })
251251 }
252252
253253 func TestSection_KeyStrings(t *testing.T) {
254 Convey("Get all key names in a section", t, func() {
255 f := ini.Empty()
256 So(f, ShouldNotBeNil)
257
258 k, err := f.Section("").NewKey("NAME", "ini")
259 So(err, ShouldBeNil)
260 So(k, ShouldNotBeNil)
254 t.Run("get all key names in a section", func(t *testing.T) {
255 f := Empty()
256 require.NotNil(t, f)
257
258 k, err := f.Section("").NewKey("NAME", "ini")
259 require.NoError(t, err)
260 require.NotNil(t, k)
261261 k, err = f.Section("").NewKey("VERSION", "v1")
262 So(err, ShouldBeNil)
263 So(k, ShouldNotBeNil)
262 require.NoError(t, err)
263 require.NotNil(t, k)
264264 k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
265 So(err, ShouldBeNil)
266 So(k, ShouldNotBeNil)
267
268 So(f.Section("").KeyStrings(), ShouldResemble, []string{"NAME", "VERSION", "IMPORT_PATH"})
265 require.NoError(t, err)
266 require.NotNil(t, k)
267
268 assert.Equal(t, []string{"NAME", "VERSION", "IMPORT_PATH"}, f.Section("").KeyStrings())
269269 })
270270 }
271271
272272 func TestSection_KeyHash(t *testing.T) {
273 Convey("Get clone of key hash", t, func() {
274 f, err := ini.Load([]byte(`
273 t.Run("get clone of key hash", func(t *testing.T) {
274 f, err := Load([]byte(`
275275 key = one
276276 [log]
277277 name = app
282282 name = app2
283283 file = b.log
284284 `))
285 So(err, ShouldBeNil)
286 So(f, ShouldNotBeNil)
287
288 So(f.Section("").Key("key").String(), ShouldEqual, "two")
285 require.NoError(t, err)
286 require.NotNil(t, f)
287
288 assert.Equal(t, "two", f.Section("").Key("key").String())
289289
290290 hash := f.Section("log").KeysHash()
291291 relation := map[string]string{
293293 "file": "b.log",
294294 }
295295 for k, v := range hash {
296 So(v, ShouldEqual, relation[k])
296 assert.Equal(t, relation[k], v)
297297 }
298298 })
299299 }
300300
301301 func TestSection_DeleteKey(t *testing.T) {
302 Convey("Delete a key", t, func() {
303 f := ini.Empty()
304 So(f, ShouldNotBeNil)
305
306 k, err := f.Section("").NewKey("NAME", "ini")
307 So(err, ShouldBeNil)
308 So(k, ShouldNotBeNil)
309
310 So(f.Section("").HasKey("NAME"), ShouldBeTrue)
302 t.Run("delete a key", func(t *testing.T) {
303 f := Empty()
304 require.NotNil(t, f)
305
306 k, err := f.Section("").NewKey("NAME", "ini")
307 require.NoError(t, err)
308 require.NotNil(t, k)
309
310 assert.True(t, f.Section("").HasKey("NAME"))
311311 f.Section("").DeleteKey("NAME")
312 So(f.Section("").HasKey("NAME"), ShouldBeFalse)
313 })
314 }
312 assert.False(t, f.Section("").HasKey("NAME"))
313 })
314 }
1111 // License for the specific language governing permissions and limitations
1212 // under the License.
1313
14 package ini_test
14 package ini
1515
1616 import (
1717 "bytes"
2020 "testing"
2121 "time"
2222
23 . "github.com/smartystreets/goconvey/convey"
24
25 "gopkg.in/ini.v1"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
2625 )
2726
2827 type testNested struct {
214213 `
215214
216215 func Test_MapToStruct(t *testing.T) {
217 Convey("Map to struct", t, func() {
218 Convey("Map file to struct", func() {
216 t.Run("map to struct", func(t *testing.T) {
217 t.Run("map file to struct", func(t *testing.T) {
219218 ts := new(testStruct)
220 So(ini.MapTo(ts, []byte(confDataStruct)), ShouldBeNil)
221
222 So(ts.Name, ShouldEqual, "Unknwon")
223 So(ts.Age, ShouldEqual, 21)
224 So(ts.Male, ShouldBeTrue)
225 So(ts.Money, ShouldEqual, 1.25)
226 So(ts.Unsigned, ShouldEqual, 3)
227
228 t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
229 So(err, ShouldBeNil)
230 So(ts.Born.String(), ShouldEqual, t.String())
219 assert.NoError(t, MapTo(ts, []byte(confDataStruct)))
220
221 assert.Equal(t, "Unknwon", ts.Name)
222 assert.Equal(t, 21, ts.Age)
223 assert.True(t, ts.Male)
224 assert.Equal(t, 1.25, ts.Money)
225 assert.Equal(t, uint(3), ts.Unsigned)
226
227 ti, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
228 require.NoError(t, err)
229 assert.Equal(t, ti.String(), ts.Born.String())
231230
232231 dur, err := time.ParseDuration("2h45m")
233 So(err, ShouldBeNil)
234 So(ts.Time.Seconds(), ShouldEqual, dur.Seconds())
235
236 So(ts.OldVersionTime*time.Second, ShouldEqual, 30*time.Second)
237
238 So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston")
239 So(ts.Others.Visits[0].String(), ShouldEqual, t.String())
240 So(fmt.Sprint(ts.Others.Years), ShouldEqual, "[1993 1994]")
241 So(fmt.Sprint(ts.Others.Numbers), ShouldEqual, "[10010 10086]")
242 So(fmt.Sprint(ts.Others.Ages), ShouldEqual, "[18 19]")
243 So(fmt.Sprint(ts.Others.Populations), ShouldEqual, "[12345678 98765432]")
244 So(fmt.Sprint(ts.Others.Coordinates), ShouldEqual, "[192.168 10.11]")
245 So(fmt.Sprint(ts.Others.Flags), ShouldEqual, "[true false]")
246 So(ts.Others.Note, ShouldEqual, "Hello world!")
247 So(ts.TestEmbeded.GPA, ShouldEqual, 2.8)
248
249 So(strings.Join(ts.OthersPtr.Cities, ","), ShouldEqual, "HangZhou,Boston")
250 So(ts.OthersPtr.Visits[0].String(), ShouldEqual, t.String())
251 So(fmt.Sprint(ts.OthersPtr.Years), ShouldEqual, "[1993 1994]")
252 So(fmt.Sprint(ts.OthersPtr.Numbers), ShouldEqual, "[10010 10086]")
253 So(fmt.Sprint(ts.OthersPtr.Ages), ShouldEqual, "[18 19]")
254 So(fmt.Sprint(ts.OthersPtr.Populations), ShouldEqual, "[12345678 98765432]")
255 So(fmt.Sprint(ts.OthersPtr.Coordinates), ShouldEqual, "[192.168 10.11]")
256 So(fmt.Sprint(ts.OthersPtr.Flags), ShouldEqual, "[true false]")
257 So(ts.OthersPtr.Note, ShouldEqual, "Hello world!")
258
259 So(ts.NilPtr, ShouldBeNil)
260
261 So(*ts.BoolPtr, ShouldEqual, false)
262 So(ts.BoolPtrNil, ShouldEqual, nil)
263 So(*ts.FloatPtr, ShouldEqual, 0)
264 So(ts.FloatPtrNil, ShouldEqual, nil)
265 So(*ts.IntPtr, ShouldEqual, 0)
266 So(ts.IntPtrNil, ShouldEqual, nil)
267 So(*ts.UintPtr, ShouldEqual, 0)
268 So(ts.UintPtrNil, ShouldEqual, nil)
269 So(*ts.StringPtr, ShouldEqual, "")
270 So(ts.StringPtrNil, ShouldEqual, nil)
271 So(*ts.TimePtr, ShouldNotEqual, nil)
272 So(ts.TimePtrNil, ShouldEqual, nil)
273 So(*ts.DurationPtr, ShouldEqual, 0)
274 So(ts.DurationPtrNil, ShouldEqual, nil)
275 })
276
277 Convey("Map section to struct", func() {
232 require.NoError(t, err)
233 assert.Equal(t, dur.Seconds(), ts.Time.Seconds())
234
235 assert.Equal(t, 30*time.Second, ts.OldVersionTime*time.Second)
236
237 assert.Equal(t, "HangZhou,Boston", strings.Join(ts.Others.Cities, ","))
238 assert.Equal(t, ti.String(), ts.Others.Visits[0].String())
239 assert.Equal(t, "[1993 1994]", fmt.Sprint(ts.Others.Years))
240 assert.Equal(t, "[10010 10086]", fmt.Sprint(ts.Others.Numbers))
241 assert.Equal(t, "[18 19]", fmt.Sprint(ts.Others.Ages))
242 assert.Equal(t, "[12345678 98765432]", fmt.Sprint(ts.Others.Populations))
243 assert.Equal(t, "[192.168 10.11]", fmt.Sprint(ts.Others.Coordinates))
244 assert.Equal(t, "[true false]", fmt.Sprint(ts.Others.Flags))
245 assert.Equal(t, "Hello world!", ts.Others.Note)
246 assert.Equal(t, 2.8, ts.TestEmbeded.GPA)
247
248 assert.Equal(t, "HangZhou,Boston", strings.Join(ts.OthersPtr.Cities, ","))
249 assert.Equal(t, ti.String(), ts.OthersPtr.Visits[0].String())
250 assert.Equal(t, "[1993 1994]", fmt.Sprint(ts.OthersPtr.Years))
251 assert.Equal(t, "[10010 10086]", fmt.Sprint(ts.OthersPtr.Numbers))
252 assert.Equal(t, "[18 19]", fmt.Sprint(ts.OthersPtr.Ages))
253 assert.Equal(t, "[12345678 98765432]", fmt.Sprint(ts.OthersPtr.Populations))
254 assert.Equal(t, "[192.168 10.11]", fmt.Sprint(ts.OthersPtr.Coordinates))
255 assert.Equal(t, "[true false]", fmt.Sprint(ts.OthersPtr.Flags))
256 assert.Equal(t, "Hello world!", ts.OthersPtr.Note)
257
258 assert.Nil(t, ts.NilPtr)
259
260 assert.Equal(t, false, *ts.BoolPtr)
261 assert.Nil(t, ts.BoolPtrNil)
262 assert.Equal(t, float64(0), *ts.FloatPtr)
263 assert.Nil(t, ts.FloatPtrNil)
264 assert.Equal(t, 0, *ts.IntPtr)
265 assert.Nil(t, ts.IntPtrNil)
266 assert.Equal(t, uint(0), *ts.UintPtr)
267 assert.Nil(t, ts.UintPtrNil)
268 assert.Equal(t, "", *ts.StringPtr)
269 assert.Nil(t, ts.StringPtrNil)
270 assert.NotNil(t, *ts.TimePtr)
271 assert.Nil(t, ts.TimePtrNil)
272 assert.Equal(t, time.Duration(0), *ts.DurationPtr)
273 assert.Nil(t, ts.DurationPtrNil)
274 })
275
276 t.Run("map section to struct", func(t *testing.T) {
278277 foobar := new(fooBar)
279 f, err := ini.Load([]byte(confDataStruct))
280 So(err, ShouldBeNil)
281
282 So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil)
283 So(foobar.Here, ShouldEqual, "there")
284 So(foobar.When, ShouldEqual, "then")
285 })
286
287 Convey("Map to non-pointer struct", func() {
288 f, err := ini.Load([]byte(confDataStruct))
289 So(err, ShouldBeNil)
290 So(f, ShouldNotBeNil)
291
292 So(f.MapTo(testStruct{}), ShouldNotBeNil)
293 })
294
295 Convey("Map to unsupported type", func() {
296 f, err := ini.Load([]byte(confDataStruct))
297 So(err, ShouldBeNil)
298 So(f, ShouldNotBeNil)
278 f, err := Load([]byte(confDataStruct))
279 require.NoError(t, err)
280
281 assert.NoError(t, f.Section("foo.bar").MapTo(foobar))
282 assert.Equal(t, "there", foobar.Here)
283 assert.Equal(t, "then", foobar.When)
284 })
285
286 t.Run("map to non-pointer struct", func(t *testing.T) {
287 f, err := Load([]byte(confDataStruct))
288 require.NoError(t, err)
289 require.NotNil(t, f)
290
291 assert.Error(t, f.MapTo(testStruct{}))
292 })
293
294 t.Run("map to unsupported type", func(t *testing.T) {
295 f, err := Load([]byte(confDataStruct))
296 require.NoError(t, err)
297 require.NotNil(t, f)
299298
300299 f.NameMapper = func(raw string) string {
301300 if raw == "Byte" {
303302 }
304303 return raw
305304 }
306 So(f.MapTo(&unsupport{}), ShouldNotBeNil)
307 So(f.MapTo(&unsupport2{}), ShouldNotBeNil)
308 So(f.MapTo(&unsupport4{}), ShouldNotBeNil)
309 })
310
311 Convey("Map to omitempty field", func() {
305 assert.Error(t, f.MapTo(&unsupport{}))
306 assert.Error(t, f.MapTo(&unsupport2{}))
307 assert.Error(t, f.MapTo(&unsupport4{}))
308 })
309
310 t.Run("map to omitempty field", func(t *testing.T) {
312311 ts := new(testStruct)
313 So(ini.MapTo(ts, []byte(confDataStruct)), ShouldBeNil)
314
315 So(ts.Omitted, ShouldEqual, true)
316 })
317
318 Convey("Map with shadows", func() {
319 f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, []byte(confDataStruct))
320 So(err, ShouldBeNil)
312 assert.NoError(t, MapTo(ts, []byte(confDataStruct)))
313
314 assert.Equal(t, true, ts.Omitted)
315 })
316
317 t.Run("map with shadows", func(t *testing.T) {
318 f, err := LoadSources(LoadOptions{AllowShadows: true}, []byte(confDataStruct))
319 require.NoError(t, err)
321320 ts := new(testStruct)
322 So(f.MapTo(ts), ShouldBeNil)
323
324 So(strings.Join(ts.Shadows, " "), ShouldEqual, "1 2 3 4")
325 So(fmt.Sprintf("%v", ts.ShadowInts), ShouldEqual, "[1 2 3 4]")
326 })
327
328 Convey("Map from invalid data source", func() {
329 So(ini.MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
330 })
331
332 Convey("Map to wrong types and gain default values", func() {
333 f, err := ini.Load([]byte(invalidDataConfStruct))
334 So(err, ShouldBeNil)
335
336 t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
337 So(err, ShouldBeNil)
338 dv := &defaultValue{"Joe", 10, true, nil, 1.25, t, []string{"HangZhou", "Boston"}}
339 So(f.MapTo(dv), ShouldBeNil)
340 So(dv.Name, ShouldEqual, "Joe")
341 So(dv.Age, ShouldEqual, 10)
342 So(dv.Male, ShouldBeTrue)
343 So(dv.Money, ShouldEqual, 1.25)
344 So(dv.Born.String(), ShouldEqual, t.String())
345 So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston")
346 })
347
348 Convey("Map to extended base", func() {
349 f, err := ini.Load([]byte(confDataStruct))
350 So(err, ShouldBeNil)
351 So(f, ShouldNotBeNil)
321 assert.NoError(t, f.MapTo(ts))
322
323 assert.Equal(t, "1 2 3 4", strings.Join(ts.Shadows, " "))
324 assert.Equal(t, "[1 2 3 4]", fmt.Sprintf("%v", ts.ShadowInts))
325 })
326
327 t.Run("map from invalid data source", func(t *testing.T) {
328 assert.Error(t, MapTo(&testStruct{}, "hi"))
329 })
330
331 t.Run("map to wrong types and gain default values", func(t *testing.T) {
332 f, err := Load([]byte(invalidDataConfStruct))
333 require.NoError(t, err)
334
335 ti, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
336 require.NoError(t, err)
337 dv := &defaultValue{"Joe", 10, true, nil, 1.25, ti, []string{"HangZhou", "Boston"}}
338 assert.NoError(t, f.MapTo(dv))
339 assert.Equal(t, "Joe", dv.Name)
340 assert.Equal(t, 10, dv.Age)
341 assert.True(t, dv.Male)
342 assert.Equal(t, 1.25, dv.Money)
343 assert.Equal(t, ti.String(), dv.Born.String())
344 assert.Equal(t, "HangZhou,Boston", strings.Join(dv.Cities, ","))
345 })
346
347 t.Run("map to extended base", func(t *testing.T) {
348 f, err := Load([]byte(confDataStruct))
349 require.NoError(t, err)
350 require.NotNil(t, f)
352351 te := testExtend{}
353 So(f.Section("extended").MapTo(&te), ShouldBeNil)
354 So(te.Base, ShouldBeTrue)
355 So(te.Extend, ShouldBeTrue)
352 assert.NoError(t, f.Section("extended").MapTo(&te))
353 assert.True(t, te.Base)
354 assert.True(t, te.Extend)
356355 })
357356 })
358357
359 Convey("Map to struct in strict mode", t, func() {
360 f, err := ini.Load([]byte(`
358 t.Run("map to struct in strict mode", func(t *testing.T) {
359 f, err := Load([]byte(`
361360 name=bruce
362361 age=a30`))
363 So(err, ShouldBeNil)
362 require.NoError(t, err)
364363
365364 type Strict struct {
366365 Name string `ini:"name"`
368367 }
369368 s := new(Strict)
370369
371 So(f.Section("").StrictMapTo(s), ShouldNotBeNil)
370 assert.Error(t, f.Section("").StrictMapTo(s))
372371 })
373372
374 Convey("Map slice in strict mode", t, func() {
375 f, err := ini.Load([]byte(`
373 t.Run("map slice in strict mode", func(t *testing.T) {
374 f, err := Load([]byte(`
376375 names=alice, bruce`))
377 So(err, ShouldBeNil)
376 require.NoError(t, err)
378377
379378 type Strict struct {
380379 Names []string `ini:"names"`
381380 }
382381 s := new(Strict)
383382
384 So(f.Section("").StrictMapTo(s), ShouldBeNil)
385 So(fmt.Sprint(s.Names), ShouldEqual, "[alice bruce]")
383 assert.NoError(t, f.Section("").StrictMapTo(s))
384 assert.Equal(t, "[alice bruce]", fmt.Sprint(s.Names))
386385 })
387386 }
388387
389388 func Test_MapToStructNonUniqueSections(t *testing.T) {
390 Convey("Map to struct non unique", t, func() {
391 Convey("Map file to struct non unique", func() {
392 f, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, []byte(confNonUniqueSectionDataStruct))
393 So(err, ShouldBeNil)
389 t.Run("map to struct non unique", func(t *testing.T) {
390 t.Run("map file to struct non unique", func(t *testing.T) {
391 f, err := LoadSources(LoadOptions{AllowNonUniqueSections: true}, []byte(confNonUniqueSectionDataStruct))
392 require.NoError(t, err)
394393 ts := new(testNonUniqueSectionsStruct)
395394
396 So(f.MapTo(ts), ShouldBeNil)
397
398 So(ts.Interface.Address, ShouldEqual, "10.2.0.1/24")
399 So(ts.Interface.ListenPort, ShouldEqual, 34777)
400 So(ts.Interface.PrivateKey, ShouldEqual, "privServerKey")
401
402 So(ts.Peer[0].PublicKey, ShouldEqual, "pubClientKey")
403 So(ts.Peer[0].PresharedKey, ShouldEqual, "psKey")
404 So(ts.Peer[0].AllowedIPs[0], ShouldEqual, "10.2.0.2/32")
405 So(ts.Peer[0].AllowedIPs[1], ShouldEqual, "fd00:2::2/128")
406
407 So(ts.Peer[1].PublicKey, ShouldEqual, "pubClientKey2")
408 So(ts.Peer[1].PresharedKey, ShouldEqual, "psKey2")
409 So(ts.Peer[1].AllowedIPs[0], ShouldEqual, "10.2.0.3/32")
410 So(ts.Peer[1].AllowedIPs[1], ShouldEqual, "fd00:2::3/128")
411 })
412
413 Convey("Map non unique section to struct", func() {
395 assert.NoError(t, f.MapTo(ts))
396
397 assert.Equal(t, "10.2.0.1/24", ts.Interface.Address)
398 assert.Equal(t, 34777, ts.Interface.ListenPort)
399 assert.Equal(t, "privServerKey", ts.Interface.PrivateKey)
400
401 assert.Equal(t, "pubClientKey", ts.Peer[0].PublicKey)
402 assert.Equal(t, "psKey", ts.Peer[0].PresharedKey)
403 assert.Equal(t, "10.2.0.2/32", ts.Peer[0].AllowedIPs[0])
404 assert.Equal(t, "fd00:2::2/128", ts.Peer[0].AllowedIPs[1])
405
406 assert.Equal(t, "pubClientKey2", ts.Peer[1].PublicKey)
407 assert.Equal(t, "psKey2", ts.Peer[1].PresharedKey)
408 assert.Equal(t, "10.2.0.3/32", ts.Peer[1].AllowedIPs[0])
409 assert.Equal(t, "fd00:2::3/128", ts.Peer[1].AllowedIPs[1])
410 })
411
412 t.Run("map non unique section to struct", func(t *testing.T) {
414413 newPeer := new(testPeer)
415414 newPeerSlice := make([]testPeer, 0)
416415
417 f, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, []byte(confNonUniqueSectionDataStruct))
418 So(err, ShouldBeNil)
416 f, err := LoadSources(LoadOptions{AllowNonUniqueSections: true}, []byte(confNonUniqueSectionDataStruct))
417 require.NoError(t, err)
419418
420419 // try only first one
421 So(f.Section("Peer").MapTo(newPeer), ShouldBeNil)
422 So(newPeer.PublicKey, ShouldEqual, "pubClientKey")
423 So(newPeer.PresharedKey, ShouldEqual, "psKey")
424 So(newPeer.AllowedIPs[0], ShouldEqual, "10.2.0.2/32")
425 So(newPeer.AllowedIPs[1], ShouldEqual, "fd00:2::2/128")
420 assert.NoError(t, f.Section("Peer").MapTo(newPeer))
421 assert.Equal(t, "pubClientKey", newPeer.PublicKey)
422 assert.Equal(t, "psKey", newPeer.PresharedKey)
423 assert.Equal(t, "10.2.0.2/32", newPeer.AllowedIPs[0])
424 assert.Equal(t, "fd00:2::2/128", newPeer.AllowedIPs[1])
426425
427426 // try all
428 So(f.Section("Peer").MapTo(&newPeerSlice), ShouldBeNil)
429 So(newPeerSlice[0].PublicKey, ShouldEqual, "pubClientKey")
430 So(newPeerSlice[0].PresharedKey, ShouldEqual, "psKey")
431 So(newPeerSlice[0].AllowedIPs[0], ShouldEqual, "10.2.0.2/32")
432 So(newPeerSlice[0].AllowedIPs[1], ShouldEqual, "fd00:2::2/128")
433
434 So(newPeerSlice[1].PublicKey, ShouldEqual, "pubClientKey2")
435 So(newPeerSlice[1].PresharedKey, ShouldEqual, "psKey2")
436 So(newPeerSlice[1].AllowedIPs[0], ShouldEqual, "10.2.0.3/32")
437 So(newPeerSlice[1].AllowedIPs[1], ShouldEqual, "fd00:2::3/128")
438 })
439
440 Convey("Map non unique sections with subsections to struct", func() {
441 iniFile, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, strings.NewReader(`
427 assert.NoError(t, f.Section("Peer").MapTo(&newPeerSlice))
428 assert.Equal(t, "pubClientKey", newPeerSlice[0].PublicKey)
429 assert.Equal(t, "psKey", newPeerSlice[0].PresharedKey)
430 assert.Equal(t, "10.2.0.2/32", newPeerSlice[0].AllowedIPs[0])
431 assert.Equal(t, "fd00:2::2/128", newPeerSlice[0].AllowedIPs[1])
432
433 assert.Equal(t, "pubClientKey2", newPeerSlice[1].PublicKey)
434 assert.Equal(t, "psKey2", newPeerSlice[1].PresharedKey)
435 assert.Equal(t, "10.2.0.3/32", newPeerSlice[1].AllowedIPs[0])
436 assert.Equal(t, "fd00:2::3/128", newPeerSlice[1].AllowedIPs[1])
437 })
438
439 t.Run("map non unique sections with subsections to struct", func(t *testing.T) {
440 iniFile, err := LoadSources(LoadOptions{AllowNonUniqueSections: true}, strings.NewReader(`
442441 [Section]
443442 FieldInSubSection = 1
444443 FieldInSubSection2 = 2
449448 FieldInSubSection2 = 5
450449 FieldInSection = 6
451450 `))
452 So(err, ShouldBeNil)
451 require.NoError(t, err)
453452
454453 type SubSection struct {
455454 FieldInSubSection string `ini:"FieldInSubSection"`
470469
471470 f := new(File)
472471 err = iniFile.MapTo(f)
473 So(err, ShouldBeNil)
474
475 So(f.Sections[0].FieldInSubSection, ShouldEqual, "1")
476 So(f.Sections[0].FieldInSubSection2, ShouldEqual, "2")
477 So(f.Sections[0].FieldInSection, ShouldEqual, "3")
478
479 So(f.Sections[1].FieldInSubSection, ShouldEqual, "4")
480 So(f.Sections[1].FieldInSubSection2, ShouldEqual, "5")
481 So(f.Sections[1].FieldInSection, ShouldEqual, "6")
472 require.NoError(t, err)
473
474 assert.Equal(t, "1", f.Sections[0].FieldInSubSection)
475 assert.Equal(t, "2", f.Sections[0].FieldInSubSection2)
476 assert.Equal(t, "3", f.Sections[0].FieldInSection)
477
478 assert.Equal(t, "4", f.Sections[1].FieldInSubSection)
479 assert.Equal(t, "5", f.Sections[1].FieldInSubSection2)
480 assert.Equal(t, "6", f.Sections[1].FieldInSection)
482481 })
483482 })
484483 }
485484
486485 func Test_ReflectFromStruct(t *testing.T) {
487 Convey("Reflect from struct", t, func() {
486 t.Run("reflect from struct", func(t *testing.T) {
488487 type Embeded struct {
489488 Dates []time.Time `delim:"|" comment:"Time data"`
490489 Places []string
509508 *Embeded `ini:"infos" comment:"Embeded section"`
510509 }
511510
512 t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
513 So(err, ShouldBeNil)
514 a := &Author{"Unknwon", true, nil, 21, 100, 2.8, t, "", "ignored",
511 ti, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
512 require.NoError(t, err)
513 a := &Author{"Unknwon", true, nil, 21, 100, 2.8, ti, "", "ignored",
515514 &Embeded{
516 []time.Time{t, t},
515 []time.Time{ti, ti},
517516 []string{"HangZhou", "Boston"},
518517 []int{1993, 1994},
519518 []int64{10010, 10086},
523522 []bool{true, false},
524523 []int{},
525524 }}
526 cfg := ini.Empty()
527 So(ini.ReflectFrom(cfg, a), ShouldBeNil)
525 cfg := Empty()
526 assert.NoError(t, ReflectFrom(cfg, a))
528527
529528 var buf bytes.Buffer
530529 _, err = cfg.WriteTo(&buf)
531 So(err, ShouldBeNil)
532 So(buf.String(), ShouldEqual, `NAME = Unknwon
530 require.NoError(t, err)
531 assert.Equal(t, `NAME = Unknwon
533532 Male = true
534533 Optional =
535534 ; Author's age
551550 Flags = true,false
552551 None =
553552
554 `)
555
556 Convey("Reflect from non-point struct", func() {
557 So(ini.ReflectFrom(cfg, Author{}), ShouldNotBeNil)
558 })
559
560 Convey("Reflect from struct with omitempty", func() {
561 cfg := ini.Empty()
553 `,
554 buf.String(),
555 )
556
557 t.Run("reflect from non-point struct", func(t *testing.T) {
558 assert.Error(t, ReflectFrom(cfg, Author{}))
559 })
560
561 t.Run("reflect from struct with omitempty", func(t *testing.T) {
562 cfg := Empty()
562563 type SpecialStruct struct {
563564 FirstName string `ini:"first_name"`
564565 LastName string `ini:"last_name,omitempty"`
579580 NotEmpty: 9,
580581 }
581582
582 So(ini.ReflectFrom(cfg, special), ShouldBeNil)
583 assert.NoError(t, ReflectFrom(cfg, special))
583584
584585 var buf bytes.Buffer
585586 _, err = cfg.WriteTo(&buf)
586 So(buf.String(), ShouldEqual, `first_name = John
587 assert.Equal(t, `first_name = John
587588 last_name = Doe
588589 omitempty = 9
589590
590 `)
591 })
592
593 Convey("Reflect from struct with non-anonymous structure pointer", func() {
594 cfg := ini.Empty()
591 `,
592 buf.String(),
593 )
594 })
595
596 t.Run("reflect from struct with non-anonymous structure pointer", func(t *testing.T) {
597 cfg := Empty()
595598 type Rpc struct {
596599 Enable bool `ini:"enable"`
597600 Type string `ini:"type"`
610613 Name: "name",
611614 },
612615 }
613 So(cfg.ReflectFrom(config), ShouldBeNil)
616 assert.NoError(t, cfg.ReflectFrom(config))
614617
615618 var buf bytes.Buffer
616619 _, err = cfg.WriteTo(&buf)
617 So(buf.String(), ShouldEqual, `[rpc]
620 assert.Equal(t, `[rpc]
618621 enable = true
619622 type = type
620623 addr = address
621624 name = name
622625
623 `)
626 `,
627 buf.String(),
628 )
624629 })
625630 })
626631 }
627632
628633 func Test_ReflectFromStructNonUniqueSections(t *testing.T) {
629 Convey("Reflect from struct with non unique sections", t, func() {
634 t.Run("reflect from struct with non unique sections", func(t *testing.T) {
630635 nonUnique := &testNonUniqueSectionsStruct{
631636 Interface: testInterface{
632637 Address: "10.2.0.1/24",
647652 },
648653 }
649654
650 cfg := ini.Empty(ini.LoadOptions{
655 cfg := Empty(LoadOptions{
651656 AllowNonUniqueSections: true,
652657 })
653658
654 So(ini.ReflectFrom(cfg, nonUnique), ShouldBeNil)
659 assert.NoError(t, ReflectFrom(cfg, nonUnique))
655660
656661 var buf bytes.Buffer
657662 _, err := cfg.WriteTo(&buf)
658 So(err, ShouldBeNil)
659 So(buf.String(), ShouldEqual, confNonUniqueSectionDataStruct)
663 require.NoError(t, err)
664 assert.Equal(t, confNonUniqueSectionDataStruct, buf.String())
660665
661666 // note: using ReflectFrom from should overwrite the existing sections
662667 err = cfg.Section("Peer").ReflectFrom([]*testPeer{
672677 },
673678 })
674679
675 So(err, ShouldBeNil)
680 require.NoError(t, err)
676681
677682 buf = bytes.Buffer{}
678683 _, err = cfg.WriteTo(&buf)
679 So(err, ShouldBeNil)
680 So(buf.String(), ShouldEqual, `[Interface]
684 require.NoError(t, err)
685 assert.Equal(t, `[Interface]
681686 Address = 10.2.0.1/24
682687 ListenPort = 34777
683688 PrivateKey = privServerKey
692697 PresharedKey = psKey4
693698 AllowedIPs = 10.2.0.5/32,fd00:2::5/128
694699
695 `)
700 `,
701 buf.String(),
702 )
696703
697704 // note: using ReflectFrom from should overwrite the existing sections
698705 err = cfg.Section("Peer").ReflectFrom(&testPeer{
701708 AllowedIPs: []string{"10.2.0.6/32,fd00:2::6/128"},
702709 })
703710
704 So(err, ShouldBeNil)
711 require.NoError(t, err)
705712
706713 buf = bytes.Buffer{}
707714 _, err = cfg.WriteTo(&buf)
708 So(err, ShouldBeNil)
709 So(buf.String(), ShouldEqual, `[Interface]
715 require.NoError(t, err)
716 assert.Equal(t, `[Interface]
710717 Address = 10.2.0.1/24
711718 ListenPort = 34777
712719 PrivateKey = privServerKey
716723 PresharedKey = psKey5
717724 AllowedIPs = 10.2.0.6/32,fd00:2::6/128
718725
719 `)
726 `,
727 buf.String(),
728 )
720729 })
721730 }
722731
723732 // Inspired by https://github.com/go-ini/ini/issues/196
724733 func TestMapToAndReflectFromStructWithShadows(t *testing.T) {
725 Convey("Map to struct and then reflect with shadows should generate original config content", t, func() {
734 t.Run("map to struct and then reflect with shadows should generate original config content", func(t *testing.T) {
726735 type include struct {
727736 Paths []string `ini:"path,omitempty,allowshadow"`
728737 }
729738
730 cfg, err := ini.LoadSources(ini.LoadOptions{
739 cfg, err := LoadSources(LoadOptions{
731740 AllowShadows: true,
732741 }, []byte(`
733742 [include]
734743 path = /tmp/gpm-profiles/test5.profile
735744 path = /tmp/gpm-profiles/test1.profile`))
736 So(err, ShouldBeNil)
745 require.NoError(t, err)
737746
738747 sec := cfg.Section("include")
739748 inc := new(include)
740749 err = sec.MapTo(inc)
741 So(err, ShouldBeNil)
750 require.NoError(t, err)
742751
743752 err = sec.ReflectFrom(inc)
744 So(err, ShouldBeNil)
753 require.NoError(t, err)
745754
746755 var buf bytes.Buffer
747756 _, err = cfg.WriteTo(&buf)
748 So(err, ShouldBeNil)
749 So(buf.String(), ShouldEqual, `[include]
757 require.NoError(t, err)
758 assert.Equal(t, `[include]
750759 path = /tmp/gpm-profiles/test5.profile
751760 path = /tmp/gpm-profiles/test1.profile
752761
753 `)
754
755 Convey("Reflect from struct with shadows", func() {
756 cfg := ini.Empty(ini.LoadOptions{
762 `,
763 buf.String(),
764 )
765
766 t.Run("reflect from struct with shadows", func(t *testing.T) {
767 cfg := Empty(LoadOptions{
757768 AllowShadows: true,
758769 })
759770 type ShadowStruct struct {
786797 None: []int{},
787798 }
788799
789 So(ini.ReflectFrom(cfg, shadow), ShouldBeNil)
800 assert.NoError(t, ReflectFrom(cfg, shadow))
790801
791802 var buf bytes.Buffer
792803 _, err := cfg.WriteTo(&buf)
793 So(err, ShouldBeNil)
794 So(buf.String(), ShouldEqual, `sa = s1
804 require.NoError(t, err)
805 assert.Equal(t, `sa = s1
795806 sa = s2
796807 allowshadow = s3
797808 allowshadow = s4
812823 Flags = false
813824 None =
814825
815 `)
826 `,
827 buf.String(),
828 )
816829 })
817830 })
818831 }
822835 }
823836
824837 func Test_NameGetter(t *testing.T) {
825 Convey("Test name mappers", t, func() {
826 So(ini.MapToWithMapper(&testMapper{}, ini.TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
827
828 cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
829 So(err, ShouldBeNil)
830 So(cfg, ShouldNotBeNil)
831
832 cfg.NameMapper = ini.SnackCase
838 t.Run("test name mappers", func(t *testing.T) {
839 assert.NoError(t, MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")))
840
841 cfg, err := Load([]byte("PACKAGE_NAME=ini"))
842 require.NoError(t, err)
843 require.NotNil(t, cfg)
844
845 cfg.NameMapper = SnackCase
833846 tg := new(testMapper)
834 So(cfg.MapTo(tg), ShouldBeNil)
835 So(tg.PackageName, ShouldEqual, "ini")
847 assert.NoError(t, cfg.MapTo(tg))
848 assert.Equal(t, "ini", tg.PackageName)
836849 })
837850 }
838851
841854 }
842855
843856 func Test_Duration(t *testing.T) {
844 Convey("Duration less than 16m50s", t, func() {
857 t.Run("duration less than 16m50s", func(t *testing.T) {
845858 ds := new(testDurationStruct)
846 So(ini.MapTo(ds, []byte("Duration=16m49s")), ShouldBeNil)
859 assert.NoError(t, MapTo(ds, []byte("Duration=16m49s")))
847860
848861 dur, err := time.ParseDuration("16m49s")
849 So(err, ShouldBeNil)
850 So(ds.Duration.Seconds(), ShouldEqual, dur.Seconds())
862 require.NoError(t, err)
863 assert.Equal(t, dur.Seconds(), ds.Duration.Seconds())
851864 })
852865 }
853866
858871
859872 type Employers []*Employer
860873
861 func (es Employers) ReflectINIStruct(f *ini.File) error {
874 func (es Employers) ReflectINIStruct(f *File) error {
862875 for _, e := range es {
863876 f.Section(e.Name).Key("Title").SetValue(e.Title)
864877 }
867880
868881 // Inspired by https://github.com/go-ini/ini/issues/199
869882 func Test_StructReflector(t *testing.T) {
870 Convey("Reflect with StructReflector interface", t, func() {
883 t.Run("reflect with StructReflector interface", func(t *testing.T) {
871884 p := &struct {
872885 FirstName string
873886 Employer Employers
885898 },
886899 }
887900
888 f := ini.Empty()
889 So(f.ReflectFrom(p), ShouldBeNil)
901 f := Empty()
902 assert.NoError(t, f.ReflectFrom(p))
890903
891904 var buf bytes.Buffer
892905 _, err := f.WriteTo(&buf)
893 So(err, ShouldBeNil)
894
895 So(buf.String(), ShouldEqual, `FirstName = Andrew
906 require.NoError(t, err)
907
908 assert.Equal(t, `FirstName = Andrew
896909
897910 [Employer "VMware"]
898911 Title = Staff II Engineer
900913 [Employer "EMC"]
901914 Title = Consultant Engineer
902915
903 `)
916 `,
917 buf.String(),
918 )
904919 })
905920 }