1package toml
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io/ioutil"
8	"reflect"
9	"strings"
10	"testing"
11	"time"
12)
13
14type basicMarshalTestStruct struct {
15	String     string                      `toml:"string"`
16	StringList []string                    `toml:"strlist"`
17	Sub        basicMarshalTestSubStruct   `toml:"subdoc"`
18	SubList    []basicMarshalTestSubStruct `toml:"sublist"`
19}
20
21type basicMarshalTestSubStruct struct {
22	String2 string
23}
24
25var basicTestData = basicMarshalTestStruct{
26	String:     "Hello",
27	StringList: []string{"Howdy", "Hey There"},
28	Sub:        basicMarshalTestSubStruct{"One"},
29	SubList:    []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
30}
31
32var basicTestToml = []byte(`string = "Hello"
33strlist = ["Howdy","Hey There"]
34
35[subdoc]
36  String2 = "One"
37
38[[sublist]]
39  String2 = "Two"
40
41[[sublist]]
42  String2 = "Three"
43`)
44
45func TestBasicMarshal(t *testing.T) {
46	result, err := Marshal(basicTestData)
47	if err != nil {
48		t.Fatal(err)
49	}
50	expected := basicTestToml
51	if !bytes.Equal(result, expected) {
52		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
53	}
54}
55
56func TestBasicUnmarshal(t *testing.T) {
57	result := basicMarshalTestStruct{}
58	err := Unmarshal(basicTestToml, &result)
59	expected := basicTestData
60	if err != nil {
61		t.Fatal(err)
62	}
63	if !reflect.DeepEqual(result, expected) {
64		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
65	}
66}
67
68type testDoc struct {
69	Title       string            `toml:"title"`
70	Basics      testDocBasics     `toml:"basic"`
71	BasicLists  testDocBasicLists `toml:"basic_lists"`
72	BasicMap    map[string]string `toml:"basic_map"`
73	Subdocs     testDocSubs       `toml:"subdoc"`
74	SubDocList  []testSubDoc      `toml:"subdoclist"`
75	SubDocPtrs  []*testSubDoc     `toml:"subdocptrs"`
76	err         int               `toml:"shouldntBeHere"`
77	unexported  int               `toml:"shouldntBeHere"`
78	Unexported2 int               `toml:"-"`
79}
80
81type testDocBasics struct {
82	Bool       bool      `toml:"bool"`
83	Date       time.Time `toml:"date"`
84	Float      float32   `toml:"float"`
85	Int        int       `toml:"int"`
86	Uint       uint      `toml:"uint"`
87	String     *string   `toml:"string"`
88	unexported int       `toml:"shouldntBeHere"`
89}
90
91type testDocBasicLists struct {
92	Bools   []bool      `toml:"bools"`
93	Dates   []time.Time `toml:"dates"`
94	Floats  []*float32  `toml:"floats"`
95	Ints    []int       `toml:"ints"`
96	Strings []string    `toml:"strings"`
97	UInts   []uint      `toml:"uints"`
98}
99
100type testDocSubs struct {
101	First  testSubDoc  `toml:"first"`
102	Second *testSubDoc `toml:"second"`
103}
104
105type testSubDoc struct {
106	Name       string `toml:"name"`
107	unexported int    `toml:"shouldntBeHere"`
108}
109
110var biteMe = "Bite me"
111var float1 float32 = 12.3
112var float2 float32 = 45.6
113var float3 float32 = 78.9
114var subdoc = testSubDoc{"Second", 0}
115
116var docData = testDoc{
117	Title:       "TOML Marshal Testing",
118	unexported:  0,
119	Unexported2: 0,
120	Basics: testDocBasics{
121		Bool:       true,
122		Date:       time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
123		Float:      123.4,
124		Int:        5000,
125		Uint:       5001,
126		String:     &biteMe,
127		unexported: 0,
128	},
129	BasicLists: testDocBasicLists{
130		Bools: []bool{true, false, true},
131		Dates: []time.Time{
132			time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
133			time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC),
134		},
135		Floats:  []*float32{&float1, &float2, &float3},
136		Ints:    []int{8001, 8001, 8002},
137		Strings: []string{"One", "Two", "Three"},
138		UInts:   []uint{5002, 5003},
139	},
140	BasicMap: map[string]string{
141		"one": "one",
142		"two": "two",
143	},
144	Subdocs: testDocSubs{
145		First:  testSubDoc{"First", 0},
146		Second: &subdoc,
147	},
148	SubDocList: []testSubDoc{
149		{"List.First", 0},
150		{"List.Second", 0},
151	},
152	SubDocPtrs: []*testSubDoc{&subdoc},
153}
154
155func TestDocMarshal(t *testing.T) {
156	result, err := Marshal(docData)
157	if err != nil {
158		t.Fatal(err)
159	}
160	expected, _ := ioutil.ReadFile("marshal_test.toml")
161	if !bytes.Equal(result, expected) {
162		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
163	}
164}
165
166func TestDocUnmarshal(t *testing.T) {
167	result := testDoc{}
168	tomlData, _ := ioutil.ReadFile("marshal_test.toml")
169	err := Unmarshal(tomlData, &result)
170	expected := docData
171	if err != nil {
172		t.Fatal(err)
173	}
174	if !reflect.DeepEqual(result, expected) {
175		resStr, _ := json.MarshalIndent(result, "", "  ")
176		expStr, _ := json.MarshalIndent(expected, "", "  ")
177		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
178	}
179}
180
181func TestDocPartialUnmarshal(t *testing.T) {
182	result := testDocSubs{}
183
184	tree, _ := LoadFile("marshal_test.toml")
185	subTree := tree.Get("subdoc").(*Tree)
186	err := subTree.Unmarshal(&result)
187	expected := docData.Subdocs
188	if err != nil {
189		t.Fatal(err)
190	}
191	if !reflect.DeepEqual(result, expected) {
192		resStr, _ := json.MarshalIndent(result, "", "  ")
193		expStr, _ := json.MarshalIndent(expected, "", "  ")
194		t.Errorf("Bad partial unmartial: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
195	}
196}
197
198type tomlTypeCheckTest struct {
199	name string
200	item interface{}
201	typ  int //0=primitive, 1=otherslice, 2=treeslice, 3=tree
202}
203
204func TestTypeChecks(t *testing.T) {
205	tests := []tomlTypeCheckTest{
206		{"integer", 2, 0},
207		{"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0},
208		{"stringlist", []string{"hello", "hi"}, 1},
209		{"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
210		{"objectlist", []tomlTypeCheckTest{}, 2},
211		{"object", tomlTypeCheckTest{}, 3},
212	}
213
214	for _, test := range tests {
215		expected := []bool{false, false, false, false}
216		expected[test.typ] = true
217		result := []bool{
218			isPrimitive(reflect.TypeOf(test.item)),
219			isOtherSlice(reflect.TypeOf(test.item)),
220			isTreeSlice(reflect.TypeOf(test.item)),
221			isTree(reflect.TypeOf(test.item)),
222		}
223		if !reflect.DeepEqual(expected, result) {
224			t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result)
225		}
226	}
227}
228
229type unexportedMarshalTestStruct struct {
230	String      string                      `toml:"string"`
231	StringList  []string                    `toml:"strlist"`
232	Sub         basicMarshalTestSubStruct   `toml:"subdoc"`
233	SubList     []basicMarshalTestSubStruct `toml:"sublist"`
234	unexported  int                         `toml:"shouldntBeHere"`
235	Unexported2 int                         `toml:"-"`
236}
237
238var unexportedTestData = unexportedMarshalTestStruct{
239	String:      "Hello",
240	StringList:  []string{"Howdy", "Hey There"},
241	Sub:         basicMarshalTestSubStruct{"One"},
242	SubList:     []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
243	unexported:  0,
244	Unexported2: 0,
245}
246
247var unexportedTestToml = []byte(`string = "Hello"
248strlist = ["Howdy","Hey There"]
249unexported = 1
250shouldntBeHere = 2
251
252[subdoc]
253  String2 = "One"
254
255[[sublist]]
256  String2 = "Two"
257
258[[sublist]]
259  String2 = "Three"
260`)
261
262func TestUnexportedUnmarshal(t *testing.T) {
263	result := unexportedMarshalTestStruct{}
264	err := Unmarshal(unexportedTestToml, &result)
265	expected := unexportedTestData
266	if err != nil {
267		t.Fatal(err)
268	}
269	if !reflect.DeepEqual(result, expected) {
270		t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result)
271	}
272}
273
274type errStruct struct {
275	Bool   bool      `toml:"bool"`
276	Date   time.Time `toml:"date"`
277	Float  float64   `toml:"float"`
278	Int    int16     `toml:"int"`
279	String *string   `toml:"string"`
280}
281
282var errTomls = []string{
283	"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
284	"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
285	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
286	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"",
287	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
288	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
289	"bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
290	"bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
291	"bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"",
292	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"",
293	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1",
294}
295
296type mapErr struct {
297	Vals map[string]float64
298}
299
300type intErr struct {
301	Int1  int
302	Int2  int8
303	Int3  int16
304	Int4  int32
305	Int5  int64
306	UInt1 uint
307	UInt2 uint8
308	UInt3 uint16
309	UInt4 uint32
310	UInt5 uint64
311	Flt1  float32
312	Flt2  float64
313}
314
315var intErrTomls = []string{
316	"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",
317	"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",
318	"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",
319	"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",
320	"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",
321	"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",
322	"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",
323	"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",
324	"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",
325	"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",
326	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0",
327	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []",
328}
329
330func TestErrUnmarshal(t *testing.T) {
331	for ind, toml := range errTomls {
332		result := errStruct{}
333		err := Unmarshal([]byte(toml), &result)
334		if err == nil {
335			t.Errorf("Expected err from case %d\n", ind)
336		}
337	}
338	result2 := mapErr{}
339	err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
340	if err == nil {
341		t.Errorf("Expected err from map")
342	}
343	for ind, toml := range intErrTomls {
344		result3 := intErr{}
345		err := Unmarshal([]byte(toml), &result3)
346		if err == nil {
347			t.Errorf("Expected int err from case %d\n", ind)
348		}
349	}
350}
351
352type emptyMarshalTestStruct struct {
353	Title      string                  `toml:"title"`
354	Bool       bool                    `toml:"bool"`
355	Int        int                     `toml:"int"`
356	String     string                  `toml:"string"`
357	StringList []string                `toml:"stringlist"`
358	Ptr        *basicMarshalTestStruct `toml:"ptr"`
359	Map        map[string]string       `toml:"map"`
360}
361
362var emptyTestData = emptyMarshalTestStruct{
363	Title:      "Placeholder",
364	Bool:       false,
365	Int:        0,
366	String:     "",
367	StringList: []string{},
368	Ptr:        nil,
369	Map:        map[string]string{},
370}
371
372var emptyTestToml = []byte(`bool = false
373int = 0
374string = ""
375stringlist = []
376title = "Placeholder"
377
378[map]
379`)
380
381type emptyMarshalTestStruct2 struct {
382	Title      string                  `toml:"title"`
383	Bool       bool                    `toml:"bool,omitempty"`
384	Int        int                     `toml:"int, omitempty"`
385	String     string                  `toml:"string,omitempty "`
386	StringList []string                `toml:"stringlist,omitempty"`
387	Ptr        *basicMarshalTestStruct `toml:"ptr,omitempty"`
388	Map        map[string]string       `toml:"map,omitempty"`
389}
390
391var emptyTestData2 = emptyMarshalTestStruct2{
392	Title:      "Placeholder",
393	Bool:       false,
394	Int:        0,
395	String:     "",
396	StringList: []string{},
397	Ptr:        nil,
398	Map:        map[string]string{},
399}
400
401var emptyTestToml2 = []byte(`title = "Placeholder"
402`)
403
404func TestEmptyMarshal(t *testing.T) {
405	result, err := Marshal(emptyTestData)
406	if err != nil {
407		t.Fatal(err)
408	}
409	expected := emptyTestToml
410	if !bytes.Equal(result, expected) {
411		t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
412	}
413}
414
415func TestEmptyMarshalOmit(t *testing.T) {
416	result, err := Marshal(emptyTestData2)
417	if err != nil {
418		t.Fatal(err)
419	}
420	expected := emptyTestToml2
421	if !bytes.Equal(result, expected) {
422		t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
423	}
424}
425
426func TestEmptyUnmarshal(t *testing.T) {
427	result := emptyMarshalTestStruct{}
428	err := Unmarshal(emptyTestToml, &result)
429	expected := emptyTestData
430	if err != nil {
431		t.Fatal(err)
432	}
433	if !reflect.DeepEqual(result, expected) {
434		t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result)
435	}
436}
437
438func TestEmptyUnmarshalOmit(t *testing.T) {
439	result := emptyMarshalTestStruct2{}
440	err := Unmarshal(emptyTestToml, &result)
441	expected := emptyTestData2
442	if err != nil {
443		t.Fatal(err)
444	}
445	if !reflect.DeepEqual(result, expected) {
446		t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result)
447	}
448}
449
450type pointerMarshalTestStruct struct {
451	Str       *string
452	List      *[]string
453	ListPtr   *[]*string
454	Map       *map[string]string
455	MapPtr    *map[string]*string
456	EmptyStr  *string
457	EmptyList *[]string
458	EmptyMap  *map[string]string
459	DblPtr    *[]*[]*string
460}
461
462var pointerStr = "Hello"
463var pointerList = []string{"Hello back"}
464var pointerListPtr = []*string{&pointerStr}
465var pointerMap = map[string]string{"response": "Goodbye"}
466var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
467var pointerTestData = pointerMarshalTestStruct{
468	Str:       &pointerStr,
469	List:      &pointerList,
470	ListPtr:   &pointerListPtr,
471	Map:       &pointerMap,
472	MapPtr:    &pointerMapPtr,
473	EmptyStr:  nil,
474	EmptyList: nil,
475	EmptyMap:  nil,
476}
477
478var pointerTestToml = []byte(`List = ["Hello back"]
479ListPtr = ["Hello"]
480Str = "Hello"
481
482[Map]
483  response = "Goodbye"
484
485[MapPtr]
486  alternate = "Hello"
487`)
488
489func TestPointerMarshal(t *testing.T) {
490	result, err := Marshal(pointerTestData)
491	if err != nil {
492		t.Fatal(err)
493	}
494	expected := pointerTestToml
495	if !bytes.Equal(result, expected) {
496		t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
497	}
498}
499
500func TestPointerUnmarshal(t *testing.T) {
501	result := pointerMarshalTestStruct{}
502	err := Unmarshal(pointerTestToml, &result)
503	expected := pointerTestData
504	if err != nil {
505		t.Fatal(err)
506	}
507	if !reflect.DeepEqual(result, expected) {
508		t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result)
509	}
510}
511
512func TestUnmarshalTypeMismatch(t *testing.T) {
513	result := pointerMarshalTestStruct{}
514	err := Unmarshal([]byte("List = 123"), &result)
515	if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") {
516		t.Errorf("Type mismatch must be reported: got %v", err.Error())
517	}
518}
519
520type nestedMarshalTestStruct struct {
521	String [][]string
522	//Struct [][]basicMarshalTestSubStruct
523	StringPtr *[]*[]*string
524	// StructPtr *[]*[]*basicMarshalTestSubStruct
525}
526
527var str1 = "Three"
528var str2 = "Four"
529var strPtr = []*string{&str1, &str2}
530var strPtr2 = []*[]*string{&strPtr}
531
532var nestedTestData = nestedMarshalTestStruct{
533	String:    [][]string{{"Five", "Six"}, {"One", "Two"}},
534	StringPtr: &strPtr2,
535}
536
537var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]]
538StringPtr = [["Three","Four"]]
539`)
540
541func TestNestedMarshal(t *testing.T) {
542	result, err := Marshal(nestedTestData)
543	if err != nil {
544		t.Fatal(err)
545	}
546	expected := nestedTestToml
547	if !bytes.Equal(result, expected) {
548		t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
549	}
550}
551
552func TestNestedUnmarshal(t *testing.T) {
553	result := nestedMarshalTestStruct{}
554	err := Unmarshal(nestedTestToml, &result)
555	expected := nestedTestData
556	if err != nil {
557		t.Fatal(err)
558	}
559	if !reflect.DeepEqual(result, expected) {
560		t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result)
561	}
562}
563
564type customMarshalerParent struct {
565	Self    customMarshaler   `toml:"me"`
566	Friends []customMarshaler `toml:"friends"`
567}
568
569type customMarshaler struct {
570	FirsName string
571	LastName string
572}
573
574func (c customMarshaler) MarshalTOML() ([]byte, error) {
575	fullName := fmt.Sprintf("%s %s", c.FirsName, c.LastName)
576	return []byte(fullName), nil
577}
578
579var customMarshalerData = customMarshaler{FirsName: "Sally", LastName: "Fields"}
580var customMarshalerToml = []byte(`Sally Fields`)
581var nestedCustomMarshalerData = customMarshalerParent{
582	Self:    customMarshaler{FirsName: "Maiku", LastName: "Suteda"},
583	Friends: []customMarshaler{customMarshalerData},
584}
585var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
586me = "Maiku Suteda"
587`)
588
589func TestCustomMarshaler(t *testing.T) {
590	result, err := Marshal(customMarshalerData)
591	if err != nil {
592		t.Fatal(err)
593	}
594	expected := customMarshalerToml
595	if !bytes.Equal(result, expected) {
596		t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
597	}
598}
599
600func TestNestedCustomMarshaler(t *testing.T) {
601	result, err := Marshal(nestedCustomMarshalerData)
602	if err != nil {
603		t.Fatal(err)
604	}
605	expected := nestedCustomMarshalerToml
606	if !bytes.Equal(result, expected) {
607		t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
608	}
609}
610
611var commentTestToml = []byte(`
612# it's a comment on type
613[postgres]
614  # isCommented = "dvalue"
615  noComment = "cvalue"
616
617  # A comment on AttrB with a
618  # break line
619  password = "bvalue"
620
621  # A comment on AttrA
622  user = "avalue"
623
624  [[postgres.My]]
625
626    # a comment on my on typeC
627    My = "Foo"
628
629  [[postgres.My]]
630
631    # a comment on my on typeC
632    My = "Baar"
633`)
634
635func TestMarshalComment(t *testing.T) {
636	type TypeC struct {
637		My string `comment:"a comment on my on typeC"`
638	}
639	type TypeB struct {
640		AttrA string `toml:"user" comment:"A comment on AttrA"`
641		AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"`
642		AttrC string `toml:"noComment"`
643		AttrD string `toml:"isCommented" commented:"true"`
644		My    []TypeC
645	}
646	type TypeA struct {
647		TypeB TypeB `toml:"postgres" comment:"it's a comment on type"`
648	}
649
650	ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
651	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}}
652	result, err := Marshal(config)
653	if err != nil {
654		t.Fatal(err)
655	}
656	expected := commentTestToml
657	if !bytes.Equal(result, expected) {
658		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
659	}
660}
661
662type mapsTestStruct struct {
663	Simple map[string]string
664	Paths  map[string]string
665	Other  map[string]float64
666	X      struct {
667		Y struct {
668			Z map[string]bool
669		}
670	}
671}
672
673var mapsTestData = mapsTestStruct{
674	Simple: map[string]string{
675		"one plus one": "two",
676		"next":         "three",
677	},
678	Paths: map[string]string{
679		"/this/is/a/path": "/this/is/also/a/path",
680		"/heloo.txt":      "/tmp/lololo.txt",
681	},
682	Other: map[string]float64{
683		"testing": 3.9999,
684	},
685	X: struct{ Y struct{ Z map[string]bool } }{
686		Y: struct{ Z map[string]bool }{
687			Z: map[string]bool{
688				"is.Nested": true,
689			},
690		},
691	},
692}
693var mapsTestToml = []byte(`
694[Other]
695  "testing" = 3.9999
696
697[Paths]
698  "/heloo.txt" = "/tmp/lololo.txt"
699  "/this/is/a/path" = "/this/is/also/a/path"
700
701[Simple]
702  "next" = "three"
703  "one plus one" = "two"
704
705[X]
706
707  [X.Y]
708
709    [X.Y.Z]
710      "is.Nested" = true
711`)
712
713func TestEncodeQuotedMapKeys(t *testing.T) {
714	var buf bytes.Buffer
715	if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil {
716		t.Fatal(err)
717	}
718	result := buf.Bytes()
719	expected := mapsTestToml
720	if !bytes.Equal(result, expected) {
721		t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
722	}
723}
724
725func TestDecodeQuotedMapKeys(t *testing.T) {
726	result := mapsTestStruct{}
727	err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result)
728	expected := mapsTestData
729	if err != nil {
730		t.Fatal(err)
731	}
732	if !reflect.DeepEqual(result, expected) {
733		t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
734	}
735}
736
737type structArrayNoTag struct {
738	A struct {
739		B []int64
740		C []int64
741	}
742}
743
744func TestMarshalArray(t *testing.T) {
745	expected := []byte(`
746[A]
747  B = [1,2,3]
748  C = [1]
749`)
750
751	m := structArrayNoTag{
752		A: struct {
753			B []int64
754			C []int64
755		}{
756			B: []int64{1, 2, 3},
757			C: []int64{1},
758		},
759	}
760
761	b, err := Marshal(m)
762
763	if err != nil {
764		t.Fatal(err)
765	}
766
767	if !bytes.Equal(b, expected) {
768		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
769	}
770}
771
772func TestMarshalArrayOnePerLine(t *testing.T) {
773	expected := []byte(`
774[A]
775  B = [
776    1,
777    2,
778    3,
779  ]
780  C = [1]
781`)
782
783	m := structArrayNoTag{
784		A: struct {
785			B []int64
786			C []int64
787		}{
788			B: []int64{1, 2, 3},
789			C: []int64{1},
790		},
791	}
792
793	var buf bytes.Buffer
794	encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true)
795	err := encoder.Encode(m)
796
797	if err != nil {
798		t.Fatal(err)
799	}
800
801	b := buf.Bytes()
802
803	if !bytes.Equal(b, expected) {
804		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
805	}
806}
807