TESTING: improve overall code quality

This commit is contained in:
Unknwon
2017-10-23 06:27:56 -04:00
parent cd55d8d297
commit 7006dbe48c
16 changed files with 1380 additions and 831 deletions
+4 -1
View File
@@ -1,4 +1,4 @@
.PHONY: build test bench vet
.PHONY: build test bench vet coverage
build: vet bench
@@ -10,3 +10,6 @@ bench:
vet:
go vet
coverage:
go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out
+118
View File
@@ -0,0 +1,118 @@
// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini_test
import (
"testing"
"gopkg.in/ini.v1"
)
func newTestFile(block bool) *ini.File {
c, _ := ini.Load([]byte(_CONF_DATA))
c.BlockMode = block
return c
}
func Benchmark_Key_Value(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_Value_NonBlock(b *testing.B) {
c := newTestFile(false)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_Value_ViaSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").Value()
}
}
func Benchmark_Key_Value_ViaSection_NonBlock(b *testing.B) {
c := newTestFile(false)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").Value()
}
}
func Benchmark_Key_Value_Direct(b *testing.B) {
c := newTestFile(true)
key := c.Section("").Key("NAME")
for i := 0; i < b.N; i++ {
key.Value()
}
}
func Benchmark_Key_Value_Direct_NonBlock(b *testing.B) {
c := newTestFile(false)
key := c.Section("").Key("NAME")
for i := 0; i < b.N; i++ {
key.Value()
}
}
func Benchmark_Key_String(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
_ = c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_String_NonBlock(b *testing.B) {
c := newTestFile(false)
for i := 0; i < b.N; i++ {
_ = c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_String_ViaSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
_ = sec.Key("NAME").String()
}
}
func Benchmark_Key_String_ViaSection_NonBlock(b *testing.B) {
c := newTestFile(false)
sec := c.Section("")
for i := 0; i < b.N; i++ {
_ = sec.Key("NAME").String()
}
}
func Benchmark_Key_SetValue(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").SetValue("10")
}
}
func Benchmark_Key_SetValue_VisSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").SetValue("10")
}
}
+12 -8
View File
@@ -27,20 +27,17 @@ import (
// File represents a combination of a or more INI file(s) in memory.
type File struct {
options LoadOptions
dataSources []dataSource
// Should make things safe, but sometimes doesn't matter.
BlockMode bool
// Make sure data is safe in multiple goroutines.
lock sync.RWMutex
// Allow combination of multiple data sources.
dataSources []dataSource
// Actual data is stored here.
sections map[string]*Section
// To keep data in order.
sectionList []string
options LoadOptions
// Actual data is stored here.
sections map[string]*Section
NameMapper
ValueMapper
@@ -259,6 +256,13 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
if _, err := buf.WriteString(sec.rawBody); err != nil {
return nil, err
}
if PrettySection {
// Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err
}
}
continue
}
+343
View File
@@ -0,0 +1,343 @@
// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini_test
import (
"bytes"
"testing"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func TestEmpty(t *testing.T) {
Convey("Create an empty object", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
// Should only have the default section
So(len(f.Sections()), ShouldEqual, 1)
// Default section should not contain any key
So(len(f.Section("").Keys()), ShouldBeZeroValue)
})
}
func TestFile_NewSection(t *testing.T) {
Convey("Create a new section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
sec, err := f.NewSection("author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(sec.Name(), ShouldEqual, "author")
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author"})
Convey("With duplicated name", func() {
sec, err := f.NewSection("author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
// Does nothing if section already exists
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author"})
})
Convey("With empty string", func() {
_, err := f.NewSection("")
So(err, ShouldNotBeNil)
})
})
}
func TestFile_NewRawSection(t *testing.T) {
Convey("Create a new raw section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000`)
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(sec.Name(), ShouldEqual, "comments")
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "comments"})
So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000`)
Convey("With duplicated name", func() {
sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`)
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "comments"})
// Overwrite previous existed section
So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000`)
})
Convey("With empty string", func() {
_, err := f.NewRawSection("", "")
So(err, ShouldNotBeNil)
})
})
}
func TestFile_NewSections(t *testing.T) {
Convey("Create new sections", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
So(f.NewSections("package", "author"), ShouldBeNil)
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "package", "author"})
Convey("With duplicated name", func() {
So(f.NewSections("author", "features"), ShouldBeNil)
// Ignore section already exists
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "package", "author", "features"})
})
Convey("With empty string", func() {
So(f.NewSections("", ""), ShouldNotBeNil)
})
})
}
func TestFile_GetSection(t *testing.T) {
Convey("Get a section", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
sec, err := f.GetSection("author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(sec.Name(), ShouldEqual, "author")
Convey("Section not exists", func() {
_, err := f.GetSection("404")
So(err, ShouldNotBeNil)
})
})
}
func TestFile_Section(t *testing.T) {
Convey("Get a section", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
sec := f.Section("author")
So(sec, ShouldNotBeNil)
So(sec.Name(), ShouldEqual, "author")
Convey("Section not exists", func() {
sec := f.Section("404")
So(sec, ShouldNotBeNil)
So(sec.Name(), ShouldEqual, "404")
})
})
}
func TestFile_Sections(t *testing.T) {
Convey("Get all sections", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
secs := f.Sections()
names := []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}
So(len(secs), ShouldEqual, len(names))
for i, name := range names {
So(secs[i].Name(), ShouldEqual, name)
}
})
}
func TestFile_ChildSections(t *testing.T) {
Convey("Get child sections by parent name", t, func() {
f, err := ini.Load([]byte(`
[node]
[node.biz1]
[node.biz2]
[node.biz3]
[node.bizN]
`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
children := f.ChildSections("node")
names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"}
So(len(children), ShouldEqual, len(names))
for i, name := range names {
So(children[i].Name(), ShouldEqual, name)
}
})
}
func TestFile_SectionStrings(t *testing.T) {
Convey("Get all section names", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"})
})
}
func TestFile_DeleteSection(t *testing.T) {
Convey("Delete a section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
f.NewSections("author", "package", "features")
f.DeleteSection("features")
f.DeleteSection("")
So(f.SectionStrings(), ShouldResemble, []string{"author", "package"})
})
}
func TestFile_Append(t *testing.T) {
Convey("Append a data source", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
So(f.Append(_MINIMAL_CONF, []byte(`
[author]
NAME = Unknwon`)), ShouldBeNil)
Convey("With bad input", func() {
So(f.Append(123), ShouldNotBeNil)
So(f.Append(_MINIMAL_CONF, 123), ShouldNotBeNil)
})
})
}
func TestFile_WriteTo(t *testing.T) {
Convey("Write content to somewhere", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
f.Section("author").Comment = `Information about package author
# Bio can be written in multiple lines.`
f.Section("author").Key("NAME").Comment = "This is author name"
f.Section("note").NewBooleanKey("boolean_key")
f.Section("note").NewKey("more", "notes")
var buf bytes.Buffer
_, err = f.WriteTo(&buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `; Package name
NAME = ini
; Package version
VERSION = v1
; Package import path
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
; Information about package author
# Bio can be written in multiple lines.
[author]
; This is author name
NAME = Unknwon
E-MAIL = u@gogs.io
GITHUB = https://github.com/%(NAME)s
# Succeeding comment
BIO = """Gopher.
Coding addict.
Good man.
"""
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
UNUSED_KEY = should be deleted
[features]
- = Support read/write comments of keys and sections
- = Support auto-increment of key names
- = Support load multiple files to overwrite key values
[types]
STRING = str
BOOL = true
BOOL_FALSE = false
FLOAT64 = 1.25
INT = 10
TIME = 2015-01-01T20:17:05Z
DURATION = 2h45m
UINT = 3
[array]
STRINGS = en, zh, de
FLOAT64S = 1.1, 2.2, 3.3
INTS = 1, 2, 3
UINTS = 1, 2, 3
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
[note]
empty_lines = next line is empty
boolean_key
more = notes
; Comment before the section
; This is a comment for the section too
[comments]
; Comment before key
key = value
; This is a comment for key2
key2 = value2
key3 = "one", "two", "three"
[string escapes]
key1 = value1, value2, value3
key2 = value1\, value2
key3 = val\ue1, value2
key4 = value1\\, value\\\\2
key5 = value1\,, value2
key6 = aaa bbb\ and\ space ccc
[advance]
value with quotes = some value
value quote2 again = some value
includes comment sign = `+"`"+"my#password"+"`"+`
includes comment sign2 = `+"`"+"my;password"+"`"+`
true = 2+3=5
`+"`"+`1+1=2`+"`"+` = true
`+"`"+`6+1=7`+"`"+` = true
"""`+"`"+`5+5`+"`"+`""" = 10
`+"`"+`"6+6"`+"`"+` = 12
`+"`"+`7-2=4`+"`"+` = false
ADDRESS = """404 road,
NotFound, State, 50000"""
two_lines = how about continuation lines?
lots_of_lines = 1 2 3 4
`)
})
}
func TestFile_SaveTo(t *testing.T) {
Convey("Write content to somewhere", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
So(f.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil)
})
}
+1 -13
View File
@@ -32,7 +32,7 @@ const (
// Maximum allowed depth when recursively substituing variable names.
_DEPTH_VALUES = 99
_VERSION = "1.30.1"
_VERSION = "1.30.2"
)
// Version returns current package version literal.
@@ -89,18 +89,6 @@ func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
return os.Open(s.name)
}
type bytesReadCloser struct {
reader io.Reader
}
func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
return rc.reader.Read(p)
}
func (rc *bytesReadCloser) Close() error {
return nil
}
// sourceData represents an object that contains content in memory.
type sourceData struct {
data []byte
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Version(t *testing.T) {
Convey("Get version", t, func() {
So(Version(), ShouldEqual, _VERSION)
})
}
func Test_isSlice(t *testing.T) {
Convey("Check if a string is in the slice", t, func() {
ss := []string{"a", "b", "c"}
So(inSlice("a", ss), ShouldBeTrue)
So(inSlice("d", ss), ShouldBeFalse)
})
}
+222 -417
View File
@@ -12,25 +12,19 @@
// License for the specific language governing permissions and limitations
// under the License.
package ini
package ini_test
import (
"bytes"
"io/ioutil"
"strings"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func Test_Version(t *testing.T) {
Convey("Get version", t, func() {
So(Version(), ShouldEqual, _VERSION)
})
}
const _CONF_DATA = `
const (
_CONF_DATA = `
; Package name
NAME = ini
; Package version
@@ -47,419 +41,238 @@ GITHUB = https://github.com/%(NAME)s
BIO = """Gopher.
Coding addict.
Good man.
""" # Succeeding comment
""" # Succeeding comment`
_MINIMAL_CONF = "testdata/minimal.ini"
_FULL_CONF = "testdata/full.ini"
_NOT_FOUND_CONF = "testdata/404.ini"
)
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
UNUSED_KEY = should be deleted
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
[types]
STRING = str
BOOL = true
BOOL_FALSE = false
FLOAT64 = 1.25
INT = 10
TIME = 2015-01-01T20:17:05Z
DURATION = 2h45m
UINT = 3
[array]
STRINGS = en, zh, de
FLOAT64S = 1.1, 2.2, 3.3
INTS = 1, 2, 3
UINTS = 1, 2, 3
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
[note]
empty_lines = next line is empty\
; Comment before the section
[comments] ; This is a comment for the section too
; Comment before key
key = "value"
key2 = "value2" ; This is a comment for key2
key3 = "one", "two", "three"
[string escapes]
key1 = value1, value2, value3
key2 = value1\, value2
key3 = val\ue1, value2
key4 = value1\\, value\\\\2
key5 = value1\,, value2
key6 = aaa bbb\ and\ space ccc
[advance]
value with quotes = "some value"
value quote2 again = 'some value'
includes comment sign = ` + "`" + "my#password" + "`" + `
includes comment sign2 = ` + "`" + "my;password" + "`" + `
true = 2+3=5
"1+1=2" = true
"""6+1=7""" = true
"""` + "`" + `5+5` + "`" + `""" = 10
` + "`" + `"6+6"` + "`" + ` = 12
` + "`" + `7-2=4` + "`" + ` = false
ADDRESS = ` + "`" + `404 road,
NotFound, State, 50000` + "`" + `
two_lines = how about \
continuation lines?
lots_of_lines = 1 \
2 \
3 \
4 \
`
func Test_Load(t *testing.T) {
Convey("Load from data sources", t, func() {
Convey("Load with empty data", func() {
So(Empty(), ShouldNotBeNil)
})
Convey("Load with multiple data sources", func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini", ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA))))
func TestLoad(t *testing.T) {
Convey("Load from good data sources", t, func() {
f, err := ini.Load([]byte(`
NAME = ini
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s`),
"testdata/minimal.ini",
ioutil.NopCloser(bytes.NewReader([]byte(`
[author]
NAME = Unknwon
`))),
)
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
f, err := Load([]byte(_CONF_DATA), "testdata/404.ini")
So(err, ShouldNotBeNil)
So(f, ShouldBeNil)
// Vaildate values make sure all sources are loaded correctly
sec := f.Section("")
So(sec.Key("NAME").String(), ShouldEqual, "ini")
So(sec.Key("VERSION").String(), ShouldEqual, "v1")
So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
sec = f.Section("author")
So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
So(sec.Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
})
Convey("Load with io.ReadCloser", func() {
cfg, err := Load(ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA))))
Convey("Load from bad data sources", t, func() {
Convey("Invalid input", func() {
_, err := ini.Load(_NOT_FOUND_CONF)
So(err, ShouldNotBeNil)
})
Convey("Unsupported type", func() {
_, err := ini.Load(123)
So(err, ShouldNotBeNil)
})
})
}
func TestLoadSources(t *testing.T) {
Convey("Load from data sources with options", t, func() {
Convey("Ignore nonexistent files", func() {
f, err := ini.LooseLoad(_NOT_FOUND_CONF, _MINIMAL_CONF)
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(cfg.Section("").Key("NAME").String(), ShouldEqual, "ini")
})
})
Convey("Bad load process", t, func() {
Convey("Load from invalid data sources", func() {
_, err := Load(_CONF_DATA)
So(err, ShouldNotBeNil)
f, err := Load("testdata/404.ini")
So(err, ShouldNotBeNil)
So(f, ShouldBeNil)
_, err = Load(1)
So(err, ShouldNotBeNil)
_, err = Load([]byte(""), 1)
So(err, ShouldNotBeNil)
})
Convey("Load with bad section name", func() {
_, err := Load([]byte("[]"))
So(err, ShouldNotBeNil)
_, err = Load([]byte("["))
So(err, ShouldNotBeNil)
})
Convey("Load with bad keys", func() {
_, err := Load([]byte(`"""name`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`"""name"""`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`""=1`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`=`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`name`))
So(err, ShouldNotBeNil)
})
Convey("Load with bad values", func() {
_, err := Load([]byte(`name="""Unknwon`))
Convey("Inverse case", func() {
_, err = ini.Load(_NOT_FOUND_CONF)
So(err, ShouldNotBeNil)
})
})
Convey("Get section and key insensitively", t, func() {
cfg, err := InsensitiveLoad([]byte(_CONF_DATA), "testdata/conf.ini")
Convey("Insensitive to section and key names", func() {
f, err := ini.InsensitiveLoad(_MINIMAL_CONF)
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
sec, err := cfg.GetSection("Author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
key, err := sec.GetKey("E-mail")
Convey("Write out", func() {
var buf bytes.Buffer
_, err := f.WriteTo(&buf)
So(err, ShouldBeNil)
So(key, ShouldNotBeNil)
So(buf.String(), ShouldEqual, `[author]
e-mail = u@gogs.io
`)
})
Convey("Load with ignoring continuation lines", t, func() {
cfg, err := LoadSources(LoadOptions{IgnoreContinuation: true}, []byte(`key1=a\b\
Convey("Inverse case", func() {
f, err := ini.Load(_MINIMAL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
})
})
Convey("Ignore continuation lines", func() {
f, err := ini.LoadSources(ini.LoadOptions{
IgnoreContinuation: true,
}, []byte(`
key1=a\b\
key2=c\d\
key3=value`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
So(f.Section("").Key("key3").String(), ShouldEqual, "value")
Convey("Inverse case", func() {
f, err := ini.Load([]byte(`
key1=a\b\
key2=c\d\`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(cfg.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
So(cfg.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`)
})
})
Convey("Load with ignoring inline comments", t, func() {
cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, []byte(`key1=value ;comment
key2=value #comment2`))
Convey("Ignore inline comments", func() {
f, err := ini.LoadSources(ini.LoadOptions{
IgnoreInlineComment: true,
}, []byte(`
key1=value ;comment
key2=value2 #comment2`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(cfg.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
So(cfg.Section("").Key("key2").String(), ShouldEqual, `value #comment2`)
So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`)
var buf bytes.Buffer
cfg.WriteTo(&buf)
So(buf.String(), ShouldEqual, `key1 = value ;comment
key2 = value #comment2
Convey("Inverse case", func() {
f, err := ini.Load([]byte(`
key1=value ;comment
key2=value2 #comment2`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
`)
So(f.Section("").Key("key1").String(), ShouldEqual, `value`)
So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`)
So(f.Section("").Key("key2").String(), ShouldEqual, `value2`)
So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`)
})
})
Convey("Load with boolean type keys", t, func() {
cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, []byte(`key1=hello
key2
#key3
key4
key5`))
Convey("Allow boolean type keys", func() {
f, err := ini.LoadSources(ini.LoadOptions{
AllowBooleanKeys: true,
}, []byte(`
key1=hello
#key2
key3`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(strings.Join(cfg.Section("").KeyStrings(), ","), ShouldEqual, "key1,key2,key4,key5")
So(cfg.Section("").Key("key2").MustBool(false), ShouldBeTrue)
So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"})
So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue)
Convey("Write out", func() {
var buf bytes.Buffer
cfg.WriteTo(&buf)
// there is always a trailing \n at the end of the section
_, err := f.WriteTo(&buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `key1 = hello
key2
# key3
key4
key5
# key2
key3
`)
})
}
func Test_File_ChildSections(t *testing.T) {
Convey("Find child sections by parent name", t, func() {
cfg, err := Load([]byte(`
[node]
Convey("Inverse case", func() {
_, err := ini.Load([]byte(`
key1=hello
#key2
key3`))
So(err, ShouldNotBeNil)
})
})
[node.biz1]
[node.biz2]
[node.biz3]
[node.bizN]
`))
Convey("Allow shadow keys", func() {
f, err := ini.ShadowLoad([]byte(`
[remote "origin"]
url = https://github.com/Antergone/test1.git
url = https://github.com/Antergone/test2.git
fetch = +refs/heads/*:refs/remotes/origin/*`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
children := cfg.ChildSections("node")
names := make([]string, len(children))
for i := range children {
names[i] = children[i].name
}
So(strings.Join(names, ","), ShouldEqual, "node.biz1,node.biz2,node.biz3,node.bizN")
So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{
"https://github.com/Antergone/test1.git",
"https://github.com/Antergone/test2.git",
})
}
So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*")
func Test_LooseLoad(t *testing.T) {
Convey("Loose load from data sources", t, func() {
Convey("Loose load mixed with nonexistent file", func() {
cfg, err := LooseLoad("testdata/404.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
var fake struct {
Name string `ini:"name"`
}
So(cfg.MapTo(&fake), ShouldBeNil)
cfg, err = LooseLoad([]byte("name=Unknwon"), "testdata/404.ini")
So(err, ShouldBeNil)
So(cfg.Section("").Key("name").String(), ShouldEqual, "Unknwon")
So(cfg.MapTo(&fake), ShouldBeNil)
So(fake.Name, ShouldEqual, "Unknwon")
})
})
}
func Test_LoadOptions_UnescapeValueDoubleQuotes(t *testing.T) {
Convey("Load with option UnescapeValueDoubleQuotes enabled", t, func() {
cfg, err := LoadSources(LoadOptions{UnescapeValueDoubleQuotes: true},
[]byte(`create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(cfg.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
})
}
func Test_File_Append(t *testing.T) {
Convey("Append data sources", t, func() {
cfg, err := Load([]byte(""))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(cfg.Append([]byte(""), []byte("")), ShouldBeNil)
Convey("Append bad data sources", func() {
So(cfg.Append(1), ShouldNotBeNil)
So(cfg.Append([]byte(""), 1), ShouldNotBeNil)
})
})
}
func Test_File_WriteTo(t *testing.T) {
Convey("Write to somewhere", t, func() {
Convey("Write out", func() {
var buf bytes.Buffer
cfg := Empty()
cfg.WriteTo(&buf)
})
}
func Test_File_SaveTo_WriteTo(t *testing.T) {
Convey("Save file", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
_, err := f.WriteTo(&buf)
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.Section("").Key("NAME").Comment = "Package name"
cfg.Section("author").Comment = `Information about package author
# Bio can be written in multiple lines.`
cfg.Section("advanced").Key("val w/ pound").SetValue("my#password")
cfg.Section("advanced").Key("longest key has a colon : yes/no").SetValue("yes")
So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
cfg.Section("author").Key("NAME").Comment = "This is author name"
So(cfg.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil)
var buf bytes.Buffer
_, err = cfg.WriteToIndent(&buf, "\t")
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `; Package name
NAME = ini
; Package version
VERSION = v1
; Package import path
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
; Information about package author
# Bio can be written in multiple lines.
[author]
; This is author name
NAME = Unknwon
E-MAIL = u@gogs.io
GITHUB = https://github.com/%(NAME)s
# Succeeding comment
BIO = """Gopher.
Coding addict.
Good man.
"""
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
UNUSED_KEY = should be deleted
[features]
- = Support read/write comments of keys and sections
- = Support auto-increment of key names
- = Support load multiple files to overwrite key values
[types]
STRING = str
BOOL = true
BOOL_FALSE = false
FLOAT64 = 1.25
INT = 10
TIME = 2015-01-01T20:17:05Z
DURATION = 2h45m
UINT = 3
[array]
STRINGS = en, zh, de
FLOAT64S = 1.1, 2.2, 3.3
INTS = 1, 2, 3
UINTS = 1, 2, 3
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
[note]
empty_lines = next line is empty
; Comment before the section
; This is a comment for the section too
[comments]
; Comment before key
key = value
; This is a comment for key2
key2 = value2
key3 = "one", "two", "three"
[string escapes]
key1 = value1, value2, value3
key2 = value1\, value2
key3 = val\ue1, value2
key4 = value1\\, value\\\\2
key5 = value1\,, value2
key6 = aaa bbb\ and\ space ccc
[advance]
value with quotes = some value
value quote2 again = some value
includes comment sign = `+"`"+"my#password"+"`"+`
includes comment sign2 = `+"`"+"my;password"+"`"+`
true = 2+3=5
`+"`"+`1+1=2`+"`"+` = true
`+"`"+`6+1=7`+"`"+` = true
"""`+"`"+`5+5`+"`"+`""" = 10
`+"`"+`"6+6"`+"`"+` = 12
`+"`"+`7-2=4`+"`"+` = false
ADDRESS = """404 road,
NotFound, State, 50000"""
two_lines = how about continuation lines?
lots_of_lines = 1 2 3 4
[advanced]
val w/ pound = `+"`"+`my#password`+"`"+`
`+"`"+`longest key has a colon : yes/no`+"`"+` = yes
So(buf.String(), ShouldEqual, `[remote "origin"]
url = https://github.com/Antergone/test1.git
url = https://github.com/Antergone/test2.git
fetch = +refs/heads/*:refs/remotes/origin/*
`)
})
}
func Test_File_WriteTo_SectionRaw(t *testing.T) {
Convey("Write a INI with a raw section", t, func() {
var buf bytes.Buffer
cfg, err := LoadSources(
LoadOptions{
UnparseableSections: []string{"CORE_LESSON", "COMMENTS"},
},
"testdata/aicc.ini")
Convey("Inverse case", func() {
f, err := ini.Load([]byte(`
[remote "origin"]
url = https://github.com/Antergone/test1.git
url = https://github.com/Antergone/test2.git`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.WriteToIndent(&buf, "\t")
So(buf.String(), ShouldEqual, `[Core]
So(f, ShouldNotBeNil)
So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
})
})
Convey("Unescape double quotes inside value", func() {
f, err := ini.LoadSources(ini.LoadOptions{
UnescapeValueDoubleQuotes: true,
}, []byte(`
create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
Convey("Inverse case", func() {
f, err := ini.Load([]byte(`
create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`)
})
})
Convey("Allow unparseable sections", func() {
f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: true,
UnparseableSections: []string{"core_lesson", "comments"},
}, []byte(`
Lesson_Location = 87
Lesson_Status = C
Score = 3
@@ -468,51 +281,43 @@ func Test_File_WriteTo_SectionRaw(t *testing.T) {
[CORE_LESSON]
my lesson state data 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 end my lesson state data
[COMMENTS]
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
So(f.Section("").Key("score").String(), ShouldEqual, "3")
So(f.Section("").Body(), ShouldBeEmpty)
So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 end my lesson state data`)
So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)
Convey("Write out", func() {
var buf bytes.Buffer
_, err := f.WriteTo(&buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `lesson_location = 87
lesson_status = C
score = 3
time = 00:02:30
[core_lesson]
my lesson state data 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 end my lesson state data
[comments]
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
`)
})
}
// Helpers for slice tests.
func float64sEqual(values []float64, expected ...float64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func intsEqual(values []int, expected ...int) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func int64sEqual(values []int64, expected ...int64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func uintsEqual(values []uint, expected ...uint) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func uint64sEqual(values []uint64, expected ...uint64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func timesEqual(values []time.Time, expected ...time.Time) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i].String(), ShouldEqual, v.String())
}
Convey("Inverse case", func() {
_, err := ini.Load([]byte(`
[CORE_LESSON]
my lesson state data 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 end my lesson state data`))
So(err, ShouldNotBeNil)
})
})
})
}
+1 -2
View File
@@ -26,6 +26,7 @@ import (
// Key represents a key under a section.
type Key struct {
s *Section
Comment string
name string
value string
isAutoIncrement bool
@@ -33,8 +34,6 @@ type Key struct {
isShadow bool
shadows []*Key
Comment string
}
// newKey simply return a key object with given values.
+139 -242
View File
@@ -12,26 +12,108 @@
// License for the specific language governing permissions and limitations
// under the License.
package ini
package ini_test
import (
"bytes"
"fmt"
"strings"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func Test_Key(t *testing.T) {
Convey("Test getting and setting values", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
func TestKey_AddShadow(t *testing.T) {
Convey("Add shadow to a key", t, func() {
f, err := ini.ShadowLoad([]byte(`
[notes]
-: note1`))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
Convey("Get values in default section", func() {
sec := cfg.Section("")
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.AddShadow("ini.v1"), ShouldBeNil)
So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"})
Convey("Add shadow to boolean key", func() {
k, err := f.Section("").NewBooleanKey("published")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.AddShadow("beta"), ShouldNotBeNil)
})
Convey("Add shadow to auto-increment key", func() {
So(f.Section("notes").Key("#1").AddShadow("beta"), ShouldNotBeNil)
})
})
Convey("Shadow is not allowed", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.AddShadow("ini.v1"), ShouldNotBeNil)
})
}
// Helpers for slice tests.
func float64sEqual(values []float64, expected ...float64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func intsEqual(values []int, expected ...int) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func int64sEqual(values []int64, expected ...int64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func uintsEqual(values []uint, expected ...uint) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func uint64sEqual(values []uint64, expected ...uint64) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i], ShouldEqual, v)
}
}
func timesEqual(values []time.Time, expected ...time.Time) {
So(values, ShouldHaveLength, len(expected))
for i, v := range expected {
So(values[i].String(), ShouldEqual, v.String())
}
}
func TestKey_Helpers(t *testing.T) {
Convey("Getting and setting values", t, func() {
f, err := ini.Load(_FULL_CONF)
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
Convey("Get string representation", func() {
sec := f.Section("")
So(sec, ShouldNotBeNil)
So(sec.Key("NAME").Value(), ShouldEqual, "ini")
So(sec.Key("NAME").String(), ShouldEqual, "ini")
@@ -40,55 +122,65 @@ func Test_Key(t *testing.T) {
}), ShouldEqual, "ini")
So(sec.Key("NAME").Comment, ShouldEqual, "; Package name")
So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
Convey("With ValueMapper", func() {
f.ValueMapper = func(in string) string {
if in == "gopkg.in/%(NAME)s.%(VERSION)s" {
return "github.com/go-ini/ini"
}
return in
}
So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "github.com/go-ini/ini")
})
})
Convey("Get values in non-default section", func() {
sec := cfg.Section("author")
sec := f.Section("author")
So(sec, ShouldNotBeNil)
So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon")
sec = cfg.Section("package")
sec = f.Section("package")
So(sec, ShouldNotBeNil)
So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
})
Convey("Get auto-increment key names", func() {
keys := cfg.Section("features").Keys()
keys := f.Section("features").Keys()
for i, k := range keys {
So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1))
}
})
Convey("Get parent-keys that are available to the child section", func() {
parentKeys := cfg.Section("package.sub").ParentKeys()
parentKeys := f.Section("package.sub").ParentKeys()
for _, k := range parentKeys {
So(k.Name(), ShouldEqual, "CLONE_URL")
}
})
Convey("Get overwrite value", func() {
So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
})
Convey("Get sections", func() {
sections := cfg.Sections()
for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} {
sections := f.Sections()
for i, name := range []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} {
So(sections[i].Name(), ShouldEqual, name)
}
})
Convey("Get parent section value", func() {
So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
So(cfg.Section("package.fake.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
So(f.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
So(f.Section("package.fake.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
})
Convey("Get multiple line value", func() {
So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n")
So(f.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n")
})
Convey("Get values with type", func() {
sec := cfg.Section("types")
sec := f.Section("types")
v1, err := sec.Key("BOOL").Bool()
So(err, ShouldBeNil)
So(v1, ShouldBeTrue)
@@ -168,7 +260,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get value with candidates", func() {
sec := cfg.Section("types")
sec := f.Section("types")
So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str")
So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10)
@@ -194,7 +286,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get values in range", func() {
sec := cfg.Section("types")
sec := f.Section("types")
So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25)
So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10)
So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10)
@@ -218,7 +310,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get values into slice", func() {
sec := cfg.Section("array")
sec := f.Section("array")
So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de")
So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0)
@@ -244,7 +336,7 @@ func Test_Key(t *testing.T) {
})
Convey("Test string slice escapes", func() {
sec := cfg.Section("string escapes")
sec := f.Section("string escapes")
So(sec.Key("key1").Strings(","), ShouldResemble, []string{"value1", "value2", "value3"})
So(sec.Key("key2").Strings(","), ShouldResemble, []string{"value1, value2"})
So(sec.Key("key3").Strings(","), ShouldResemble, []string{`val\ue1`, "value2"})
@@ -254,7 +346,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get valid values into slice", func() {
sec := cfg.Section("array")
sec := f.Section("array")
vals1 := sec.Key("FLOAT64S").ValidFloat64s(",")
float64sEqual(vals1, 1.1, 2.2, 3.3)
@@ -277,7 +369,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get values one type into slice of another type", func() {
sec := cfg.Section("array")
sec := f.Section("array")
vals1 := sec.Key("STRINGS").ValidFloat64s(",")
So(vals1, ShouldBeEmpty)
@@ -298,7 +390,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get valid values into slice without errors", func() {
sec := cfg.Section("array")
sec := f.Section("array")
vals1, err := sec.Key("FLOAT64S").StrictFloat64s(",")
So(err, ShouldBeNil)
float64sEqual(vals1, 1.1, 2.2, 3.3)
@@ -327,7 +419,7 @@ func Test_Key(t *testing.T) {
})
Convey("Get invalid values into slice", func() {
sec := cfg.Section("array")
sec := f.Section("array")
vals1, err := sec.Key("STRINGS").StrictFloat64s(",")
So(vals1, ShouldBeEmpty)
So(err, ShouldNotBeNil)
@@ -352,232 +444,37 @@ func Test_Key(t *testing.T) {
So(vals6, ShouldBeEmpty)
So(err, ShouldNotBeNil)
})
Convey("Get key hash", func() {
cfg.Section("").KeysHash()
})
}
Convey("Set key value", func() {
k := cfg.Section("author").Key("NAME")
k.SetValue("无闻")
So(k.String(), ShouldEqual, "无闻")
})
Convey("Get key strings", func() {
So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,BOOL_FALSE,FLOAT64,INT,TIME,DURATION,UINT")
})
Convey("Delete a key", func() {
cfg.Section("package.sub").DeleteKey("UNUSED_KEY")
_, err := cfg.Section("package.sub").GetKey("UNUSED_KEY")
So(err, ShouldNotBeNil)
})
Convey("Has Key (backwards compatible)", func() {
sec := cfg.Section("package.sub")
haskey1 := sec.Haskey("UNUSED_KEY")
haskey2 := sec.Haskey("CLONE_URL")
haskey3 := sec.Haskey("CLONE_URL_NO")
So(haskey1, ShouldBeTrue)
So(haskey2, ShouldBeTrue)
So(haskey3, ShouldBeFalse)
})
Convey("Has Key", func() {
sec := cfg.Section("package.sub")
haskey1 := sec.HasKey("UNUSED_KEY")
haskey2 := sec.HasKey("CLONE_URL")
haskey3 := sec.HasKey("CLONE_URL_NO")
So(haskey1, ShouldBeTrue)
So(haskey2, ShouldBeTrue)
So(haskey3, ShouldBeFalse)
})
Convey("Has Value", func() {
sec := cfg.Section("author")
hasvalue1 := sec.HasValue("Unknwon")
hasvalue2 := sec.HasValue("doc")
So(hasvalue1, ShouldBeTrue)
So(hasvalue2, ShouldBeFalse)
})
})
Convey("Test getting and setting bad values", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
func TestKey_StringsWithShadows(t *testing.T) {
Convey("Get strings of shadows of a key", t, func() {
f, err := ini.ShadowLoad([]byte(""))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
Convey("Create new key with empty name", func() {
k, err := cfg.Section("").NewKey("", "")
So(err, ShouldNotBeNil)
So(k, ShouldBeNil)
})
Convey("Create new section with empty name", func() {
s, err := cfg.NewSection("")
So(err, ShouldNotBeNil)
So(s, ShouldBeNil)
})
Convey("Create new sections with empty name", func() {
So(cfg.NewSections(""), ShouldNotBeNil)
})
Convey("Get section that not exists", func() {
s, err := cfg.GetSection("404")
So(err, ShouldNotBeNil)
So(s, ShouldBeNil)
s = cfg.Section("404")
So(s, ShouldNotBeNil)
})
})
Convey("Test key hash clone", t, func() {
cfg, err := Load([]byte(strings.Replace("network=tcp,addr=127.0.0.1:6379,db=4,pool_size=100,idle_timeout=180", ",", "\n", -1)))
k, err := f.Section("").NewKey("NUMS", "1,2")
So(err, ShouldBeNil)
for _, v := range cfg.Section("").KeysHash() {
So(len(v), ShouldBeGreaterThan, 0)
}
})
Convey("Key has empty value", t, func() {
_conf := `key1=
key2= ; comment`
cfg, err := Load([]byte(_conf))
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("NUMS", "4,5,6")
So(err, ShouldBeNil)
So(cfg.Section("").Key("key1").Value(), ShouldBeEmpty)
So(k, ShouldNotBeNil)
So(k.StringsWithShadows(","), ShouldResemble, []string{"1", "2", "4", "5", "6"})
})
}
const _CONF_GIT_CONFIG = `
[remote "origin"]
url = https://github.com/Antergone/test1.git
url = https://github.com/Antergone/test2.git
`
func TestKey_SetValue(t *testing.T) {
Convey("Set value of key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
func Test_Key_Shadows(t *testing.T) {
Convey("Shadows keys", t, func() {
Convey("Disable shadows", func() {
cfg, err := Load([]byte(_CONF_GIT_CONFIG))
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(cfg.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
})
So(k, ShouldNotBeNil)
So(k.Value(), ShouldEqual, "ini")
Convey("Enable shadows", func() {
cfg, err := ShadowLoad([]byte(_CONF_GIT_CONFIG))
So(err, ShouldBeNil)
So(cfg.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
So(strings.Join(cfg.Section(`remote "origin"`).Key("url").ValueWithShadows(), " "), ShouldEqual,
"https://github.com/Antergone/test1.git https://github.com/Antergone/test2.git")
Convey("Save with shadows", func() {
var buf bytes.Buffer
_, err := cfg.WriteTo(&buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `[remote "origin"]
url = https://github.com/Antergone/test1.git
url = https://github.com/Antergone/test2.git
`)
})
})
k.SetValue("ini.v1")
So(k.Value(), ShouldEqual, "ini.v1")
})
}
func newTestFile(block bool) *File {
c, _ := Load([]byte(_CONF_DATA))
c.BlockMode = block
return c
}
func Benchmark_Key_Value(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_Value_NonBlock(b *testing.B) {
c := newTestFile(false)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_Value_ViaSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").Value()
}
}
func Benchmark_Key_Value_ViaSection_NonBlock(b *testing.B) {
c := newTestFile(false)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").Value()
}
}
func Benchmark_Key_Value_Direct(b *testing.B) {
c := newTestFile(true)
key := c.Section("").Key("NAME")
for i := 0; i < b.N; i++ {
key.Value()
}
}
func Benchmark_Key_Value_Direct_NonBlock(b *testing.B) {
c := newTestFile(false)
key := c.Section("").Key("NAME")
for i := 0; i < b.N; i++ {
key.Value()
}
}
func Benchmark_Key_String(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
_ = c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_String_NonBlock(b *testing.B) {
c := newTestFile(false)
for i := 0; i < b.N; i++ {
_ = c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_String_ViaSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
_ = sec.Key("NAME").String()
}
}
func Benchmark_Key_String_ViaSection_NonBlock(b *testing.B) {
c := newTestFile(false)
sec := c.Section("")
for i := 0; i < b.N; i++ {
_ = sec.Key("NAME").String()
}
}
func Benchmark_Key_SetValue(b *testing.B) {
c := newTestFile(true)
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").SetValue("10")
}
}
func Benchmark_Key_SetValue_VisSection(b *testing.B) {
c := newTestFile(true)
sec := c.Section("")
for i := 0; i < b.N; i++ {
sec.Key("NAME").SetValue("10")
}
}
+42 -7
View File
@@ -12,31 +12,66 @@
// License for the specific language governing permissions and limitations
// under the License.
package ini
package ini_test
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func Test_BOM(t *testing.T) {
func TestBOM(t *testing.T) {
Convey("Test handling BOM", t, func() {
Convey("UTF-8-BOM", func() {
cfg, err := Load("testdata/UTF-8-BOM.ini")
f, err := ini.Load("testdata/UTF-8-BOM.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
})
Convey("UTF-16-LE-BOM", func() {
cfg, err := Load("testdata/UTF-16-LE-BOM.ini")
f, err := ini.Load("testdata/UTF-16-LE-BOM.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
})
Convey("UTF-16-BE-BOM", func() {
})
})
}
func TestBadLoad(t *testing.T) {
Convey("Load with bad data", t, func() {
Convey("Bad section name", func() {
_, err := ini.Load([]byte("[]"))
So(err, ShouldNotBeNil)
_, err = ini.Load([]byte("["))
So(err, ShouldNotBeNil)
})
Convey("Bad keys", func() {
_, err := ini.Load([]byte(`"""name`))
So(err, ShouldNotBeNil)
_, err = ini.Load([]byte(`"""name"""`))
So(err, ShouldNotBeNil)
_, err = ini.Load([]byte(`""=1`))
So(err, ShouldNotBeNil)
_, err = ini.Load([]byte(`=`))
So(err, ShouldNotBeNil)
_, err = ini.Load([]byte(`name`))
So(err, ShouldNotBeNil)
})
Convey("Bad values", func() {
_, err := ini.Load([]byte(`name="""Unknwon`))
So(err, ShouldNotBeNil)
})
})
}
+9
View File
@@ -54,6 +54,14 @@ func (s *Section) Body() string {
return strings.TrimSpace(s.rawBody)
}
// SetBody updates body content only if section is raw.
func (s *Section) SetBody(body string) {
if !s.isRawSection {
return
}
s.rawBody = body
}
// NewKey creates a new key to given section.
func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 {
@@ -136,6 +144,7 @@ func (s *Section) HasKey(name string) bool {
}
// Haskey is a backwards-compatible name for HasKey.
// TODO: delete me in v2
func (s *Section) Haskey(name string) bool {
return s.HasKey(name)
}
+277 -39
View File
@@ -12,64 +12,302 @@
// License for the specific language governing permissions and limitations
// under the License.
package ini
package ini_test
import (
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func Test_Section(t *testing.T) {
Convey("Test CRD sections", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
func TestSection_SetBody(t *testing.T) {
Convey("Set body of raw section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
Convey("Get section strings", func() {
So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,comments,string escapes,advance")
})
Convey("Delete a section", func() {
cfg.DeleteSection("")
So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION)
})
Convey("Create new sections", func() {
cfg.NewSections("test", "test2")
_, err := cfg.GetSection("test")
sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000`)
So(err, ShouldBeNil)
_, err = cfg.GetSection("test2")
So(sec, ShouldNotBeNil)
So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000`)
sec.SetBody("1111111111111111111000000000000000001110000")
So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000`)
Convey("Set for non-raw section", func() {
sec, err := f.NewSection("author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
So(sec.Body(), ShouldBeEmpty)
sec.SetBody("1111111111111111111000000000000000001110000")
So(sec.Body(), ShouldBeEmpty)
})
})
}
func Test_SectionRaw(t *testing.T) {
Convey("Test section raw string", t, func() {
cfg, err := LoadSources(
LoadOptions{
Insensitive: true,
UnparseableSections: []string{"core_lesson", "comments"},
},
"testdata/aicc.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
func TestSection_NewKey(t *testing.T) {
Convey("Create a new key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
Convey("Get section strings", func() {
So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,core,core_lesson,comments")
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.Name(), ShouldEqual, "NAME")
So(k.Value(), ShouldEqual, "ini")
Convey("With duplicated name", func() {
k, err := f.Section("").NewKey("NAME", "ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
// Overwrite previous existed key
So(k.Value(), ShouldEqual, "ini.v1")
})
Convey("Validate non-raw section", func() {
val, err := cfg.Section("core").GetKey("lesson_status")
So(err, ShouldBeNil)
So(val.String(), ShouldEqual, "C")
Convey("With empty string", func() {
_, err := f.Section("").NewKey("", "")
So(err, ShouldNotBeNil)
})
})
Convey("Validate raw section", func() {
So(cfg.Section("core_lesson").Body(), ShouldEqual, `my lesson state data 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 end my lesson state data`)
Convey("Create keys with same name and allow shadow", t, func() {
f, err := ini.ShadowLoad([]byte(""))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("NAME", "ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"})
})
}
func TestSection_NewBooleanKey(t *testing.T) {
Convey("Create a new boolean key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewBooleanKey("start-ssh-server")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.Name(), ShouldEqual, "start-ssh-server")
So(k.Value(), ShouldEqual, "true")
Convey("With empty string", func() {
_, err := f.Section("").NewBooleanKey("")
So(err, ShouldNotBeNil)
})
})
}
func TestSection_GetKey(t *testing.T) {
Convey("Get a key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").GetKey("NAME")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.Name(), ShouldEqual, "NAME")
So(k.Value(), ShouldEqual, "ini")
Convey("Key not exists", func() {
_, err := f.Section("").GetKey("404")
So(err, ShouldNotBeNil)
})
Convey("Key exists in parent section", func() {
k, err := f.Section("parent").NewKey("AGE", "18")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("parent.child.son").GetKey("AGE")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(k.Value(), ShouldEqual, "18")
})
})
}
func TestSection_HasKey(t *testing.T) {
Convey("Check if a key exists", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(f.Section("").HasKey("NAME"), ShouldBeTrue)
So(f.Section("").Haskey("NAME"), ShouldBeTrue)
So(f.Section("").HasKey("404"), ShouldBeFalse)
So(f.Section("").Haskey("404"), ShouldBeFalse)
})
}
func TestSection_HasValue(t *testing.T) {
Convey("Check if contains a value in any key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(f.Section("").HasValue("ini"), ShouldBeTrue)
So(f.Section("").HasValue("404"), ShouldBeFalse)
})
}
func TestSection_Key(t *testing.T) {
Convey("Get a key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k = f.Section("").Key("NAME")
So(k, ShouldNotBeNil)
So(k.Name(), ShouldEqual, "NAME")
So(k.Value(), ShouldEqual, "ini")
Convey("Key not exists", func() {
k := f.Section("").Key("404")
So(k, ShouldNotBeNil)
So(k.Name(), ShouldEqual, "404")
})
Convey("Key exists in parent section", func() {
k, err := f.Section("parent").NewKey("AGE", "18")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k = f.Section("parent.child.son").Key("AGE")
So(k, ShouldNotBeNil)
So(k.Value(), ShouldEqual, "18")
})
})
}
func TestSection_Keys(t *testing.T) {
Convey("Get all keys in a section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("VERSION", "v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
keys := f.Section("").Keys()
names := []string{"NAME", "VERSION", "IMPORT_PATH"}
So(len(keys), ShouldEqual, len(names))
for i, name := range names {
So(keys[i].Name(), ShouldEqual, name)
}
})
}
func TestSection_ParentKeys(t *testing.T) {
Convey("Get all keys of parent sections", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("package").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("package").NewKey("VERSION", "v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
keys := f.Section("package.sub.sub2").ParentKeys()
names := []string{"NAME", "VERSION", "IMPORT_PATH"}
So(len(keys), ShouldEqual, len(names))
for i, name := range names {
So(keys[i].Name(), ShouldEqual, name)
}
})
}
func TestSection_KeyStrings(t *testing.T) {
Convey("Get all key names in a section", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("VERSION", "v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(f.Section("").KeyStrings(), ShouldResemble, []string{"NAME", "VERSION", "IMPORT_PATH"})
})
}
func TestSection_KeyHash(t *testing.T) {
Convey("Get clone of key hash", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("VERSION", "v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
hash := f.Section("").KeysHash()
relation := map[string]string{
"NAME": "ini",
"VERSION": "v1",
"IMPORT_PATH": "gopkg.in/ini.v1",
}
for k, v := range hash {
So(v, ShouldEqual, relation[k])
}
})
}
func TestSection_DeleteKey(t *testing.T) {
Convey("Delete a key", t, func() {
f := ini.Empty()
So(f, ShouldNotBeNil)
k, err := f.Section("").NewKey("NAME", "ini")
So(err, ShouldBeNil)
So(k, ShouldNotBeNil)
So(f.Section("").HasKey("NAME"), ShouldBeTrue)
f.Section("").DeleteKey("NAME")
So(f.Section("").HasKey("NAME"), ShouldBeFalse)
})
}
+34 -31
View File
@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations
// under the License.
package ini
package ini_test
import (
"bytes"
@@ -22,6 +22,7 @@ import (
"time"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
type testNested struct {
@@ -126,11 +127,11 @@ Born = nil
Cities =
`
func Test_Struct(t *testing.T) {
func Test_MapToStruct(t *testing.T) {
Convey("Map to struct", t, func() {
Convey("Map file to struct", func() {
ts := new(testStruct)
So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
So(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
So(ts.Name, ShouldEqual, "Unknwon")
So(ts.Age, ShouldEqual, 21)
@@ -159,7 +160,7 @@ func Test_Struct(t *testing.T) {
Convey("Map section to struct", func() {
foobar := new(fooBar)
f, err := Load([]byte(_CONF_DATA_STRUCT))
f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil)
@@ -168,58 +169,58 @@ func Test_Struct(t *testing.T) {
})
Convey("Map to non-pointer struct", func() {
cfg, err := Load([]byte(_CONF_DATA_STRUCT))
f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
So(cfg.MapTo(testStruct{}), ShouldNotBeNil)
So(f.MapTo(testStruct{}), ShouldNotBeNil)
})
Convey("Map to unsupported type", func() {
cfg, err := Load([]byte(_CONF_DATA_STRUCT))
f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(f, ShouldNotBeNil)
cfg.NameMapper = func(raw string) string {
f.NameMapper = func(raw string) string {
if raw == "Byte" {
return "NAME"
}
return raw
}
So(cfg.MapTo(&unsupport{}), ShouldNotBeNil)
So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil)
So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil)
So(f.MapTo(&unsupport{}), ShouldNotBeNil)
So(f.MapTo(&unsupport2{}), ShouldNotBeNil)
So(f.MapTo(&unsupport4{}), ShouldNotBeNil)
})
Convey("Map to omitempty field", func() {
ts := new(testStruct)
So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
So(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
So(ts.Omitted, ShouldEqual, true)
})
Convey("Map with shadows", func() {
cfg, err := LoadSources(LoadOptions{AllowShadows: true}, []byte(_CONF_DATA_STRUCT))
f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, []byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
ts := new(testStruct)
So(cfg.MapTo(ts), ShouldBeNil)
So(f.MapTo(ts), ShouldBeNil)
So(strings.Join(ts.Shadows, " "), ShouldEqual, "1 2 3 4")
So(fmt.Sprintf("%v", ts.ShadowInts), ShouldEqual, "[1 2 3 4]")
})
Convey("Map from invalid data source", func() {
So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
So(ini.MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
})
Convey("Map to wrong types and gain default values", func() {
cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT))
f, err := ini.Load([]byte(_INVALID_DATA_CONF_STRUCT))
So(err, ShouldBeNil)
t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
So(err, ShouldBeNil)
dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}}
So(cfg.MapTo(dv), ShouldBeNil)
So(f.MapTo(dv), ShouldBeNil)
So(dv.Name, ShouldEqual, "Joe")
So(dv.Age, ShouldEqual, 10)
So(dv.Male, ShouldBeTrue)
@@ -230,7 +231,7 @@ func Test_Struct(t *testing.T) {
})
Convey("Map to struct in strict mode", t, func() {
cfg, err := Load([]byte(`
f, err := ini.Load([]byte(`
name=bruce
age=a30`))
So(err, ShouldBeNil)
@@ -241,11 +242,11 @@ age=a30`))
}
s := new(Strict)
So(cfg.Section("").StrictMapTo(s), ShouldNotBeNil)
So(f.Section("").StrictMapTo(s), ShouldNotBeNil)
})
Convey("Map slice in strict mode", t, func() {
cfg, err := Load([]byte(`
f, err := ini.Load([]byte(`
names=alice, bruce`))
So(err, ShouldBeNil)
@@ -254,10 +255,12 @@ names=alice, bruce`))
}
s := new(Strict)
So(cfg.Section("").StrictMapTo(s), ShouldBeNil)
So(f.Section("").StrictMapTo(s), ShouldBeNil)
So(fmt.Sprint(s.Names), ShouldEqual, "[alice bruce]")
})
}
func Test_ReflectFromStruct(t *testing.T) {
Convey("Reflect from struct", t, func() {
type Embeded struct {
Dates []time.Time `delim:"|" comment:"Time data"`
@@ -293,8 +296,8 @@ names=alice, bruce`))
[]float64{192.168, 10.11},
[]int{},
}}
cfg := Empty()
So(ReflectFrom(cfg, a), ShouldBeNil)
cfg := ini.Empty()
So(ini.ReflectFrom(cfg, a), ShouldBeNil)
var buf bytes.Buffer
_, err = cfg.WriteTo(&buf)
@@ -322,11 +325,11 @@ None =
`)
Convey("Reflect from non-point struct", func() {
So(ReflectFrom(cfg, Author{}), ShouldNotBeNil)
So(ini.ReflectFrom(cfg, Author{}), ShouldNotBeNil)
})
Convey("Reflect from struct with omitempty", func() {
cfg := Empty()
cfg := ini.Empty()
type SpecialStruct struct {
FirstName string `ini:"first_name"`
LastName string `ini:"last_name"`
@@ -336,7 +339,7 @@ None =
NotEmpty int `ini:"omitempty"`
}
So(ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil)
So(ini.ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil)
var buf bytes.Buffer
_, err = cfg.WriteTo(&buf)
@@ -355,13 +358,13 @@ type testMapper struct {
func Test_NameGetter(t *testing.T) {
Convey("Test name mappers", t, func() {
So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
So(ini.MapToWithMapper(&testMapper{}, ini.TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
cfg, err := Load([]byte("PACKAGE_NAME=ini"))
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.NameMapper = AllCapsUnderscore
cfg.NameMapper = ini.AllCapsUnderscore
tg := new(testMapper)
So(cfg.MapTo(tg), ShouldBeNil)
So(tg.PackageName, ShouldEqual, "ini")
-11
View File
@@ -1,11 +0,0 @@
[Core]
Lesson_Location = 87
Lesson_Status = C
Score = 3
Time = 00:02:30
[CORE_LESSON]
my lesson state data – 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 – end my lesson state data
[COMMENTS]
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
+83
View File
@@ -0,0 +1,83 @@
; Package name
NAME = ini
; Package version
VERSION = v1
; Package import path
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
# Information about package author
# Bio can be written in multiple lines.
[author]
NAME = Unknwon
E-MAIL = u@gogs.io
GITHUB = https://github.com/%(NAME)s
BIO = """Gopher.
Coding addict.
Good man.
""" # Succeeding comment
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
UNUSED_KEY = should be deleted
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
[types]
STRING = str
BOOL = true
BOOL_FALSE = false
FLOAT64 = 1.25
INT = 10
TIME = 2015-01-01T20:17:05Z
DURATION = 2h45m
UINT = 3
[array]
STRINGS = en, zh, de
FLOAT64S = 1.1, 2.2, 3.3
INTS = 1, 2, 3
UINTS = 1, 2, 3
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
[note]
empty_lines = next line is empty\
; Comment before the section
[comments] ; This is a comment for the section too
; Comment before key
key = "value"
key2 = "value2" ; This is a comment for key2
key3 = "one", "two", "three"
[string escapes]
key1 = value1, value2, value3
key2 = value1\, value2
key3 = val\ue1, value2
key4 = value1\\, value\\\\2
key5 = value1\,, value2
key6 = aaa bbb\ and\ space ccc
[advance]
value with quotes = "some value"
value quote2 again = 'some value'
includes comment sign = `my#password`
includes comment sign2 = `my;password`
true = 2+3=5
"1+1=2" = true
"""6+1=7""" = true
"""`5+5`""" = 10
`"6+6"` = 12
`7-2=4` = false
ADDRESS = `404 road,
NotFound, State, 50000`
two_lines = how about \
continuation lines?
lots_of_lines = 1 \
2 \
3 \
4 \
View File