Files
go-toml/internal/imported_tests/unmarshal_imported_test.go
T
2022-03-02 09:24:01 -05:00

2327 lines
53 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package imported_tests
// Those tests were imported directly from go-toml v1
// https://raw.githubusercontent.com/pelletier/go-toml/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal_test.go
// They have been cleaned up to only include Unmarshal tests, and only depend
// on the public API. Tests related to strict mode have been commented out and
// marked as skipped until we figure out if that's something we want in v2.
import (
"bytes"
"errors"
"fmt"
"reflect"
"strconv"
"testing"
"time"
"github.com/pelletier/go-toml/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type basicMarshalTestStruct struct {
String string `toml:"Zstring"`
StringList []string `toml:"Ystrlist"`
BasicMarshalTestSubAnonymousStruct
Sub basicMarshalTestSubStruct `toml:"Xsubdoc"`
SubList []basicMarshalTestSubStruct `toml:"Wsublist"`
}
type basicMarshalTestSubStruct struct {
String2 string
}
type BasicMarshalTestSubAnonymousStruct struct {
String3 string
}
var basicTestData = basicMarshalTestStruct{
String: "Hello",
StringList: []string{"Howdy", "Hey There"},
BasicMarshalTestSubAnonymousStruct: BasicMarshalTestSubAnonymousStruct{"One"},
Sub: basicMarshalTestSubStruct{"Two"},
SubList: []basicMarshalTestSubStruct{{"Three"}, {"Four"}},
}
var basicTestToml = []byte(`String3 = "One"
Ystrlist = ["Howdy", "Hey There"]
Zstring = "Hello"
[[Wsublist]]
String2 = "Three"
[[Wsublist]]
String2 = "Four"
[Xsubdoc]
String2 = "Two"
`)
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"
`)
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 := toml.Unmarshal(doc, &config)
require.NoError(t, err)
expected := Conf{
Name: "rui",
Age: 18,
Inter: map[string]interface{}{
"FirstName": "wang",
"LastName": "jl",
"Age": int64(100),
},
}
assert.Equal(t, expected, config)
}
func TestBasicUnmarshal(t *testing.T) {
result := basicMarshalTestStruct{}
err := toml.Unmarshal(basicTestToml, &result)
require.NoError(t, err)
require.Equal(t, basicTestData, result)
}
type quotedKeyMarshalTestStruct struct {
String string `toml:"Z.string-àéù"`
Float float64 `toml:"Yfloat-𝟘"`
Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"`
SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"`
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{
String: "Hello",
Float: 3.5,
Sub: basicMarshalTestSubStruct{"One"},
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5
"Z.string-àéù" = "Hello"
[["W.sublist-𝟘"]]
String2 = "Two"
[["W.sublist-𝟘"]]
String2 = "Three"
["Xsubdoc-àéù"]
String2 = "One"
`)
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"` // nolint:structcheck,unused
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"
float1 float32 = 12.3
float2 float32 = 45.6
float3 float32 = 78.9
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},
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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 TestDocUnmarshal(t *testing.T) {
result := testDoc{}
err := toml.Unmarshal(marshalTestToml, &result)
expected := docData
require.NoError(t, err)
assert.Equal(t, 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 := toml.Unmarshal(unexportedTestToml, &result)
require.NoError(t, err)
assert.Equal(t, unexportedTestData, 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"`
}
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) {
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",
}
for ind, x := range errTomls {
t.Run(fmt.Sprintf("Base Case %d", ind), func(t *testing.T) {
result := errStruct{}
err := toml.Unmarshal([]byte(x), &result)
if err == nil {
t.Errorf("Expected err from case %d\n", ind)
}
})
}
result2 := mapErr{}
err := toml.Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
if err == nil {
t.Errorf("Expected err from map")
}
for ind, x := range intErrTomls {
result3 := intErr{}
err := toml.Unmarshal([]byte(x), &result3)
if err == nil {
t.Errorf("Expected int err from case %d\n", ind)
}
}
}
var emptyTestToml = []byte(`bool = false
int = 0
string = ""
stringlist = []
title = "Placeholder"
[map]
`)
func TestEmptytomlUnmarshal(t *testing.T) {
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"`
}
emptyTestData := emptyMarshalTestStruct{
Title: "Placeholder",
Bool: false,
Int: 0,
String: "",
StringList: []string{},
Ptr: nil,
Map: nil,
}
result := emptyMarshalTestStruct{}
err := toml.Unmarshal(emptyTestToml, &result)
require.NoError(t, err)
assert.Equal(t, emptyTestData, 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"
pointerList = []string{"Hello back"}
pointerListPtr = []*string{&pointerStr}
pointerMap = map[string]string{"response": "Goodbye"}
pointerMapPtr = map[string]*string{"alternate": &pointerStr}
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 TestPointerUnmarshal(t *testing.T) {
result := pointerMarshalTestStruct{}
err := toml.Unmarshal(pointerTestToml, &result)
require.NoError(t, err)
assert.Equal(t, pointerTestData, result)
}
func TestUnmarshalTypeMismatch(t *testing.T) {
result := pointerMarshalTestStruct{}
err := toml.Unmarshal([]byte("List = 123"), &result)
assert.Error(t, err)
}
type nestedMarshalTestStruct struct {
String [][]string
// Struct [][]basicMarshalTestSubStruct
StringPtr *[]*[]*string
// StructPtr *[]*[]*basicMarshalTestSubStruct
}
var (
str1 = "Three"
str2 = "Four"
strPtr = []*string{&str1, &str2}
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 TestNestedUnmarshal(t *testing.T) {
result := nestedMarshalTestStruct{}
err := toml.Unmarshal(nestedTestToml, &result)
require.NoError(t, err)
assert.Equal(t, nestedTestData, result)
}
type customMarshalerParent struct {
Self customMarshaler `toml:"me"`
Friends []customMarshaler `toml:"friends"`
}
type customMarshaler struct {
FirstName string
LastName string
}
func (c customMarshaler) MarshalTOML() ([]byte, error) {
fullName := fmt.Sprintf("%s %s", c.FirstName, c.LastName)
return []byte(fullName), nil
}
var customMarshalerData = customMarshaler{FirstName: "Sally", LastName: "Fields"}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var customMarshalerToml = []byte(`Sally Fields`)
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var nestedCustomMarshalerData = customMarshalerParent{
Self: customMarshaler{FirstName: "Maiku", LastName: "Suteda"},
Friends: []customMarshaler{customMarshalerData},
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
me = "Maiku Suteda"
`)
var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends]
FirstName = "Sally"
LastName = "Fields"`)
type IntOrString string
func (x *IntOrString) MarshalTOML() ([]byte, error) {
s := *(*string)(x)
_, err := strconv.Atoi(s)
if err != nil {
return []byte(fmt.Sprintf(`"%s"`, s)), nil
}
return []byte(s), nil
}
func TestUnmarshalTextMarshaler(t *testing.T) {
nested := struct {
Friends textMarshaler `toml:"friends"`
}{}
expected := struct {
Friends textMarshaler `toml:"friends"`
}{
Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"},
}
err := toml.Unmarshal(nestedCustomMarshalerTomlForUnmarshal, &nested)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(nested, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, nested)
}
}
// TODO: Remove nolint once type and methods are used by a test
//nolint:unused
type precedentMarshaler struct {
FirstName string
LastName string
}
//nolint:unused
func (m precedentMarshaler) MarshalText() ([]byte, error) {
return []byte("shadowed"), nil
}
//nolint:unused
func (m precedentMarshaler) MarshalTOML() ([]byte, error) {
fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName)
return []byte(fullName), nil
}
// TODO: Remove nolint once type and method are used by a test
//nolint:unused
type customPointerMarshaler struct {
FirstName string
LastName string
}
//nolint:unused
func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) {
return []byte(`"hidden"`), nil
}
// TODO: Remove nolint once type and method are used by a test
//nolint:unused
type textPointerMarshaler struct {
FirstName string
LastName string
}
//nolint:unused
func (m *textPointerMarshaler) MarshalText() ([]byte, error) {
return []byte("hidden"), nil
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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"
`)
type mapsTestStruct struct {
Simple map[string]string
Paths map[string]string
Other map[string]float64
X struct {
Y struct {
Z map[string]bool
}
}
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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,
},
},
},
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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
`)
// TODO: Remove nolint once type is used by a test
//nolint:deadcode,unused
type structArrayNoTag struct {
A struct {
B []int64
C []int64
}
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var customTagTestToml = []byte(`
[postgres]
password = "bvalue"
user = "avalue"
[[postgres.My]]
My = "Foo"
[[postgres.My]]
My = "Baar"
`)
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var customCommentTagTestToml = []byte(`
# db connection
[postgres]
# db pass
password = "bvalue"
# db user
user = "avalue"
`)
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var customCommentedTagTestToml = []byte(`
[postgres]
# password = "bvalue"
# user = "avalue"
`)
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 _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
result := Test{}
err := toml.Unmarshal(test.input, &result)
require.NoError(t, err)
assert.Equal(t, test.expected, result)
})
}
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var customMultilineTagTestToml = []byte(`int_slice = [
1,
2,
3,
]
`)
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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
`)
// TODO: Remove nolint once type is used by a test
//nolint:deadcode
type testDocCustomTag struct {
Doc testDocBasicsCustomTag `file:"document"`
}
// TODO: Remove nolint once type is used by a test
//nolint:deadcode
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"`
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,varcheck
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 TestUnmarshalMap(t *testing.T) {
testToml := []byte(`
a = 1
b = 2
c = 3
`)
var result map[string]int
err := toml.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 := toml.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 TestUnmarshalTypeTableHeader(t *testing.T) {
testToml := []byte(`
[test]
a = 1
`)
type header string
var result map[header]map[string]int
err := toml.Unmarshal(testToml, &result)
if err != nil {
t.Errorf("Received unexpected error: %s", err)
return
}
expected := map[header]map[string]int{
"test": map[string]int{"a": 1},
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
}
}
func TestUnmarshalNonPointer(t *testing.T) {
a := 1
err := toml.Unmarshal([]byte{}, a)
if err == nil {
t.Fatal("unmarshal should err when given a non pointer")
}
}
func TestUnmarshalInvalidPointerKind(t *testing.T) {
t.Skipf("should this really be an error?")
a := 1
err := toml.Unmarshal([]byte{}, &a)
assert.Error(t, err)
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused
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"`
}
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
var testDurationToml = []byte(`
nanosec = "1ns"
microsec1 = "1us"
microsec2 = "1µs"
millisec = "1ms"
sec = "1s"
min = "1m"
hour = "1h"
mixed = "1h1m1s1ms1µs1ns"
a_string = "15s"
`)
// TODO: Remove nolint once var is used by a test
//nolint:deadcode,unused,varcheck
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"
`)
// TODO: Remove nolint once type is used by a test
//nolint:deadcode,unused
type testBadDuration struct {
Val time.Duration `toml:"val"`
}
// TODO: add back camelCase test
var testCamelCaseKeyToml = []byte(`fooBar = 10`) //nolint:unused
//nolint:unused
func TestUnmarshalCamelCaseKey(t *testing.T) {
t.Skipf("don't know if it is a good idea to automatically convert like that yet")
var x struct {
FooBar int
B int
}
if err := toml.Unmarshal(testCamelCaseKeyToml, &x); err != nil {
t.Fatal(err)
}
if x.FooBar != 10 {
t.Fatal("Did not set camelCase'd key")
}
}
func TestUnmarshalNegativeUint(t *testing.T) {
t.Skipf("not sure if we this should always error")
type check struct{ U uint } // nolint:unused
err := toml.Unmarshal([]byte("U = -1"), &check{})
assert.Error(t, err)
}
func TestUnmarshalCheckConversionFloatInt(t *testing.T) {
type conversionCheck struct {
U uint
I int
F float64
}
type TestCase struct {
desc string
input string
}
testCases := []TestCase{
{
desc: "unsigned int",
input: `U = 1e300`,
},
{
desc: "int",
input: `I = 1e300`,
},
{
desc: "float",
input: `F = 9223372036854775806`,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
err := toml.Unmarshal([]byte(test.input), &conversionCheck{})
require.Error(t, err)
})
}
}
func TestUnmarshalOverflow(t *testing.T) {
type overflow struct {
U8 uint8
I8 int8
F32 float32
}
type TestCase struct {
desc string
input string
}
testCases := []TestCase{
{
desc: "byte",
input: `U8 = 300`,
},
{
desc: "int8",
input: `I8 = 300`,
},
{
desc: "float32",
input: `F32 = 1e300`,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
err := toml.Unmarshal([]byte(test.input), &overflow{})
require.Error(t, err)
})
}
}
func TestUnmarshalDefault(t *testing.T) {
t.Skipf("don't know if it is a good idea to have `default`")
t.Run("main", func(t *testing.T) {
type EmbeddedStruct struct {
StringField string `default:"c"`
}
type aliasUint uint
var doc struct {
StringField string `default:"a"`
BoolField bool `default:"true"`
UintField uint `default:"1"`
Uint8Field uint8 `default:"8"`
Uint16Field uint16 `default:"16"`
Uint32Field uint32 `default:"32"`
Uint64Field uint64 `default:"64"`
IntField int `default:"-1"`
Int8Field int8 `default:"-8"`
Int16Field int16 `default:"-16"`
Int32Field int32 `default:"-32"`
Int64Field int64 `default:"-64"`
Float32Field float32 `default:"32.1"`
Float64Field float64 `default:"64.1"`
DurationField time.Duration `default:"120ms"`
DurationField2 time.Duration `default:"120000000"`
NonEmbeddedStruct struct {
StringField string `default:"b"`
}
EmbeddedStruct
AliasUintField aliasUint `default:"1000"`
}
err := toml.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.UintField != 1 {
t.Errorf("UintField should be 1, not %d", doc.UintField)
}
if doc.Uint8Field != 8 {
t.Errorf("Uint8Field should be 8, not %d", doc.Uint8Field)
}
if doc.Uint16Field != 16 {
t.Errorf("Uint16Field should be 16, not %d", doc.Uint16Field)
}
if doc.Uint32Field != 32 {
t.Errorf("Uint32Field should be 32, not %d", doc.Uint32Field)
}
if doc.Uint64Field != 64 {
t.Errorf("Uint64Field should be 64, not %d", doc.Uint64Field)
}
if doc.IntField != -1 {
t.Errorf("IntField should be -1, not %d", doc.IntField)
}
if doc.Int8Field != -8 {
t.Errorf("Int8Field should be -8, not %d", doc.Int8Field)
}
if doc.Int16Field != -16 {
t.Errorf("Int16Field should be -16, not %d", doc.Int16Field)
}
if doc.Int32Field != -32 {
t.Errorf("Int32Field should be -32, not %d", doc.Int32Field)
}
if doc.Int64Field != -64 {
t.Errorf("Int64Field should be -64, not %d", doc.Int64Field)
}
if doc.Float32Field != 32.1 {
t.Errorf("Float32Field should be 32.1, not %f", doc.Float32Field)
}
if doc.Float64Field != 64.1 {
t.Errorf("Float64Field should be 64.1, not %f", doc.Float64Field)
}
if doc.DurationField != 120*time.Millisecond {
t.Errorf("DurationField should be 120ms, not %s", doc.DurationField.String())
}
if doc.DurationField2 != 120*time.Millisecond {
t.Errorf("DurationField2 should be 120000000, not %d", doc.DurationField2)
}
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)
}
if doc.AliasUintField != 1000 {
t.Errorf("AliasUintField should be 1000, not %d", doc.AliasUintField)
}
})
t.Run("failure bool", func(t *testing.T) {
var doc struct {
Field bool `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
t.Run("failure int", func(t *testing.T) {
var doc struct {
Field int `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
t.Run("failure int64", func(t *testing.T) {
var doc struct {
Field int64 `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
t.Run("failure float64", func(t *testing.T) {
var doc struct {
Field float64 `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
t.Run("failure duration", func(t *testing.T) {
var doc struct {
Field time.Duration `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
t.Run("failure unsupported", func(t *testing.T) {
var doc struct {
Field struct{} `default:"blah"`
}
err := toml.Unmarshal([]byte(``), &doc)
if err == nil {
t.Fatal("should error")
}
})
}
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 := toml.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) {
t.Skipf("TODO: what does encoding/json do?")
type Nested struct {
Value string `toml:"nested"`
}
type Deep struct {
Nested
}
type Document struct {
Deep
Value string `toml:"own"`
}
var doc Document
err := toml.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) {
doc := `
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 := toml.Unmarshal([]byte(doc), &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 := toml.Unmarshal([]byte(doc), &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 TestUnmarshalLocalDate(t *testing.T) {
t.Run("ToLocalDate", func(t *testing.T) {
type dateStruct struct {
Date toml.LocalDate
}
doc := `date = 1979-05-27`
var obj dateStruct
err := toml.Unmarshal([]byte(doc), &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
}
doc := `date = 1979-05-27`
var obj dateStruct
err := toml.Unmarshal([]byte(doc), &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 TestUnmarshalLocalDateTime(t *testing.T) {
examples := []struct {
name string
in string
out toml.LocalDateTime
}{
{
name: "normal",
in: "1979-05-27T07:32:00",
out: toml.LocalDateTime{
LocalDate: toml.LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
LocalTime: toml.LocalTime{
Hour: 7,
Minute: 32,
Second: 0,
Nanosecond: 0,
},
},
},
{
name: "with nanoseconds",
in: "1979-05-27T00:32:00.999999",
out: toml.LocalDateTime{
LocalDate: toml.LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
LocalTime: toml.LocalTime{
Hour: 0,
Minute: 32,
Second: 0,
Nanosecond: 999999000,
Precision: 6,
},
},
},
}
for i, example := range examples {
doc := fmt.Sprintf(`date = %s`, example.in)
t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) {
type dateStruct struct {
Date toml.LocalDateTime
}
var obj dateStruct
err := toml.Unmarshal([]byte(doc), &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 := toml.Unmarshal([]byte(doc), &obj)
if err != nil {
t.Fatal(err)
}
if obj.Date.Year() != example.out.Year {
t.Errorf("expected year %d, got %d", example.out.Year, obj.Date.Year())
}
if obj.Date.Month() != time.Month(example.out.Month) {
t.Errorf("expected month %d, got %d", example.out.Month, obj.Date.Month())
}
if obj.Date.Day() != example.out.Day {
t.Errorf("expected day %d, got %d", example.out.Day, obj.Date.Day())
}
if obj.Date.Hour() != example.out.Hour {
t.Errorf("expected hour %d, got %d", example.out.Hour, obj.Date.Hour())
}
if obj.Date.Minute() != example.out.Minute {
t.Errorf("expected minute %d, got %d", example.out.Minute, obj.Date.Minute())
}
if obj.Date.Second() != example.out.Second {
t.Errorf("expected second %d, got %d", example.out.Second, obj.Date.Second())
}
if obj.Date.Nanosecond() != example.out.Nanosecond {
t.Errorf("expected nanoseconds %d, got %d", example.out.Nanosecond, obj.Date.Nanosecond())
}
})
}
}
func TestUnmarshalLocalTime(t *testing.T) {
examples := []struct {
name string
in string
out toml.LocalTime
}{
{
name: "normal",
in: "07:32:00",
out: toml.LocalTime{
Hour: 7,
Minute: 32,
Second: 0,
Nanosecond: 0,
},
},
{
name: "with nanoseconds",
in: "00:32:00.999999",
out: toml.LocalTime{
Hour: 0,
Minute: 32,
Second: 0,
Nanosecond: 999999000,
Precision: 6,
},
},
}
for i, example := range examples {
doc := fmt.Sprintf(`Time = %s`, example.in)
t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) {
type dateStruct struct {
Time toml.LocalTime
}
var obj dateStruct
err := toml.Unmarshal([]byte(doc), &obj)
if err != nil {
t.Fatal(err)
}
if obj.Time != example.out {
t.Errorf("expected '%s', got '%s'", example.out, obj.Time)
}
})
}
}
// 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 := toml.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 TestUnmarshalToNilInterface(t *testing.T) {
doc := []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: []interface{}{
map[string]interface{}{
"Name": "Allen",
"Age": int64(20),
},
map[string]interface{}{
"Name": "Jack",
"Age": int64(23),
},
},
}
actual := OuterStruct{}
err := toml.Unmarshal(doc, &actual)
require.NoError(t, err)
assert.Equal(t, expected, actual)
}
func TestUnmarshalToNonNilInterface(t *testing.T) {
doc := []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: []interface{}{int64(1), int64(2), int64(3)},
StructField: map[string]interface{}{"InnerField": "After1"},
MapField: map[string]interface{}{
"MapField1": []interface{}{int64(4), int64(5), int64(6)},
"MapField2": map[string]interface{}{
"A": "A",
},
"MapField3": false,
},
PointerField: map[string]interface{}{"InnerField": "After2"},
NilField: nil,
InterfacePointerField: &s,
StructArrayField: []interface{}{
map[string]interface{}{"InnerField": "After3"},
map[string]interface{}{"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"},
},
}
err := toml.Unmarshal(doc, &actual)
require.NoError(t, err)
assert.Equal(t, expected, actual)
}
func TestUnmarshalNil(t *testing.T) {
assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), nil))
assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), (*struct{})(nil)))
}
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" `
}
type arrayStruct struct {
Slice [4]string ` toml:"str_slice" `
SlicePtr *[4]string ` toml:"str_slice_ptr" `
IntSlice [4]int ` toml:"int_slice" `
IntSlicePtr *[4]int ` toml:"int_slice_ptr" `
StructSlice [4]basicMarshalTestSubStruct ` toml:"struct_slice" `
StructSlicePtr *[4]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" `
}
type arrayTooSmallStruct struct {
Slice [1]string ` toml:"str_slice" `
StructSlice [1]basicMarshalTestSubStruct ` toml:"struct_slice" `
}
func TestUnmarshalSlice(t *testing.T) {
var actual sliceStruct
err := toml.Unmarshal(sliceTomlDemo, &actual)
require.NoError(t, 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"}},
}
assert.Equal(t, expected, actual)
}
func TestUnmarshalSliceFail(t *testing.T) {
var actual sliceStruct
assert.Error(t, toml.Unmarshal([]byte(`str_slice = [1, 2]`), &actual))
}
func TestUnmarshalSliceFail2(t *testing.T) {
doc := `str_slice=[1,2]`
var actual sliceStruct
assert.Error(t, toml.Unmarshal([]byte(doc), &actual))
}
func TestUnmarshalMixedTypeSlice(t *testing.T) {
type TestStruct struct {
ArrayField []interface{}
}
//doc := []byte(`ArrayField = [3.14,100,true,"hello world",{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]]
//`)
doc := []byte(`ArrayField = [{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]]
`)
actual := TestStruct{}
expected := TestStruct{
ArrayField: []interface{}{
//3.14,
//int64(100),
//true,
//"hello world",
map[string]interface{}{
"Field": "inner1",
},
[]interface{}{
map[string]interface{}{"Field": "inner2"},
map[string]interface{}{"Field": "inner3"},
},
},
}
err := toml.Unmarshal(doc, &actual)
require.NoError(t, err)
assert.Equal(t, expected, actual)
}
func TestUnmarshalArray(t *testing.T) {
var err error
var actual arrayStruct
err = toml.Unmarshal(sliceTomlDemo, &actual)
require.NoError(t, err)
expected := arrayStruct{
Slice: [4]string{"Howdy", "Hey There"},
SlicePtr: &[4]string{"Howdy", "Hey There"},
IntSlice: [4]int{1, 2},
IntSlicePtr: &[4]int{1, 2},
StructSlice: [4]basicMarshalTestSubStruct{{"1"}, {"2"}},
StructSlicePtr: &[4]basicMarshalTestSubStruct{{"1"}, {"2"}},
}
assert.Equal(t, expected, actual)
}
func TestUnmarshalArrayFail3(t *testing.T) {
doc := `[[struct_slice]]
String2="1"
[[struct_slice]]
String2="2"`
var actual arrayTooSmallStruct
err := toml.Unmarshal([]byte(doc), &actual)
assert.Error(t, err)
}
func decoder(doc string) *toml.Decoder {
return toml.NewDecoder(bytes.NewReader([]byte(doc)))
}
func strictDecoder(doc string) *toml.Decoder {
d := decoder(doc)
d.DisallowUnknownFields()
return d
}
func TestDecoderStrict(t *testing.T) {
input := `
[decoded]
key = ""
[undecoded]
key = ""
[undecoded.inner]
key = ""
[[undecoded.array]]
key = ""
[[undecoded.array]]
key = ""
`
var doc struct {
Decoded struct {
Key string
}
}
err := strictDecoder(input).Decode(&doc)
require.Error(t, err)
require.IsType(t, &toml.StrictMissingError{}, err)
se := err.(*toml.StrictMissingError)
keys := []toml.Key{}
for _, e := range se.Errors {
keys = append(keys, e.Key())
}
expectedKeys := []toml.Key{
{"undecoded"},
{"undecoded", "inner"},
{"undecoded", "array"},
{"undecoded", "array"},
}
require.Equal(t, expectedKeys, keys)
err = decoder(input).Decode(&doc)
require.NoError(t, err)
var m map[string]interface{}
err = decoder(input).Decode(&m)
}
func TestDecoderStrictValid(t *testing.T) {
input := `
[decoded]
key = ""
`
var doc struct {
Decoded struct {
Key string
}
}
err := strictDecoder(input).Decode(&doc)
require.NoError(t, err)
}
type docUnmarshalTOML struct {
Decoded struct {
Key string
}
}
func (d *docUnmarshalTOML) UnmarshalTOML(i interface{}) error {
if iMap, ok := i.(map[string]interface{}); !ok {
return fmt.Errorf("type assertion error: wants %T, have %T", map[string]interface{}{}, i)
} else if key, ok := iMap["key"]; !ok {
return fmt.Errorf("key '%s' not in map", "key")
} else if keyString, ok := key.(string); !ok {
return fmt.Errorf("type assertion error: wants %T, have %T", "", key)
} else {
d.Decoded.Key = keyString
}
return nil
}
func TestDecoderStrictCustomUnmarshal(t *testing.T) {
t.Skip()
//input := `key = "ok"`
//var doc docUnmarshalTOML
//err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc)
//if err != nil {
// t.Fatal("unexpected error:", err)
//}
//if doc.Decoded.Key != "ok" {
// t.Errorf("Bad unmarshal: expected ok, got %v", doc.Decoded.Key)
//}
}
type parent struct {
Doc docUnmarshalTOML
DocPointer *docUnmarshalTOML
}
func TestCustomUnmarshal(t *testing.T) {
t.Skip("not sure if UnmarshalTOML is a good idea")
input := `
[Doc]
key = "ok1"
[DocPointer]
key = "ok2"
`
var d parent
err := toml.Unmarshal([]byte(input), &d)
require.NoError(t, err)
assert.Equal(t, "ok1", d.Doc.Decoded.Key)
assert.Equal(t, "ok2", d.DocPointer.Decoded.Key)
}
func TestCustomUnmarshalError(t *testing.T) {
t.Skip("not sure if UnmarshalTOML is a good idea")
input := `
[Doc]
key = 1
[DocPointer]
key = "ok2"
`
expected := "(2, 1): unmarshal toml: type assertion error: wants string, have int64"
var d parent
err := toml.Unmarshal([]byte(input), &d)
if err == nil {
t.Error("expected error, got none")
} else if err.Error() != expected {
t.Errorf("expect err: %s, got: %s", expected, err.Error())
}
}
type intWrapper struct {
Value int
}
func (w *intWrapper) UnmarshalText(text []byte) error {
var err error
if w.Value, err = strconv.Atoi(string(text)); err == nil {
return nil
}
if b, err := strconv.ParseBool(string(text)); err == nil {
if b {
w.Value = 1
}
return nil
}
if f, err := strconv.ParseFloat(string(text), 32); err == nil {
w.Value = int(f)
return nil
}
return fmt.Errorf("unsupported: %s", text)
}
func TestTextUnmarshal(t *testing.T) {
var doc struct {
UnixTime intWrapper
Version *intWrapper
Bool intWrapper
Int intWrapper
Float intWrapper
}
input := `
UnixTime = "12"
Version = "42"
Bool = true
Int = 21
Float = 2.0
`
err := toml.Unmarshal([]byte(input), &doc)
require.NoError(t, err)
assert.Equal(t, 12, doc.UnixTime.Value)
assert.Equal(t, 42, doc.Version.Value)
assert.Equal(t, 1, doc.Bool.Value)
assert.Equal(t, 21, doc.Int.Value)
assert.Equal(t, 2, doc.Float.Value)
}
func TestTextUnmarshalError(t *testing.T) {
var doc struct {
Failer intWrapper
}
input := `Failer = "hello"`
if err := toml.Unmarshal([]byte(input), &doc); err == nil {
t.Fatalf("expected err, got none")
}
}
// issue406
func TestPreserveNotEmptyField(t *testing.T) {
doc := []byte(`Field1 = "ccc"`)
type Inner struct {
InnerField1 string
InnerField2 int
}
type TestStruct struct {
Field1 string
Field2 int
Field3 Inner
}
actual := TestStruct{
"aaa",
100,
Inner{
"bbb",
200,
},
}
expected := TestStruct{
"ccc",
100,
Inner{
"bbb",
200,
},
}
err := toml.Unmarshal(doc, &actual)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
}
}
// github issue 432
func TestUnmarshalEmptyInterface(t *testing.T) {
doc := []byte(`User = "pelletier"`)
var v interface{}
err := toml.Unmarshal(doc, &v)
if err != nil {
t.Fatal(err)
}
require.IsType(t, map[string]interface{}{}, v)
x := v.(map[string]interface{})
assert.Equal(t, "pelletier", x["User"])
}
func TestUnmarshalEmptyInterfaceDeep(t *testing.T) {
t.Skipf("TODO")
doc := []byte(`
User = "pelletier"
Age = 99
[foo]
bar = 42
`)
var v interface{}
err := toml.Unmarshal(doc, &v)
if err != nil {
t.Fatal(err)
}
x, ok := v.(map[string]interface{})
if !ok {
t.Fatal(err)
}
expected := map[string]interface{}{
"User": "pelletier",
"Age": 99,
"foo": map[string]interface{}{
"bar": 42,
},
}
reflect.DeepEqual(x, expected)
}
type Config struct {
Key string `toml:"key"`
Obj Custom `toml:"obj"`
}
type Custom struct {
v string
}
func (c *Custom) UnmarshalTOML(v interface{}) error {
c.v = "called"
return nil
}
func TestGithubIssue431(t *testing.T) {
doc := `key = "value"`
var c Config
if err := toml.Unmarshal([]byte(doc), &c); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if c.Key != "value" {
t.Errorf("expected c.Key='value', not '%s'", c.Key)
}
if c.Obj.v == "called" {
t.Errorf("UnmarshalTOML should not have been called")
}
}
type durationString struct {
time.Duration
}
func (d *durationString) UnmarshalTOML(v interface{}) error {
d.Duration = 10 * time.Second
return nil
}
type config437Error struct{}
func (e *config437Error) UnmarshalTOML(v interface{}) error {
return errors.New("expected")
}
type config437 struct {
HTTP struct {
PingTimeout durationString `toml:"PingTimeout"`
ErrorField config437Error
} `toml:"HTTP"`
}
func TestGithubIssue437(t *testing.T) {
t.Skipf("unmarshalTOML not implemented")
src := `
[HTTP]
PingTimeout = "32m"
`
cfg := &config437{}
cfg.HTTP.PingTimeout = durationString{time.Second}
err := toml.Unmarshal([]byte(src), cfg)
if err != nil {
t.Fatalf("unexpected errors %s", err)
}
expected := durationString{10 * time.Second}
if cfg.HTTP.PingTimeout != expected {
t.Fatalf("expected '%s', got '%s'", expected, cfg.HTTP.PingTimeout)
}
}
func TestLeafUnmarshalerError(t *testing.T) {
src := `
[HTTP]
ErrorField = "foo"
`
cfg := &config437{}
err := toml.Unmarshal([]byte(src), cfg)
if err == nil {
t.Fatalf("error was expected")
}
}