d1e0fc37ce
Currently, the marshalling code encodes the embedded structs as sub-tables. This is a bit unexpected, as it differs from what encoding/json does in that case: https://play.golang.org/p/KDPaGtrijV1 Unmarshalling code handles this scenario gracefully. This PR adapts the encoder to behave like encoding/json. Fields in an embedded struct are promoted to the top level table. In case the embedded struct is named in the tag, it will still encode as a sub-table. The added PromoteAnonymous option on the Encoder allows configuring the old behavior, where anonymous structs are encoded as sub-tables. On duplicate keys, the behavior of encoding/json is mimicked: Fields from anonymous structs are shadowed by regular fields. An example is added to show the affects of setting PromoteAnonymous.
3055 lines
68 KiB
Go
3055 lines
68 KiB
Go
package toml
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type basicMarshalTestStruct struct {
|
|
String string `toml:"Zstring"`
|
|
StringList []string `toml:"Ystrlist"`
|
|
Sub basicMarshalTestSubStruct `toml:"Xsubdoc"`
|
|
SubList []basicMarshalTestSubStruct `toml:"Wsublist"`
|
|
}
|
|
|
|
type basicMarshalTestSubStruct struct {
|
|
String2 string
|
|
}
|
|
|
|
var basicTestData = basicMarshalTestStruct{
|
|
String: "Hello",
|
|
StringList: []string{"Howdy", "Hey There"},
|
|
Sub: basicMarshalTestSubStruct{"One"},
|
|
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
|
|
}
|
|
|
|
var basicTestToml = []byte(`Ystrlist = ["Howdy","Hey There"]
|
|
Zstring = "Hello"
|
|
|
|
[[Wsublist]]
|
|
String2 = "Two"
|
|
|
|
[[Wsublist]]
|
|
String2 = "Three"
|
|
|
|
[Xsubdoc]
|
|
String2 = "One"
|
|
`)
|
|
|
|
var basicTestTomlOrdered = []byte(`Zstring = "Hello"
|
|
Ystrlist = ["Howdy","Hey There"]
|
|
|
|
[Xsubdoc]
|
|
String2 = "One"
|
|
|
|
[[Wsublist]]
|
|
String2 = "Two"
|
|
|
|
[[Wsublist]]
|
|
String2 = "Three"
|
|
`)
|
|
|
|
var marshalTestToml = []byte(`title = "TOML Marshal Testing"
|
|
|
|
[basic]
|
|
bool = true
|
|
date = 1979-05-27T07:32:00Z
|
|
float = 123.4
|
|
float64 = 123.456782132399
|
|
int = 5000
|
|
string = "Bite me"
|
|
uint = 5001
|
|
|
|
[basic_lists]
|
|
bools = [true,false,true]
|
|
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
|
floats = [12.3,45.6,78.9]
|
|
ints = [8001,8001,8002]
|
|
strings = ["One","Two","Three"]
|
|
uints = [5002,5003]
|
|
|
|
[basic_map]
|
|
one = "one"
|
|
two = "two"
|
|
|
|
[subdoc]
|
|
|
|
[subdoc.first]
|
|
name = "First"
|
|
|
|
[subdoc.second]
|
|
name = "Second"
|
|
|
|
[[subdoclist]]
|
|
name = "List.First"
|
|
|
|
[[subdoclist]]
|
|
name = "List.Second"
|
|
|
|
[[subdocptrs]]
|
|
name = "Second"
|
|
`)
|
|
|
|
var marshalOrderPreserveToml = []byte(`title = "TOML Marshal Testing"
|
|
|
|
[basic_lists]
|
|
floats = [12.3,45.6,78.9]
|
|
bools = [true,false,true]
|
|
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
|
ints = [8001,8001,8002]
|
|
uints = [5002,5003]
|
|
strings = ["One","Two","Three"]
|
|
|
|
[[subdocptrs]]
|
|
name = "Second"
|
|
|
|
[basic_map]
|
|
one = "one"
|
|
two = "two"
|
|
|
|
[subdoc]
|
|
|
|
[subdoc.second]
|
|
name = "Second"
|
|
|
|
[subdoc.first]
|
|
name = "First"
|
|
|
|
[basic]
|
|
uint = 5001
|
|
bool = true
|
|
float = 123.4
|
|
float64 = 123.456782132399
|
|
int = 5000
|
|
string = "Bite me"
|
|
date = 1979-05-27T07:32:00Z
|
|
|
|
[[subdoclist]]
|
|
name = "List.First"
|
|
|
|
[[subdoclist]]
|
|
name = "List.Second"
|
|
`)
|
|
|
|
var mashalOrderPreserveMapToml = []byte(`title = "TOML Marshal Testing"
|
|
|
|
[basic_map]
|
|
one = "one"
|
|
two = "two"
|
|
|
|
[long_map]
|
|
a7 = "1"
|
|
b3 = "2"
|
|
c8 = "3"
|
|
d4 = "4"
|
|
e6 = "5"
|
|
f5 = "6"
|
|
g10 = "7"
|
|
h1 = "8"
|
|
i2 = "9"
|
|
j9 = "10"
|
|
`)
|
|
|
|
type Conf struct {
|
|
Name string
|
|
Age int
|
|
Inter interface{}
|
|
}
|
|
|
|
type NestedStruct struct {
|
|
FirstName string
|
|
LastName string
|
|
Age int
|
|
}
|
|
|
|
var doc = []byte(`Name = "rui"
|
|
Age = 18
|
|
|
|
[Inter]
|
|
FirstName = "wang"
|
|
LastName = "jl"
|
|
Age = 100`)
|
|
|
|
func TestInterface(t *testing.T) {
|
|
var config Conf
|
|
config.Inter = &NestedStruct{}
|
|
err := Unmarshal(doc, &config)
|
|
expected := Conf{
|
|
Name: "rui",
|
|
Age: 18,
|
|
Inter: &NestedStruct{
|
|
FirstName: "wang",
|
|
LastName: "jl",
|
|
Age: 100,
|
|
},
|
|
}
|
|
if err != nil || !reflect.DeepEqual(config, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, config)
|
|
}
|
|
}
|
|
|
|
func TestBasicMarshal(t *testing.T) {
|
|
result, err := Marshal(basicTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := basicTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestBasicMarshalOrdered(t *testing.T) {
|
|
var result bytes.Buffer
|
|
err := NewEncoder(&result).Order(OrderPreserve).Encode(basicTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := basicTestTomlOrdered
|
|
if !bytes.Equal(result.Bytes(), expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
|
|
}
|
|
}
|
|
|
|
func TestBasicMarshalWithPointer(t *testing.T) {
|
|
result, err := Marshal(&basicTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := basicTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestBasicMarshalOrderedWithPointer(t *testing.T) {
|
|
var result bytes.Buffer
|
|
err := NewEncoder(&result).Order(OrderPreserve).Encode(&basicTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := basicTestTomlOrdered
|
|
if !bytes.Equal(result.Bytes(), expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
|
|
}
|
|
}
|
|
|
|
func TestBasicUnmarshal(t *testing.T) {
|
|
result := basicMarshalTestStruct{}
|
|
err := Unmarshal(basicTestToml, &result)
|
|
expected := basicTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
type testDoc struct {
|
|
Title string `toml:"title"`
|
|
BasicLists testDocBasicLists `toml:"basic_lists"`
|
|
SubDocPtrs []*testSubDoc `toml:"subdocptrs"`
|
|
BasicMap map[string]string `toml:"basic_map"`
|
|
Subdocs testDocSubs `toml:"subdoc"`
|
|
Basics testDocBasics `toml:"basic"`
|
|
SubDocList []testSubDoc `toml:"subdoclist"`
|
|
err int `toml:"shouldntBeHere"`
|
|
unexported int `toml:"shouldntBeHere"`
|
|
Unexported2 int `toml:"-"`
|
|
}
|
|
|
|
type testMapDoc struct {
|
|
Title string `toml:"title"`
|
|
BasicMap map[string]string `toml:"basic_map"`
|
|
LongMap map[string]string `toml:"long_map"`
|
|
}
|
|
|
|
type testDocBasics struct {
|
|
Uint uint `toml:"uint"`
|
|
Bool bool `toml:"bool"`
|
|
Float32 float32 `toml:"float"`
|
|
Float64 float64 `toml:"float64"`
|
|
Int int `toml:"int"`
|
|
String *string `toml:"string"`
|
|
Date time.Time `toml:"date"`
|
|
unexported int `toml:"shouldntBeHere"`
|
|
}
|
|
|
|
type testDocBasicLists struct {
|
|
Floats []*float32 `toml:"floats"`
|
|
Bools []bool `toml:"bools"`
|
|
Dates []time.Time `toml:"dates"`
|
|
Ints []int `toml:"ints"`
|
|
UInts []uint `toml:"uints"`
|
|
Strings []string `toml:"strings"`
|
|
}
|
|
|
|
type testDocSubs struct {
|
|
Second *testSubDoc `toml:"second"`
|
|
First testSubDoc `toml:"first"`
|
|
}
|
|
|
|
type testSubDoc struct {
|
|
Name string `toml:"name"`
|
|
unexported int `toml:"shouldntBeHere"`
|
|
}
|
|
|
|
var biteMe = "Bite me"
|
|
var float1 float32 = 12.3
|
|
var float2 float32 = 45.6
|
|
var float3 float32 = 78.9
|
|
var subdoc = testSubDoc{"Second", 0}
|
|
|
|
var docData = testDoc{
|
|
Title: "TOML Marshal Testing",
|
|
unexported: 0,
|
|
Unexported2: 0,
|
|
Basics: testDocBasics{
|
|
Bool: true,
|
|
Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
|
Float32: 123.4,
|
|
Float64: 123.456782132399,
|
|
Int: 5000,
|
|
Uint: 5001,
|
|
String: &biteMe,
|
|
unexported: 0,
|
|
},
|
|
BasicLists: testDocBasicLists{
|
|
Bools: []bool{true, false, true},
|
|
Dates: []time.Time{
|
|
time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
|
time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC),
|
|
},
|
|
Floats: []*float32{&float1, &float2, &float3},
|
|
Ints: []int{8001, 8001, 8002},
|
|
Strings: []string{"One", "Two", "Three"},
|
|
UInts: []uint{5002, 5003},
|
|
},
|
|
BasicMap: map[string]string{
|
|
"one": "one",
|
|
"two": "two",
|
|
},
|
|
Subdocs: testDocSubs{
|
|
First: testSubDoc{"First", 0},
|
|
Second: &subdoc,
|
|
},
|
|
SubDocList: []testSubDoc{
|
|
{"List.First", 0},
|
|
{"List.Second", 0},
|
|
},
|
|
SubDocPtrs: []*testSubDoc{&subdoc},
|
|
}
|
|
|
|
var mapTestDoc = testMapDoc{
|
|
Title: "TOML Marshal Testing",
|
|
BasicMap: map[string]string{
|
|
"one": "one",
|
|
"two": "two",
|
|
},
|
|
LongMap: map[string]string{
|
|
"h1": "8",
|
|
"i2": "9",
|
|
"b3": "2",
|
|
"d4": "4",
|
|
"f5": "6",
|
|
"e6": "5",
|
|
"a7": "1",
|
|
"c8": "3",
|
|
"j9": "10",
|
|
"g10": "7",
|
|
},
|
|
}
|
|
|
|
func TestDocMarshal(t *testing.T) {
|
|
result, err := Marshal(docData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result, marshalTestToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
|
|
}
|
|
}
|
|
|
|
func TestDocMarshalOrdered(t *testing.T) {
|
|
var result bytes.Buffer
|
|
err := NewEncoder(&result).Order(OrderPreserve).Encode(docData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result.Bytes(), marshalOrderPreserveToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalOrderPreserveToml, result.Bytes())
|
|
}
|
|
}
|
|
|
|
func TestDocMarshalMaps(t *testing.T) {
|
|
result, err := Marshal(mapTestDoc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result, mashalOrderPreserveMapToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result)
|
|
}
|
|
}
|
|
|
|
func TestDocMarshalOrderedMaps(t *testing.T) {
|
|
var result bytes.Buffer
|
|
err := NewEncoder(&result).Order(OrderPreserve).Encode(mapTestDoc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result.Bytes(), mashalOrderPreserveMapToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result.Bytes())
|
|
}
|
|
}
|
|
|
|
func TestDocMarshalPointer(t *testing.T) {
|
|
result, err := Marshal(&docData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(result, marshalTestToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
|
|
}
|
|
}
|
|
|
|
func TestDocUnmarshal(t *testing.T) {
|
|
result := testDoc{}
|
|
err := Unmarshal(marshalTestToml, &result)
|
|
expected := docData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
resStr, _ := json.MarshalIndent(result, "", " ")
|
|
expStr, _ := json.MarshalIndent(expected, "", " ")
|
|
t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
|
|
}
|
|
}
|
|
|
|
func TestDocPartialUnmarshal(t *testing.T) {
|
|
file, err := ioutil.TempFile("", "test-*.toml")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(file.Name())
|
|
|
|
err = ioutil.WriteFile(file.Name(), marshalTestToml, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tree, _ := LoadFile(file.Name())
|
|
subTree := tree.Get("subdoc").(*Tree)
|
|
|
|
result := testDocSubs{}
|
|
err = subTree.Unmarshal(&result)
|
|
expected := docData.Subdocs
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
resStr, _ := json.MarshalIndent(result, "", " ")
|
|
expStr, _ := json.MarshalIndent(expected, "", " ")
|
|
t.Errorf("Bad partial unmartial: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
|
|
}
|
|
}
|
|
|
|
type tomlTypeCheckTest struct {
|
|
name string
|
|
item interface{}
|
|
typ int //0=primitive, 1=otherslice, 2=treeslice, 3=tree
|
|
}
|
|
|
|
func TestTypeChecks(t *testing.T) {
|
|
tests := []tomlTypeCheckTest{
|
|
{"bool", true, 0},
|
|
{"bool", false, 0},
|
|
{"int", int(2), 0},
|
|
{"int8", int8(2), 0},
|
|
{"int16", int16(2), 0},
|
|
{"int32", int32(2), 0},
|
|
{"int64", int64(2), 0},
|
|
{"uint", uint(2), 0},
|
|
{"uint8", uint8(2), 0},
|
|
{"uint16", uint16(2), 0},
|
|
{"uint32", uint32(2), 0},
|
|
{"uint64", uint64(2), 0},
|
|
{"float32", float32(3.14), 0},
|
|
{"float64", float64(3.14), 0},
|
|
{"string", "lorem ipsum", 0},
|
|
{"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0},
|
|
{"stringlist", []string{"hello", "hi"}, 1},
|
|
{"stringlistptr", &[]string{"hello", "hi"}, 1},
|
|
{"stringarray", [2]string{"hello", "hi"}, 1},
|
|
{"stringarrayptr", &[2]string{"hello", "hi"}, 1},
|
|
{"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
|
|
{"timelistptr", &[]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
|
|
{"timearray", [1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
|
|
{"timearrayptr", &[1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
|
|
{"objectlist", []tomlTypeCheckTest{}, 2},
|
|
{"objectlistptr", &[]tomlTypeCheckTest{}, 2},
|
|
{"objectarray", [2]tomlTypeCheckTest{{}, {}}, 2},
|
|
{"objectlistptr", &[2]tomlTypeCheckTest{{}, {}}, 2},
|
|
{"object", tomlTypeCheckTest{}, 3},
|
|
{"objectptr", &tomlTypeCheckTest{}, 3},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
expected := []bool{false, false, false, false}
|
|
expected[test.typ] = true
|
|
result := []bool{
|
|
isPrimitive(reflect.TypeOf(test.item)),
|
|
isOtherSequence(reflect.TypeOf(test.item)),
|
|
isTreeSequence(reflect.TypeOf(test.item)),
|
|
isTree(reflect.TypeOf(test.item)),
|
|
}
|
|
if !reflect.DeepEqual(expected, result) {
|
|
t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
type unexportedMarshalTestStruct struct {
|
|
String string `toml:"string"`
|
|
StringList []string `toml:"strlist"`
|
|
Sub basicMarshalTestSubStruct `toml:"subdoc"`
|
|
SubList []basicMarshalTestSubStruct `toml:"sublist"`
|
|
unexported int `toml:"shouldntBeHere"`
|
|
Unexported2 int `toml:"-"`
|
|
}
|
|
|
|
var unexportedTestData = unexportedMarshalTestStruct{
|
|
String: "Hello",
|
|
StringList: []string{"Howdy", "Hey There"},
|
|
Sub: basicMarshalTestSubStruct{"One"},
|
|
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
|
|
unexported: 0,
|
|
Unexported2: 0,
|
|
}
|
|
|
|
var unexportedTestToml = []byte(`string = "Hello"
|
|
strlist = ["Howdy","Hey There"]
|
|
unexported = 1
|
|
shouldntBeHere = 2
|
|
|
|
[subdoc]
|
|
String2 = "One"
|
|
|
|
[[sublist]]
|
|
String2 = "Two"
|
|
|
|
[[sublist]]
|
|
String2 = "Three"
|
|
`)
|
|
|
|
func TestUnexportedUnmarshal(t *testing.T) {
|
|
result := unexportedMarshalTestStruct{}
|
|
err := Unmarshal(unexportedTestToml, &result)
|
|
expected := unexportedTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
type errStruct struct {
|
|
Bool bool `toml:"bool"`
|
|
Date time.Time `toml:"date"`
|
|
Float float64 `toml:"float"`
|
|
Int int16 `toml:"int"`
|
|
String *string `toml:"string"`
|
|
}
|
|
|
|
var errTomls = []string{
|
|
"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
|
|
"bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"",
|
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1",
|
|
}
|
|
|
|
type mapErr struct {
|
|
Vals map[string]float64
|
|
}
|
|
|
|
type intErr struct {
|
|
Int1 int
|
|
Int2 int8
|
|
Int3 int16
|
|
Int4 int32
|
|
Int5 int64
|
|
UInt1 uint
|
|
UInt2 uint8
|
|
UInt3 uint16
|
|
UInt4 uint32
|
|
UInt5 uint64
|
|
Flt1 float32
|
|
Flt2 float64
|
|
}
|
|
|
|
var intErrTomls = []string{
|
|
"Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0",
|
|
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []",
|
|
}
|
|
|
|
func TestErrUnmarshal(t *testing.T) {
|
|
for ind, toml := range errTomls {
|
|
result := errStruct{}
|
|
err := Unmarshal([]byte(toml), &result)
|
|
if err == nil {
|
|
t.Errorf("Expected err from case %d\n", ind)
|
|
}
|
|
}
|
|
result2 := mapErr{}
|
|
err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
|
|
if err == nil {
|
|
t.Errorf("Expected err from map")
|
|
}
|
|
for ind, toml := range intErrTomls {
|
|
result3 := intErr{}
|
|
err := Unmarshal([]byte(toml), &result3)
|
|
if err == nil {
|
|
t.Errorf("Expected int err from case %d\n", ind)
|
|
}
|
|
}
|
|
}
|
|
|
|
type emptyMarshalTestStruct struct {
|
|
Title string `toml:"title"`
|
|
Bool bool `toml:"bool"`
|
|
Int int `toml:"int"`
|
|
String string `toml:"string"`
|
|
StringList []string `toml:"stringlist"`
|
|
Ptr *basicMarshalTestStruct `toml:"ptr"`
|
|
Map map[string]string `toml:"map"`
|
|
}
|
|
|
|
var emptyTestData = emptyMarshalTestStruct{
|
|
Title: "Placeholder",
|
|
Bool: false,
|
|
Int: 0,
|
|
String: "",
|
|
StringList: []string{},
|
|
Ptr: nil,
|
|
Map: map[string]string{},
|
|
}
|
|
|
|
var emptyTestToml = []byte(`bool = false
|
|
int = 0
|
|
string = ""
|
|
stringlist = []
|
|
title = "Placeholder"
|
|
|
|
[map]
|
|
`)
|
|
|
|
type emptyMarshalTestStruct2 struct {
|
|
Title string `toml:"title"`
|
|
Bool bool `toml:"bool,omitempty"`
|
|
Int int `toml:"int, omitempty"`
|
|
String string `toml:"string,omitempty "`
|
|
StringList []string `toml:"stringlist,omitempty"`
|
|
Ptr *basicMarshalTestStruct `toml:"ptr,omitempty"`
|
|
Map map[string]string `toml:"map,omitempty"`
|
|
}
|
|
|
|
var emptyTestData2 = emptyMarshalTestStruct2{
|
|
Title: "Placeholder",
|
|
Bool: false,
|
|
Int: 0,
|
|
String: "",
|
|
StringList: []string{},
|
|
Ptr: nil,
|
|
Map: map[string]string{},
|
|
}
|
|
|
|
var emptyTestToml2 = []byte(`title = "Placeholder"
|
|
`)
|
|
|
|
func TestEmptyMarshal(t *testing.T) {
|
|
result, err := Marshal(emptyTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := emptyTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestEmptyMarshalOmit(t *testing.T) {
|
|
result, err := Marshal(emptyTestData2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := emptyTestToml2
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestEmptyUnmarshal(t *testing.T) {
|
|
result := emptyMarshalTestStruct{}
|
|
err := Unmarshal(emptyTestToml, &result)
|
|
expected := emptyTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestEmptyUnmarshalOmit(t *testing.T) {
|
|
result := emptyMarshalTestStruct2{}
|
|
err := Unmarshal(emptyTestToml, &result)
|
|
expected := emptyTestData2
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
type pointerMarshalTestStruct struct {
|
|
Str *string
|
|
List *[]string
|
|
ListPtr *[]*string
|
|
Map *map[string]string
|
|
MapPtr *map[string]*string
|
|
EmptyStr *string
|
|
EmptyList *[]string
|
|
EmptyMap *map[string]string
|
|
DblPtr *[]*[]*string
|
|
}
|
|
|
|
var pointerStr = "Hello"
|
|
var pointerList = []string{"Hello back"}
|
|
var pointerListPtr = []*string{&pointerStr}
|
|
var pointerMap = map[string]string{"response": "Goodbye"}
|
|
var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
|
|
var pointerTestData = pointerMarshalTestStruct{
|
|
Str: &pointerStr,
|
|
List: &pointerList,
|
|
ListPtr: &pointerListPtr,
|
|
Map: &pointerMap,
|
|
MapPtr: &pointerMapPtr,
|
|
EmptyStr: nil,
|
|
EmptyList: nil,
|
|
EmptyMap: nil,
|
|
}
|
|
|
|
var pointerTestToml = []byte(`List = ["Hello back"]
|
|
ListPtr = ["Hello"]
|
|
Str = "Hello"
|
|
|
|
[Map]
|
|
response = "Goodbye"
|
|
|
|
[MapPtr]
|
|
alternate = "Hello"
|
|
`)
|
|
|
|
func TestPointerMarshal(t *testing.T) {
|
|
result, err := Marshal(pointerTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := pointerTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestPointerUnmarshal(t *testing.T) {
|
|
result := pointerMarshalTestStruct{}
|
|
err := Unmarshal(pointerTestToml, &result)
|
|
expected := pointerTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalTypeMismatch(t *testing.T) {
|
|
result := pointerMarshalTestStruct{}
|
|
err := Unmarshal([]byte("List = 123"), &result)
|
|
if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") {
|
|
t.Errorf("Type mismatch must be reported: got %v", err.Error())
|
|
}
|
|
}
|
|
|
|
type nestedMarshalTestStruct struct {
|
|
String [][]string
|
|
//Struct [][]basicMarshalTestSubStruct
|
|
StringPtr *[]*[]*string
|
|
// StructPtr *[]*[]*basicMarshalTestSubStruct
|
|
}
|
|
|
|
var str1 = "Three"
|
|
var str2 = "Four"
|
|
var strPtr = []*string{&str1, &str2}
|
|
var strPtr2 = []*[]*string{&strPtr}
|
|
|
|
var nestedTestData = nestedMarshalTestStruct{
|
|
String: [][]string{{"Five", "Six"}, {"One", "Two"}},
|
|
StringPtr: &strPtr2,
|
|
}
|
|
|
|
var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]]
|
|
StringPtr = [["Three","Four"]]
|
|
`)
|
|
|
|
func TestNestedMarshal(t *testing.T) {
|
|
result, err := Marshal(nestedTestData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := nestedTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestNestedUnmarshal(t *testing.T) {
|
|
result := nestedMarshalTestStruct{}
|
|
err := Unmarshal(nestedTestToml, &result)
|
|
expected := nestedTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
type customMarshalerParent struct {
|
|
Self customMarshaler `toml:"me"`
|
|
Friends []customMarshaler `toml:"friends"`
|
|
}
|
|
|
|
type customMarshaler struct {
|
|
FirsName string
|
|
LastName string
|
|
}
|
|
|
|
func (c customMarshaler) MarshalTOML() ([]byte, error) {
|
|
fullName := fmt.Sprintf("%s %s", c.FirsName, c.LastName)
|
|
return []byte(fullName), nil
|
|
}
|
|
|
|
var customMarshalerData = customMarshaler{FirsName: "Sally", LastName: "Fields"}
|
|
var customMarshalerToml = []byte(`Sally Fields`)
|
|
var nestedCustomMarshalerData = customMarshalerParent{
|
|
Self: customMarshaler{FirsName: "Maiku", LastName: "Suteda"},
|
|
Friends: []customMarshaler{customMarshalerData},
|
|
}
|
|
var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
|
|
me = "Maiku Suteda"
|
|
`)
|
|
|
|
func TestCustomMarshaler(t *testing.T) {
|
|
result, err := Marshal(customMarshalerData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := customMarshalerToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestNestedCustomMarshaler(t *testing.T) {
|
|
result, err := Marshal(nestedCustomMarshalerData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := nestedCustomMarshalerToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
var commentTestToml = []byte(`
|
|
# it's a comment on type
|
|
[postgres]
|
|
# isCommented = "dvalue"
|
|
noComment = "cvalue"
|
|
|
|
# A comment on AttrB with a
|
|
# break line
|
|
password = "bvalue"
|
|
|
|
# A comment on AttrA
|
|
user = "avalue"
|
|
|
|
[[postgres.My]]
|
|
|
|
# a comment on my on typeC
|
|
My = "Foo"
|
|
|
|
[[postgres.My]]
|
|
|
|
# a comment on my on typeC
|
|
My = "Baar"
|
|
`)
|
|
|
|
func TestMarshalComment(t *testing.T) {
|
|
type TypeC struct {
|
|
My string `comment:"a comment on my on typeC"`
|
|
}
|
|
type TypeB struct {
|
|
AttrA string `toml:"user" comment:"A comment on AttrA"`
|
|
AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"`
|
|
AttrC string `toml:"noComment"`
|
|
AttrD string `toml:"isCommented" commented:"true"`
|
|
My []TypeC
|
|
}
|
|
type TypeA struct {
|
|
TypeB TypeB `toml:"postgres" comment:"it's a comment on type"`
|
|
}
|
|
|
|
ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
|
|
config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}}
|
|
result, err := Marshal(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := commentTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestMarshalMultilineCommented(t *testing.T) {
|
|
expectedToml := []byte(`# MultilineArray = [
|
|
# 100,
|
|
# 200,
|
|
# 300,
|
|
# ]
|
|
# MultilineNestedArray = [
|
|
# [
|
|
# "a",
|
|
# "b",
|
|
# "c",
|
|
# ],
|
|
# [
|
|
# "d",
|
|
# "e",
|
|
# "f",
|
|
# ],
|
|
# ]
|
|
# MultilineString = """
|
|
# I
|
|
# am
|
|
# Allen"""
|
|
NonCommented = "Not commented line"
|
|
`)
|
|
type StructWithMultiline struct {
|
|
NonCommented string
|
|
MultilineString string `commented:"true" multiline:"true"`
|
|
MultilineArray []int `commented:"true"`
|
|
MultilineNestedArray [][]string `commented:"true"`
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
enc := NewEncoder(&buf)
|
|
if err := enc.ArraysWithOneElementPerLine(true).Encode(StructWithMultiline{
|
|
NonCommented: "Not commented line",
|
|
MultilineString: "I\nam\nAllen",
|
|
MultilineArray: []int{100, 200, 300},
|
|
MultilineNestedArray: [][]string{
|
|
{"a", "b", "c"},
|
|
{"d", "e", "f"},
|
|
},
|
|
}); err == nil {
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expectedToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedToml, result)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestMarshalNonPrimitiveTypeCommented(t *testing.T) {
|
|
expectedToml := []byte(`
|
|
# [CommentedMapField]
|
|
|
|
# [CommentedMapField.CommentedMapField1]
|
|
# SingleLineString = "This line should be commented out"
|
|
|
|
# [CommentedMapField.CommentedMapField2]
|
|
# SingleLineString = "This line should be commented out"
|
|
|
|
# [CommentedStructField]
|
|
|
|
# [CommentedStructField.CommentedStructField]
|
|
# MultilineArray = [
|
|
# 1,
|
|
# 2,
|
|
# ]
|
|
# MultilineNestedArray = [
|
|
# [
|
|
# 10,
|
|
# 20,
|
|
# ],
|
|
# [
|
|
# 100,
|
|
# 200,
|
|
# ],
|
|
# ]
|
|
# MultilineString = """
|
|
# This line
|
|
# should be
|
|
# commented out"""
|
|
|
|
# [CommentedStructField.NotCommentedStructField]
|
|
# MultilineArray = [
|
|
# 1,
|
|
# 2,
|
|
# ]
|
|
# MultilineNestedArray = [
|
|
# [
|
|
# 10,
|
|
# 20,
|
|
# ],
|
|
# [
|
|
# 100,
|
|
# 200,
|
|
# ],
|
|
# ]
|
|
# MultilineString = """
|
|
# This line
|
|
# should be
|
|
# commented out"""
|
|
|
|
[NotCommentedStructField]
|
|
|
|
# [NotCommentedStructField.CommentedStructField]
|
|
# MultilineArray = [
|
|
# 1,
|
|
# 2,
|
|
# ]
|
|
# MultilineNestedArray = [
|
|
# [
|
|
# 10,
|
|
# 20,
|
|
# ],
|
|
# [
|
|
# 100,
|
|
# 200,
|
|
# ],
|
|
# ]
|
|
# MultilineString = """
|
|
# This line
|
|
# should be
|
|
# commented out"""
|
|
|
|
[NotCommentedStructField.NotCommentedStructField]
|
|
MultilineArray = [
|
|
3,
|
|
4,
|
|
]
|
|
MultilineNestedArray = [
|
|
[
|
|
30,
|
|
40,
|
|
],
|
|
[
|
|
300,
|
|
400,
|
|
],
|
|
]
|
|
MultilineString = """
|
|
This line
|
|
should NOT be
|
|
commented out"""
|
|
`)
|
|
type InnerStruct struct {
|
|
MultilineString string `multiline:"true"`
|
|
MultilineArray []int
|
|
MultilineNestedArray [][]int
|
|
}
|
|
type MiddleStruct struct {
|
|
NotCommentedStructField InnerStruct
|
|
CommentedStructField InnerStruct `commented:"true"`
|
|
}
|
|
type OuterStruct struct {
|
|
CommentedStructField MiddleStruct `commented:"true"`
|
|
NotCommentedStructField MiddleStruct
|
|
CommentedMapField map[string]struct{ SingleLineString string } `commented:"true"`
|
|
}
|
|
|
|
commentedTestStruct := OuterStruct{
|
|
CommentedStructField: MiddleStruct{
|
|
NotCommentedStructField: InnerStruct{
|
|
MultilineString: "This line\nshould be\ncommented out",
|
|
MultilineArray: []int{1, 2},
|
|
MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
|
|
},
|
|
CommentedStructField: InnerStruct{
|
|
MultilineString: "This line\nshould be\ncommented out",
|
|
MultilineArray: []int{1, 2},
|
|
MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
|
|
},
|
|
},
|
|
NotCommentedStructField: MiddleStruct{
|
|
NotCommentedStructField: InnerStruct{
|
|
MultilineString: "This line\nshould NOT be\ncommented out",
|
|
MultilineArray: []int{3, 4},
|
|
MultilineNestedArray: [][]int{{30, 40}, {300, 400}},
|
|
},
|
|
CommentedStructField: InnerStruct{
|
|
MultilineString: "This line\nshould be\ncommented out",
|
|
MultilineArray: []int{1, 2},
|
|
MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
|
|
},
|
|
},
|
|
CommentedMapField: map[string]struct{ SingleLineString string }{
|
|
"CommentedMapField1": {
|
|
SingleLineString: "This line should be commented out",
|
|
},
|
|
"CommentedMapField2": {
|
|
SingleLineString: "This line should be commented out",
|
|
},
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
enc := NewEncoder(&buf)
|
|
if err := enc.ArraysWithOneElementPerLine(true).Encode(commentedTestStruct); err == nil {
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expectedToml) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedToml, result)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type mapsTestStruct struct {
|
|
Simple map[string]string
|
|
Paths map[string]string
|
|
Other map[string]float64
|
|
X struct {
|
|
Y struct {
|
|
Z map[string]bool
|
|
}
|
|
}
|
|
}
|
|
|
|
var mapsTestData = mapsTestStruct{
|
|
Simple: map[string]string{
|
|
"one plus one": "two",
|
|
"next": "three",
|
|
},
|
|
Paths: map[string]string{
|
|
"/this/is/a/path": "/this/is/also/a/path",
|
|
"/heloo.txt": "/tmp/lololo.txt",
|
|
},
|
|
Other: map[string]float64{
|
|
"testing": 3.9999,
|
|
},
|
|
X: struct{ Y struct{ Z map[string]bool } }{
|
|
Y: struct{ Z map[string]bool }{
|
|
Z: map[string]bool{
|
|
"is.Nested": true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
var mapsTestToml = []byte(`
|
|
[Other]
|
|
"testing" = 3.9999
|
|
|
|
[Paths]
|
|
"/heloo.txt" = "/tmp/lololo.txt"
|
|
"/this/is/a/path" = "/this/is/also/a/path"
|
|
|
|
[Simple]
|
|
"next" = "three"
|
|
"one plus one" = "two"
|
|
|
|
[X]
|
|
|
|
[X.Y]
|
|
|
|
[X.Y.Z]
|
|
"is.Nested" = true
|
|
`)
|
|
|
|
func TestEncodeQuotedMapKeys(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result := buf.Bytes()
|
|
expected := mapsTestToml
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestDecodeQuotedMapKeys(t *testing.T) {
|
|
result := mapsTestStruct{}
|
|
err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result)
|
|
expected := mapsTestData
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
type structArrayNoTag struct {
|
|
A struct {
|
|
B []int64
|
|
C []int64
|
|
}
|
|
}
|
|
|
|
func TestMarshalArray(t *testing.T) {
|
|
expected := []byte(`
|
|
[A]
|
|
B = [1,2,3]
|
|
C = [1]
|
|
`)
|
|
|
|
m := structArrayNoTag{
|
|
A: struct {
|
|
B []int64
|
|
C []int64
|
|
}{
|
|
B: []int64{1, 2, 3},
|
|
C: []int64{1},
|
|
},
|
|
}
|
|
|
|
b, err := Marshal(m)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(b, expected) {
|
|
t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
|
|
}
|
|
}
|
|
|
|
func TestMarshalArrayOnePerLine(t *testing.T) {
|
|
expected := []byte(`
|
|
[A]
|
|
B = [
|
|
1,
|
|
2,
|
|
3,
|
|
]
|
|
C = [1]
|
|
`)
|
|
|
|
m := structArrayNoTag{
|
|
A: struct {
|
|
B []int64
|
|
C []int64
|
|
}{
|
|
B: []int64{1, 2, 3},
|
|
C: []int64{1},
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true)
|
|
err := encoder.Encode(m)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
b := buf.Bytes()
|
|
|
|
if !bytes.Equal(b, expected) {
|
|
t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
|
|
}
|
|
}
|
|
|
|
var customTagTestToml = []byte(`
|
|
[postgres]
|
|
password = "bvalue"
|
|
user = "avalue"
|
|
|
|
[[postgres.My]]
|
|
My = "Foo"
|
|
|
|
[[postgres.My]]
|
|
My = "Baar"
|
|
`)
|
|
|
|
func TestMarshalCustomTag(t *testing.T) {
|
|
type TypeC struct {
|
|
My string
|
|
}
|
|
type TypeB struct {
|
|
AttrA string `file:"user"`
|
|
AttrB string `file:"password"`
|
|
My []TypeC
|
|
}
|
|
type TypeA struct {
|
|
TypeB TypeB `file:"postgres"`
|
|
}
|
|
|
|
ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
|
|
config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", My: ta}}
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).SetTagName("file").Encode(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := customTagTestToml
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
var customCommentTagTestToml = []byte(`
|
|
# db connection
|
|
[postgres]
|
|
|
|
# db pass
|
|
password = "bvalue"
|
|
|
|
# db user
|
|
user = "avalue"
|
|
`)
|
|
|
|
func TestMarshalCustomComment(t *testing.T) {
|
|
type TypeB struct {
|
|
AttrA string `toml:"user" descr:"db user"`
|
|
AttrB string `toml:"password" descr:"db pass"`
|
|
}
|
|
type TypeA struct {
|
|
TypeB TypeB `toml:"postgres" descr:"db connection"`
|
|
}
|
|
|
|
config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).SetTagComment("descr").Encode(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := customCommentTagTestToml
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
var customCommentedTagTestToml = []byte(`
|
|
[postgres]
|
|
# password = "bvalue"
|
|
# user = "avalue"
|
|
`)
|
|
|
|
func TestMarshalCustomCommented(t *testing.T) {
|
|
type TypeB struct {
|
|
AttrA string `toml:"user" disable:"true"`
|
|
AttrB string `toml:"password" disable:"true"`
|
|
}
|
|
type TypeA struct {
|
|
TypeB TypeB `toml:"postgres"`
|
|
}
|
|
|
|
config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).SetTagCommented("disable").Encode(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := customCommentedTagTestToml
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestMarshalDirectMultilineString(t *testing.T) {
|
|
tree := newTree()
|
|
tree.SetWithOptions("mykey", SetOptions{
|
|
Multiline: true,
|
|
}, "my\x11multiline\nstring\ba\tb\fc\rd\"e\\!")
|
|
result, err := tree.Marshal()
|
|
if err != nil {
|
|
t.Fatal("marshal should not error:", err)
|
|
}
|
|
expected := []byte("mykey = \"\"\"\nmy\\u0011multiline\nstring\\ba\tb\\fc\rd\"e\\!\"\"\"\n")
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalTabInStringAndQuotedKey(t *testing.T) {
|
|
type Test struct {
|
|
Field1 string `toml:"Fie ld1"`
|
|
Field2 string
|
|
}
|
|
|
|
type TestCase struct {
|
|
desc string
|
|
input []byte
|
|
expected Test
|
|
}
|
|
|
|
testCases := []TestCase{
|
|
{
|
|
desc: "multiline string with tab",
|
|
input: []byte("Field2 = \"\"\"\nhello\tworld\"\"\""),
|
|
expected: Test{
|
|
Field2: "hello\tworld",
|
|
},
|
|
},
|
|
{
|
|
desc: "quoted key with tab",
|
|
input: []byte("\"Fie\tld1\" = \"key with tab\""),
|
|
expected: Test{
|
|
Field1: "key with tab",
|
|
},
|
|
},
|
|
{
|
|
desc: "basic string tab",
|
|
input: []byte("Field2 = \"hello\tworld\""),
|
|
expected: Test{
|
|
Field2: "hello\tworld",
|
|
},
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
result := Test{}
|
|
err := Unmarshal(testCases[i].input, &result)
|
|
if err != nil {
|
|
t.Errorf("%s test error:%v", testCases[i].desc, err)
|
|
continue
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, testCases[i].expected) {
|
|
t.Errorf("%s test error: expected\n-----\n%+v\n-----\ngot\n-----\n%+v\n-----\n",
|
|
testCases[i].desc, testCases[i].expected, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
var customMultilineTagTestToml = []byte(`int_slice = [
|
|
1,
|
|
2,
|
|
3,
|
|
]
|
|
`)
|
|
|
|
func TestMarshalCustomMultiline(t *testing.T) {
|
|
type TypeA struct {
|
|
AttrA []int `toml:"int_slice" mltln:"true"`
|
|
}
|
|
|
|
config := TypeA{AttrA: []int{1, 2, 3}}
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).ArraysWithOneElementPerLine(true).SetTagMultiline("mltln").Encode(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := customMultilineTagTestToml
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestMultilineWithAdjacentQuotationMarks(t *testing.T) {
|
|
type testStruct struct {
|
|
Str string `multiline:"true"`
|
|
}
|
|
type testCase struct {
|
|
expected []byte
|
|
data testStruct
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
expected: []byte(`Str = """
|
|
hello\""""
|
|
`),
|
|
data: testStruct{
|
|
Str: "hello\"",
|
|
},
|
|
},
|
|
{
|
|
expected: []byte(`Str = """
|
|
""\"""\"""\""""
|
|
`),
|
|
data: testStruct{
|
|
Str: "\"\"\"\"\"\"\"\"\"",
|
|
},
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
result, err := Marshal(testCases[i].data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(result, testCases[i].expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n",
|
|
testCases[i].expected, result)
|
|
} else {
|
|
var data testStruct
|
|
if err = Unmarshal(result, &data); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.Str != testCases[i].data.Str {
|
|
t.Errorf("Round trip test fail: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n",
|
|
testCases[i].data.Str, data.Str)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMarshalEmbedTree(t *testing.T) {
|
|
expected := []byte(`OuterField1 = "Out"
|
|
OuterField2 = 1024
|
|
|
|
[TreeField]
|
|
InnerField1 = "In"
|
|
InnerField2 = 2048
|
|
|
|
[TreeField.EmbedStruct]
|
|
EmbedField = "Embed"
|
|
`)
|
|
type InnerStruct struct {
|
|
InnerField1 string
|
|
InnerField2 int
|
|
EmbedStruct struct {
|
|
EmbedField string
|
|
}
|
|
}
|
|
|
|
type OuterStruct struct {
|
|
OuterField1 string
|
|
OuterField2 int
|
|
TreeField *Tree
|
|
}
|
|
|
|
tree, err := Load(`
|
|
InnerField1 = "In"
|
|
InnerField2 = 2048
|
|
|
|
[EmbedStruct]
|
|
EmbedField = "Embed"
|
|
`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
out := OuterStruct{
|
|
"Out",
|
|
1024,
|
|
tree,
|
|
}
|
|
actual, _ := Marshal(out)
|
|
|
|
if !bytes.Equal(actual, expected) {
|
|
t.Errorf("Bad marshal: expected %s, got %s", expected, actual)
|
|
}
|
|
}
|
|
|
|
var testDocBasicToml = []byte(`
|
|
[document]
|
|
bool_val = true
|
|
date_val = 1979-05-27T07:32:00Z
|
|
float_val = 123.4
|
|
int_val = 5000
|
|
string_val = "Bite me"
|
|
uint_val = 5001
|
|
`)
|
|
|
|
type testDocCustomTag struct {
|
|
Doc testDocBasicsCustomTag `file:"document"`
|
|
}
|
|
type testDocBasicsCustomTag struct {
|
|
Bool bool `file:"bool_val"`
|
|
Date time.Time `file:"date_val"`
|
|
Float float32 `file:"float_val"`
|
|
Int int `file:"int_val"`
|
|
Uint uint `file:"uint_val"`
|
|
String *string `file:"string_val"`
|
|
unexported int `file:"shouldntBeHere"`
|
|
}
|
|
|
|
var testDocCustomTagData = testDocCustomTag{
|
|
Doc: testDocBasicsCustomTag{
|
|
Bool: true,
|
|
Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
|
Float: 123.4,
|
|
Int: 5000,
|
|
Uint: 5001,
|
|
String: &biteMe,
|
|
unexported: 0,
|
|
},
|
|
}
|
|
|
|
func TestUnmarshalCustomTag(t *testing.T) {
|
|
buf := bytes.NewBuffer(testDocBasicToml)
|
|
|
|
result := testDocCustomTag{}
|
|
err := NewDecoder(buf).SetTagName("file").Decode(&result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := testDocCustomTagData
|
|
if !reflect.DeepEqual(result, expected) {
|
|
resStr, _ := json.MarshalIndent(result, "", " ")
|
|
expStr, _ := json.MarshalIndent(expected, "", " ")
|
|
t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
|
|
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalMap(t *testing.T) {
|
|
testToml := []byte(`
|
|
a = 1
|
|
b = 2
|
|
c = 3
|
|
`)
|
|
var result map[string]int
|
|
err := Unmarshal(testToml, &result)
|
|
if err != nil {
|
|
t.Errorf("Received unexpected error: %s", err)
|
|
return
|
|
}
|
|
|
|
expected := map[string]int{
|
|
"a": 1,
|
|
"b": 2,
|
|
"c": 3,
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalMapWithTypedKey(t *testing.T) {
|
|
testToml := []byte(`
|
|
a = 1
|
|
b = 2
|
|
c = 3
|
|
`)
|
|
|
|
type letter string
|
|
var result map[letter]int
|
|
err := Unmarshal(testToml, &result)
|
|
if err != nil {
|
|
t.Errorf("Received unexpected error: %s", err)
|
|
return
|
|
}
|
|
|
|
expected := map[letter]int{
|
|
"a": 1,
|
|
"b": 2,
|
|
"c": 3,
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalNonPointer(t *testing.T) {
|
|
a := 1
|
|
err := Unmarshal([]byte{}, a)
|
|
if err == nil {
|
|
t.Fatal("unmarshal should err when given a non pointer")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalInvalidPointerKind(t *testing.T) {
|
|
a := 1
|
|
err := Unmarshal([]byte{}, &a)
|
|
if err == nil {
|
|
t.Fatal("unmarshal should err when given an invalid pointer type")
|
|
}
|
|
}
|
|
|
|
func TestMarshalSlice(t *testing.T) {
|
|
m := make([]int, 1)
|
|
m[0] = 1
|
|
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).Encode(&m)
|
|
if err == nil {
|
|
t.Error("expected error, got nil")
|
|
return
|
|
}
|
|
if err.Error() != "Only pointer to struct can be marshaled to TOML" {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestMarshalSlicePointer(t *testing.T) {
|
|
m := make([]int, 1)
|
|
m[0] = 1
|
|
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).Encode(m)
|
|
if err == nil {
|
|
t.Error("expected error, got nil")
|
|
return
|
|
}
|
|
if err.Error() != "Only a struct or map can be marshaled to TOML" {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
type testDuration struct {
|
|
Nanosec time.Duration `toml:"nanosec"`
|
|
Microsec1 time.Duration `toml:"microsec1"`
|
|
Microsec2 *time.Duration `toml:"microsec2"`
|
|
Millisec time.Duration `toml:"millisec"`
|
|
Sec time.Duration `toml:"sec"`
|
|
Min time.Duration `toml:"min"`
|
|
Hour time.Duration `toml:"hour"`
|
|
Mixed time.Duration `toml:"mixed"`
|
|
AString string `toml:"a_string"`
|
|
}
|
|
|
|
var testDurationToml = []byte(`
|
|
nanosec = "1ns"
|
|
microsec1 = "1us"
|
|
microsec2 = "1µs"
|
|
millisec = "1ms"
|
|
sec = "1s"
|
|
min = "1m"
|
|
hour = "1h"
|
|
mixed = "1h1m1s1ms1µs1ns"
|
|
a_string = "15s"
|
|
`)
|
|
|
|
func TestUnmarshalDuration(t *testing.T) {
|
|
buf := bytes.NewBuffer(testDurationToml)
|
|
|
|
result := testDuration{}
|
|
err := NewDecoder(buf).Decode(&result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ms := time.Duration(1) * time.Microsecond
|
|
expected := testDuration{
|
|
Nanosec: 1,
|
|
Microsec1: time.Microsecond,
|
|
Microsec2: &ms,
|
|
Millisec: time.Millisecond,
|
|
Sec: time.Second,
|
|
Min: time.Minute,
|
|
Hour: time.Hour,
|
|
Mixed: time.Hour +
|
|
time.Minute +
|
|
time.Second +
|
|
time.Millisecond +
|
|
time.Microsecond +
|
|
time.Nanosecond,
|
|
AString: "15s",
|
|
}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
resStr, _ := json.MarshalIndent(result, "", " ")
|
|
expStr, _ := json.MarshalIndent(expected, "", " ")
|
|
t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
|
|
|
|
}
|
|
}
|
|
|
|
var testDurationToml2 = []byte(`a_string = "15s"
|
|
hour = "1h0m0s"
|
|
microsec1 = "1µs"
|
|
microsec2 = "1µs"
|
|
millisec = "1ms"
|
|
min = "1m0s"
|
|
mixed = "1h1m1.001001001s"
|
|
nanosec = "1ns"
|
|
sec = "1s"
|
|
`)
|
|
|
|
func TestMarshalDuration(t *testing.T) {
|
|
ms := time.Duration(1) * time.Microsecond
|
|
data := testDuration{
|
|
Nanosec: 1,
|
|
Microsec1: time.Microsecond,
|
|
Microsec2: &ms,
|
|
Millisec: time.Millisecond,
|
|
Sec: time.Second,
|
|
Min: time.Minute,
|
|
Hour: time.Hour,
|
|
Mixed: time.Hour +
|
|
time.Minute +
|
|
time.Second +
|
|
time.Millisecond +
|
|
time.Microsecond +
|
|
time.Nanosecond,
|
|
AString: "15s",
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
err := NewEncoder(&buf).Encode(data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := testDurationToml2
|
|
result := buf.Bytes()
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
}
|
|
|
|
type testBadDuration struct {
|
|
Val time.Duration `toml:"val"`
|
|
}
|
|
|
|
var testBadDurationToml = []byte(`val = "1z"`)
|
|
|
|
func TestUnmarshalBadDuration(t *testing.T) {
|
|
buf := bytes.NewBuffer(testBadDurationToml)
|
|
|
|
result := testBadDuration{}
|
|
err := NewDecoder(buf).Decode(&result)
|
|
if err == nil {
|
|
t.Fatal()
|
|
}
|
|
if err.Error() != "(1, 1): Can't convert 1z(string) to time.Duration. time: unknown unit z in duration 1z" {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
}
|
|
|
|
var testCamelCaseKeyToml = []byte(`fooBar = 10`)
|
|
|
|
func TestUnmarshalCamelCaseKey(t *testing.T) {
|
|
var x struct {
|
|
FooBar int
|
|
B int
|
|
}
|
|
|
|
if err := Unmarshal(testCamelCaseKeyToml, &x); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if x.FooBar != 10 {
|
|
t.Fatal("Did not set camelCase'd key")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefault(t *testing.T) {
|
|
type EmbeddedStruct struct {
|
|
StringField string `default:"c"`
|
|
}
|
|
|
|
var doc struct {
|
|
StringField string `default:"a"`
|
|
BoolField bool `default:"true"`
|
|
IntField int `default:"1"`
|
|
Int64Field int64 `default:"2"`
|
|
Float64Field float64 `default:"3.1"`
|
|
NonEmbeddedStruct struct {
|
|
StringField string `default:"b"`
|
|
}
|
|
EmbeddedStruct
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if doc.BoolField != true {
|
|
t.Errorf("BoolField should be true, not %t", doc.BoolField)
|
|
}
|
|
if doc.StringField != "a" {
|
|
t.Errorf("StringField should be \"a\", not %s", doc.StringField)
|
|
}
|
|
if doc.IntField != 1 {
|
|
t.Errorf("IntField should be 1, not %d", doc.IntField)
|
|
}
|
|
if doc.Int64Field != 2 {
|
|
t.Errorf("Int64Field should be 2, not %d", doc.Int64Field)
|
|
}
|
|
if doc.Float64Field != 3.1 {
|
|
t.Errorf("Float64Field should be 3.1, not %f", doc.Float64Field)
|
|
}
|
|
if doc.NonEmbeddedStruct.StringField != "b" {
|
|
t.Errorf("StringField should be \"b\", not %s", doc.NonEmbeddedStruct.StringField)
|
|
}
|
|
if doc.EmbeddedStruct.StringField != "c" {
|
|
t.Errorf("StringField should be \"c\", not %s", doc.EmbeddedStruct.StringField)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefaultFailureBool(t *testing.T) {
|
|
var doc struct {
|
|
Field bool `default:"blah"`
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefaultFailureInt(t *testing.T) {
|
|
var doc struct {
|
|
Field int `default:"blah"`
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefaultFailureInt64(t *testing.T) {
|
|
var doc struct {
|
|
Field int64 `default:"blah"`
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefaultFailureFloat64(t *testing.T) {
|
|
var doc struct {
|
|
Field float64 `default:"blah"`
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalDefaultFailureUnsupported(t *testing.T) {
|
|
var doc struct {
|
|
Field struct{} `default:"blah"`
|
|
}
|
|
|
|
err := Unmarshal([]byte(``), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestMarshalNestedAnonymousStructs(t *testing.T) {
|
|
type Embedded struct {
|
|
Value string `toml:"value"`
|
|
Top struct {
|
|
Value string `toml:"value"`
|
|
} `toml:"top"`
|
|
}
|
|
|
|
type Named struct {
|
|
Value string `toml:"value"`
|
|
}
|
|
|
|
var doc struct {
|
|
Embedded
|
|
Named `toml:"named"`
|
|
Anonymous struct {
|
|
Value string `toml:"value"`
|
|
} `toml:"anonymous"`
|
|
}
|
|
|
|
expected := `value = ""
|
|
|
|
[anonymous]
|
|
value = ""
|
|
|
|
[named]
|
|
value = ""
|
|
|
|
[top]
|
|
value = ""
|
|
`
|
|
|
|
result, err := Marshal(doc)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err.Error())
|
|
}
|
|
if !bytes.Equal(result, []byte(expected)) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
|
|
}
|
|
}
|
|
|
|
func TestEncoderPromoteNestedAnonymousStructs(t *testing.T) {
|
|
type Embedded struct {
|
|
Value string `toml:"value"`
|
|
}
|
|
|
|
var doc struct {
|
|
Embedded
|
|
}
|
|
|
|
expected := `
|
|
[Embedded]
|
|
value = ""
|
|
`
|
|
var buf bytes.Buffer
|
|
if err := NewEncoder(&buf).PromoteAnonymous(true).Encode(doc); err != nil {
|
|
t.Fatalf("unexpected error: %s", err.Error())
|
|
}
|
|
if !bytes.Equal(buf.Bytes(), []byte(expected)) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, buf.String())
|
|
}
|
|
}
|
|
|
|
func TestMarshalNestedAnonymousStructs_DuplicateField(t *testing.T) {
|
|
type Embedded struct {
|
|
Value string `toml:"value"`
|
|
Top struct {
|
|
Value string `toml:"value"`
|
|
} `toml:"top"`
|
|
}
|
|
|
|
var doc struct {
|
|
Value string `toml:"value"`
|
|
Embedded
|
|
}
|
|
doc.Embedded.Value = "shadowed"
|
|
doc.Value = "shadows"
|
|
|
|
expected := `value = "shadows"
|
|
|
|
[top]
|
|
value = ""
|
|
`
|
|
|
|
result, err := Marshal(doc)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err.Error())
|
|
}
|
|
if !bytes.Equal(result, []byte(expected)) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalNestedAnonymousStructs(t *testing.T) {
|
|
type Nested struct {
|
|
Value string `toml:"nested_field"`
|
|
}
|
|
type Deep struct {
|
|
Nested
|
|
}
|
|
type Document struct {
|
|
Deep
|
|
Value string `toml:"own_field"`
|
|
}
|
|
|
|
var doc Document
|
|
|
|
err := Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc)
|
|
if err != nil {
|
|
t.Fatal("should not error")
|
|
}
|
|
if doc.Value != "own value" || doc.Nested.Value != "nested value" {
|
|
t.Fatal("unexpected values")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) {
|
|
type Nested struct {
|
|
Value string `toml:"nested"`
|
|
}
|
|
type Deep struct {
|
|
Nested
|
|
}
|
|
type Document struct {
|
|
Deep
|
|
Value string `toml:"own"`
|
|
}
|
|
|
|
var doc Document
|
|
|
|
err := Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
type unexportedFieldPreservationTest struct {
|
|
Exported string `toml:"exported"`
|
|
unexported string
|
|
Nested1 unexportedFieldPreservationTestNested `toml:"nested1"`
|
|
Nested2 *unexportedFieldPreservationTestNested `toml:"nested2"`
|
|
Nested3 *unexportedFieldPreservationTestNested `toml:"nested3"`
|
|
Slice1 []unexportedFieldPreservationTestNested `toml:"slice1"`
|
|
Slice2 []*unexportedFieldPreservationTestNested `toml:"slice2"`
|
|
}
|
|
|
|
type unexportedFieldPreservationTestNested struct {
|
|
Exported1 string `toml:"exported1"`
|
|
unexported1 string
|
|
}
|
|
|
|
func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
|
|
toml := `
|
|
exported = "visible"
|
|
unexported = "ignored"
|
|
|
|
[nested1]
|
|
exported1 = "visible1"
|
|
unexported1 = "ignored1"
|
|
|
|
[nested2]
|
|
exported1 = "visible2"
|
|
unexported1 = "ignored2"
|
|
|
|
[nested3]
|
|
exported1 = "visible3"
|
|
unexported1 = "ignored3"
|
|
|
|
[[slice1]]
|
|
exported1 = "visible3"
|
|
|
|
[[slice1]]
|
|
exported1 = "visible4"
|
|
|
|
[[slice2]]
|
|
exported1 = "visible5"
|
|
`
|
|
|
|
t.Run("unexported field should not be set from toml", func(t *testing.T) {
|
|
var actual unexportedFieldPreservationTest
|
|
err := Unmarshal([]byte(toml), &actual)
|
|
|
|
if err != nil {
|
|
t.Fatal("did not expect an error")
|
|
}
|
|
|
|
expect := unexportedFieldPreservationTest{
|
|
Exported: "visible",
|
|
unexported: "",
|
|
Nested1: unexportedFieldPreservationTestNested{"visible1", ""},
|
|
Nested2: &unexportedFieldPreservationTestNested{"visible2", ""},
|
|
Nested3: &unexportedFieldPreservationTestNested{"visible3", ""},
|
|
Slice1: []unexportedFieldPreservationTestNested{
|
|
{Exported1: "visible3"},
|
|
{Exported1: "visible4"},
|
|
},
|
|
Slice2: []*unexportedFieldPreservationTestNested{
|
|
{Exported1: "visible5"},
|
|
},
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, expect) {
|
|
t.Fatalf("%+v did not equal %+v", actual, expect)
|
|
}
|
|
})
|
|
|
|
t.Run("unexported field should be preserved", func(t *testing.T) {
|
|
actual := unexportedFieldPreservationTest{
|
|
Exported: "foo",
|
|
unexported: "bar",
|
|
Nested1: unexportedFieldPreservationTestNested{"baz", "bax"},
|
|
Nested2: nil,
|
|
Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"},
|
|
}
|
|
err := Unmarshal([]byte(toml), &actual)
|
|
|
|
if err != nil {
|
|
t.Fatal("did not expect an error")
|
|
}
|
|
|
|
expect := unexportedFieldPreservationTest{
|
|
Exported: "visible",
|
|
unexported: "bar",
|
|
Nested1: unexportedFieldPreservationTestNested{"visible1", "bax"},
|
|
Nested2: &unexportedFieldPreservationTestNested{"visible2", ""},
|
|
Nested3: &unexportedFieldPreservationTestNested{"visible3", "bax"},
|
|
Slice1: []unexportedFieldPreservationTestNested{
|
|
{Exported1: "visible3"},
|
|
{Exported1: "visible4"},
|
|
},
|
|
Slice2: []*unexportedFieldPreservationTestNested{
|
|
{Exported1: "visible5"},
|
|
},
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, expect) {
|
|
t.Fatalf("%+v did not equal %+v", actual, expect)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTreeMarshal(t *testing.T) {
|
|
cases := [][]byte{
|
|
basicTestToml,
|
|
marshalTestToml,
|
|
emptyTestToml,
|
|
pointerTestToml,
|
|
}
|
|
for _, expected := range cases {
|
|
t.Run("", func(t *testing.T) {
|
|
tree, err := LoadBytes(expected)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result, err := tree.Marshal()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalArrays(t *testing.T) {
|
|
cases := []struct {
|
|
Data interface{}
|
|
Expected string
|
|
}{
|
|
{
|
|
Data: struct {
|
|
XY [2]int
|
|
}{
|
|
XY: [2]int{1, 2},
|
|
},
|
|
Expected: `XY = [1,2]
|
|
`,
|
|
},
|
|
{
|
|
Data: struct {
|
|
XY [1][2]int
|
|
}{
|
|
XY: [1][2]int{{1, 2}},
|
|
},
|
|
Expected: `XY = [[1,2]]
|
|
`,
|
|
},
|
|
{
|
|
Data: struct {
|
|
XY [1][]int
|
|
}{
|
|
XY: [1][]int{{1, 2}},
|
|
},
|
|
Expected: `XY = [[1,2]]
|
|
`,
|
|
},
|
|
{
|
|
Data: struct {
|
|
XY [][2]int
|
|
}{
|
|
XY: [][2]int{{1, 2}},
|
|
},
|
|
Expected: `XY = [[1,2]]
|
|
`,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run("", func(t *testing.T) {
|
|
result, err := Marshal(tc.Data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(result, []byte(tc.Expected)) {
|
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", []byte(tc.Expected), result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalLocalDate(t *testing.T) {
|
|
t.Run("ToLocalDate", func(t *testing.T) {
|
|
type dateStruct struct {
|
|
Date LocalDate
|
|
}
|
|
|
|
toml := `date = 1979-05-27`
|
|
|
|
var obj dateStruct
|
|
|
|
err := Unmarshal([]byte(toml), &obj)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if obj.Date.Year != 1979 {
|
|
t.Errorf("expected year 1979, got %d", obj.Date.Year)
|
|
}
|
|
if obj.Date.Month != 5 {
|
|
t.Errorf("expected month 5, got %d", obj.Date.Month)
|
|
}
|
|
if obj.Date.Day != 27 {
|
|
t.Errorf("expected day 27, got %d", obj.Date.Day)
|
|
}
|
|
})
|
|
|
|
t.Run("ToLocalDate", func(t *testing.T) {
|
|
type dateStruct struct {
|
|
Date time.Time
|
|
}
|
|
|
|
toml := `date = 1979-05-27`
|
|
|
|
var obj dateStruct
|
|
|
|
err := Unmarshal([]byte(toml), &obj)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if obj.Date.Year() != 1979 {
|
|
t.Errorf("expected year 1979, got %d", obj.Date.Year())
|
|
}
|
|
if obj.Date.Month() != 5 {
|
|
t.Errorf("expected month 5, got %d", obj.Date.Month())
|
|
}
|
|
if obj.Date.Day() != 27 {
|
|
t.Errorf("expected day 27, got %d", obj.Date.Day())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMarshalLocalDate(t *testing.T) {
|
|
type dateStruct struct {
|
|
Date LocalDate
|
|
}
|
|
|
|
obj := dateStruct{Date: LocalDate{
|
|
Year: 1979,
|
|
Month: 5,
|
|
Day: 27,
|
|
}}
|
|
|
|
b, err := Marshal(obj)
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
got := string(b)
|
|
expected := `Date = 1979-05-27
|
|
`
|
|
|
|
if got != expected {
|
|
t.Errorf("expected '%s', got '%s'", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalLocalDateTime(t *testing.T) {
|
|
examples := []struct {
|
|
name string
|
|
in string
|
|
out LocalDateTime
|
|
}{
|
|
{
|
|
name: "normal",
|
|
in: "1979-05-27T07:32:00",
|
|
out: LocalDateTime{
|
|
Date: LocalDate{
|
|
Year: 1979,
|
|
Month: 5,
|
|
Day: 27,
|
|
},
|
|
Time: LocalTime{
|
|
Hour: 7,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 0,
|
|
},
|
|
}},
|
|
{
|
|
name: "with nanoseconds",
|
|
in: "1979-05-27T00:32:00.999999",
|
|
out: LocalDateTime{
|
|
Date: LocalDate{
|
|
Year: 1979,
|
|
Month: 5,
|
|
Day: 27,
|
|
},
|
|
Time: LocalTime{
|
|
Hour: 0,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 999999000,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, example := range examples {
|
|
toml := fmt.Sprintf(`date = %s`, example.in)
|
|
|
|
t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) {
|
|
type dateStruct struct {
|
|
Date LocalDateTime
|
|
}
|
|
|
|
var obj dateStruct
|
|
|
|
err := Unmarshal([]byte(toml), &obj)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if obj.Date != example.out {
|
|
t.Errorf("expected '%s', got '%s'", example.out, obj.Date)
|
|
}
|
|
})
|
|
|
|
t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) {
|
|
type dateStruct struct {
|
|
Date time.Time
|
|
}
|
|
|
|
var obj dateStruct
|
|
|
|
err := Unmarshal([]byte(toml), &obj)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if obj.Date.Year() != example.out.Date.Year {
|
|
t.Errorf("expected year %d, got %d", example.out.Date.Year, obj.Date.Year())
|
|
}
|
|
if obj.Date.Month() != example.out.Date.Month {
|
|
t.Errorf("expected month %d, got %d", example.out.Date.Month, obj.Date.Month())
|
|
}
|
|
if obj.Date.Day() != example.out.Date.Day {
|
|
t.Errorf("expected day %d, got %d", example.out.Date.Day, obj.Date.Day())
|
|
}
|
|
if obj.Date.Hour() != example.out.Time.Hour {
|
|
t.Errorf("expected hour %d, got %d", example.out.Time.Hour, obj.Date.Hour())
|
|
}
|
|
if obj.Date.Minute() != example.out.Time.Minute {
|
|
t.Errorf("expected minute %d, got %d", example.out.Time.Minute, obj.Date.Minute())
|
|
}
|
|
if obj.Date.Second() != example.out.Time.Second {
|
|
t.Errorf("expected second %d, got %d", example.out.Time.Second, obj.Date.Second())
|
|
}
|
|
if obj.Date.Nanosecond() != example.out.Time.Nanosecond {
|
|
t.Errorf("expected nanoseconds %d, got %d", example.out.Time.Nanosecond, obj.Date.Nanosecond())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalLocalDateTime(t *testing.T) {
|
|
type dateStruct struct {
|
|
DateTime LocalDateTime
|
|
}
|
|
|
|
examples := []struct {
|
|
name string
|
|
in LocalDateTime
|
|
out string
|
|
}{
|
|
{
|
|
name: "normal",
|
|
out: "DateTime = 1979-05-27T07:32:00\n",
|
|
in: LocalDateTime{
|
|
Date: LocalDate{
|
|
Year: 1979,
|
|
Month: 5,
|
|
Day: 27,
|
|
},
|
|
Time: LocalTime{
|
|
Hour: 7,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 0,
|
|
},
|
|
}},
|
|
{
|
|
name: "with nanoseconds",
|
|
out: "DateTime = 1979-05-27T00:32:00.999999000\n",
|
|
in: LocalDateTime{
|
|
Date: LocalDate{
|
|
Year: 1979,
|
|
Month: 5,
|
|
Day: 27,
|
|
},
|
|
Time: LocalTime{
|
|
Hour: 0,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 999999000,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, example := range examples {
|
|
t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
|
|
obj := dateStruct{
|
|
DateTime: example.in,
|
|
}
|
|
b, err := Marshal(obj)
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
got := string(b)
|
|
|
|
if got != example.out {
|
|
t.Errorf("expected '%s', got '%s'", example.out, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalLocalTime(t *testing.T) {
|
|
examples := []struct {
|
|
name string
|
|
in string
|
|
out LocalTime
|
|
}{
|
|
{
|
|
name: "normal",
|
|
in: "07:32:00",
|
|
out: LocalTime{
|
|
Hour: 7,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 0,
|
|
},
|
|
},
|
|
{
|
|
name: "with nanoseconds",
|
|
in: "00:32:00.999999",
|
|
out: LocalTime{
|
|
Hour: 0,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 999999000,
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, example := range examples {
|
|
toml := fmt.Sprintf(`Time = %s`, example.in)
|
|
|
|
t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) {
|
|
type dateStruct struct {
|
|
Time LocalTime
|
|
}
|
|
|
|
var obj dateStruct
|
|
|
|
err := Unmarshal([]byte(toml), &obj)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if obj.Time != example.out {
|
|
t.Errorf("expected '%s', got '%s'", example.out, obj.Time)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalLocalTime(t *testing.T) {
|
|
type timeStruct struct {
|
|
Time LocalTime
|
|
}
|
|
|
|
examples := []struct {
|
|
name string
|
|
in LocalTime
|
|
out string
|
|
}{
|
|
{
|
|
name: "normal",
|
|
out: "Time = 07:32:00\n",
|
|
in: LocalTime{
|
|
Hour: 7,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 0,
|
|
}},
|
|
{
|
|
name: "with nanoseconds",
|
|
out: "Time = 00:32:00.999999000\n",
|
|
in: LocalTime{
|
|
Hour: 0,
|
|
Minute: 32,
|
|
Second: 0,
|
|
Nanosecond: 999999000,
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, example := range examples {
|
|
t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
|
|
obj := timeStruct{
|
|
Time: example.in,
|
|
}
|
|
b, err := Marshal(obj)
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
got := string(b)
|
|
|
|
if got != example.out {
|
|
t.Errorf("expected '%s', got '%s'", example.out, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// test case for issue #339
|
|
func TestUnmarshalSameInnerField(t *testing.T) {
|
|
type InterStruct2 struct {
|
|
Test string
|
|
Name string
|
|
Age int
|
|
}
|
|
type Inter2 struct {
|
|
Name string
|
|
Age int
|
|
InterStruct2 InterStruct2
|
|
}
|
|
type Server struct {
|
|
Name string `toml:"name"`
|
|
Inter2 Inter2 `toml:"inter2"`
|
|
}
|
|
|
|
var server Server
|
|
|
|
if err := Unmarshal([]byte(`name = "123"
|
|
[inter2]
|
|
name = "inter2"
|
|
age = 222`), &server); err == nil {
|
|
expected := Server{
|
|
Name: "123",
|
|
Inter2: Inter2{
|
|
Name: "inter2",
|
|
Age: 222,
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(server, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, server)
|
|
}
|
|
} else {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMarshalInterface(t *testing.T) {
|
|
type InnerStruct struct {
|
|
InnerField string
|
|
}
|
|
|
|
type OuterStruct struct {
|
|
PrimitiveField interface{}
|
|
ArrayField interface{}
|
|
StructArrayField interface{}
|
|
MapField map[string]interface{}
|
|
StructField interface{}
|
|
PointerField interface{}
|
|
NilField interface{}
|
|
InterfacePointerField *interface{}
|
|
}
|
|
|
|
type ShouldNotSupportStruct struct {
|
|
InterfaceArray []interface{}
|
|
}
|
|
|
|
expected := []byte(`ArrayField = [1,2,3]
|
|
InterfacePointerField = "hello world"
|
|
PrimitiveField = "string"
|
|
|
|
[MapField]
|
|
key1 = "value1"
|
|
key2 = false
|
|
|
|
[MapField.key3]
|
|
InnerField = "value3"
|
|
|
|
[PointerField]
|
|
InnerField = "yyy"
|
|
|
|
[[StructArrayField]]
|
|
InnerField = "s1"
|
|
|
|
[[StructArrayField]]
|
|
InnerField = "s2"
|
|
|
|
[StructField]
|
|
InnerField = "xxx"
|
|
`)
|
|
|
|
var h interface{} = "hello world"
|
|
if result, err := Marshal(OuterStruct{
|
|
"string",
|
|
[]int{1, 2, 3},
|
|
[]InnerStruct{{"s1"}, {"s2"}},
|
|
map[string]interface{}{
|
|
"key1": "value1",
|
|
"key2": false,
|
|
"key3": InnerStruct{"value3"},
|
|
"nil value": nil,
|
|
},
|
|
InnerStruct{
|
|
"xxx",
|
|
},
|
|
&InnerStruct{
|
|
"yyy",
|
|
},
|
|
nil,
|
|
&h,
|
|
}); err == nil {
|
|
if !bytes.Equal(result, expected) {
|
|
t.Errorf("Bad marshal: expected\n----\n%s\n----\ngot\n----\n%s\n----\n", expected, result)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// according to the toml standard, data types of array may not be mixed
|
|
if _, err := Marshal(ShouldNotSupportStruct{[]interface{}{1, "a", true}}); err == nil {
|
|
t.Errorf("Should not support []interface{} marshaling")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalToNilInterface(t *testing.T) {
|
|
toml := []byte(`
|
|
PrimitiveField = "Hello"
|
|
ArrayField = [1,2,3]
|
|
InterfacePointerField = "World"
|
|
|
|
[StructField]
|
|
Field1 = 123
|
|
Field2 = "Field2"
|
|
|
|
[MapField]
|
|
MapField1 = [4,5,6]
|
|
MapField2 = {A = "A"}
|
|
MapField3 = false
|
|
|
|
[[StructArrayField]]
|
|
Name = "Allen"
|
|
Age = 20
|
|
|
|
[[StructArrayField]]
|
|
Name = "Jack"
|
|
Age = 23
|
|
`)
|
|
|
|
type OuterStruct struct {
|
|
PrimitiveField interface{}
|
|
ArrayField interface{}
|
|
StructArrayField interface{}
|
|
MapField map[string]interface{}
|
|
StructField interface{}
|
|
NilField interface{}
|
|
InterfacePointerField *interface{}
|
|
}
|
|
|
|
var s interface{} = "World"
|
|
expected := OuterStruct{
|
|
PrimitiveField: "Hello",
|
|
ArrayField: []interface{}{int64(1), int64(2), int64(3)},
|
|
StructField: map[string]interface{}{
|
|
"Field1": int64(123),
|
|
"Field2": "Field2",
|
|
},
|
|
MapField: map[string]interface{}{
|
|
"MapField1": []interface{}{int64(4), int64(5), int64(6)},
|
|
"MapField2": map[string]interface{}{
|
|
"A": "A",
|
|
},
|
|
"MapField3": false,
|
|
},
|
|
NilField: nil,
|
|
InterfacePointerField: &s,
|
|
StructArrayField: []map[string]interface{}{
|
|
{
|
|
"Name": "Allen",
|
|
"Age": int64(20),
|
|
},
|
|
{
|
|
"Name": "Jack",
|
|
"Age": int64(23),
|
|
},
|
|
},
|
|
}
|
|
actual := OuterStruct{}
|
|
if err := Unmarshal(toml, &actual); err == nil {
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalToNonNilInterface(t *testing.T) {
|
|
toml := []byte(`
|
|
PrimitiveField = "Allen"
|
|
ArrayField = [1,2,3]
|
|
|
|
[StructField]
|
|
InnerField = "After1"
|
|
|
|
[PointerField]
|
|
InnerField = "After2"
|
|
|
|
[InterfacePointerField]
|
|
InnerField = "After"
|
|
|
|
[MapField]
|
|
MapField1 = [4,5,6]
|
|
MapField2 = {A = "A"}
|
|
MapField3 = false
|
|
|
|
[[StructArrayField]]
|
|
InnerField = "After3"
|
|
|
|
[[StructArrayField]]
|
|
InnerField = "After4"
|
|
`)
|
|
type InnerStruct struct {
|
|
InnerField interface{}
|
|
}
|
|
|
|
type OuterStruct struct {
|
|
PrimitiveField interface{}
|
|
ArrayField interface{}
|
|
StructArrayField interface{}
|
|
MapField map[string]interface{}
|
|
StructField interface{}
|
|
PointerField interface{}
|
|
NilField interface{}
|
|
InterfacePointerField *interface{}
|
|
}
|
|
|
|
var s interface{} = InnerStruct{"After"}
|
|
expected := OuterStruct{
|
|
PrimitiveField: "Allen",
|
|
ArrayField: []int{1, 2, 3},
|
|
StructField: InnerStruct{InnerField: "After1"},
|
|
MapField: map[string]interface{}{
|
|
"MapField1": []interface{}{int64(4), int64(5), int64(6)},
|
|
"MapField2": map[string]interface{}{
|
|
"A": "A",
|
|
},
|
|
"MapField3": false,
|
|
},
|
|
PointerField: &InnerStruct{InnerField: "After2"},
|
|
NilField: nil,
|
|
InterfacePointerField: &s,
|
|
StructArrayField: []InnerStruct{
|
|
{InnerField: "After3"},
|
|
{InnerField: "After4"},
|
|
},
|
|
}
|
|
actual := OuterStruct{
|
|
PrimitiveField: "aaa",
|
|
ArrayField: []int{100, 200, 300, 400},
|
|
StructField: InnerStruct{InnerField: "Before1"},
|
|
MapField: map[string]interface{}{
|
|
"MapField1": []int{4, 5, 6},
|
|
"MapField2": map[string]string{
|
|
"B": "BBB",
|
|
},
|
|
"MapField3": true,
|
|
},
|
|
PointerField: &InnerStruct{InnerField: "Before2"},
|
|
NilField: nil,
|
|
InterfacePointerField: &s,
|
|
StructArrayField: []InnerStruct{
|
|
{InnerField: "Before3"},
|
|
{InnerField: "Before4"},
|
|
},
|
|
}
|
|
if err := Unmarshal(toml, &actual); err == nil {
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalEmbedTree(t *testing.T) {
|
|
toml := []byte(`
|
|
OuterField1 = "Out"
|
|
OuterField2 = 1024
|
|
|
|
[TreeField]
|
|
InnerField1 = "In"
|
|
InnerField2 = 2048
|
|
|
|
[TreeField.EmbedStruct]
|
|
EmbedField = "Embed"
|
|
|
|
`)
|
|
type InnerStruct struct {
|
|
InnerField1 string
|
|
InnerField2 int
|
|
EmbedStruct struct {
|
|
EmbedField string
|
|
}
|
|
}
|
|
|
|
type OuterStruct struct {
|
|
OuterField1 string
|
|
OuterField2 int
|
|
TreeField *Tree
|
|
}
|
|
|
|
out := OuterStruct{}
|
|
actual := InnerStruct{}
|
|
expected := InnerStruct{
|
|
"In",
|
|
2048,
|
|
struct {
|
|
EmbedField string
|
|
}{
|
|
EmbedField: "Embed",
|
|
},
|
|
}
|
|
if err := Unmarshal(toml, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := out.TreeField.Unmarshal(&actual); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestMarshalNil(t *testing.T) {
|
|
if _, err := Marshal(nil); err == nil {
|
|
t.Errorf("Expected err from nil marshal")
|
|
}
|
|
if _, err := Marshal((*struct{})(nil)); err == nil {
|
|
t.Errorf("Expected err from nil marshal")
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalNil(t *testing.T) {
|
|
if err := Unmarshal([]byte(`whatever = "whatever"`), nil); err == nil {
|
|
t.Errorf("Expected err from nil marshal")
|
|
}
|
|
if err := Unmarshal([]byte(`whatever = "whatever"`), (*struct{})(nil)); err == nil {
|
|
t.Errorf("Expected err from nil marshal")
|
|
}
|
|
}
|
|
|
|
var sliceTomlDemo = []byte(`str_slice = ["Howdy","Hey There"]
|
|
str_slice_ptr= ["Howdy","Hey There"]
|
|
int_slice=[1,2]
|
|
int_slice_ptr=[1,2]
|
|
[[struct_slice]]
|
|
String2="1"
|
|
[[struct_slice]]
|
|
String2="2"
|
|
[[struct_slice_ptr]]
|
|
String2="1"
|
|
[[struct_slice_ptr]]
|
|
String2="2"
|
|
`)
|
|
|
|
type sliceStruct struct {
|
|
Slice []string ` toml:"str_slice" `
|
|
SlicePtr *[]string ` toml:"str_slice_ptr" `
|
|
IntSlice []int ` toml:"int_slice" `
|
|
IntSlicePtr *[]int ` toml:"int_slice_ptr" `
|
|
StructSlice []basicMarshalTestSubStruct ` toml:"struct_slice" `
|
|
StructSlicePtr *[]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" `
|
|
}
|
|
|
|
func TestUnmarshalSlice(t *testing.T) {
|
|
tree, _ := LoadBytes(sliceTomlDemo)
|
|
tree, _ = TreeFromMap(tree.ToMap())
|
|
|
|
var actual sliceStruct
|
|
err := tree.Unmarshal(&actual)
|
|
if err != nil {
|
|
t.Error("shound not err", err)
|
|
}
|
|
expected := sliceStruct{
|
|
Slice: []string{"Howdy", "Hey There"},
|
|
SlicePtr: &[]string{"Howdy", "Hey There"},
|
|
IntSlice: []int{1, 2},
|
|
IntSlicePtr: &[]int{1, 2},
|
|
StructSlice: []basicMarshalTestSubStruct{{"1"}, {"2"}},
|
|
StructSlicePtr: &[]basicMarshalTestSubStruct{{"1"}, {"2"}},
|
|
}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
|
|
}
|
|
|
|
}
|
|
|
|
func TestUnmarshalSliceFail(t *testing.T) {
|
|
tree, _ := TreeFromMap(map[string]interface{}{
|
|
"str_slice": []int{1, 2},
|
|
})
|
|
|
|
var actual sliceStruct
|
|
err := tree.Unmarshal(&actual)
|
|
if err.Error() != "(0, 0): Can't convert 1(int64) to string" {
|
|
t.Error("expect err:(0, 0): Can't convert 1(int64) to string but got ", err)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalSliceFail2(t *testing.T) {
|
|
tree, _ := Load(`str_slice=[1,2]`)
|
|
|
|
var actual sliceStruct
|
|
err := tree.Unmarshal(&actual)
|
|
if err.Error() != "(1, 1): Can't convert 1(int64) to string" {
|
|
t.Error("expect err:(1, 1): Can't convert 1(int64) to string but got ", err)
|
|
}
|
|
|
|
}
|