1package toml
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io/ioutil"
8	"os"
9	"reflect"
10	"strings"
11	"testing"
12	"time"
13)
14
15type basicMarshalTestStruct struct {
16	String     string                      `toml:"Zstring"`
17	StringList []string                    `toml:"Ystrlist"`
18	Sub        basicMarshalTestSubStruct   `toml:"Xsubdoc"`
19	SubList    []basicMarshalTestSubStruct `toml:"Wsublist"`
20}
21
22type basicMarshalTestSubStruct struct {
23	String2 string
24}
25
26var basicTestData = basicMarshalTestStruct{
27	String:     "Hello",
28	StringList: []string{"Howdy", "Hey There"},
29	Sub:        basicMarshalTestSubStruct{"One"},
30	SubList:    []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
31}
32
33var basicTestToml = []byte(`Ystrlist = ["Howdy","Hey There"]
34Zstring = "Hello"
35
36[[Wsublist]]
37  String2 = "Two"
38
39[[Wsublist]]
40  String2 = "Three"
41
42[Xsubdoc]
43  String2 = "One"
44`)
45
46var basicTestTomlOrdered = []byte(`Zstring = "Hello"
47Ystrlist = ["Howdy","Hey There"]
48
49[Xsubdoc]
50  String2 = "One"
51
52[[Wsublist]]
53  String2 = "Two"
54
55[[Wsublist]]
56  String2 = "Three"
57`)
58
59var marshalTestToml = []byte(`title = "TOML Marshal Testing"
60
61[basic]
62  bool = true
63  date = 1979-05-27T07:32:00Z
64  float = 123.4
65  float64 = 123.456782132399
66  int = 5000
67  string = "Bite me"
68  uint = 5001
69
70[basic_lists]
71  bools = [true,false,true]
72  dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
73  floats = [12.3,45.6,78.9]
74  ints = [8001,8001,8002]
75  strings = ["One","Two","Three"]
76  uints = [5002,5003]
77
78[basic_map]
79  one = "one"
80  two = "two"
81
82[subdoc]
83
84  [subdoc.first]
85    name = "First"
86
87  [subdoc.second]
88    name = "Second"
89
90[[subdoclist]]
91  name = "List.First"
92
93[[subdoclist]]
94  name = "List.Second"
95
96[[subdocptrs]]
97  name = "Second"
98`)
99
100var marshalOrderPreserveToml = []byte(`title = "TOML Marshal Testing"
101
102[basic_lists]
103  floats = [12.3,45.6,78.9]
104  bools = [true,false,true]
105  dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
106  ints = [8001,8001,8002]
107  uints = [5002,5003]
108  strings = ["One","Two","Three"]
109
110[[subdocptrs]]
111  name = "Second"
112
113[basic_map]
114  one = "one"
115  two = "two"
116
117[subdoc]
118
119  [subdoc.second]
120    name = "Second"
121
122  [subdoc.first]
123    name = "First"
124
125[basic]
126  uint = 5001
127  bool = true
128  float = 123.4
129  float64 = 123.456782132399
130  int = 5000
131  string = "Bite me"
132  date = 1979-05-27T07:32:00Z
133
134[[subdoclist]]
135  name = "List.First"
136
137[[subdoclist]]
138  name = "List.Second"
139`)
140
141var mashalOrderPreserveMapToml = []byte(`title = "TOML Marshal Testing"
142
143[basic_map]
144  one = "one"
145  two = "two"
146
147[long_map]
148  a7 = "1"
149  b3 = "2"
150  c8 = "3"
151  d4 = "4"
152  e6 = "5"
153  f5 = "6"
154  g10 = "7"
155  h1 = "8"
156  i2 = "9"
157  j9 = "10"
158`)
159
160func TestBasicMarshal(t *testing.T) {
161	result, err := Marshal(basicTestData)
162	if err != nil {
163		t.Fatal(err)
164	}
165	expected := basicTestToml
166	if !bytes.Equal(result, expected) {
167		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
168	}
169}
170
171func TestBasicMarshalOrdered(t *testing.T) {
172	var result bytes.Buffer
173	err := NewEncoder(&result).Order(OrderPreserve).Encode(basicTestData)
174	if err != nil {
175		t.Fatal(err)
176	}
177	expected := basicTestTomlOrdered
178	if !bytes.Equal(result.Bytes(), expected) {
179		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
180	}
181}
182
183func TestBasicMarshalWithPointer(t *testing.T) {
184	result, err := Marshal(&basicTestData)
185	if err != nil {
186		t.Fatal(err)
187	}
188	expected := basicTestToml
189	if !bytes.Equal(result, expected) {
190		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
191	}
192}
193
194func TestBasicMarshalOrderedWithPointer(t *testing.T) {
195	var result bytes.Buffer
196	err := NewEncoder(&result).Order(OrderPreserve).Encode(&basicTestData)
197	if err != nil {
198		t.Fatal(err)
199	}
200	expected := basicTestTomlOrdered
201	if !bytes.Equal(result.Bytes(), expected) {
202		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
203	}
204}
205
206func TestBasicUnmarshal(t *testing.T) {
207	result := basicMarshalTestStruct{}
208	err := Unmarshal(basicTestToml, &result)
209	expected := basicTestData
210	if err != nil {
211		t.Fatal(err)
212	}
213	if !reflect.DeepEqual(result, expected) {
214		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
215	}
216}
217
218type testDoc struct {
219	Title       string            `toml:"title"`
220	BasicLists  testDocBasicLists `toml:"basic_lists"`
221	SubDocPtrs  []*testSubDoc     `toml:"subdocptrs"`
222	BasicMap    map[string]string `toml:"basic_map"`
223	Subdocs     testDocSubs       `toml:"subdoc"`
224	Basics      testDocBasics     `toml:"basic"`
225	SubDocList  []testSubDoc      `toml:"subdoclist"`
226	err         int               `toml:"shouldntBeHere"`
227	unexported  int               `toml:"shouldntBeHere"`
228	Unexported2 int               `toml:"-"`
229}
230
231type testMapDoc struct {
232	Title    string            `toml:"title"`
233	BasicMap map[string]string `toml:"basic_map"`
234	LongMap  map[string]string `toml:"long_map"`
235}
236
237type testDocBasics struct {
238	Uint       uint      `toml:"uint"`
239	Bool       bool      `toml:"bool"`
240	Float32    float32   `toml:"float"`
241	Float64    float64   `toml:"float64"`
242	Int        int       `toml:"int"`
243	String     *string   `toml:"string"`
244	Date       time.Time `toml:"date"`
245	unexported int       `toml:"shouldntBeHere"`
246}
247
248type testDocBasicLists struct {
249	Floats  []*float32  `toml:"floats"`
250	Bools   []bool      `toml:"bools"`
251	Dates   []time.Time `toml:"dates"`
252	Ints    []int       `toml:"ints"`
253	UInts   []uint      `toml:"uints"`
254	Strings []string    `toml:"strings"`
255}
256
257type testDocSubs struct {
258	Second *testSubDoc `toml:"second"`
259	First  testSubDoc  `toml:"first"`
260}
261
262type testSubDoc struct {
263	Name       string `toml:"name"`
264	unexported int    `toml:"shouldntBeHere"`
265}
266
267var biteMe = "Bite me"
268var float1 float32 = 12.3
269var float2 float32 = 45.6
270var float3 float32 = 78.9
271var subdoc = testSubDoc{"Second", 0}
272
273var docData = testDoc{
274	Title:       "TOML Marshal Testing",
275	unexported:  0,
276	Unexported2: 0,
277	Basics: testDocBasics{
278		Bool:       true,
279		Date:       time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
280		Float32:    123.4,
281		Float64:    123.456782132399,
282		Int:        5000,
283		Uint:       5001,
284		String:     &biteMe,
285		unexported: 0,
286	},
287	BasicLists: testDocBasicLists{
288		Bools: []bool{true, false, true},
289		Dates: []time.Time{
290			time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
291			time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC),
292		},
293		Floats:  []*float32{&float1, &float2, &float3},
294		Ints:    []int{8001, 8001, 8002},
295		Strings: []string{"One", "Two", "Three"},
296		UInts:   []uint{5002, 5003},
297	},
298	BasicMap: map[string]string{
299		"one": "one",
300		"two": "two",
301	},
302	Subdocs: testDocSubs{
303		First:  testSubDoc{"First", 0},
304		Second: &subdoc,
305	},
306	SubDocList: []testSubDoc{
307		{"List.First", 0},
308		{"List.Second", 0},
309	},
310	SubDocPtrs: []*testSubDoc{&subdoc},
311}
312
313var mapTestDoc = testMapDoc{
314	Title: "TOML Marshal Testing",
315	BasicMap: map[string]string{
316		"one": "one",
317		"two": "two",
318	},
319	LongMap: map[string]string{
320		"h1":  "8",
321		"i2":  "9",
322		"b3":  "2",
323		"d4":  "4",
324		"f5":  "6",
325		"e6":  "5",
326		"a7":  "1",
327		"c8":  "3",
328		"j9":  "10",
329		"g10": "7",
330	},
331}
332
333func TestDocMarshal(t *testing.T) {
334	result, err := Marshal(docData)
335	if err != nil {
336		t.Fatal(err)
337	}
338	if !bytes.Equal(result, marshalTestToml) {
339		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
340	}
341}
342
343func TestDocMarshalOrdered(t *testing.T) {
344	var result bytes.Buffer
345	err := NewEncoder(&result).Order(OrderPreserve).Encode(docData)
346	if err != nil {
347		t.Fatal(err)
348	}
349	if !bytes.Equal(result.Bytes(), marshalOrderPreserveToml) {
350		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalOrderPreserveToml, result.Bytes())
351	}
352}
353
354func TestDocMarshalMaps(t *testing.T) {
355	result, err := Marshal(mapTestDoc)
356	if err != nil {
357		t.Fatal(err)
358	}
359	if !bytes.Equal(result, mashalOrderPreserveMapToml) {
360		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result)
361	}
362}
363
364func TestDocMarshalOrderedMaps(t *testing.T) {
365	var result bytes.Buffer
366	err := NewEncoder(&result).Order(OrderPreserve).Encode(mapTestDoc)
367	if err != nil {
368		t.Fatal(err)
369	}
370	if !bytes.Equal(result.Bytes(), mashalOrderPreserveMapToml) {
371		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result.Bytes())
372	}
373}
374
375func TestDocMarshalPointer(t *testing.T) {
376	result, err := Marshal(&docData)
377	if err != nil {
378		t.Fatal(err)
379	}
380
381	if !bytes.Equal(result, marshalTestToml) {
382		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
383	}
384}
385
386func TestDocUnmarshal(t *testing.T) {
387	result := testDoc{}
388	err := Unmarshal(marshalTestToml, &result)
389	expected := docData
390	if err != nil {
391		t.Fatal(err)
392	}
393	if !reflect.DeepEqual(result, expected) {
394		resStr, _ := json.MarshalIndent(result, "", "  ")
395		expStr, _ := json.MarshalIndent(expected, "", "  ")
396		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
397	}
398}
399
400func TestDocPartialUnmarshal(t *testing.T) {
401	file, err := ioutil.TempFile("", "test-*.toml")
402	if err != nil {
403		t.Fatal(err)
404	}
405	defer os.Remove(file.Name())
406
407	err = ioutil.WriteFile(file.Name(), marshalTestToml, 0)
408	if err != nil {
409		t.Fatal(err)
410	}
411
412	tree, _ := LoadFile(file.Name())
413	subTree := tree.Get("subdoc").(*Tree)
414
415	result := testDocSubs{}
416	err = subTree.Unmarshal(&result)
417	expected := docData.Subdocs
418	if err != nil {
419		t.Fatal(err)
420	}
421	if !reflect.DeepEqual(result, expected) {
422		resStr, _ := json.MarshalIndent(result, "", "  ")
423		expStr, _ := json.MarshalIndent(expected, "", "  ")
424		t.Errorf("Bad partial unmartial: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
425	}
426}
427
428type tomlTypeCheckTest struct {
429	name string
430	item interface{}
431	typ  int //0=primitive, 1=otherslice, 2=treeslice, 3=tree
432}
433
434func TestTypeChecks(t *testing.T) {
435	tests := []tomlTypeCheckTest{
436		{"bool", true, 0},
437		{"bool", false, 0},
438		{"int", int(2), 0},
439		{"int8", int8(2), 0},
440		{"int16", int16(2), 0},
441		{"int32", int32(2), 0},
442		{"int64", int64(2), 0},
443		{"uint", uint(2), 0},
444		{"uint8", uint8(2), 0},
445		{"uint16", uint16(2), 0},
446		{"uint32", uint32(2), 0},
447		{"uint64", uint64(2), 0},
448		{"float32", float32(3.14), 0},
449		{"float64", float64(3.14), 0},
450		{"string", "lorem ipsum", 0},
451		{"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0},
452		{"stringlist", []string{"hello", "hi"}, 1},
453		{"stringlistptr", &[]string{"hello", "hi"}, 1},
454		{"stringarray", [2]string{"hello", "hi"}, 1},
455		{"stringarrayptr", &[2]string{"hello", "hi"}, 1},
456		{"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
457		{"timelistptr", &[]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
458		{"timearray", [1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
459		{"timearrayptr", &[1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
460		{"objectlist", []tomlTypeCheckTest{}, 2},
461		{"objectlistptr", &[]tomlTypeCheckTest{}, 2},
462		{"objectarray", [2]tomlTypeCheckTest{{}, {}}, 2},
463		{"objectlistptr", &[2]tomlTypeCheckTest{{}, {}}, 2},
464		{"object", tomlTypeCheckTest{}, 3},
465		{"objectptr", &tomlTypeCheckTest{}, 3},
466	}
467
468	for _, test := range tests {
469		expected := []bool{false, false, false, false}
470		expected[test.typ] = true
471		result := []bool{
472			isPrimitive(reflect.TypeOf(test.item)),
473			isOtherSequence(reflect.TypeOf(test.item)),
474			isTreeSequence(reflect.TypeOf(test.item)),
475			isTree(reflect.TypeOf(test.item)),
476		}
477		if !reflect.DeepEqual(expected, result) {
478			t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result)
479		}
480	}
481}
482
483type unexportedMarshalTestStruct struct {
484	String      string                      `toml:"string"`
485	StringList  []string                    `toml:"strlist"`
486	Sub         basicMarshalTestSubStruct   `toml:"subdoc"`
487	SubList     []basicMarshalTestSubStruct `toml:"sublist"`
488	unexported  int                         `toml:"shouldntBeHere"`
489	Unexported2 int                         `toml:"-"`
490}
491
492var unexportedTestData = unexportedMarshalTestStruct{
493	String:      "Hello",
494	StringList:  []string{"Howdy", "Hey There"},
495	Sub:         basicMarshalTestSubStruct{"One"},
496	SubList:     []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
497	unexported:  0,
498	Unexported2: 0,
499}
500
501var unexportedTestToml = []byte(`string = "Hello"
502strlist = ["Howdy","Hey There"]
503unexported = 1
504shouldntBeHere = 2
505
506[subdoc]
507  String2 = "One"
508
509[[sublist]]
510  String2 = "Two"
511
512[[sublist]]
513  String2 = "Three"
514`)
515
516func TestUnexportedUnmarshal(t *testing.T) {
517	result := unexportedMarshalTestStruct{}
518	err := Unmarshal(unexportedTestToml, &result)
519	expected := unexportedTestData
520	if err != nil {
521		t.Fatal(err)
522	}
523	if !reflect.DeepEqual(result, expected) {
524		t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result)
525	}
526}
527
528type errStruct struct {
529	Bool   bool      `toml:"bool"`
530	Date   time.Time `toml:"date"`
531	Float  float64   `toml:"float"`
532	Int    int16     `toml:"int"`
533	String *string   `toml:"string"`
534}
535
536var errTomls = []string{
537	"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
538	"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
539	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
540	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"",
541	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
542	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
543	"bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
544	"bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
545	"bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"",
546	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"",
547	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1",
548}
549
550type mapErr struct {
551	Vals map[string]float64
552}
553
554type intErr struct {
555	Int1  int
556	Int2  int8
557	Int3  int16
558	Int4  int32
559	Int5  int64
560	UInt1 uint
561	UInt2 uint8
562	UInt3 uint16
563	UInt4 uint32
564	UInt5 uint64
565	Flt1  float32
566	Flt2  float64
567}
568
569var intErrTomls = []string{
570	"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",
571	"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",
572	"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",
573	"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",
574	"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",
575	"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",
576	"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",
577	"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",
578	"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",
579	"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",
580	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0",
581	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []",
582}
583
584func TestErrUnmarshal(t *testing.T) {
585	for ind, toml := range errTomls {
586		result := errStruct{}
587		err := Unmarshal([]byte(toml), &result)
588		if err == nil {
589			t.Errorf("Expected err from case %d\n", ind)
590		}
591	}
592	result2 := mapErr{}
593	err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
594	if err == nil {
595		t.Errorf("Expected err from map")
596	}
597	for ind, toml := range intErrTomls {
598		result3 := intErr{}
599		err := Unmarshal([]byte(toml), &result3)
600		if err == nil {
601			t.Errorf("Expected int err from case %d\n", ind)
602		}
603	}
604}
605
606type emptyMarshalTestStruct struct {
607	Title      string                  `toml:"title"`
608	Bool       bool                    `toml:"bool"`
609	Int        int                     `toml:"int"`
610	String     string                  `toml:"string"`
611	StringList []string                `toml:"stringlist"`
612	Ptr        *basicMarshalTestStruct `toml:"ptr"`
613	Map        map[string]string       `toml:"map"`
614}
615
616var emptyTestData = emptyMarshalTestStruct{
617	Title:      "Placeholder",
618	Bool:       false,
619	Int:        0,
620	String:     "",
621	StringList: []string{},
622	Ptr:        nil,
623	Map:        map[string]string{},
624}
625
626var emptyTestToml = []byte(`bool = false
627int = 0
628string = ""
629stringlist = []
630title = "Placeholder"
631
632[map]
633`)
634
635type emptyMarshalTestStruct2 struct {
636	Title      string                  `toml:"title"`
637	Bool       bool                    `toml:"bool,omitempty"`
638	Int        int                     `toml:"int, omitempty"`
639	String     string                  `toml:"string,omitempty "`
640	StringList []string                `toml:"stringlist,omitempty"`
641	Ptr        *basicMarshalTestStruct `toml:"ptr,omitempty"`
642	Map        map[string]string       `toml:"map,omitempty"`
643}
644
645var emptyTestData2 = emptyMarshalTestStruct2{
646	Title:      "Placeholder",
647	Bool:       false,
648	Int:        0,
649	String:     "",
650	StringList: []string{},
651	Ptr:        nil,
652	Map:        map[string]string{},
653}
654
655var emptyTestToml2 = []byte(`title = "Placeholder"
656`)
657
658func TestEmptyMarshal(t *testing.T) {
659	result, err := Marshal(emptyTestData)
660	if err != nil {
661		t.Fatal(err)
662	}
663	expected := emptyTestToml
664	if !bytes.Equal(result, expected) {
665		t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
666	}
667}
668
669func TestEmptyMarshalOmit(t *testing.T) {
670	result, err := Marshal(emptyTestData2)
671	if err != nil {
672		t.Fatal(err)
673	}
674	expected := emptyTestToml2
675	if !bytes.Equal(result, expected) {
676		t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
677	}
678}
679
680func TestEmptyUnmarshal(t *testing.T) {
681	result := emptyMarshalTestStruct{}
682	err := Unmarshal(emptyTestToml, &result)
683	expected := emptyTestData
684	if err != nil {
685		t.Fatal(err)
686	}
687	if !reflect.DeepEqual(result, expected) {
688		t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result)
689	}
690}
691
692func TestEmptyUnmarshalOmit(t *testing.T) {
693	result := emptyMarshalTestStruct2{}
694	err := Unmarshal(emptyTestToml, &result)
695	expected := emptyTestData2
696	if err != nil {
697		t.Fatal(err)
698	}
699	if !reflect.DeepEqual(result, expected) {
700		t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result)
701	}
702}
703
704type pointerMarshalTestStruct struct {
705	Str       *string
706	List      *[]string
707	ListPtr   *[]*string
708	Map       *map[string]string
709	MapPtr    *map[string]*string
710	EmptyStr  *string
711	EmptyList *[]string
712	EmptyMap  *map[string]string
713	DblPtr    *[]*[]*string
714}
715
716var pointerStr = "Hello"
717var pointerList = []string{"Hello back"}
718var pointerListPtr = []*string{&pointerStr}
719var pointerMap = map[string]string{"response": "Goodbye"}
720var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
721var pointerTestData = pointerMarshalTestStruct{
722	Str:       &pointerStr,
723	List:      &pointerList,
724	ListPtr:   &pointerListPtr,
725	Map:       &pointerMap,
726	MapPtr:    &pointerMapPtr,
727	EmptyStr:  nil,
728	EmptyList: nil,
729	EmptyMap:  nil,
730}
731
732var pointerTestToml = []byte(`List = ["Hello back"]
733ListPtr = ["Hello"]
734Str = "Hello"
735
736[Map]
737  response = "Goodbye"
738
739[MapPtr]
740  alternate = "Hello"
741`)
742
743func TestPointerMarshal(t *testing.T) {
744	result, err := Marshal(pointerTestData)
745	if err != nil {
746		t.Fatal(err)
747	}
748	expected := pointerTestToml
749	if !bytes.Equal(result, expected) {
750		t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
751	}
752}
753
754func TestPointerUnmarshal(t *testing.T) {
755	result := pointerMarshalTestStruct{}
756	err := Unmarshal(pointerTestToml, &result)
757	expected := pointerTestData
758	if err != nil {
759		t.Fatal(err)
760	}
761	if !reflect.DeepEqual(result, expected) {
762		t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result)
763	}
764}
765
766func TestUnmarshalTypeMismatch(t *testing.T) {
767	result := pointerMarshalTestStruct{}
768	err := Unmarshal([]byte("List = 123"), &result)
769	if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") {
770		t.Errorf("Type mismatch must be reported: got %v", err.Error())
771	}
772}
773
774type nestedMarshalTestStruct struct {
775	String [][]string
776	//Struct [][]basicMarshalTestSubStruct
777	StringPtr *[]*[]*string
778	// StructPtr *[]*[]*basicMarshalTestSubStruct
779}
780
781var str1 = "Three"
782var str2 = "Four"
783var strPtr = []*string{&str1, &str2}
784var strPtr2 = []*[]*string{&strPtr}
785
786var nestedTestData = nestedMarshalTestStruct{
787	String:    [][]string{{"Five", "Six"}, {"One", "Two"}},
788	StringPtr: &strPtr2,
789}
790
791var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]]
792StringPtr = [["Three","Four"]]
793`)
794
795func TestNestedMarshal(t *testing.T) {
796	result, err := Marshal(nestedTestData)
797	if err != nil {
798		t.Fatal(err)
799	}
800	expected := nestedTestToml
801	if !bytes.Equal(result, expected) {
802		t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
803	}
804}
805
806func TestNestedUnmarshal(t *testing.T) {
807	result := nestedMarshalTestStruct{}
808	err := Unmarshal(nestedTestToml, &result)
809	expected := nestedTestData
810	if err != nil {
811		t.Fatal(err)
812	}
813	if !reflect.DeepEqual(result, expected) {
814		t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result)
815	}
816}
817
818type customMarshalerParent struct {
819	Self    customMarshaler   `toml:"me"`
820	Friends []customMarshaler `toml:"friends"`
821}
822
823type customMarshaler struct {
824	FirsName string
825	LastName string
826}
827
828func (c customMarshaler) MarshalTOML() ([]byte, error) {
829	fullName := fmt.Sprintf("%s %s", c.FirsName, c.LastName)
830	return []byte(fullName), nil
831}
832
833var customMarshalerData = customMarshaler{FirsName: "Sally", LastName: "Fields"}
834var customMarshalerToml = []byte(`Sally Fields`)
835var nestedCustomMarshalerData = customMarshalerParent{
836	Self:    customMarshaler{FirsName: "Maiku", LastName: "Suteda"},
837	Friends: []customMarshaler{customMarshalerData},
838}
839var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
840me = "Maiku Suteda"
841`)
842
843func TestCustomMarshaler(t *testing.T) {
844	result, err := Marshal(customMarshalerData)
845	if err != nil {
846		t.Fatal(err)
847	}
848	expected := customMarshalerToml
849	if !bytes.Equal(result, expected) {
850		t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
851	}
852}
853
854func TestNestedCustomMarshaler(t *testing.T) {
855	result, err := Marshal(nestedCustomMarshalerData)
856	if err != nil {
857		t.Fatal(err)
858	}
859	expected := nestedCustomMarshalerToml
860	if !bytes.Equal(result, expected) {
861		t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
862	}
863}
864
865var commentTestToml = []byte(`
866# it's a comment on type
867[postgres]
868  # isCommented = "dvalue"
869  noComment = "cvalue"
870
871  # A comment on AttrB with a
872  # break line
873  password = "bvalue"
874
875  # A comment on AttrA
876  user = "avalue"
877
878  [[postgres.My]]
879
880    # a comment on my on typeC
881    My = "Foo"
882
883  [[postgres.My]]
884
885    # a comment on my on typeC
886    My = "Baar"
887`)
888
889func TestMarshalComment(t *testing.T) {
890	type TypeC struct {
891		My string `comment:"a comment on my on typeC"`
892	}
893	type TypeB struct {
894		AttrA string `toml:"user" comment:"A comment on AttrA"`
895		AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"`
896		AttrC string `toml:"noComment"`
897		AttrD string `toml:"isCommented" commented:"true"`
898		My    []TypeC
899	}
900	type TypeA struct {
901		TypeB TypeB `toml:"postgres" comment:"it's a comment on type"`
902	}
903
904	ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
905	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}}
906	result, err := Marshal(config)
907	if err != nil {
908		t.Fatal(err)
909	}
910	expected := commentTestToml
911	if !bytes.Equal(result, expected) {
912		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
913	}
914}
915
916type mapsTestStruct struct {
917	Simple map[string]string
918	Paths  map[string]string
919	Other  map[string]float64
920	X      struct {
921		Y struct {
922			Z map[string]bool
923		}
924	}
925}
926
927var mapsTestData = mapsTestStruct{
928	Simple: map[string]string{
929		"one plus one": "two",
930		"next":         "three",
931	},
932	Paths: map[string]string{
933		"/this/is/a/path": "/this/is/also/a/path",
934		"/heloo.txt":      "/tmp/lololo.txt",
935	},
936	Other: map[string]float64{
937		"testing": 3.9999,
938	},
939	X: struct{ Y struct{ Z map[string]bool } }{
940		Y: struct{ Z map[string]bool }{
941			Z: map[string]bool{
942				"is.Nested": true,
943			},
944		},
945	},
946}
947var mapsTestToml = []byte(`
948[Other]
949  "testing" = 3.9999
950
951[Paths]
952  "/heloo.txt" = "/tmp/lololo.txt"
953  "/this/is/a/path" = "/this/is/also/a/path"
954
955[Simple]
956  "next" = "three"
957  "one plus one" = "two"
958
959[X]
960
961  [X.Y]
962
963    [X.Y.Z]
964      "is.Nested" = true
965`)
966
967func TestEncodeQuotedMapKeys(t *testing.T) {
968	var buf bytes.Buffer
969	if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil {
970		t.Fatal(err)
971	}
972	result := buf.Bytes()
973	expected := mapsTestToml
974	if !bytes.Equal(result, expected) {
975		t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
976	}
977}
978
979func TestDecodeQuotedMapKeys(t *testing.T) {
980	result := mapsTestStruct{}
981	err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result)
982	expected := mapsTestData
983	if err != nil {
984		t.Fatal(err)
985	}
986	if !reflect.DeepEqual(result, expected) {
987		t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
988	}
989}
990
991type structArrayNoTag struct {
992	A struct {
993		B []int64
994		C []int64
995	}
996}
997
998func TestMarshalArray(t *testing.T) {
999	expected := []byte(`
1000[A]
1001  B = [1,2,3]
1002  C = [1]
1003`)
1004
1005	m := structArrayNoTag{
1006		A: struct {
1007			B []int64
1008			C []int64
1009		}{
1010			B: []int64{1, 2, 3},
1011			C: []int64{1},
1012		},
1013	}
1014
1015	b, err := Marshal(m)
1016
1017	if err != nil {
1018		t.Fatal(err)
1019	}
1020
1021	if !bytes.Equal(b, expected) {
1022		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
1023	}
1024}
1025
1026func TestMarshalArrayOnePerLine(t *testing.T) {
1027	expected := []byte(`
1028[A]
1029  B = [
1030    1,
1031    2,
1032    3,
1033  ]
1034  C = [1]
1035`)
1036
1037	m := structArrayNoTag{
1038		A: struct {
1039			B []int64
1040			C []int64
1041		}{
1042			B: []int64{1, 2, 3},
1043			C: []int64{1},
1044		},
1045	}
1046
1047	var buf bytes.Buffer
1048	encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true)
1049	err := encoder.Encode(m)
1050
1051	if err != nil {
1052		t.Fatal(err)
1053	}
1054
1055	b := buf.Bytes()
1056
1057	if !bytes.Equal(b, expected) {
1058		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
1059	}
1060}
1061
1062var customTagTestToml = []byte(`
1063[postgres]
1064  password = "bvalue"
1065  user = "avalue"
1066
1067  [[postgres.My]]
1068    My = "Foo"
1069
1070  [[postgres.My]]
1071    My = "Baar"
1072`)
1073
1074func TestMarshalCustomTag(t *testing.T) {
1075	type TypeC struct {
1076		My string
1077	}
1078	type TypeB struct {
1079		AttrA string `file:"user"`
1080		AttrB string `file:"password"`
1081		My    []TypeC
1082	}
1083	type TypeA struct {
1084		TypeB TypeB `file:"postgres"`
1085	}
1086
1087	ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
1088	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", My: ta}}
1089	var buf bytes.Buffer
1090	err := NewEncoder(&buf).SetTagName("file").Encode(config)
1091	if err != nil {
1092		t.Fatal(err)
1093	}
1094	expected := customTagTestToml
1095	result := buf.Bytes()
1096	if !bytes.Equal(result, expected) {
1097		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1098	}
1099}
1100
1101var customCommentTagTestToml = []byte(`
1102# db connection
1103[postgres]
1104
1105  # db pass
1106  password = "bvalue"
1107
1108  # db user
1109  user = "avalue"
1110`)
1111
1112func TestMarshalCustomComment(t *testing.T) {
1113	type TypeB struct {
1114		AttrA string `toml:"user" descr:"db user"`
1115		AttrB string `toml:"password" descr:"db pass"`
1116	}
1117	type TypeA struct {
1118		TypeB TypeB `toml:"postgres" descr:"db connection"`
1119	}
1120
1121	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
1122	var buf bytes.Buffer
1123	err := NewEncoder(&buf).SetTagComment("descr").Encode(config)
1124	if err != nil {
1125		t.Fatal(err)
1126	}
1127	expected := customCommentTagTestToml
1128	result := buf.Bytes()
1129	if !bytes.Equal(result, expected) {
1130		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1131	}
1132}
1133
1134var customCommentedTagTestToml = []byte(`
1135[postgres]
1136  # password = "bvalue"
1137  # user = "avalue"
1138`)
1139
1140func TestMarshalCustomCommented(t *testing.T) {
1141	type TypeB struct {
1142		AttrA string `toml:"user" disable:"true"`
1143		AttrB string `toml:"password" disable:"true"`
1144	}
1145	type TypeA struct {
1146		TypeB TypeB `toml:"postgres"`
1147	}
1148
1149	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
1150	var buf bytes.Buffer
1151	err := NewEncoder(&buf).SetTagCommented("disable").Encode(config)
1152	if err != nil {
1153		t.Fatal(err)
1154	}
1155	expected := customCommentedTagTestToml
1156	result := buf.Bytes()
1157	if !bytes.Equal(result, expected) {
1158		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1159	}
1160}
1161
1162func TestMarshalDirectMultilineString(t *testing.T) {
1163	tree := newTree()
1164	tree.SetWithOptions("mykey", SetOptions{
1165		Multiline: true,
1166	}, "my\x11multiline\nstring\ba\tb\fc\rd\"e\\!")
1167	result, err := tree.Marshal()
1168	if err != nil {
1169		t.Fatal("marshal should not error:", err)
1170	}
1171	expected := []byte("mykey = \"\"\"\nmy\\u0011multiline\nstring\\ba\tb\\fc\rd\"e\\!\"\"\"\n")
1172	if !bytes.Equal(result, expected) {
1173		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1174	}
1175}
1176
1177var customMultilineTagTestToml = []byte(`int_slice = [
1178  1,
1179  2,
1180  3,
1181]
1182`)
1183
1184func TestMarshalCustomMultiline(t *testing.T) {
1185	type TypeA struct {
1186		AttrA []int `toml:"int_slice" mltln:"true"`
1187	}
1188
1189	config := TypeA{AttrA: []int{1, 2, 3}}
1190	var buf bytes.Buffer
1191	err := NewEncoder(&buf).ArraysWithOneElementPerLine(true).SetTagMultiline("mltln").Encode(config)
1192	if err != nil {
1193		t.Fatal(err)
1194	}
1195	expected := customMultilineTagTestToml
1196	result := buf.Bytes()
1197	if !bytes.Equal(result, expected) {
1198		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1199	}
1200}
1201
1202var testDocBasicToml = []byte(`
1203[document]
1204  bool_val = true
1205  date_val = 1979-05-27T07:32:00Z
1206  float_val = 123.4
1207  int_val = 5000
1208  string_val = "Bite me"
1209  uint_val = 5001
1210`)
1211
1212type testDocCustomTag struct {
1213	Doc testDocBasicsCustomTag `file:"document"`
1214}
1215type testDocBasicsCustomTag struct {
1216	Bool       bool      `file:"bool_val"`
1217	Date       time.Time `file:"date_val"`
1218	Float      float32   `file:"float_val"`
1219	Int        int       `file:"int_val"`
1220	Uint       uint      `file:"uint_val"`
1221	String     *string   `file:"string_val"`
1222	unexported int       `file:"shouldntBeHere"`
1223}
1224
1225var testDocCustomTagData = testDocCustomTag{
1226	Doc: testDocBasicsCustomTag{
1227		Bool:       true,
1228		Date:       time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
1229		Float:      123.4,
1230		Int:        5000,
1231		Uint:       5001,
1232		String:     &biteMe,
1233		unexported: 0,
1234	},
1235}
1236
1237func TestUnmarshalCustomTag(t *testing.T) {
1238	buf := bytes.NewBuffer(testDocBasicToml)
1239
1240	result := testDocCustomTag{}
1241	err := NewDecoder(buf).SetTagName("file").Decode(&result)
1242	if err != nil {
1243		t.Fatal(err)
1244	}
1245	expected := testDocCustomTagData
1246	if !reflect.DeepEqual(result, expected) {
1247		resStr, _ := json.MarshalIndent(result, "", "  ")
1248		expStr, _ := json.MarshalIndent(expected, "", "  ")
1249		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
1250
1251	}
1252}
1253
1254func TestUnmarshalMap(t *testing.T) {
1255	testToml := []byte(`
1256		a = 1
1257		b = 2
1258		c = 3
1259		`)
1260	var result map[string]int
1261	err := Unmarshal(testToml, &result)
1262	if err != nil {
1263		t.Errorf("Received unexpected error: %s", err)
1264		return
1265	}
1266
1267	expected := map[string]int{
1268		"a": 1,
1269		"b": 2,
1270		"c": 3,
1271	}
1272
1273	if !reflect.DeepEqual(result, expected) {
1274		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
1275	}
1276}
1277
1278func TestUnmarshalMapWithTypedKey(t *testing.T) {
1279	testToml := []byte(`
1280		a = 1
1281		b = 2
1282		c = 3
1283		`)
1284
1285	type letter string
1286	var result map[letter]int
1287	err := Unmarshal(testToml, &result)
1288	if err != nil {
1289		t.Errorf("Received unexpected error: %s", err)
1290		return
1291	}
1292
1293	expected := map[letter]int{
1294		"a": 1,
1295		"b": 2,
1296		"c": 3,
1297	}
1298
1299	if !reflect.DeepEqual(result, expected) {
1300		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
1301	}
1302}
1303
1304func TestUnmarshalNonPointer(t *testing.T) {
1305	a := 1
1306	err := Unmarshal([]byte{}, a)
1307	if err == nil {
1308		t.Fatal("unmarshal should err when given a non pointer")
1309	}
1310}
1311
1312func TestUnmarshalInvalidPointerKind(t *testing.T) {
1313	a := 1
1314	err := Unmarshal([]byte{}, &a)
1315	if err == nil {
1316		t.Fatal("unmarshal should err when given an invalid pointer type")
1317	}
1318}
1319
1320func TestMarshalSlice(t *testing.T) {
1321	m := make([]int, 1)
1322	m[0] = 1
1323
1324	var buf bytes.Buffer
1325	err := NewEncoder(&buf).Encode(&m)
1326	if err == nil {
1327		t.Error("expected error, got nil")
1328		return
1329	}
1330	if err.Error() != "Only pointer to struct can be marshaled to TOML" {
1331		t.Fail()
1332	}
1333}
1334
1335func TestMarshalSlicePointer(t *testing.T) {
1336	m := make([]int, 1)
1337	m[0] = 1
1338
1339	var buf bytes.Buffer
1340	err := NewEncoder(&buf).Encode(m)
1341	if err == nil {
1342		t.Error("expected error, got nil")
1343		return
1344	}
1345	if err.Error() != "Only a struct or map can be marshaled to TOML" {
1346		t.Fail()
1347	}
1348}
1349
1350type testDuration struct {
1351	Nanosec   time.Duration  `toml:"nanosec"`
1352	Microsec1 time.Duration  `toml:"microsec1"`
1353	Microsec2 *time.Duration `toml:"microsec2"`
1354	Millisec  time.Duration  `toml:"millisec"`
1355	Sec       time.Duration  `toml:"sec"`
1356	Min       time.Duration  `toml:"min"`
1357	Hour      time.Duration  `toml:"hour"`
1358	Mixed     time.Duration  `toml:"mixed"`
1359	AString   string         `toml:"a_string"`
1360}
1361
1362var testDurationToml = []byte(`
1363nanosec = "1ns"
1364microsec1 = "1us"
1365microsec2 = "1µs"
1366millisec = "1ms"
1367sec = "1s"
1368min = "1m"
1369hour = "1h"
1370mixed = "1h1m1s1ms1µs1ns"
1371a_string = "15s"
1372`)
1373
1374func TestUnmarshalDuration(t *testing.T) {
1375	buf := bytes.NewBuffer(testDurationToml)
1376
1377	result := testDuration{}
1378	err := NewDecoder(buf).Decode(&result)
1379	if err != nil {
1380		t.Fatal(err)
1381	}
1382	ms := time.Duration(1) * time.Microsecond
1383	expected := testDuration{
1384		Nanosec:   1,
1385		Microsec1: time.Microsecond,
1386		Microsec2: &ms,
1387		Millisec:  time.Millisecond,
1388		Sec:       time.Second,
1389		Min:       time.Minute,
1390		Hour:      time.Hour,
1391		Mixed: time.Hour +
1392			time.Minute +
1393			time.Second +
1394			time.Millisecond +
1395			time.Microsecond +
1396			time.Nanosecond,
1397		AString: "15s",
1398	}
1399	if !reflect.DeepEqual(result, expected) {
1400		resStr, _ := json.MarshalIndent(result, "", "  ")
1401		expStr, _ := json.MarshalIndent(expected, "", "  ")
1402		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
1403
1404	}
1405}
1406
1407var testDurationToml2 = []byte(`a_string = "15s"
1408hour = "1h0m0s"
1409microsec1 = "1µs"
1410microsec2 = "1µs"
1411millisec = "1ms"
1412min = "1m0s"
1413mixed = "1h1m1.001001001s"
1414nanosec = "1ns"
1415sec = "1s"
1416`)
1417
1418func TestMarshalDuration(t *testing.T) {
1419	ms := time.Duration(1) * time.Microsecond
1420	data := testDuration{
1421		Nanosec:   1,
1422		Microsec1: time.Microsecond,
1423		Microsec2: &ms,
1424		Millisec:  time.Millisecond,
1425		Sec:       time.Second,
1426		Min:       time.Minute,
1427		Hour:      time.Hour,
1428		Mixed: time.Hour +
1429			time.Minute +
1430			time.Second +
1431			time.Millisecond +
1432			time.Microsecond +
1433			time.Nanosecond,
1434		AString: "15s",
1435	}
1436
1437	var buf bytes.Buffer
1438	err := NewEncoder(&buf).Encode(data)
1439	if err != nil {
1440		t.Fatal(err)
1441	}
1442	expected := testDurationToml2
1443	result := buf.Bytes()
1444	if !bytes.Equal(result, expected) {
1445		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1446	}
1447}
1448
1449type testBadDuration struct {
1450	Val time.Duration `toml:"val"`
1451}
1452
1453var testBadDurationToml = []byte(`val = "1z"`)
1454
1455func TestUnmarshalBadDuration(t *testing.T) {
1456	buf := bytes.NewBuffer(testBadDurationToml)
1457
1458	result := testBadDuration{}
1459	err := NewDecoder(buf).Decode(&result)
1460	if err == nil {
1461		t.Fatal()
1462	}
1463	if err.Error() != "(1, 1): Can't convert 1z(string) to time.Duration. time: unknown unit z in duration 1z" {
1464		t.Fatalf("unexpected error: %s", err)
1465	}
1466}
1467
1468var testCamelCaseKeyToml = []byte(`fooBar = 10`)
1469
1470func TestUnmarshalCamelCaseKey(t *testing.T) {
1471	var x struct {
1472		FooBar int
1473		B      int
1474	}
1475
1476	if err := Unmarshal(testCamelCaseKeyToml, &x); err != nil {
1477		t.Fatal(err)
1478	}
1479
1480	if x.FooBar != 10 {
1481		t.Fatal("Did not set camelCase'd key")
1482	}
1483}
1484
1485func TestUnmarshalDefault(t *testing.T) {
1486	var doc struct {
1487		StringField  string  `default:"a"`
1488		BoolField    bool    `default:"true"`
1489		IntField     int     `default:"1"`
1490		Int64Field   int64   `default:"2"`
1491		Float64Field float64 `default:"3.1"`
1492	}
1493
1494	err := Unmarshal([]byte(``), &doc)
1495	if err != nil {
1496		t.Fatal(err)
1497	}
1498	if doc.BoolField != true {
1499		t.Errorf("BoolField should be true, not %t", doc.BoolField)
1500	}
1501	if doc.StringField != "a" {
1502		t.Errorf("StringField should be \"a\", not %s", doc.StringField)
1503	}
1504	if doc.IntField != 1 {
1505		t.Errorf("IntField should be 1, not %d", doc.IntField)
1506	}
1507	if doc.Int64Field != 2 {
1508		t.Errorf("Int64Field should be 2, not %d", doc.Int64Field)
1509	}
1510	if doc.Float64Field != 3.1 {
1511		t.Errorf("Float64Field should be 3.1, not %f", doc.Float64Field)
1512	}
1513}
1514
1515func TestUnmarshalDefaultFailureBool(t *testing.T) {
1516	var doc struct {
1517		Field bool `default:"blah"`
1518	}
1519
1520	err := Unmarshal([]byte(``), &doc)
1521	if err == nil {
1522		t.Fatal("should error")
1523	}
1524}
1525
1526func TestUnmarshalDefaultFailureInt(t *testing.T) {
1527	var doc struct {
1528		Field int `default:"blah"`
1529	}
1530
1531	err := Unmarshal([]byte(``), &doc)
1532	if err == nil {
1533		t.Fatal("should error")
1534	}
1535}
1536
1537func TestUnmarshalDefaultFailureInt64(t *testing.T) {
1538	var doc struct {
1539		Field int64 `default:"blah"`
1540	}
1541
1542	err := Unmarshal([]byte(``), &doc)
1543	if err == nil {
1544		t.Fatal("should error")
1545	}
1546}
1547
1548func TestUnmarshalDefaultFailureFloat64(t *testing.T) {
1549	var doc struct {
1550		Field float64 `default:"blah"`
1551	}
1552
1553	err := Unmarshal([]byte(``), &doc)
1554	if err == nil {
1555		t.Fatal("should error")
1556	}
1557}
1558
1559func TestUnmarshalDefaultFailureUnsupported(t *testing.T) {
1560	var doc struct {
1561		Field struct{} `default:"blah"`
1562	}
1563
1564	err := Unmarshal([]byte(``), &doc)
1565	if err == nil {
1566		t.Fatal("should error")
1567	}
1568}
1569
1570func TestUnmarshalNestedAnonymousStructs(t *testing.T) {
1571	type Nested struct {
1572		Value string `toml:"nested_field"`
1573	}
1574	type Deep struct {
1575		Nested
1576	}
1577	type Document struct {
1578		Deep
1579		Value string `toml:"own_field"`
1580	}
1581
1582	var doc Document
1583
1584	err := Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc)
1585	if err != nil {
1586		t.Fatal("should not error")
1587	}
1588	if doc.Value != "own value" || doc.Nested.Value != "nested value" {
1589		t.Fatal("unexpected values")
1590	}
1591}
1592
1593func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) {
1594	type Nested struct {
1595		Value string `toml:"nested"`
1596	}
1597	type Deep struct {
1598		Nested
1599	}
1600	type Document struct {
1601		Deep
1602		Value string `toml:"own"`
1603	}
1604
1605	var doc Document
1606
1607	err := Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc)
1608	if err == nil {
1609		t.Fatal("should error")
1610	}
1611}
1612
1613type unexportedFieldPreservationTest struct {
1614	Exported   string `toml:"exported"`
1615	unexported string
1616	Nested1    unexportedFieldPreservationTestNested    `toml:"nested1"`
1617	Nested2    *unexportedFieldPreservationTestNested   `toml:"nested2"`
1618	Nested3    *unexportedFieldPreservationTestNested   `toml:"nested3"`
1619	Slice1     []unexportedFieldPreservationTestNested  `toml:"slice1"`
1620	Slice2     []*unexportedFieldPreservationTestNested `toml:"slice2"`
1621}
1622
1623type unexportedFieldPreservationTestNested struct {
1624	Exported1   string `toml:"exported1"`
1625	unexported1 string
1626}
1627
1628func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
1629	toml := `
1630	exported = "visible"
1631	unexported = "ignored"
1632
1633	[nested1]
1634	exported1 = "visible1"
1635	unexported1 = "ignored1"
1636
1637	[nested2]
1638	exported1 = "visible2"
1639	unexported1 = "ignored2"
1640
1641	[nested3]
1642	exported1 = "visible3"
1643	unexported1 = "ignored3"
1644
1645	[[slice1]]
1646	exported1 = "visible3"
1647
1648	[[slice1]]
1649	exported1 = "visible4"
1650
1651	[[slice2]]
1652	exported1 = "visible5"
1653	`
1654
1655	t.Run("unexported field should not be set from toml", func(t *testing.T) {
1656		var actual unexportedFieldPreservationTest
1657		err := Unmarshal([]byte(toml), &actual)
1658
1659		if err != nil {
1660			t.Fatal("did not expect an error")
1661		}
1662
1663		expect := unexportedFieldPreservationTest{
1664			Exported:   "visible",
1665			unexported: "",
1666			Nested1:    unexportedFieldPreservationTestNested{"visible1", ""},
1667			Nested2:    &unexportedFieldPreservationTestNested{"visible2", ""},
1668			Nested3:    &unexportedFieldPreservationTestNested{"visible3", ""},
1669			Slice1: []unexportedFieldPreservationTestNested{
1670				{Exported1: "visible3"},
1671				{Exported1: "visible4"},
1672			},
1673			Slice2: []*unexportedFieldPreservationTestNested{
1674				{Exported1: "visible5"},
1675			},
1676		}
1677
1678		if !reflect.DeepEqual(actual, expect) {
1679			t.Fatalf("%+v did not equal %+v", actual, expect)
1680		}
1681	})
1682
1683	t.Run("unexported field should be preserved", func(t *testing.T) {
1684		actual := unexportedFieldPreservationTest{
1685			Exported:   "foo",
1686			unexported: "bar",
1687			Nested1:    unexportedFieldPreservationTestNested{"baz", "bax"},
1688			Nested2:    nil,
1689			Nested3:    &unexportedFieldPreservationTestNested{"baz", "bax"},
1690		}
1691		err := Unmarshal([]byte(toml), &actual)
1692
1693		if err != nil {
1694			t.Fatal("did not expect an error")
1695		}
1696
1697		expect := unexportedFieldPreservationTest{
1698			Exported:   "visible",
1699			unexported: "bar",
1700			Nested1:    unexportedFieldPreservationTestNested{"visible1", "bax"},
1701			Nested2:    &unexportedFieldPreservationTestNested{"visible2", ""},
1702			Nested3:    &unexportedFieldPreservationTestNested{"visible3", "bax"},
1703			Slice1: []unexportedFieldPreservationTestNested{
1704				{Exported1: "visible3"},
1705				{Exported1: "visible4"},
1706			},
1707			Slice2: []*unexportedFieldPreservationTestNested{
1708				{Exported1: "visible5"},
1709			},
1710		}
1711
1712		if !reflect.DeepEqual(actual, expect) {
1713			t.Fatalf("%+v did not equal %+v", actual, expect)
1714		}
1715	})
1716}
1717
1718func TestTreeMarshal(t *testing.T) {
1719	cases := [][]byte{
1720		basicTestToml,
1721		marshalTestToml,
1722		emptyTestToml,
1723		pointerTestToml,
1724	}
1725	for _, expected := range cases {
1726		t.Run("", func(t *testing.T) {
1727			tree, err := LoadBytes(expected)
1728			if err != nil {
1729				t.Fatal(err)
1730			}
1731			result, err := tree.Marshal()
1732			if err != nil {
1733				t.Fatal(err)
1734			}
1735			if !bytes.Equal(result, expected) {
1736				t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
1737			}
1738		})
1739	}
1740}
1741
1742func TestMarshalArrays(t *testing.T) {
1743	cases := []struct {
1744		Data     interface{}
1745		Expected string
1746	}{
1747		{
1748			Data: struct {
1749				XY [2]int
1750			}{
1751				XY: [2]int{1, 2},
1752			},
1753			Expected: `XY = [1,2]
1754`,
1755		},
1756		{
1757			Data: struct {
1758				XY [1][2]int
1759			}{
1760				XY: [1][2]int{{1, 2}},
1761			},
1762			Expected: `XY = [[1,2]]
1763`,
1764		},
1765		{
1766			Data: struct {
1767				XY [1][]int
1768			}{
1769				XY: [1][]int{{1, 2}},
1770			},
1771			Expected: `XY = [[1,2]]
1772`,
1773		},
1774		{
1775			Data: struct {
1776				XY [][2]int
1777			}{
1778				XY: [][2]int{{1, 2}},
1779			},
1780			Expected: `XY = [[1,2]]
1781`,
1782		},
1783	}
1784	for _, tc := range cases {
1785		t.Run("", func(t *testing.T) {
1786			result, err := Marshal(tc.Data)
1787			if err != nil {
1788				t.Fatal(err)
1789			}
1790			if !bytes.Equal(result, []byte(tc.Expected)) {
1791				t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", []byte(tc.Expected), result)
1792			}
1793		})
1794	}
1795}
1796
1797func TestUnmarshalLocalDate(t *testing.T) {
1798	t.Run("ToLocalDate", func(t *testing.T) {
1799		type dateStruct struct {
1800			Date LocalDate
1801		}
1802
1803		toml := `date = 1979-05-27`
1804
1805		var obj dateStruct
1806
1807		err := Unmarshal([]byte(toml), &obj)
1808
1809		if err != nil {
1810			t.Fatal(err)
1811		}
1812
1813		if obj.Date.Year != 1979 {
1814			t.Errorf("expected year 1979, got %d", obj.Date.Year)
1815		}
1816		if obj.Date.Month != 5 {
1817			t.Errorf("expected month 5, got %d", obj.Date.Month)
1818		}
1819		if obj.Date.Day != 27 {
1820			t.Errorf("expected day 27, got %d", obj.Date.Day)
1821		}
1822	})
1823
1824	t.Run("ToLocalDate", func(t *testing.T) {
1825		type dateStruct struct {
1826			Date time.Time
1827		}
1828
1829		toml := `date = 1979-05-27`
1830
1831		var obj dateStruct
1832
1833		err := Unmarshal([]byte(toml), &obj)
1834
1835		if err != nil {
1836			t.Fatal(err)
1837		}
1838
1839		if obj.Date.Year() != 1979 {
1840			t.Errorf("expected year 1979, got %d", obj.Date.Year())
1841		}
1842		if obj.Date.Month() != 5 {
1843			t.Errorf("expected month 5, got %d", obj.Date.Month())
1844		}
1845		if obj.Date.Day() != 27 {
1846			t.Errorf("expected day 27, got %d", obj.Date.Day())
1847		}
1848	})
1849}
1850
1851func TestMarshalLocalDate(t *testing.T) {
1852	type dateStruct struct {
1853		Date LocalDate
1854	}
1855
1856	obj := dateStruct{Date: LocalDate{
1857		Year:  1979,
1858		Month: 5,
1859		Day:   27,
1860	}}
1861
1862	b, err := Marshal(obj)
1863
1864	if err != nil {
1865		t.Fatalf("unexpected error: %v", err)
1866	}
1867
1868	got := string(b)
1869	expected := `Date = 1979-05-27
1870`
1871
1872	if got != expected {
1873		t.Errorf("expected '%s', got '%s'", expected, got)
1874	}
1875}
1876
1877func TestUnmarshalLocalDateTime(t *testing.T) {
1878	examples := []struct {
1879		name string
1880		in   string
1881		out  LocalDateTime
1882	}{
1883		{
1884			name: "normal",
1885			in:   "1979-05-27T07:32:00",
1886			out: LocalDateTime{
1887				Date: LocalDate{
1888					Year:  1979,
1889					Month: 5,
1890					Day:   27,
1891				},
1892				Time: LocalTime{
1893					Hour:       7,
1894					Minute:     32,
1895					Second:     0,
1896					Nanosecond: 0,
1897				},
1898			}},
1899		{
1900			name: "with nanoseconds",
1901			in:   "1979-05-27T00:32:00.999999",
1902			out: LocalDateTime{
1903				Date: LocalDate{
1904					Year:  1979,
1905					Month: 5,
1906					Day:   27,
1907				},
1908				Time: LocalTime{
1909					Hour:       0,
1910					Minute:     32,
1911					Second:     0,
1912					Nanosecond: 999999000,
1913				},
1914			},
1915		},
1916	}
1917
1918	for i, example := range examples {
1919		toml := fmt.Sprintf(`date = %s`, example.in)
1920
1921		t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) {
1922			type dateStruct struct {
1923				Date LocalDateTime
1924			}
1925
1926			var obj dateStruct
1927
1928			err := Unmarshal([]byte(toml), &obj)
1929
1930			if err != nil {
1931				t.Fatal(err)
1932			}
1933
1934			if obj.Date != example.out {
1935				t.Errorf("expected '%s', got '%s'", example.out, obj.Date)
1936			}
1937		})
1938
1939		t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) {
1940			type dateStruct struct {
1941				Date time.Time
1942			}
1943
1944			var obj dateStruct
1945
1946			err := Unmarshal([]byte(toml), &obj)
1947
1948			if err != nil {
1949				t.Fatal(err)
1950			}
1951
1952			if obj.Date.Year() != example.out.Date.Year {
1953				t.Errorf("expected year %d, got %d", example.out.Date.Year, obj.Date.Year())
1954			}
1955			if obj.Date.Month() != example.out.Date.Month {
1956				t.Errorf("expected month %d, got %d", example.out.Date.Month, obj.Date.Month())
1957			}
1958			if obj.Date.Day() != example.out.Date.Day {
1959				t.Errorf("expected day %d, got %d", example.out.Date.Day, obj.Date.Day())
1960			}
1961			if obj.Date.Hour() != example.out.Time.Hour {
1962				t.Errorf("expected hour %d, got %d", example.out.Time.Hour, obj.Date.Hour())
1963			}
1964			if obj.Date.Minute() != example.out.Time.Minute {
1965				t.Errorf("expected minute %d, got %d", example.out.Time.Minute, obj.Date.Minute())
1966			}
1967			if obj.Date.Second() != example.out.Time.Second {
1968				t.Errorf("expected second %d, got %d", example.out.Time.Second, obj.Date.Second())
1969			}
1970			if obj.Date.Nanosecond() != example.out.Time.Nanosecond {
1971				t.Errorf("expected nanoseconds %d, got %d", example.out.Time.Nanosecond, obj.Date.Nanosecond())
1972			}
1973		})
1974	}
1975}
1976
1977func TestMarshalLocalDateTime(t *testing.T) {
1978	type dateStruct struct {
1979		DateTime LocalDateTime
1980	}
1981
1982	examples := []struct {
1983		name string
1984		in   LocalDateTime
1985		out  string
1986	}{
1987		{
1988			name: "normal",
1989			out:  "DateTime = 1979-05-27T07:32:00\n",
1990			in: LocalDateTime{
1991				Date: LocalDate{
1992					Year:  1979,
1993					Month: 5,
1994					Day:   27,
1995				},
1996				Time: LocalTime{
1997					Hour:       7,
1998					Minute:     32,
1999					Second:     0,
2000					Nanosecond: 0,
2001				},
2002			}},
2003		{
2004			name: "with nanoseconds",
2005			out:  "DateTime = 1979-05-27T00:32:00.999999000\n",
2006			in: LocalDateTime{
2007				Date: LocalDate{
2008					Year:  1979,
2009					Month: 5,
2010					Day:   27,
2011				},
2012				Time: LocalTime{
2013					Hour:       0,
2014					Minute:     32,
2015					Second:     0,
2016					Nanosecond: 999999000,
2017				},
2018			},
2019		},
2020	}
2021
2022	for i, example := range examples {
2023		t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
2024			obj := dateStruct{
2025				DateTime: example.in,
2026			}
2027			b, err := Marshal(obj)
2028
2029			if err != nil {
2030				t.Fatalf("unexpected error: %v", err)
2031			}
2032
2033			got := string(b)
2034
2035			if got != example.out {
2036				t.Errorf("expected '%s', got '%s'", example.out, got)
2037			}
2038		})
2039	}
2040}
2041
2042func TestUnmarshalLocalTime(t *testing.T) {
2043	examples := []struct {
2044		name string
2045		in   string
2046		out  LocalTime
2047	}{
2048		{
2049			name: "normal",
2050			in:   "07:32:00",
2051			out: LocalTime{
2052				Hour:       7,
2053				Minute:     32,
2054				Second:     0,
2055				Nanosecond: 0,
2056			},
2057		},
2058		{
2059			name: "with nanoseconds",
2060			in:   "00:32:00.999999",
2061			out: LocalTime{
2062				Hour:       0,
2063				Minute:     32,
2064				Second:     0,
2065				Nanosecond: 999999000,
2066			},
2067		},
2068	}
2069
2070	for i, example := range examples {
2071		toml := fmt.Sprintf(`Time = %s`, example.in)
2072
2073		t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) {
2074			type dateStruct struct {
2075				Time LocalTime
2076			}
2077
2078			var obj dateStruct
2079
2080			err := Unmarshal([]byte(toml), &obj)
2081
2082			if err != nil {
2083				t.Fatal(err)
2084			}
2085
2086			if obj.Time != example.out {
2087				t.Errorf("expected '%s', got '%s'", example.out, obj.Time)
2088			}
2089		})
2090	}
2091}
2092
2093func TestMarshalLocalTime(t *testing.T) {
2094	type timeStruct struct {
2095		Time LocalTime
2096	}
2097
2098	examples := []struct {
2099		name string
2100		in   LocalTime
2101		out  string
2102	}{
2103		{
2104			name: "normal",
2105			out:  "Time = 07:32:00\n",
2106			in: LocalTime{
2107				Hour:       7,
2108				Minute:     32,
2109				Second:     0,
2110				Nanosecond: 0,
2111			}},
2112		{
2113			name: "with nanoseconds",
2114			out:  "Time = 00:32:00.999999000\n",
2115			in: LocalTime{
2116				Hour:       0,
2117				Minute:     32,
2118				Second:     0,
2119				Nanosecond: 999999000,
2120			},
2121		},
2122	}
2123
2124	for i, example := range examples {
2125		t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
2126			obj := timeStruct{
2127				Time: example.in,
2128			}
2129			b, err := Marshal(obj)
2130
2131			if err != nil {
2132				t.Fatalf("unexpected error: %v", err)
2133			}
2134
2135			got := string(b)
2136
2137			if got != example.out {
2138				t.Errorf("expected '%s', got '%s'", example.out, got)
2139			}
2140		})
2141	}
2142}
2143