261 lines
5.5 KiB
Go
261 lines
5.5 KiB
Go
|
package toml
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/kylelemons/godebug/diff"
|
||
|
"github.com/kylelemons/godebug/pretty"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// These settings make 'mismatch' output look nicer.
|
||
|
pretty.DefaultConfig.ShortList = 80
|
||
|
pretty.CompareConfig.ShortList = 80
|
||
|
}
|
||
|
|
||
|
type testMarshaler struct{ S string }
|
||
|
|
||
|
type testMarshalerPtr struct{ S string }
|
||
|
|
||
|
func (t testMarshaler) MarshalTOML() ([]byte, error) {
|
||
|
return []byte(t.S), nil
|
||
|
}
|
||
|
|
||
|
func (t *testMarshalerPtr) MarshalTOML() ([]byte, error) {
|
||
|
return []byte(t.S), nil
|
||
|
}
|
||
|
|
||
|
type testMarshalerRec struct{ replacement interface{} }
|
||
|
|
||
|
type testMarshalerRecPtr struct{ replacement interface{} }
|
||
|
|
||
|
func (t testMarshalerRec) MarshalTOML() (interface{}, error) {
|
||
|
return t.replacement, nil
|
||
|
}
|
||
|
|
||
|
func (t *testMarshalerRecPtr) MarshalTOML() (interface{}, error) {
|
||
|
return t.replacement, nil
|
||
|
}
|
||
|
|
||
|
var marshalTests = []struct {
|
||
|
v interface{}
|
||
|
expect []byte
|
||
|
}{
|
||
|
// single string:
|
||
|
{
|
||
|
v: struct{ Name string }{"alice"},
|
||
|
expect: []byte("name = \"alice\"\n"),
|
||
|
},
|
||
|
// single int:
|
||
|
{
|
||
|
v: struct{ Age int }{7},
|
||
|
expect: []byte("age = 7\n"),
|
||
|
},
|
||
|
// multiple fields:
|
||
|
{
|
||
|
v: struct {
|
||
|
Name string
|
||
|
Age int
|
||
|
}{"alice", 7},
|
||
|
expect: []byte("name = \"alice\"\nage = 7\n"),
|
||
|
},
|
||
|
// ignored fields:
|
||
|
{
|
||
|
v: struct {
|
||
|
Name string `toml:"-"`
|
||
|
Age int
|
||
|
}{"alice", 7},
|
||
|
expect: []byte("age = 7\n"),
|
||
|
},
|
||
|
// field name override:
|
||
|
{
|
||
|
v: struct {
|
||
|
Name string `toml:"my_name"`
|
||
|
}{"bob"},
|
||
|
expect: []byte("my_name = \"bob\"\n"),
|
||
|
},
|
||
|
{
|
||
|
v: struct {
|
||
|
Name string `toml:"my_name,omitempty"`
|
||
|
}{"bob"},
|
||
|
expect: []byte("my_name = \"bob\"\n"),
|
||
|
},
|
||
|
// omitempty:
|
||
|
{
|
||
|
v: struct {
|
||
|
Name string `toml:",omitempty"`
|
||
|
}{"bob"},
|
||
|
expect: []byte("name = \"bob\"\n"),
|
||
|
},
|
||
|
// slices:
|
||
|
{
|
||
|
v: struct {
|
||
|
Ints []int
|
||
|
}{[]int{1, 2, 3}},
|
||
|
expect: []byte("ints = [1, 2, 3]\n"),
|
||
|
},
|
||
|
{
|
||
|
v: struct {
|
||
|
IntsOfInts [][]int
|
||
|
}{[][]int{{}, {1}, {}, {2}, {3, 4}}},
|
||
|
expect: []byte("ints_of_ints = [[], [1], [], [2], [3, 4]]\n"),
|
||
|
},
|
||
|
{
|
||
|
v: struct {
|
||
|
IntsOfInts [][]int
|
||
|
}{[][]int{{1, 2}, {3, 4}}},
|
||
|
expect: []byte("ints_of_ints = [[1, 2], [3, 4]]\n"),
|
||
|
},
|
||
|
// pointer:
|
||
|
{
|
||
|
v: struct{ Named *Name }{&Name{First: "name"}},
|
||
|
expect: []byte("[named]\nfirst = \"name\"\nlast = \"\"\n"),
|
||
|
},
|
||
|
// canon test document:
|
||
|
{
|
||
|
v: theTestStruct(),
|
||
|
expect: loadTestData("marshal-teststruct.toml"),
|
||
|
},
|
||
|
// funky map key types:
|
||
|
{
|
||
|
v: map[string]interface{}{
|
||
|
"intKeys": map[int]int{1: 1, 2: 2, 3: 3},
|
||
|
"marshalerKeys": map[time.Time]int{time.Time{}: 1},
|
||
|
},
|
||
|
expect: loadTestData("marshal-funkymapkeys.toml"),
|
||
|
},
|
||
|
// Marshaler:
|
||
|
{
|
||
|
v: map[string]interface{}{
|
||
|
"m1": testMarshaler{"1"},
|
||
|
"m2": &testMarshaler{"2"},
|
||
|
"m3": &testMarshalerPtr{"3"},
|
||
|
},
|
||
|
expect: loadTestData("marshal-marshaler.toml"),
|
||
|
},
|
||
|
// MarshalerRec:
|
||
|
{
|
||
|
v: map[string]interface{}{
|
||
|
"m1": testMarshalerRec{1},
|
||
|
"m2": &testMarshalerRec{2},
|
||
|
"m3": &testMarshalerRecPtr{3},
|
||
|
"sub": &testMarshalerRec{map[string]interface{}{
|
||
|
"key": 1,
|
||
|
}},
|
||
|
},
|
||
|
expect: loadTestData("marshal-marshalerrec.toml"),
|
||
|
},
|
||
|
// key escaping:
|
||
|
{
|
||
|
v: map[string]interface{}{
|
||
|
"": "empty",
|
||
|
" ": "space",
|
||
|
"ʎǝʞ": "reverse",
|
||
|
"1": "number (not quoted)",
|
||
|
"-": "dash (not quoted)",
|
||
|
"subtable with space": map[string]interface{}{
|
||
|
"depth": 1,
|
||
|
"subsubtable with space": map[string]interface{}{
|
||
|
"depth": 2,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
expect: loadTestData("marshal-key-escape.toml"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func TestMarshal(t *testing.T) {
|
||
|
for _, test := range marshalTests {
|
||
|
b, err := Marshal(test.v)
|
||
|
if err != nil {
|
||
|
t.Errorf("Unexpected error %v\nfor value:\n%s", err, pretty.Sprint(test.v))
|
||
|
}
|
||
|
if d := checkOutput(b, test.expect); d != "" {
|
||
|
t.Errorf("Output mismatch:\nValue:\n%s\nDiff:\n%s", pretty.Sprint(test.v), d)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalRoundTrip(t *testing.T) {
|
||
|
v := theTestStruct()
|
||
|
b, err := Marshal(v)
|
||
|
if err != nil {
|
||
|
t.Error("Unexpected Marshal error:", err)
|
||
|
}
|
||
|
dest := testStruct{}
|
||
|
if err := Unmarshal(b, &dest); err != nil {
|
||
|
t.Error("Unmarshal error:", err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(theTestStruct(), &dest) {
|
||
|
t.Errorf("Unmarshaled value mismatch:\n%s", pretty.Compare(v, dest))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalArrayTableEmptyParent(t *testing.T) {
|
||
|
type Baz struct {
|
||
|
Key int
|
||
|
}
|
||
|
type Bar struct {
|
||
|
Baz Baz
|
||
|
}
|
||
|
type Foo struct {
|
||
|
Bars []Bar
|
||
|
}
|
||
|
|
||
|
v := Foo{[]Bar{{Baz{1}}, {Baz{2}}}}
|
||
|
b, err := Marshal(v)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if d := checkOutput(b, loadTestData("marshal-arraytable-empty.toml")); d != "" {
|
||
|
t.Errorf("Output mismatch:\n%s", d)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalPointerError(t *testing.T) {
|
||
|
type X struct{ Sub *X }
|
||
|
want := &marshalNilError{reflect.TypeOf((*X)(nil))}
|
||
|
|
||
|
if _, err := Marshal((*X)(nil)); !reflect.DeepEqual(err, want) {
|
||
|
t.Errorf("Got %q, expected %q", err, want)
|
||
|
}
|
||
|
if _, err := Marshal(&X{nil}); !reflect.DeepEqual(err, want) {
|
||
|
t.Errorf("Got %q, expected %q", err, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalNonStruct(t *testing.T) {
|
||
|
val := []string{}
|
||
|
want := &marshalTableError{reflect.TypeOf(val)}
|
||
|
if _, err := Marshal(val); !reflect.DeepEqual(err, want) {
|
||
|
t.Errorf("Got %q, expected %q", err, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalOmitempty(t *testing.T) {
|
||
|
var x struct {
|
||
|
ZeroArray [0]int `toml:",omitempty"`
|
||
|
ZeroArray2 [10]int `toml:",omitempty"`
|
||
|
Slice []int `toml:",omitempty"`
|
||
|
Pointer *int `toml:",omitempty"`
|
||
|
Int int `toml:",omitempty"`
|
||
|
}
|
||
|
out, err := Marshal(x)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(out) > 0 {
|
||
|
t.Fatalf("want no output, got %q", out)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func checkOutput(got, want []byte) string {
|
||
|
if bytes.Equal(got, want) {
|
||
|
return ""
|
||
|
}
|
||
|
return diff.Diff(string(got), string(want))
|
||
|
}
|