package toml import ( "bytes" "encoding/json" "fmt" "io/ioutil" "reflect" "strings" "testing" "time" ) type basicMarshalTestStruct struct { String string `toml:"string"` StringList []string `toml:"strlist"` Sub basicMarshalTestSubStruct `toml:"subdoc"` SubList []basicMarshalTestSubStruct `toml:"sublist"` } type basicMarshalTestSubStruct struct { String2 string } var basicTestData = basicMarshalTestStruct{ String: "Hello", StringList: []string{"Howdy", "Hey There"}, Sub: basicMarshalTestSubStruct{"One"}, SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, } var basicTestToml = []byte(`string = "Hello" strlist = ["Howdy","Hey There"] [subdoc] String2 = "One" [[sublist]] String2 = "Two" [[sublist]] String2 = "Three" `) 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 TestBasicUnmarshal(t *testing.T) { result := basicMarshalTestStruct{} err := Unmarshal(basicTestToml, &result) expected := basicTestData if err != nil { t.Fatal(err) } if !reflect.DeepEqual(result, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) } } type testDoc struct { Title string `toml:"title"` Basics testDocBasics `toml:"basic"` BasicLists testDocBasicLists `toml:"basic_lists"` BasicMap map[string]string `toml:"basic_map"` Subdocs testDocSubs `toml:"subdoc"` SubDocList []testSubDoc `toml:"subdoclist"` SubDocPtrs []*testSubDoc `toml:"subdocptrs"` err int `toml:"shouldntBeHere"` unexported int `toml:"shouldntBeHere"` Unexported2 int `toml:"-"` } type testDocBasics struct { Bool bool `toml:"bool"` Date time.Time `toml:"date"` Float float32 `toml:"float"` Int int `toml:"int"` Uint uint `toml:"uint"` String *string `toml:"string"` unexported int `toml:"shouldntBeHere"` } type testDocBasicLists struct { Bools []bool `toml:"bools"` Dates []time.Time `toml:"dates"` Floats []*float32 `toml:"floats"` Ints []int `toml:"ints"` Strings []string `toml:"strings"` UInts []uint `toml:"uints"` } type testDocSubs struct { First testSubDoc `toml:"first"` Second *testSubDoc `toml:"second"` } 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), Float: 123.4, 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{ testSubDoc{"List.First", 0}, testSubDoc{"List.Second", 0}, }, SubDocPtrs: []*testSubDoc{&subdoc}, } func TestDocMarshal(t *testing.T) { result, err := Marshal(docData) if err != nil { t.Fatal(err) } expected, _ := ioutil.ReadFile("marshal_test.toml") if !bytes.Equal(result, expected) { t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestDocUnmarshal(t *testing.T) { result := testDoc{} tomlData, _ := ioutil.ReadFile("marshal_test.toml") err := Unmarshal(tomlData, &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) { result := testDocSubs{} tree, _ := LoadFile("marshal_test.toml") subTree := tree.Get("subdoc").(*Tree) 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{ {"integer", 2, 0}, {"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0}, {"stringlist", []string{"hello", "hi"}, 1}, {"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1}, {"objectlist", []tomlTypeCheckTest{}, 2}, {"object", tomlTypeCheckTest{}, 3}, } for _, test := range tests { expected := []bool{false, false, false, false} expected[test.typ] = true result := []bool{ isPrimitive(reflect.TypeOf(test.item)), isOtherSlice(reflect.TypeOf(test.item)), isTreeSlice(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{[]string{"Five", "Six"}, []string{"One", "Two"}}, StringPtr: &strPtr2, } var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]] StringPtr = [["Three","Four"]] `) func TestNestedMarshal(t *testing.T) { result, err := Marshal(nestedTestData) if err != nil { t.Fatal(err) } expected := nestedTestToml if !bytes.Equal(result, expected) { t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestNestedUnmarshal(t *testing.T) { result := nestedMarshalTestStruct{} err := Unmarshal(nestedTestToml, &result) expected := nestedTestData if err != nil { t.Fatal(err) } if !reflect.DeepEqual(result, expected) { t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result) } } type customMarshalerParent struct { Self customMarshaler `toml:"me"` Friends []customMarshaler `toml:"friends"` } type customMarshaler struct { FirsName string LastName string } func (c customMarshaler) MarshalTOML() ([]byte, error) { fullName := fmt.Sprintf("%s %s", c.FirsName, c.LastName) return []byte(fullName), nil } var customMarshalerData = customMarshaler{FirsName: "Sally", LastName: "Fields"} var customMarshalerToml = []byte(`Sally Fields`) var nestedCustomMarshalerData = customMarshalerParent{ Self: customMarshaler{FirsName: "Maiku", LastName: "Suteda"}, Friends: []customMarshaler{customMarshalerData}, } var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"] me = "Maiku Suteda" `) func TestCustomMarshaler(t *testing.T) { result, err := Marshal(customMarshalerData) if err != nil { t.Fatal(err) } expected := customMarshalerToml if !bytes.Equal(result, expected) { t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } func TestNestedCustomMarshaler(t *testing.T) { result, err := Marshal(nestedCustomMarshalerData) if err != nil { t.Fatal(err) } expected := nestedCustomMarshalerToml if !bytes.Equal(result, expected) { t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } var commentTestToml = []byte(` # it's a comment on type [postgres] # isCommented = "dvalue" noComment = "cvalue" # A comment on AttrB with a # break line password = "bvalue" # A comment on AttrA user = "avalue" [[postgres.My]] # a comment on my on typeC My = "Foo" [[postgres.My]] # a comment on my on typeC My = "Baar" `) func TestMarshalComment(t *testing.T) { type TypeC struct { My string `comment:"a comment on my on typeC"` } type TypeB struct { AttrA string `toml:"user" comment:"A comment on AttrA"` AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` AttrC string `toml:"noComment"` AttrD string `toml:"isCommented" commented:"true"` My []TypeC } type TypeA struct { TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` } ta := []TypeC{{My: "Foo"}, {My: "Baar"}} config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}} result, err := Marshal(config) if err != nil { t.Fatal(err) } expected := commentTestToml if !bytes.Equal(result, expected) { t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } 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) } }