123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- // Copyright 2014 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"
- "fmt"
- "strings"
- "testing"
- "time"
- . "github.com/smartystreets/goconvey/convey"
- "gopkg.in/ini.v1"
- )
- type testNested struct {
- Cities []string `delim:"|"`
- Visits []time.Time
- Years []int
- Numbers []int64
- Ages []uint
- Populations []uint64
- Coordinates []float64
- Flags []bool
- Note string
- Unused int `ini:"-"`
- }
- type TestEmbeded struct {
- GPA float64
- }
- type testStruct struct {
- Name string `ini:"NAME"`
- Age int
- Male bool
- Money float64
- Born time.Time
- Time time.Duration `ini:"Duration"`
- Others testNested
- OthersPtr *testNested
- NilPtr *testNested
- *TestEmbeded `ini:"grade"`
- Unused int `ini:"-"`
- Unsigned uint
- Omitted bool `ini:"omitthis,omitempty"`
- Shadows []string `ini:",,allowshadow"`
- ShadowInts []int `ini:"Shadows,,allowshadow"`
- BoolPtr *bool
- BoolPtrNil *bool
- FloatPtr *float64
- FloatPtrNil *float64
- IntPtr *int
- IntPtrNil *int
- UintPtr *uint
- UintPtrNil *uint
- StringPtr *string
- StringPtrNil *string
- TimePtr *time.Time
- TimePtrNil *time.Time
- DurationPtr *time.Duration
- DurationPtrNil *time.Duration
- }
- const _CONF_DATA_STRUCT = `
- NAME = Unknwon
- Age = 21
- Male = true
- Money = 1.25
- Born = 1993-10-07T20:17:05Z
- Duration = 2h45m
- Unsigned = 3
- omitthis = true
- Shadows = 1, 2
- Shadows = 3, 4
- BoolPtr = false
- FloatPtr = 0
- IntPtr = 0
- UintPtr = 0
- StringPtr = ""
- TimePtr = 0001-01-01T00:00:00Z
- DurationPtr = 0s
- [Others]
- Cities = HangZhou|Boston
- Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
- Years = 1993,1994
- Numbers = 10010,10086
- Ages = 18,19
- Populations = 12345678,98765432
- Coordinates = 192.168,10.11
- Flags = true,false
- Note = Hello world!
- [OthersPtr]
- Cities = HangZhou|Boston
- Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
- Years = 1993,1994
- Numbers = 10010,10086
- Ages = 18,19
- Populations = 12345678,98765432
- Coordinates = 192.168,10.11
- Flags = true,false
- Note = Hello world!
- [grade]
- GPA = 2.8
- [foo.bar]
- Here = there
- When = then
- `
- type unsupport struct {
- Byte byte
- }
- type unsupport2 struct {
- Others struct {
- Cities byte
- }
- }
- type Unsupport3 struct {
- Cities byte
- }
- type unsupport4 struct {
- *Unsupport3 `ini:"Others"`
- }
- type defaultValue struct {
- Name string
- Age int
- Male bool
- Optional *bool
- Money float64
- Born time.Time
- Cities []string
- }
- type fooBar struct {
- Here, When string
- }
- const _INVALID_DATA_CONF_STRUCT = `
- Name =
- Age = age
- Male = 123
- Money = money
- Born = nil
- Cities =
- `
- func Test_MapToStruct(t *testing.T) {
- Convey("Map to struct", t, func() {
- Convey("Map file to struct", func() {
- ts := new(testStruct)
- So(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
- So(ts.Name, ShouldEqual, "Unknwon")
- So(ts.Age, ShouldEqual, 21)
- So(ts.Male, ShouldBeTrue)
- So(ts.Money, ShouldEqual, 1.25)
- So(ts.Unsigned, ShouldEqual, 3)
- t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
- So(err, ShouldBeNil)
- So(ts.Born.String(), ShouldEqual, t.String())
- dur, err := time.ParseDuration("2h45m")
- So(err, ShouldBeNil)
- So(ts.Time.Seconds(), ShouldEqual, dur.Seconds())
- So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston")
- So(ts.Others.Visits[0].String(), ShouldEqual, t.String())
- So(fmt.Sprint(ts.Others.Years), ShouldEqual, "[1993 1994]")
- So(fmt.Sprint(ts.Others.Numbers), ShouldEqual, "[10010 10086]")
- So(fmt.Sprint(ts.Others.Ages), ShouldEqual, "[18 19]")
- So(fmt.Sprint(ts.Others.Populations), ShouldEqual, "[12345678 98765432]")
- So(fmt.Sprint(ts.Others.Coordinates), ShouldEqual, "[192.168 10.11]")
- So(fmt.Sprint(ts.Others.Flags), ShouldEqual, "[true false]")
- So(ts.Others.Note, ShouldEqual, "Hello world!")
- So(ts.TestEmbeded.GPA, ShouldEqual, 2.8)
- So(strings.Join(ts.OthersPtr.Cities, ","), ShouldEqual, "HangZhou,Boston")
- So(ts.OthersPtr.Visits[0].String(), ShouldEqual, t.String())
- So(fmt.Sprint(ts.OthersPtr.Years), ShouldEqual, "[1993 1994]")
- So(fmt.Sprint(ts.OthersPtr.Numbers), ShouldEqual, "[10010 10086]")
- So(fmt.Sprint(ts.OthersPtr.Ages), ShouldEqual, "[18 19]")
- So(fmt.Sprint(ts.OthersPtr.Populations), ShouldEqual, "[12345678 98765432]")
- So(fmt.Sprint(ts.OthersPtr.Coordinates), ShouldEqual, "[192.168 10.11]")
- So(fmt.Sprint(ts.OthersPtr.Flags), ShouldEqual, "[true false]")
- So(ts.OthersPtr.Note, ShouldEqual, "Hello world!")
- So(ts.NilPtr, ShouldBeNil)
- So(*ts.BoolPtr, ShouldEqual, false)
- So(ts.BoolPtrNil, ShouldEqual, nil)
- So(*ts.FloatPtr, ShouldEqual, 0)
- So(ts.FloatPtrNil, ShouldEqual, nil)
- So(*ts.IntPtr, ShouldEqual, 0)
- So(ts.IntPtrNil, ShouldEqual, nil)
- So(*ts.UintPtr, ShouldEqual, 0)
- So(ts.UintPtrNil, ShouldEqual, nil)
- So(*ts.StringPtr, ShouldEqual, "")
- So(ts.StringPtrNil, ShouldEqual, nil)
- So(*ts.TimePtr, ShouldNotEqual, nil)
- So(ts.TimePtrNil, ShouldEqual, nil)
- So(*ts.DurationPtr, ShouldEqual, 0)
- So(ts.DurationPtrNil, ShouldEqual, nil)
- })
- Convey("Map section to struct", func() {
- foobar := new(fooBar)
- f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
- So(err, ShouldBeNil)
- So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil)
- So(foobar.Here, ShouldEqual, "there")
- So(foobar.When, ShouldEqual, "then")
- })
- Convey("Map to non-pointer struct", func() {
- f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
- So(err, ShouldBeNil)
- So(f, ShouldNotBeNil)
- So(f.MapTo(testStruct{}), ShouldNotBeNil)
- })
- Convey("Map to unsupported type", func() {
- f, err := ini.Load([]byte(_CONF_DATA_STRUCT))
- So(err, ShouldBeNil)
- So(f, ShouldNotBeNil)
- f.NameMapper = func(raw string) string {
- if raw == "Byte" {
- return "NAME"
- }
- return raw
- }
- 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(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
- So(ts.Omitted, ShouldEqual, true)
- })
- Convey("Map with shadows", func() {
- f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, []byte(_CONF_DATA_STRUCT))
- So(err, ShouldBeNil)
- ts := new(testStruct)
- 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(ini.MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
- })
- Convey("Map to wrong types and gain default values", func() {
- 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, nil, 1.25, t, []string{"HangZhou", "Boston"}}
- So(f.MapTo(dv), ShouldBeNil)
- So(dv.Name, ShouldEqual, "Joe")
- So(dv.Age, ShouldEqual, 10)
- So(dv.Male, ShouldBeTrue)
- So(dv.Money, ShouldEqual, 1.25)
- So(dv.Born.String(), ShouldEqual, t.String())
- So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston")
- })
- })
- Convey("Map to struct in strict mode", t, func() {
- f, err := ini.Load([]byte(`
- name=bruce
- age=a30`))
- So(err, ShouldBeNil)
- type Strict struct {
- Name string `ini:"name"`
- Age int `ini:"age"`
- }
- s := new(Strict)
- So(f.Section("").StrictMapTo(s), ShouldNotBeNil)
- })
- Convey("Map slice in strict mode", t, func() {
- f, err := ini.Load([]byte(`
- names=alice, bruce`))
- So(err, ShouldBeNil)
- type Strict struct {
- Names []string `ini:"names"`
- }
- s := new(Strict)
- 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"`
- Places []string
- Years []int
- Numbers []int64
- Ages []uint
- Populations []uint64
- Coordinates []float64
- Flags []bool
- None []int
- }
- type Author struct {
- Name string `ini:"NAME"`
- Male bool
- Optional *bool
- Age int `comment:"Author's age"`
- Height uint
- GPA float64
- Date time.Time
- NeverMind string `ini:"-"`
- *Embeded `ini:"infos" comment:"Embeded section"`
- }
- t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
- So(err, ShouldBeNil)
- a := &Author{"Unknwon", true, nil, 21, 100, 2.8, t, "",
- &Embeded{
- []time.Time{t, t},
- []string{"HangZhou", "Boston"},
- []int{1993, 1994},
- []int64{10010, 10086},
- []uint{18, 19},
- []uint64{12345678, 98765432},
- []float64{192.168, 10.11},
- []bool{true, false},
- []int{},
- }}
- cfg := ini.Empty()
- So(ini.ReflectFrom(cfg, a), ShouldBeNil)
- var buf bytes.Buffer
- _, err = cfg.WriteTo(&buf)
- So(err, ShouldBeNil)
- So(buf.String(), ShouldEqual, `NAME = Unknwon
- Male = true
- Optional =
- ; Author's age
- Age = 21
- Height = 100
- GPA = 2.8
- Date = 1993-10-07T20:17:05Z
- ; Embeded section
- [infos]
- ; Time data
- Dates = 1993-10-07T20:17:05Z|1993-10-07T20:17:05Z
- Places = HangZhou,Boston
- Years = 1993,1994
- Numbers = 10010,10086
- Ages = 18,19
- Populations = 12345678,98765432
- Coordinates = 192.168,10.11
- Flags = true,false
- None =
- `)
- Convey("Reflect from non-point struct", func() {
- So(ini.ReflectFrom(cfg, Author{}), ShouldNotBeNil)
- })
- Convey("Reflect from struct with omitempty", func() {
- cfg := ini.Empty()
- type SpecialStruct struct {
- FirstName string `ini:"first_name"`
- LastName string `ini:"last_name"`
- JustOmitMe string `ini:"omitempty"`
- LastLogin time.Time `ini:"last_login,omitempty"`
- LastLogin2 time.Time `ini:",omitempty"`
- NotEmpty int `ini:"omitempty"`
- }
- So(ini.ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil)
- var buf bytes.Buffer
- _, err = cfg.WriteTo(&buf)
- So(buf.String(), ShouldEqual, `first_name = John
- last_name = Doe
- omitempty = 9
- `)
- })
- })
- }
- // Inspired by https://github.com/go-ini/ini/issues/196
- func TestMapToAndReflectFromStructWithShadows(t *testing.T) {
- Convey("Map to struct and then reflect with shadows should generate original config content", t, func() {
- type include struct {
- Paths []string `ini:"path,omitempty,allowshadow"`
- }
- cfg, err := ini.LoadSources(ini.LoadOptions{
- AllowShadows: true,
- }, []byte(`
- [include]
- path = /tmp/gpm-profiles/test5.profile
- path = /tmp/gpm-profiles/test1.profile`))
- So(err, ShouldBeNil)
- sec := cfg.Section("include")
- inc := new(include)
- err = sec.MapTo(inc)
- So(err, ShouldBeNil)
- err = sec.ReflectFrom(inc)
- So(err, ShouldBeNil)
- var buf bytes.Buffer
- _, err = cfg.WriteTo(&buf)
- So(err, ShouldBeNil)
- So(buf.String(), ShouldEqual, `[include]
- path = /tmp/gpm-profiles/test5.profile
- path = /tmp/gpm-profiles/test1.profile
- `)
- })
- }
- type testMapper struct {
- PackageName string
- }
- func Test_NameGetter(t *testing.T) {
- Convey("Test name mappers", t, func() {
- So(ini.MapToWithMapper(&testMapper{}, ini.TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
- cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
- So(err, ShouldBeNil)
- So(cfg, ShouldNotBeNil)
- cfg.NameMapper = ini.AllCapsUnderscore
- tg := new(testMapper)
- So(cfg.MapTo(tg), ShouldBeNil)
- So(tg.PackageName, ShouldEqual, "ini")
- })
- }
- type testDurationStruct struct {
- Duration time.Duration `ini:"Duration"`
- }
- func Test_Duration(t *testing.T) {
- Convey("Duration less than 16m50s", t, func() {
- ds := new(testDurationStruct)
- So(ini.MapTo(ds, []byte("Duration=16m49s")), ShouldBeNil)
- dur, err := time.ParseDuration("16m49s")
- So(err, ShouldBeNil)
- So(ds.Duration.Seconds(), ShouldEqual, dur.Seconds())
- })
- }
|