summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-07-22 02:06:29 +0700
committerShulhan <ms@kilabit.info>2022-07-22 21:42:49 +0700
commit95ccd72bc3d122847684ca7e3c213d909a10591c (patch)
tree234485ef050c8a9b44674b027d0ebf3170374b41
parent20b3b8735bf54e0ad9d10095e919d221f52035cc (diff)
downloadpakakeh.go-95ccd72bc3d122847684ca7e3c213d909a10591c.tar.xz
lib/ini: refactoring test to use test.Data
-rw-r--r--lib/ini/ini_test.go434
-rw-r--r--lib/ini/ini_unmarshal_test.go258
-rw-r--r--lib/ini/testdata/get.txt57
-rw-r--r--lib/ini/testdata/section_dup.ini7
-rw-r--r--lib/ini/testdata/struct/embedded.txt23
-rw-r--r--lib/ini/testdata/struct/map.txt16
-rw-r--r--lib/ini/testdata/struct/slice_of_pointer.txt58
-rw-r--r--lib/ini/testdata/struct/slice_of_primitive.txt46
-rw-r--r--lib/ini/testdata/struct/slice_of_struct.txt32
-rw-r--r--lib/ini/testdata/struct/struct.txt57
-rw-r--r--lib/ini/testdata/var_multi_empty.ini5
-rw-r--r--lib/ini/testdata/var_multi_section.ini4
12 files changed, 430 insertions, 567 deletions
diff --git a/lib/ini/ini_test.go b/lib/ini/ini_test.go
index e7f41b6c..e6788718 100644
--- a/lib/ini/ini_test.go
+++ b/lib/ini/ini_test.go
@@ -5,7 +5,11 @@
package ini
import (
+ "bytes"
+ "fmt"
+ "strings"
"testing"
+ "time"
"github.com/shuLhan/share/lib/debug"
"github.com/shuLhan/share/lib/test"
@@ -13,27 +17,122 @@ import (
const (
testdataInputIni = "testdata/input.ini"
- testdataSectionDupIni = "testdata/section_dup.ini"
- testdataVarMultiEmpty = "testdata/var_multi_empty.ini"
- testdataVarMultiSection = "testdata/var_multi_section.ini"
testdataVarWithoutSection = "testdata/var_without_section.ini"
)
-type A struct {
+type StructA struct {
X int `ini:"a::x"`
Y bool `ini:"a::y"`
}
-type B struct {
- A
+type StructB struct {
+ StructA
Z float64 `ini:"b::z"`
}
-type C struct {
- B
+type StructC struct {
+ StructB
XX byte `ini:"c::xx"`
}
+type StructMap struct {
+ Amap map[string]string `ini:"test:map"`
+}
+
+type Y struct {
+ String string `ini:"::string"`
+ Int int `ini:"::int"`
+}
+
+type X struct {
+ Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
+
+ PtrBool *bool `ini:"section:pointer:bool"`
+ PtrDuration *time.Duration `ini:"section:pointer:duration"`
+ PtrInt *int `ini:"section:pointer:int"`
+ PtrString *string `ini:"section:pointer:string"`
+ PtrTime *time.Time `ini:"section:pointer:time" layout:"2006-01-02 15:04:05"`
+
+ PtrStruct *Y `ini:"section:ptr_struct"`
+ PtrStructNil *Y `ini:"section:ptr_struct_nil"`
+
+ Struct Y `ini:"section:struct"`
+
+ String string `ini:"section::string"`
+
+ SliceStruct []Y `ini:"slice:struct"`
+
+ SlicePtrBool []*bool `ini:"slice:ptr:bool"`
+ SlicePtrDuration []*time.Duration `ini:"slice:ptr:duration"`
+ SlicePtrInt []*int `ini:"slice:ptr:int"`
+ SlicePtrString []*string `ini:"slice:ptr:string"`
+ SlicePtrStruct []*Y `ini:"slice:ptr_struct"`
+ SlicePtrTime []*time.Time `ini:"slice:ptr:time" layout:"2006-01-02 15:04:05"`
+
+ SliceBool []bool `ini:"slice::bool"`
+ SliceDuration []time.Duration `ini:"slice::duration"`
+ SliceInt []int `ini:"slice::int"`
+ SliceString []string `ini:"slice::string"`
+ SliceTime []time.Time `ini:"slice::time" layout:"2006-01-02 15:04:05"`
+
+ Duration time.Duration `ini:"section::duration"`
+ Int int `ini:"section::int"`
+ Bool bool `ini:"section::bool"`
+}
+
+func TestData(t *testing.T) {
+ var (
+ listTestData []*test.Data
+ tdata *test.Data
+ err error
+ )
+
+ listTestData, err = test.LoadDataDir("testdata/struct")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, tdata = range listTestData {
+ t.Run(tdata.Name, func(t *testing.T) {
+ var (
+ kind = tdata.Flag["kind"]
+ input = tdata.Input["default"]
+ expOut = tdata.Output["default"]
+ gotX = &X{}
+ gotC = &StructC{}
+ gotMap = &StructMap{}
+
+ obj interface{}
+ gotOut []byte
+ err error
+ )
+
+ switch kind {
+ case "":
+ return
+ case "embedded":
+ obj = gotC
+ case "map":
+ obj = gotMap
+ case "struct":
+ obj = gotX
+ }
+
+ err = Unmarshal(input, obj)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotOut, err = Marshal(obj)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, string(tdata.Desc), string(expOut), string(gotOut))
+ })
+ }
+}
+
func TestOpen(t *testing.T) {
cases := []struct {
desc string
@@ -201,43 +300,6 @@ func TestGet(t *testing.T) {
}
}
-func TestGetDefault(t *testing.T) {
- cfg, err := Open(testdataInputIni)
- if err != nil {
- t.Fatal(err)
- }
-
- cases := []struct {
- desc string
- sec string
- sub string
- key string
- def string
- exp string
- }{{
- desc: "With empty params",
- }, {
- desc: "With non existen key",
- sec: "test",
- key: "key",
- def: "def",
- exp: "def",
- }, {
- desc: "With valid key, empty default",
- sec: "user",
- key: "name",
- exp: "Shulhan",
- }}
-
- for _, c := range cases {
- t.Log(c.desc)
-
- got, _ := cfg.Get(c.sec, c.sub, c.key, c.def)
-
- test.Assert(t, "string", c.exp, got)
- }
-}
-
func TestGetInputIni(t *testing.T) {
inputIni, err := Open(testdataInputIni)
if err != nil {
@@ -458,269 +520,55 @@ func TestGetInputIni(t *testing.T) {
}
}
-func TestGetSectionDup(t *testing.T) {
- cases := []struct {
- sec string
- sub string
- keys []string
- expOK []bool
- expVals []string
- }{{
- sec: "core",
- keys: []string{
- "dupkey",
- "old",
- "new",
- "test",
- },
- expOK: []bool{
- true,
- true,
- true,
- false,
- },
- expVals: []string{
- "2",
- "value",
- "value",
- "",
- },
- }}
+func TestIni_Get(t *testing.T) {
+ var (
+ cfg *Ini
+ tdata *test.Data
+ got string
+ def string
+ tags []string
+ keys [][]byte
+ exps [][]byte
+ key []byte
+ err error
+ x int
+ ok bool
+ )
- cfg, err := Open(testdataSectionDupIni)
+ tdata, err = test.LoadData("testdata/get.txt")
if err != nil {
t.Fatal(err)
}
- for _, c := range cases {
- t.Log(c)
-
- for x, k := range c.keys {
- t.Log(" Get:", k)
-
- got, ok := cfg.Get(c.sec, c.sub, k, "")
- if !ok {
- test.Assert(t, "ok", c.expOK[x], ok)
- continue
- }
-
- test.Assert(t, k, c.expVals[x], got)
- }
- }
-}
-
-func TestGetVarMultiEmpty(t *testing.T) {
- cases := []struct {
- sec string
- sub string
- keys []string
- expOK []bool
- expVals []string
- }{{
- sec: "alias",
- keys: []string{
- "tree",
- "test",
- },
- expOK: []bool{
- true,
- false,
- },
- expVals: []string{
- "!git --no-pager log --graph ",
- "",
- },
- }, {
- sec: "section",
- keys: []string{
- "tree",
- "test",
- },
- expOK: []bool{
- false,
- true,
- },
- expVals: []string{
- "",
- "",
- },
- }}
-
- cfg, err := Open(testdataVarMultiEmpty)
+ cfg, err = Parse(tdata.Input["default"])
if err != nil {
t.Fatal(err)
}
- for _, c := range cases {
- t.Log(c)
-
- for x, k := range c.keys {
- t.Log(" Get:", k)
-
- got, ok := cfg.Get(c.sec, c.sub, k, "")
- if !ok {
- test.Assert(t, "ok", c.expOK[x], ok)
- continue
- }
-
- test.Assert(t, k, c.expVals[x], got)
- }
- }
-}
-
-func TestGetVarMultiSection(t *testing.T) {
- cases := []struct {
- sec string
- sub string
- keys []string
- expOK []bool
- expVals []string
- }{{
- sec: "alias",
- keys: []string{
- "tree",
- "test",
- },
- expOK: []bool{
- true,
- true,
- },
- expVals: []string{
- "!git --no-pager log --graph [section]",
- "",
- },
- }, {
- sec: "section",
- keys: []string{
- "test",
- },
- expOK: []bool{
- false,
- },
- expVals: []string{
- "",
- },
- }}
+ keys = bytes.Split(tdata.Input["keys"], []byte("\n"))
+ exps = bytes.Split(tdata.Output["default"], []byte("\n"))
- cfg, err := Open(testdataVarMultiSection)
- if err != nil {
- t.Fatal(err)
+ if len(keys) != len(exps) {
+ t.Fatalf("%s: input keys length %d does not match with output %d",
+ tdata.Name, len(keys), len(exps))
}
- for _, c := range cases {
- t.Log(c)
-
- for x, k := range c.keys {
- t.Log(" Get:", k)
-
- got, ok := cfg.Get(c.sec, c.sub, k, "")
- if !ok {
- test.Assert(t, "ok", c.expOK[x], ok)
- continue
- }
-
- test.Assert(t, k, c.expVals[x], got)
+ for x, key = range keys {
+ if len(key) == 0 {
+ test.Assert(t, "Get", string(exps[x]), "")
+ continue
}
- }
-}
-
-func TestMarshal_embedded(t *testing.T) {
- c := &C{
- B: B{
- A: A{
- X: 1,
- Y: true,
- },
- Z: 2.3,
- },
- XX: 4,
- }
- exp := `[a]
-x = 1
-y = true
-
-[b]
-z = 2.3
-
-[c]
-xx = 4
-`
-
- got, err := Marshal(c)
- if err != nil {
- t.Fatal(err)
- }
-
- test.Assert(t, "TestMarshal_embedded", exp, string(got))
-}
-
-// Make sure that variables loaded into a map, will be stored in
-// alphabetically orders by keys on Marshal.
-func TestMarshal_map_stable(t *testing.T) {
- type ADT struct {
- Amap map[string]string `ini:"test:map"`
- }
-
- var (
- logp = "TestMarshal_map_stable"
- adt ADT
- text []byte
- exp []byte
- got []byte
- err error
- )
-
- text = []byte(`
-[test "map"]
-c = 3
-b = 2
-a = 1
-`)
- exp = []byte(`[test "map"]
-a = 1
-b = 2
-c = 3
-`)
-
- err = Unmarshal(text, &adt)
- if err != nil {
- t.Fatalf("%s: %s", logp, err)
- }
-
- got, err = Marshal(&adt)
- if err != nil {
- t.Fatalf("%s: %s", logp, err)
- }
+ tags = strings.Split(string(key), fieldTagSeparator)
+ if len(tags) >= 4 {
+ def = tags[3]
+ } else {
+ def = ""
+ }
- test.Assert(t, "Marshal map stable", string(exp), string(got))
-}
+ got, ok = cfg.Get(tags[0], tags[1], tags[2], def)
+ got = fmt.Sprintf("%t %s.", ok, got)
-func TestUnmarshal_embedded(t *testing.T) {
- got := &C{}
- content := []byte(`[a]
-x = 1
-y = true
-[b]
-z = 2.3
-[c]
-xx = 4
-`)
- exp := &C{
- B: B{
- A: A{
- X: 1,
- Y: true,
- },
- Z: 2.3,
- },
- XX: 4,
+ test.Assert(t, "Get", string(exps[x]), got)
}
-
- err := Unmarshal(content, got)
- if err != nil {
- t.Fatal(err)
- }
-
- test.Assert(t, "TestUnmarshal_embedded", exp, got)
}
diff --git a/lib/ini/ini_unmarshal_test.go b/lib/ini/ini_unmarshal_test.go
deleted file mode 100644
index aa5f57c1..00000000
--- a/lib/ini/ini_unmarshal_test.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2021, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package ini
-
-import (
- "testing"
- "time"
-
- "github.com/shuLhan/share/lib/test"
-)
-
-type Y struct {
- String string `ini:"::string"`
- Int int `ini:"::int"`
-}
-
-type X struct {
- Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
-
- PtrBool *bool `ini:"section:pointer:bool"`
- PtrDuration *time.Duration `ini:"section:pointer:duration"`
- PtrInt *int `ini:"section:pointer:int"`
- PtrString *string `ini:"section:pointer:string"`
- PtrTime *time.Time `ini:"section:pointer:time" layout:"2006-01-02 15:04:05"`
-
- PtrStruct *Y `ini:"section:ptr_struct"`
- PtrStructNil *Y `ini:"section:ptr_struct_nil"`
-
- Struct Y `ini:"section:struct"`
-
- String string `ini:"section::string"`
-
- SliceStruct []Y `ini:"slice:struct"`
-
- SlicePtrBool []*bool `ini:"slice:ptr:bool"`
- SlicePtrDuration []*time.Duration `ini:"slice:ptr:duration"`
- SlicePtrInt []*int `ini:"slice:ptr:int"`
- SlicePtrString []*string `ini:"slice:ptr:string"`
- SlicePtrStruct []*Y `ini:"slice:ptr_struct"`
- SlicePtrTime []*time.Time `ini:"slice:ptr:time" layout:"2006-01-02 15:04:05"`
-
- SliceBool []bool `ini:"slice::bool"`
- SliceDuration []time.Duration `ini:"slice::duration"`
- SliceInt []int `ini:"slice::int"`
- SliceString []string `ini:"slice::string"`
- SliceTime []time.Time `ini:"slice::time" layout:"2006-01-02 15:04:05"`
-
- Duration time.Duration `ini:"section::duration"`
- Int int `ini:"section::int"`
- Bool bool `ini:"section::bool"`
-}
-
-func TestIni_Unmarshal(t *testing.T) {
- iniText := `
-[section "struct"]
-string = struct
-int = 1
-
-[section "ptr_struct"]
-string = ptr_struct
-int = 2
-
-[section "ptr_struct_nil"]
-string = ptr_struct_nil
-int = 3
-
-[section]
-string = a string
-int = 4
-bool = true
-duration = 4m
-time = 2021-02-28 00:12:04
-
-[section "pointer"]
-string = pointer to string
-int = 5
-bool = true
-duration = 5m
-time = 2021-02-28 00:12:05
-`
-
- got := &X{
- PtrStruct: &Y{},
- }
-
- err := Unmarshal([]byte(iniText), got)
- if err != nil {
- t.Fatal(err)
- }
-
- ptrString := "pointer to string"
- ptrInt := 5
- ptrBool := true
- ptrDuration := time.Duration(5 * time.Minute)
- ptrTime := time.Date(2021, time.February, 28, 0, 12, 5, 0, time.UTC)
-
- exp := &X{
- Struct: Y{
- String: "struct",
- Int: 1,
- },
- PtrStruct: &Y{
- String: "ptr_struct",
- Int: 2,
- },
- PtrStructNil: &Y{
- String: "ptr_struct_nil",
- Int: 3,
- },
-
- String: "a string",
- Int: 4,
- Bool: true,
- Duration: time.Duration(4 * time.Minute),
- Time: time.Date(2021, time.February, 28, 0, 12, 4, 0, time.UTC),
-
- PtrString: &ptrString,
- PtrInt: &ptrInt,
- PtrBool: &ptrBool,
- PtrDuration: &ptrDuration,
- PtrTime: &ptrTime,
- }
-
- test.Assert(t, "Unmarshal", exp, got)
-}
-
-func TestIni_Unmarshal_sliceOfStruct(t *testing.T) {
- iniText := `
-[slice "struct"]
-string = struct 0
-int = 1
-
-[slice "struct"]
-string = struct 1
-int = 2
-`
- got := &X{}
-
- err := Unmarshal([]byte(iniText), got)
- if err != nil {
- t.Fatal(err)
- }
-
- exp := &X{
- SliceStruct: []Y{{
- String: "struct 0",
- Int: 1,
- }, {
- String: "struct 1",
- Int: 2,
- }},
- }
-
- test.Assert(t, "Unmarshal slice of struct", exp, got)
-}
-
-func TestIni_Unmarshal_sliceOfPrimitive(t *testing.T) {
- iniText := `
-[slice]
-string = string 0
-int = 1
-int = 2
-bool = true
-duration = 1s
-time = 2021-02-28 03:56:01
-
-[slice]
-string = string 1
-string = string 2
-int = 3
-bool = false
-duration = 2s
-time = 2021-02-28 03:56:02
-`
- got := &X{}
-
- err := Unmarshal([]byte(iniText), got)
- if err != nil {
- t.Fatal(err)
- }
-
- exp := &X{
- SliceString: []string{"string 0", "string 1", "string 2"},
- SliceInt: []int{1, 2, 3},
- SliceBool: []bool{true, false},
- SliceDuration: []time.Duration{
- time.Second,
- 2 * time.Second,
- },
- SliceTime: []time.Time{
- time.Date(2021, time.February, 28, 3, 56, 1, 0, time.UTC),
- time.Date(2021, time.February, 28, 3, 56, 2, 0, time.UTC),
- },
- }
-
- test.Assert(t, "Unmarshal slice of primitive", exp, got)
-}
-
-func TestIni_Unmarshal_sliceOfPointer(t *testing.T) {
- iniText := `
-[slice "ptr_struct"]
-string = ptr_struct 0
-int = 1
-
-[slice "ptr_struct"]
-string = ptr_struct 1
-int = 2
-
-[slice "ptr"]
-string = string 0
-int = 1
-bool = true
-duration = 1s
-time = 2021-02-28 03:56:01
-
-[slice "ptr"]
-string = string 1
-int = 2
-bool = false
-duration = 2s
-time = 2021-02-28 03:56:02
-`
- got := &X{}
-
- err := Unmarshal([]byte(iniText), got)
- if err != nil {
- t.Fatal(err)
- }
-
- str0 := "string 0"
- str1 := "string 1"
- int0 := 1
- int1 := 2
- bool0 := true
- bool1 := false
- dur0 := time.Second
- dur1 := time.Second * 2
- time0 := time.Date(2021, time.February, 28, 3, 56, 1, 0, time.UTC)
- time1 := time.Date(2021, time.February, 28, 3, 56, 2, 0, time.UTC)
- exp := &X{
- SlicePtrStruct: []*Y{{
- String: "ptr_struct 0",
- Int: 1,
- }, {
- String: "ptr_struct 1",
- Int: 2,
- }},
- SlicePtrString: []*string{&str0, &str1},
- SlicePtrInt: []*int{&int0, &int1},
- SlicePtrBool: []*bool{&bool0, &bool1},
- SlicePtrDuration: []*time.Duration{&dur0, &dur1},
- SlicePtrTime: []*time.Time{&time0, &time1},
- }
-
- test.Assert(t, "Unmarshal slice of pointer", exp, got)
-}
diff --git a/lib/ini/testdata/get.txt b/lib/ini/testdata/get.txt
new file mode 100644
index 00000000..db74f8ba
--- /dev/null
+++ b/lib/ini/testdata/get.txt
@@ -0,0 +1,57 @@
+kind: get
+
+>>>
+[core]
+ dupkey=1
+ old=old value
+
+[core]
+ dupkey=2
+ new=new value
+
+[sec1]
+ tree = !git --no-pager log --graph \
+
+[sec2]
+ test
+
+[sec3]
+ tree = !git --no-pager log --graph \
+[sec4]
+ test
+
+>>> keys
+::
+
+core::dupkey
+core::old
+core::new
+core::notexist
+core::notexist:default value
+
+sec1::tree
+sec1::test
+sec2::tree
+sec2::test
+
+sec3::tree
+sec3::test
+sec4::test
+
+<<<
+false .
+
+true 2.
+true old value.
+true new value.
+false .
+false default value.
+
+true !git --no-pager log --graph .
+false .
+false .
+true .
+
+true !git --no-pager log --graph [sec4].
+true .
+false .
diff --git a/lib/ini/testdata/section_dup.ini b/lib/ini/testdata/section_dup.ini
deleted file mode 100644
index abe461da..00000000
--- a/lib/ini/testdata/section_dup.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[core]
- dupkey=1
- old=value
-
-[core]
- dupkey=2
- new=value
diff --git a/lib/ini/testdata/struct/embedded.txt b/lib/ini/testdata/struct/embedded.txt
new file mode 100644
index 00000000..8a5c05a3
--- /dev/null
+++ b/lib/ini/testdata/struct/embedded.txt
@@ -0,0 +1,23 @@
+kind: embedded
+
+Test unmarshalling into embedded struct.
+
+>>>
+[a]
+x = 1
+y = true
+[b]
+z = 2.3
+[c]
+xx = 4
+
+<<<
+[a]
+x = 1
+y = true
+
+[b]
+z = 2.3
+
+[c]
+xx = 4
diff --git a/lib/ini/testdata/struct/map.txt b/lib/ini/testdata/struct/map.txt
new file mode 100644
index 00000000..01be7ef1
--- /dev/null
+++ b/lib/ini/testdata/struct/map.txt
@@ -0,0 +1,16 @@
+kind: map
+
+Make sure that variables loaded into a map written alphabetical orders by keys
+on Marshal.
+
+>>>
+[test "map"]
+c = 3
+b = 2
+a = 1
+
+<<<
+[test "map"]
+a = 1
+b = 2
+c = 3
diff --git a/lib/ini/testdata/struct/slice_of_pointer.txt b/lib/ini/testdata/struct/slice_of_pointer.txt
new file mode 100644
index 00000000..a83802da
--- /dev/null
+++ b/lib/ini/testdata/struct/slice_of_pointer.txt
@@ -0,0 +1,58 @@
+kind: struct
+
+Test Unmarshal into slice of pointer.
+
+>>>
+[slice "ptr_struct"]
+string = ptr_struct 0
+int = 1
+
+[slice "ptr_struct"]
+string = ptr_struct 1
+int = 2
+
+[slice "ptr"]
+string = string 0
+int = 1
+bool = true
+duration = 1s
+time = 2021-02-28 03:56:01
+
+[slice "ptr"]
+string = string 1
+int = 2
+bool = false
+duration = 2s
+time = 2021-02-28 03:56:02
+
+<<<
+[section]
+time = 0001-01-01 00:00:00
+string =
+duration = 0s
+int = 0
+bool = false
+
+[section "struct"]
+string =
+int = 0
+
+[slice "ptr"]
+bool = true
+bool = false
+duration = 1s
+duration = 2s
+int = 1
+int = 2
+string = string 0
+string = string 1
+time = 2021-02-28 03:56:01
+time = 2021-02-28 03:56:02
+
+[slice "ptr_struct"]
+string = ptr_struct 0
+int = 1
+
+[slice "ptr_struct"]
+string = ptr_struct 1
+int = 2
diff --git a/lib/ini/testdata/struct/slice_of_primitive.txt b/lib/ini/testdata/struct/slice_of_primitive.txt
new file mode 100644
index 00000000..baefe86f
--- /dev/null
+++ b/lib/ini/testdata/struct/slice_of_primitive.txt
@@ -0,0 +1,46 @@
+kind: struct
+
+Test Unmarshal into slice of string, int, bool, time.Duration, and time.Time.
+
+>>>
+[slice]
+string = string 0
+int = 1
+int = 2
+bool = true
+duration = 1s
+time = 2021-02-28 03:56:01
+
+[slice]
+string = string 1
+string = string 2
+int = 3
+bool = false
+duration = 2s
+time = 2021-02-28 03:56:02
+
+<<<
+[section]
+time = 0001-01-01 00:00:00
+string =
+duration = 0s
+int = 0
+bool = false
+
+[section "struct"]
+string =
+int = 0
+
+[slice]
+bool = true
+bool = false
+duration = 1s
+duration = 2s
+int = 1
+int = 2
+int = 3
+string = string 0
+string = string 1
+string = string 2
+time = 2021-02-28 03:56:01
+time = 2021-02-28 03:56:02
diff --git a/lib/ini/testdata/struct/slice_of_struct.txt b/lib/ini/testdata/struct/slice_of_struct.txt
new file mode 100644
index 00000000..197791bf
--- /dev/null
+++ b/lib/ini/testdata/struct/slice_of_struct.txt
@@ -0,0 +1,32 @@
+kind: struct
+
+Test unmarshaling into slice of struct.
+
+>>>
+[slice "struct"]
+string = struct 0
+int = 1
+
+[slice "struct"]
+string = struct 1
+int = 2
+
+<<<
+[section]
+time = 0001-01-01 00:00:00
+string =
+duration = 0s
+int = 0
+bool = false
+
+[section "struct"]
+string =
+int = 0
+
+[slice "struct"]
+string = struct 0
+int = 1
+
+[slice "struct"]
+string = struct 1
+int = 2
diff --git a/lib/ini/testdata/struct/struct.txt b/lib/ini/testdata/struct/struct.txt
new file mode 100644
index 00000000..d0c4cbcf
--- /dev/null
+++ b/lib/ini/testdata/struct/struct.txt
@@ -0,0 +1,57 @@
+kind: struct
+
+Test Unmarshal-ing with some primitive types.
+
+>>>
+[section "struct"]
+string = struct
+int = 1
+
+[section "ptr_struct"]
+string = ptr_struct
+int = 2
+
+[section "ptr_struct_nil"]
+string = ptr_struct_nil
+int = 3
+
+[section]
+string = a string
+int = 4
+bool = true
+duration = 4m
+time = 2021-02-28 00:12:04
+
+[section "pointer"]
+string = pointer to string
+int = 5
+bool = true
+duration = 5m
+time = 2021-02-28 00:12:05
+
+<<<
+[section]
+time = 2021-02-28 00:12:04
+string = a string
+duration = 4m0s
+int = 4
+bool = true
+
+[section "pointer"]
+bool = true
+duration = 5m0s
+int = 5
+string = pointer to string
+time = 2021-02-28 00:12:05
+
+[section "ptr_struct"]
+string = ptr_struct
+int = 2
+
+[section "ptr_struct_nil"]
+string = ptr_struct_nil
+int = 3
+
+[section "struct"]
+string = struct
+int = 1
diff --git a/lib/ini/testdata/var_multi_empty.ini b/lib/ini/testdata/var_multi_empty.ini
deleted file mode 100644
index b29285ea..00000000
--- a/lib/ini/testdata/var_multi_empty.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[alias]
- tree = !git --no-pager log --graph \
-
-[section]
- test
diff --git a/lib/ini/testdata/var_multi_section.ini b/lib/ini/testdata/var_multi_section.ini
deleted file mode 100644
index 331cc5ef..00000000
--- a/lib/ini/testdata/var_multi_section.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[alias]
- tree = !git --no-pager log --graph \
-[section]
- test