1package toml
2
3import (
4	"strconv"
5	"testing"
6	"time"
7)
8
9type customString string
10
11type stringer struct{}
12
13func (s stringer) String() string {
14	return "stringer"
15}
16
17func validate(t *testing.T, path string, object interface{}) {
18	switch o := object.(type) {
19	case *Tree:
20		for key, tree := range o.values {
21			validate(t, path+"."+key, tree)
22		}
23	case []*Tree:
24		for index, tree := range o {
25			validate(t, path+"."+strconv.Itoa(index), tree)
26		}
27	case *tomlValue:
28		switch o.value.(type) {
29		case int64, uint64, bool, string, float64, time.Time,
30			[]int64, []uint64, []bool, []string, []float64, []time.Time:
31		default:
32			t.Fatalf("tomlValue at key %s containing incorrect type %T", path, o.value)
33		}
34	default:
35		t.Fatalf("value at key %s is of incorrect type %T", path, object)
36	}
37	t.Logf("validation ok %s as %T", path, object)
38}
39
40func validateTree(t *testing.T, tree *Tree) {
41	validate(t, "", tree)
42}
43
44func TestTreeCreateToTree(t *testing.T) {
45	data := map[string]interface{}{
46		"a_string": "bar",
47		"an_int":   42,
48		"time":     time.Now(),
49		"int8":     int8(2),
50		"int16":    int16(2),
51		"int32":    int32(2),
52		"uint8":    uint8(2),
53		"uint16":   uint16(2),
54		"uint32":   uint32(2),
55		"float32":  float32(2),
56		"a_bool":   false,
57		"stringer": stringer{},
58		"nested": map[string]interface{}{
59			"foo": "bar",
60		},
61		"array":                 []string{"a", "b", "c"},
62		"array_uint":            []uint{uint(1), uint(2)},
63		"array_table":           []map[string]interface{}{{"sub_map": 52}},
64		"array_times":           []time.Time{time.Now(), time.Now()},
65		"map_times":             map[string]time.Time{"now": time.Now()},
66		"custom_string_map_key": map[customString]interface{}{customString("custom"): "custom"},
67	}
68	tree, err := TreeFromMap(data)
69	if err != nil {
70		t.Fatal("unexpected error:", err)
71	}
72	validateTree(t, tree)
73}
74
75func TestTreeCreateToTreeInvalidLeafType(t *testing.T) {
76	_, err := TreeFromMap(map[string]interface{}{"foo": t})
77	expected := "cannot convert type *testing.T to Tree"
78	if err.Error() != expected {
79		t.Fatalf("expected error %s, got %s", expected, err.Error())
80	}
81}
82
83func TestTreeCreateToTreeInvalidMapKeyType(t *testing.T) {
84	_, err := TreeFromMap(map[string]interface{}{"foo": map[int]interface{}{2: 1}})
85	expected := "map key needs to be a string, not int (int)"
86	if err.Error() != expected {
87		t.Fatalf("expected error %s, got %s", expected, err.Error())
88	}
89}
90
91func TestTreeCreateToTreeInvalidArrayMemberType(t *testing.T) {
92	_, err := TreeFromMap(map[string]interface{}{"foo": []*testing.T{t}})
93	expected := "cannot convert type *testing.T to Tree"
94	if err.Error() != expected {
95		t.Fatalf("expected error %s, got %s", expected, err.Error())
96	}
97}
98
99func TestTreeCreateToTreeInvalidTableGroupType(t *testing.T) {
100	_, err := TreeFromMap(map[string]interface{}{"foo": []map[string]interface{}{{"hello": t}}})
101	expected := "cannot convert type *testing.T to Tree"
102	if err.Error() != expected {
103		t.Fatalf("expected error %s, got %s", expected, err.Error())
104	}
105}
106
107func TestRoundTripArrayOfTables(t *testing.T) {
108	orig := "\n[[stuff]]\n  name = \"foo\"\n  things = [\"a\", \"b\"]\n"
109	tree, err := Load(orig)
110	if err != nil {
111		t.Fatalf("unexpected error: %s", err)
112	}
113
114	m := tree.ToMap()
115
116	tree, err = TreeFromMap(m)
117	if err != nil {
118		t.Fatalf("unexpected error: %s", err)
119	}
120	want := orig
121	got := tree.String()
122
123	if got != want {
124		t.Errorf("want:\n%s\ngot:\n%s", want, got)
125	}
126}
127