package toml import ( "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "os" "reflect" "strconv" "strings" "testing" "time" ) 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 basicTestTomlCustomIndentation = []byte(`String3 = "One" Ystrlist = ["Howdy", "Hey There"] Zstring = "Hello" [[Wsublist]] String2 = "Three" [[Wsublist]] String2 = "Four" [Xsubdoc] String2 = "Two" `) var basicTestTomlOrdered = []byte(`Zstring = "Hello" Ystrlist = ["Howdy", "Hey There"] String3 = "One" [Xsubdoc] String2 = "Two" [[Wsublist]] String2 = "Three" [[Wsublist]] String2 = "Four" `) 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 TestBasicMarshalCustomIndentation(t *testing.T) { var result bytes.Buffer err := NewEncoder(&result).Indentation("\t").Encode(basicTestData) if err != nil { t.Fatal(err) } expected := basicTestTomlCustomIndentation 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 TestBasicMarshalWrongIndentation(t *testing.T) { var result bytes.Buffer err := NewEncoder(&result).Indentation(" \n").Encode(basicTestData) if err.Error() != "invalid indentation: must only contains space or tab characters" { t.Error("expect err:invalid indentation: must only contains space or tab characters but got:", err) } } 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 quotedKeyMarshalTestStruct struct { String string `toml:"Z.string-àéù"` Float float64 `toml:"Yfloat-𝟘"` Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"` SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"` } var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{ String: "Hello", Float: 3.5, Sub: basicMarshalTestSubStruct{"One"}, SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, } var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5 "Z.string-àéù" = "Hello" [["W.sublist-𝟘"]] String2 = "Two" [["W.sublist-𝟘"]] String2 = "Three" ["Xsubdoc-àéù"] String2 = "One" `) func TestBasicMarshalQuotedKey(t *testing.T) { result, err := Marshal(quotedKeyMarshalTestData) if err != nil { t.Fatal(err) } expected := quotedKeyMarshalTestToml if !bytes.Equal(result, expected) { t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestBasicUnmarshalQuotedKey(t *testing.T) { tree, err := LoadBytes(quotedKeyMarshalTestToml) if err != nil { t.Fatal(err) } var q quotedKeyMarshalTestStruct tree.Unmarshal(&q) fmt.Println(q) if !reflect.DeepEqual(quotedKeyMarshalTestData, q) { t.Errorf("Bad unmarshal: expected\n-----\n%v\n-----\ngot\n-----\n%v\n-----\n", quotedKeyMarshalTestData, q) } } 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 { 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"} var customMarshalerToml = []byte(`Sally Fields`) var nestedCustomMarshalerData = customMarshalerParent{ Self: customMarshaler{FirstName: "Maiku", LastName: "Suteda"}, Friends: []customMarshaler{customMarshalerData}, } var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"] me = "Maiku Suteda" `) var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends] FirstName = "Sally" LastName = "Fields"`) 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) } } 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 TestNestedCustomMarshaler(t *testing.T) { num := IntOrString("100") str := IntOrString("hello") var parent = struct { IntField *IntOrString `toml:"int"` StringField *IntOrString `toml:"string"` }{ &num, &str, } result, err := Marshal(parent) if err != nil { t.Fatal(err) } expected := `int = 100 string = "hello" ` if !bytes.Equal(result, []byte(expected)) { t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } type textMarshaler struct { FirstName string LastName string } func (m textMarshaler) MarshalText() ([]byte, error) { fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName) return []byte(fullName), nil } func TestTextMarshaler(t *testing.T) { m := textMarshaler{FirstName: "Sally", LastName: "Fields"} result, err := Marshal(m) if err != nil { t.Fatal(err) } expected := `Sally Fields` if !bytes.Equal(result, []byte(expected)) { t.Errorf("Bad text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestUnmarshalTextMarshaler(t *testing.T) { var nested = struct { Friends textMarshaler `toml:"friends"` }{} var expected = struct { Friends textMarshaler `toml:"friends"` }{ Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"}, } err := Unmarshal(nestedCustomMarshalerTomlForUnmarshal, &nested) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(nested, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, nested) } } func TestNestedTextMarshaler(t *testing.T) { var parent = struct { Self textMarshaler `toml:"me"` Friends []textMarshaler `toml:"friends"` Stranger *textMarshaler `toml:"stranger"` }{ Self: textMarshaler{FirstName: "Maiku", LastName: "Suteda"}, Friends: []textMarshaler{textMarshaler{FirstName: "Sally", LastName: "Fields"}}, Stranger: &textMarshaler{FirstName: "Earl", LastName: "Henson"}, } result, err := Marshal(parent) if err != nil { t.Fatal(err) } expected := `friends = ["Sally Fields"] me = "Maiku Suteda" stranger = "Earl Henson" ` if !bytes.Equal(result, []byte(expected)) { t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } type precedentMarshaler struct { FirstName string LastName string } func (m precedentMarshaler) MarshalText() ([]byte, error) { return []byte("shadowed"), nil } func (m precedentMarshaler) MarshalTOML() ([]byte, error) { fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName) return []byte(fullName), nil } func TestPrecedentMarshaler(t *testing.T) { m := textMarshaler{FirstName: "Sally", LastName: "Fields"} result, err := Marshal(m) if err != nil { t.Fatal(err) } expected := `Sally Fields` if !bytes.Equal(result, []byte(expected)) { t.Errorf("Bad text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } type customPointerMarshaler struct { FirstName string LastName string } func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) { return []byte(`"hidden"`), nil } type textPointerMarshaler struct { FirstName string LastName string } func (m *textPointerMarshaler) MarshalText() ([]byte, error) { return []byte("hidden"), nil } func TestPointerMarshaler(t *testing.T) { var parent = struct { Self customPointerMarshaler `toml:"me"` Stranger *customPointerMarshaler `toml:"stranger"` Friend textPointerMarshaler `toml:"friend"` Fiend *textPointerMarshaler `toml:"fiend"` }{ Self: customPointerMarshaler{FirstName: "Maiku", LastName: "Suteda"}, Stranger: &customPointerMarshaler{FirstName: "Earl", LastName: "Henson"}, Friend: textPointerMarshaler{FirstName: "Sally", LastName: "Fields"}, Fiend: &textPointerMarshaler{FirstName: "Casper", LastName: "Snider"}, } result, err := Marshal(parent) if err != nil { t.Fatal(err) } expected := `fiend = "hidden" stranger = "hidden" [friend] FirstName = "Sally" LastName = "Fields" [me] FirstName = "Maiku" LastName = "Suteda" ` if !bytes.Equal(result, []byte(expected)) { t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestPointerCustomMarshalerSequence(t *testing.T) { var customPointerMarshalerSlice *[]*customPointerMarshaler var customPointerMarshalerArray *[2]*customPointerMarshaler if !isCustomMarshalerSequence(reflect.TypeOf(customPointerMarshalerSlice)) { t.Errorf("error: should be a sequence of custom marshaler interfaces") } if !isCustomMarshalerSequence(reflect.TypeOf(customPointerMarshalerArray)) { t.Errorf("error: should be a sequence of custom marshaler interfaces") } } func TestPointerTextMarshalerSequence(t *testing.T) { var textPointerMarshalerSlice *[]*textPointerMarshaler var textPointerMarshalerArray *[2]*textPointerMarshaler if !isTextMarshalerSequence(reflect.TypeOf(textPointerMarshalerSlice)) { t.Errorf("error: should be a sequence of text marshaler interfaces") } if !isTextMarshalerSequence(reflect.TypeOf(textPointerMarshalerArray)) { t.Errorf("error: should be a sequence of text marshaler interfaces") } } 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() } } func TestMarshalNestedArrayInlineTables(t *testing.T) { type table struct { Value1 int `toml:"ZValue1"` Value2 int `toml:"YValue2"` Value3 int `toml:"XValue3"` } type nestedTable struct { Table table } nestedArray := struct { Simple [][]table SimplePointer *[]*[]table Nested [][]nestedTable NestedPointer *[]*[]nestedTable }{ Simple: [][]table{{{Value1: 1}, {Value1: 10}}}, SimplePointer: &[]*[]table{{{Value2: 2}}}, Nested: [][]nestedTable{{{Table: table{Value3: 3}}}}, NestedPointer: &[]*[]nestedTable{{{Table: table{Value3: -3}}}}, } expectedPreserve := `Simple = [[{ ZValue1 = 1, YValue2 = 0, XValue3 = 0 }, { ZValue1 = 10, YValue2 = 0, XValue3 = 0 }]] SimplePointer = [[{ ZValue1 = 0, YValue2 = 2, XValue3 = 0 }]] Nested = [[{ Table = { ZValue1 = 0, YValue2 = 0, XValue3 = 3 } }]] NestedPointer = [[{ Table = { ZValue1 = 0, YValue2 = 0, XValue3 = -3 } }]] ` expectedAlphabetical := `Nested = [[{ Table = { XValue3 = 3, YValue2 = 0, ZValue1 = 0 } }]] NestedPointer = [[{ Table = { XValue3 = -3, YValue2 = 0, ZValue1 = 0 } }]] Simple = [[{ XValue3 = 0, YValue2 = 0, ZValue1 = 1 }, { XValue3 = 0, YValue2 = 0, ZValue1 = 10 }]] SimplePointer = [[{ XValue3 = 0, YValue2 = 2, ZValue1 = 0 }]] ` var bufPreserve bytes.Buffer if err := NewEncoder(&bufPreserve).Order(OrderPreserve).Encode(nestedArray); err != nil { t.Fatalf("unexpected error: %s", err.Error()) } if !bytes.Equal(bufPreserve.Bytes(), []byte(expectedPreserve)) { t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedPreserve, bufPreserve.String()) } var bufAlphabetical bytes.Buffer if err := NewEncoder(&bufAlphabetical).Order(OrderAlphabetical).Encode(nestedArray); err != nil { t.Fatalf("unexpected error: %s", err.Error()) } if !bytes.Equal(bufAlphabetical.Bytes(), []byte(expectedAlphabetical)) { t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedAlphabetical, bufAlphabetical.String()) } } 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("expected bad duration error") } } 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 TestUnmarshalNegativeUint(t *testing.T) { type check struct{ U uint } tree, _ := Load("u = -1") err := tree.Unmarshal(&check{}) if err.Error() != "(1, 1): -1(int64) is negative so does not fit in uint" { t.Error("expect err:(1, 1): -1(int64) is negative so does not fit in uint but got:", err) } } func TestUnmarshalCheckConversionFloatInt(t *testing.T) { type conversionCheck struct { U uint I int F float64 } treeU, _ := Load("u = 1e300") treeI, _ := Load("i = 1e300") treeF, _ := Load("f = 9223372036854775806") errU := treeU.Unmarshal(&conversionCheck{}) errI := treeI.Unmarshal(&conversionCheck{}) errF := treeF.Unmarshal(&conversionCheck{}) if errU.Error() != "(1, 1): Can't convert 1e+300(float64) to uint" { t.Error("expect err:(1, 1): Can't convert 1e+300(float64) to uint but got:", errU) } if errI.Error() != "(1, 1): Can't convert 1e+300(float64) to int" { t.Error("expect err:(1, 1): Can't convert 1e+300(float64) to int but got:", errI) } if errF.Error() != "(1, 1): Can't convert 9223372036854775806(int64) to float64" { t.Error("expect err:(1, 1): Can't convert 9223372036854775806(int64) to float64 but got:", errF) } } func TestUnmarshalOverflow(t *testing.T) { type overflow struct { U8 uint8 I8 int8 F32 float32 } treeU8, _ := Load("u8 = 300") treeI8, _ := Load("i8 = 300") treeF32, _ := Load("f32 = 1e300") errU8 := treeU8.Unmarshal(&overflow{}) errI8 := treeI8.Unmarshal(&overflow{}) errF32 := treeF32.Unmarshal(&overflow{}) if errU8.Error() != "(1, 1): 300(int64) would overflow uint8" { t.Error("expect err:(1, 1): 300(int64) would overflow uint8 but got:", errU8) } if errI8.Error() != "(1, 1): 300(int64) would overflow int8" { t.Error("expect err:(1, 1): 300(int64) would overflow int8 but got:", errI8) } if errF32.Error() != "(1, 1): 1e+300(float64) would overflow float32" { t.Error("expect err:(1, 1): 1e+300(float64) would overflow float32 but got:", errF32) } } func TestUnmarshalDefault(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 := 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) } } 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 TestUnmarshalDefaultFailureDuration(t *testing.T) { var doc struct { Field time.Duration `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{} } 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) } } 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" ` } 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) { 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) } } func TestMarshalMixedTypeArray(t *testing.T) { type InnerStruct struct { IntField int StrField string } type TestStruct struct { ArrayField []interface{} } expected := []byte(`ArrayField = [3.14, 100, true, "hello world", { IntField = 100, StrField = "inner1" }, [{ IntField = 200, StrField = "inner2" }, { IntField = 300, StrField = "inner3" }]] `) if result, err := Marshal(TestStruct{ ArrayField: []interface{}{ 3.14, 100, true, "hello world", InnerStruct{ IntField: 100, StrField: "inner1", }, []InnerStruct{ {IntField: 200, StrField: "inner2"}, {IntField: 300, StrField: "inner3"}, }, }, }); 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) } } func TestUnmarshalMixedTypeArray(t *testing.T) { type TestStruct struct { ArrayField []interface{} } toml := []byte(`ArrayField = [3.14,100,true,"hello world",{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]] `) actual := TestStruct{} expected := TestStruct{ ArrayField: []interface{}{ 3.14, int64(100), true, "hello world", map[string]interface{}{ "Field": "inner1", }, []map[string]interface{}{ {"Field": "inner2"}, {"Field": "inner3"}, }, }, } 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 TestUnmarshalArray(t *testing.T) { var tree *Tree var err error tree, _ = LoadBytes(sliceTomlDemo) var actual1 arrayStruct err = tree.Unmarshal(&actual1) if err != nil { t.Error("shound not err", err) } tree, _ = TreeFromMap(tree.ToMap()) var actual2 arrayStruct err = tree.Unmarshal(&actual2) if err != nil { t.Error("shound not err", 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"}}, } if !reflect.DeepEqual(actual1, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual1) } if !reflect.DeepEqual(actual2, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual2) } } func TestUnmarshalArrayFail(t *testing.T) { tree, _ := TreeFromMap(map[string]interface{}{ "str_slice": []string{"Howdy", "Hey There"}, }) var actual arrayTooSmallStruct err := tree.Unmarshal(&actual) if err.Error() != "(0, 0): unmarshal: TOML array length (2) exceeds destination array length (1)" { t.Error("expect err:(0, 0): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err) } } func TestUnmarshalArrayFail2(t *testing.T) { tree, _ := Load(`str_slice=["Howdy","Hey There"]`) var actual arrayTooSmallStruct err := tree.Unmarshal(&actual) if err.Error() != "(1, 1): unmarshal: TOML array length (2) exceeds destination array length (1)" { t.Error("expect err:(1, 1): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err) } } func TestUnmarshalArrayFail3(t *testing.T) { tree, _ := Load(`[[struct_slice]] String2="1" [[struct_slice]] String2="2"`) var actual arrayTooSmallStruct err := tree.Unmarshal(&actual) if err.Error() != "(3, 1): unmarshal: TOML array length (2) exceeds destination array length (1)" { t.Error("expect err:(3, 1): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err) } } 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 } } expected := `undecoded keys: ["undecoded.array.0.key" "undecoded.array.1.key" "undecoded.inner.key" "undecoded.key"]` err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc) if err == nil { t.Error("expected error, got none") } else if err.Error() != expected { t.Errorf("expect err: %s, got: %s", expected, err.Error()) } if err := NewDecoder(bytes.NewReader([]byte(input))).Decode(&doc); err != nil { t.Errorf("unexpected err: %s", err) } var m map[string]interface{} if err := NewDecoder(bytes.NewReader([]byte(input))).Decode(&m); err != nil { t.Errorf("unexpected err: %s", err) } } func TestDecoderStrictValid(t *testing.T) { input := ` [decoded] key = "" ` var doc struct { Decoded struct { Key string } } err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc) if err != nil { t.Fatal("unexpected error:", 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) { 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) { input := ` [Doc] key = "ok1" [DocPointer] key = "ok2" ` var d parent if err := Unmarshal([]byte(input), &d); err != nil { t.Fatalf("unexpected err: %s", err.Error()) } if d.Doc.Decoded.Key != "ok1" { t.Errorf("Bad unmarshal: expected ok, got %v", d.Doc.Decoded.Key) } if d.DocPointer.Decoded.Key != "ok2" { t.Errorf("Bad unmarshal: expected ok, got %v", d.DocPointer.Decoded.Key) } } func TestCustomUnmarshalError(t *testing.T) { input := ` [Doc] key = 1 [DocPointer] key = "ok2" ` expected := "(2, 1): unmarshal toml: type assertion error: wants string, have int64" var d parent err := 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 ` if err := Unmarshal([]byte(input), &doc); err != nil { t.Fatalf("unexpected err: %s", err.Error()) } if doc.UnixTime.Value != 12 { t.Fatalf("expected UnixTime: 12 got: %d", doc.UnixTime.Value) } if doc.Version.Value != 42 { t.Fatalf("expected Version: 42 got: %d", doc.Version.Value) } if doc.Bool.Value != 1 { t.Fatalf("expected Bool: 1 got: %d", doc.Bool.Value) } if doc.Int.Value != 21 { t.Fatalf("expected Int: 21 got: %d", doc.Int.Value) } if doc.Float.Value != 2 { t.Fatalf("expected Float: 2 got: %d", doc.Float.Value) } } func TestTextUnmarshalError(t *testing.T) { var doc struct { Failer intWrapper } input := `Failer = "hello"` if err := Unmarshal([]byte(input), &doc); err == nil { t.Fatalf("expected err, got none") } } // issue406 func TestPreserveNotEmptyField(t *testing.T) { toml := []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 := Unmarshal(toml, &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 := Unmarshal(doc, &v) if err != nil { t.Fatal(err) } x, ok := v.(map[string]interface{}) if !ok { t.Fatal(err) } if x["User"] != "pelletier" { t.Fatalf("expected User=pelletier, but got %v", x) } } func TestUnmarshalEmptyInterfaceDeep(t *testing.T) { doc := []byte(` User = "pelletier" Age = 99 [foo] bar = 42 `) var v interface{} err := 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"` tree, err := LoadBytes([]byte(doc)) if err != nil { t.Fatalf("unexpected error: %s", err) } var c Config if err := tree.Unmarshal(&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) { src := ` [HTTP] PingTimeout = "32m" ` cfg := &config437{} cfg.HTTP.PingTimeout = durationString{time.Second} r := strings.NewReader(src) err := NewDecoder(r).Decode(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{} r := strings.NewReader(src) err := NewDecoder(r).Decode(cfg) if err == nil { t.Fatalf("error was expected") } }