...

Source file src/github.com/BurntSushi/toml/decode_test.go

Documentation: github.com/BurntSushi/toml

     1  package toml
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"math"
    10  	"os"
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/BurntSushi/toml/internal"
    19  )
    20  
    21  func WithTomlNext(f func()) {
    22  	os.Setenv("BURNTSUSHI_TOML_110", "")
    23  	defer func() { os.Unsetenv("BURNTSUSHI_TOML_110") }()
    24  	f()
    25  }
    26  
    27  func TestDecodeReader(t *testing.T) {
    28  	var i struct{ A int }
    29  	meta, err := DecodeReader(strings.NewReader("a = 42"), &i)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	have := fmt.Sprintf("%v %v %v", i, meta.Keys(), meta.Type("a"))
    34  	want := "{42} [a] Integer"
    35  	if have != want {
    36  		t.Errorf("\nhave: %s\nwant: %s", have, want)
    37  	}
    38  }
    39  
    40  func TestDecodeFile(t *testing.T) {
    41  	tmp, err := ioutil.TempFile("", "toml-")
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  	defer os.Remove(tmp.Name())
    46  	if _, err := tmp.WriteString("a = 42"); err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	if err := tmp.Close(); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	var i struct{ A int }
    54  	meta, err := DecodeFile(tmp.Name(), &i)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	have := fmt.Sprintf("%v %v %v", i, meta.Keys(), meta.Type("a"))
    60  	want := "{42} [a] Integer"
    61  	if have != want {
    62  		t.Errorf("\nhave: %s\nwant: %s", have, want)
    63  	}
    64  }
    65  
    66  func TestDecodeBOM(t *testing.T) {
    67  	for _, tt := range [][]byte{
    68  		[]byte("\xff\xfea = \"b\""),
    69  		[]byte("\xfe\xffa = \"b\""),
    70  		[]byte("\xef\xbb\xbfa = \"b\""),
    71  	} {
    72  		t.Run("", func(t *testing.T) {
    73  			var s struct{ A string }
    74  			_, err := Decode(string(tt), &s)
    75  			if err != nil {
    76  				t.Fatal(err)
    77  			}
    78  			if s.A != "b" {
    79  				t.Errorf(`s.A is not "b" but %q`, s.A)
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestDecodeEmbedded(t *testing.T) {
    86  	type Dog struct{ Name string }
    87  	type Age int
    88  	type cat struct{ Name string }
    89  
    90  	for _, test := range []struct {
    91  		label       string
    92  		input       string
    93  		decodeInto  interface{}
    94  		wantDecoded interface{}
    95  	}{
    96  		{
    97  			label:       "embedded struct",
    98  			input:       `Name = "milton"`,
    99  			decodeInto:  &struct{ Dog }{},
   100  			wantDecoded: &struct{ Dog }{Dog{"milton"}},
   101  		},
   102  		{
   103  			label:       "embedded non-nil pointer to struct",
   104  			input:       `Name = "milton"`,
   105  			decodeInto:  &struct{ *Dog }{},
   106  			wantDecoded: &struct{ *Dog }{&Dog{"milton"}},
   107  		},
   108  		{
   109  			label:       "embedded nil pointer to struct",
   110  			input:       ``,
   111  			decodeInto:  &struct{ *Dog }{},
   112  			wantDecoded: &struct{ *Dog }{nil},
   113  		},
   114  		{
   115  			label:       "unexported embedded struct",
   116  			input:       `Name = "socks"`,
   117  			decodeInto:  &struct{ cat }{},
   118  			wantDecoded: &struct{ cat }{cat{"socks"}},
   119  		},
   120  		{
   121  			label:       "embedded int",
   122  			input:       `Age = -5`,
   123  			decodeInto:  &struct{ Age }{},
   124  			wantDecoded: &struct{ Age }{-5},
   125  		},
   126  	} {
   127  		_, err := Decode(test.input, test.decodeInto)
   128  		if err != nil {
   129  			t.Fatal(err)
   130  		}
   131  		if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) {
   132  			t.Errorf("%s: want decoded == %+v, got %+v",
   133  				test.label, test.wantDecoded, test.decodeInto)
   134  		}
   135  	}
   136  }
   137  
   138  func TestDecodeErrors(t *testing.T) {
   139  	tests := []struct {
   140  		s       interface{}
   141  		toml    string
   142  		wantErr string
   143  	}{
   144  		{
   145  			&struct{ V int8 }{},
   146  			`V = 999`,
   147  			`toml: line 1 (last key "V"): 999 is out of range for int8`,
   148  		},
   149  		{
   150  			&struct{ V float32 }{},
   151  			`V = 999999999999999`,
   152  			`toml: line 1 (last key "V"): 999999999999999 is out of range for float32`,
   153  		},
   154  		{
   155  			&struct{ V string }{},
   156  			`V = 5`,
   157  			`toml: line 1 (last key "V"): incompatible types: TOML value has type int64; destination has type string`,
   158  		},
   159  		{
   160  			&struct{ V interface{ ASD() } }{},
   161  			`V = 999`,
   162  			`toml: line 1 (last key "V"): unsupported type interface { ASD() }`,
   163  		},
   164  		{
   165  			&struct{ V struct{ V int } }{},
   166  			`V = 999`,
   167  			`toml: line 1 (last key "V"): type mismatch for struct { V int }: expected table but found int64`,
   168  		},
   169  		{
   170  			&struct{ V [1]int }{},
   171  			`V = [1,2,3]`,
   172  			`toml: line 1 (last key "V"): expected array length 1; got TOML array of length 3`,
   173  		},
   174  		{
   175  			&struct{ V struct{ N int8 } }{},
   176  			`V.N = 999`,
   177  			`toml: line 1 (last key "V.N"): 999 is out of range for int8`,
   178  		},
   179  		{
   180  			&struct{ V struct{ N float32 } }{},
   181  			`V.N = 999999999999999`,
   182  			`toml: line 1 (last key "V.N"): 999999999999999 is out of range for float32`,
   183  		},
   184  		{
   185  			&struct{ V struct{ N string } }{},
   186  			`V.N = 5`,
   187  			`toml: line 1 (last key "V.N"): incompatible types: TOML value has type int64; destination has type string`,
   188  		},
   189  		{
   190  			&struct {
   191  				V struct{ N interface{ ASD() } }
   192  			}{},
   193  			`V.N = 999`,
   194  			`toml: line 1 (last key "V.N"): unsupported type interface { ASD() }`,
   195  		},
   196  		{
   197  			&struct{ V struct{ N struct{ V int } } }{},
   198  			`V.N = 999`,
   199  			`toml: line 1 (last key "V.N"): type mismatch for struct { V int }: expected table but found int64`,
   200  		},
   201  		{
   202  			&struct{ V struct{ N [1]int } }{},
   203  			`V.N = [1,2,3]`,
   204  			`toml: line 1 (last key "V.N"): expected array length 1; got TOML array of length 3`,
   205  		},
   206  	}
   207  
   208  	for _, tt := range tests {
   209  		_, err := Decode(tt.toml, tt.s)
   210  		if err == nil {
   211  			t.Fatal("err is nil")
   212  		}
   213  		if err.Error() != tt.wantErr {
   214  			t.Errorf("\nhave: %q\nwant: %q", err, tt.wantErr)
   215  		}
   216  	}
   217  }
   218  
   219  func TestDecodeIgnoreFields(t *testing.T) {
   220  	const input = `
   221  Number = 123
   222  - = 234
   223  `
   224  	var s struct {
   225  		Number int `toml:"-"`
   226  	}
   227  	if _, err := Decode(input, &s); err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	if s.Number != 0 {
   231  		t.Errorf("got: %d; want 0", s.Number)
   232  	}
   233  }
   234  
   235  func TestDecodeTableArrays(t *testing.T) {
   236  	var tomlTableArrays = `
   237  [[albums]]
   238  name = "Born to Run"
   239  
   240    [[albums.songs]]
   241    name = "Jungleland"
   242  
   243    [[albums.songs]]
   244    name = "Meeting Across the River"
   245  
   246  [[albums]]
   247  name = "Born in the USA"
   248  
   249    [[albums.songs]]
   250    name = "Glory Days"
   251  
   252    [[albums.songs]]
   253    name = "Dancing in the Dark"
   254  `
   255  
   256  	type Song struct {
   257  		Name string
   258  	}
   259  
   260  	type Album struct {
   261  		Name  string
   262  		Songs []Song
   263  	}
   264  
   265  	type Music struct {
   266  		Albums []Album
   267  	}
   268  
   269  	expected := Music{[]Album{
   270  		{"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
   271  		{"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
   272  	}}
   273  	var got Music
   274  	if _, err := Decode(tomlTableArrays, &got); err != nil {
   275  		t.Fatal(err)
   276  	}
   277  	if !reflect.DeepEqual(expected, got) {
   278  		t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
   279  	}
   280  }
   281  
   282  func TestDecodePointers(t *testing.T) {
   283  	type Object struct {
   284  		Type        string
   285  		Description string
   286  	}
   287  
   288  	type Dict struct {
   289  		NamedObject map[string]*Object
   290  		BaseObject  *Object
   291  		Strptr      *string
   292  		Strptrs     []*string
   293  	}
   294  	s1, s2, s3 := "blah", "abc", "def"
   295  	expected := &Dict{
   296  		Strptr:  &s1,
   297  		Strptrs: []*string{&s2, &s3},
   298  		NamedObject: map[string]*Object{
   299  			"foo": {"FOO", "fooooo!!!"},
   300  			"bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
   301  		},
   302  		BaseObject: &Object{"BASE", "da base"},
   303  	}
   304  
   305  	ex1 := `
   306  Strptr = "blah"
   307  Strptrs = ["abc", "def"]
   308  
   309  [NamedObject.foo]
   310  Type = "FOO"
   311  Description = "fooooo!!!"
   312  
   313  [NamedObject.bar]
   314  Type = "BAR"
   315  Description = "ba-ba-ba-ba-barrrr!!!"
   316  
   317  [BaseObject]
   318  Type = "BASE"
   319  Description = "da base"
   320  `
   321  	dict := new(Dict)
   322  	_, err := Decode(ex1, dict)
   323  	if err != nil {
   324  		t.Errorf("Decode error: %v", err)
   325  	}
   326  	if !reflect.DeepEqual(expected, dict) {
   327  		t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
   328  	}
   329  }
   330  
   331  func TestDecodeBadDatetime(t *testing.T) {
   332  	var x struct{ T time.Time }
   333  	for _, s := range []string{"123", "1230"} {
   334  		input := "T = " + s
   335  		if _, err := Decode(input, &x); err == nil {
   336  			t.Errorf("Expected invalid DateTime error for %q", s)
   337  		}
   338  	}
   339  }
   340  
   341  type sphere struct {
   342  	Center [3]float64
   343  	Radius float64
   344  }
   345  
   346  func TestDecodeArrayWrongSize(t *testing.T) {
   347  	var s1 sphere
   348  	if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil {
   349  		t.Fatal("Expected array type mismatch error")
   350  	}
   351  }
   352  
   353  func TestDecodeIntOverflow(t *testing.T) {
   354  	type table struct {
   355  		Value int8
   356  	}
   357  	var tab table
   358  	if _, err := Decode(`value = 500`, &tab); err == nil {
   359  		t.Fatal("Expected integer out-of-bounds error.")
   360  	}
   361  }
   362  
   363  func TestDecodeFloatOverflow(t *testing.T) {
   364  	tests := []struct {
   365  		value    string
   366  		overflow bool
   367  	}{
   368  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat64), true},
   369  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat64), true},
   370  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat32*1.1), true},
   371  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat32*1.1), true},
   372  		{fmt.Sprintf(`F32 = %d`, maxSafeFloat32Int+1), true},
   373  		{fmt.Sprintf(`F32 = %d`, -maxSafeFloat32Int-1), true},
   374  		{fmt.Sprintf(`F64 = %d`, maxSafeFloat64Int+1), true},
   375  		{fmt.Sprintf(`F64 = %d`, -maxSafeFloat64Int-1), true},
   376  
   377  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat32), false},
   378  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat32), false},
   379  		{fmt.Sprintf(`F32 = %d`, maxSafeFloat32Int), false},
   380  		{fmt.Sprintf(`F32 = %d`, -maxSafeFloat32Int), false},
   381  		{fmt.Sprintf(`F64 = %f`, math.MaxFloat64), false},
   382  		{fmt.Sprintf(`F64 = %f`, -math.MaxFloat64), false},
   383  		{fmt.Sprintf(`F64 = %f`, math.MaxFloat32), false},
   384  		{fmt.Sprintf(`F64 = %f`, -math.MaxFloat32), false},
   385  		{fmt.Sprintf(`F64 = %d`, maxSafeFloat64Int), false},
   386  		{fmt.Sprintf(`F64 = %d`, -maxSafeFloat64Int), false},
   387  	}
   388  
   389  	for _, tt := range tests {
   390  		t.Run("", func(t *testing.T) {
   391  			var tab struct {
   392  				F32 float32
   393  				F64 float64
   394  			}
   395  			_, err := Decode(tt.value, &tab)
   396  
   397  			if tt.overflow && err == nil {
   398  				t.Fatal("expected error, but err is nil")
   399  			}
   400  			if (tt.overflow && !errorContains(err, "out of range")) || (!tt.overflow && err != nil) {
   401  				t.Fatalf("unexpected error:\n%v", err)
   402  			}
   403  		})
   404  	}
   405  }
   406  
   407  func TestDecodeSizedInts(t *testing.T) {
   408  	type table struct {
   409  		U8  uint8
   410  		U16 uint16
   411  		U32 uint32
   412  		U64 uint64
   413  		U   uint
   414  		I8  int8
   415  		I16 int16
   416  		I32 int32
   417  		I64 int64
   418  		I   int
   419  	}
   420  	answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1}
   421  	toml := `
   422  	u8 = 1
   423  	u16 = 1
   424  	u32 = 1
   425  	u64 = 1
   426  	u = 1
   427  	i8 = -1
   428  	i16 = -1
   429  	i32 = -1
   430  	i64 = -1
   431  	i = -1
   432  	`
   433  	var tab table
   434  	if _, err := Decode(toml, &tab); err != nil {
   435  		t.Fatal(err.Error())
   436  	}
   437  	if answer != tab {
   438  		t.Fatalf("Expected %#v but got %#v", answer, tab)
   439  	}
   440  }
   441  
   442  type NopUnmarshalTOML int
   443  
   444  func (n *NopUnmarshalTOML) UnmarshalTOML(p interface{}) error {
   445  	*n = 42
   446  	return nil
   447  }
   448  
   449  func TestDecodeTypes(t *testing.T) {
   450  	type (
   451  		mystr   string
   452  		myiface interface{}
   453  	)
   454  
   455  	for _, tt := range []struct {
   456  		v       interface{}
   457  		want    string
   458  		wantErr string
   459  	}{
   460  		{new(map[string]bool), "&map[F:true]", ""},
   461  		{new(map[mystr]bool), "&map[F:true]", ""},
   462  		{new(NopUnmarshalTOML), "42", ""},
   463  		{new(map[interface{}]bool), "&map[F:true]", ""},
   464  		{new(map[myiface]bool), "&map[F:true]", ""},
   465  
   466  		{3, "", `toml: cannot decode to non-pointer "int"`},
   467  		{map[string]interface{}{}, "", `toml: cannot decode to non-pointer "map[string]interface {}"`},
   468  
   469  		{(*int)(nil), "", `toml: cannot decode to nil value of "*int"`},
   470  		{(*Unmarshaler)(nil), "", `toml: cannot decode to nil value of "*toml.Unmarshaler"`},
   471  		{nil, "", `toml: cannot decode to non-pointer <nil>`},
   472  
   473  		{new(map[int]string), "", "toml: cannot decode to a map with non-string key type"},
   474  
   475  		{new(struct{ F int }), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
   476  		{new(map[string]int), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
   477  		{new(int), "", `toml: cannot decode to type int`},
   478  		{new([]int), "", "toml: cannot decode to type []int"},
   479  	} {
   480  		t.Run(fmt.Sprintf("%T", tt.v), func(t *testing.T) {
   481  			_, err := Decode(`F = true`, tt.v)
   482  			if !errorContains(err, tt.wantErr) {
   483  				t.Fatalf("wrong error\nhave: %q\nwant: %q", err, tt.wantErr)
   484  			}
   485  
   486  			if err == nil {
   487  				have := fmt.Sprintf("%v", tt.v)
   488  				if n, ok := tt.v.(*NopUnmarshalTOML); ok {
   489  					have = fmt.Sprintf("%v", *n)
   490  				}
   491  				if have != tt.want {
   492  					t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   493  				}
   494  			}
   495  		})
   496  	}
   497  }
   498  
   499  func TestUnmarshaler(t *testing.T) {
   500  	var tomlBlob = `
   501  [dishes.hamboogie]
   502  name = "Hamboogie with fries"
   503  price = 10.99
   504  
   505  [[dishes.hamboogie.ingredients]]
   506  name = "Bread Bun"
   507  
   508  [[dishes.hamboogie.ingredients]]
   509  name = "Lettuce"
   510  
   511  [[dishes.hamboogie.ingredients]]
   512  name = "Real Beef Patty"
   513  
   514  [[dishes.hamboogie.ingredients]]
   515  name = "Tomato"
   516  
   517  [dishes.eggsalad]
   518  name = "Egg Salad with rice"
   519  price = 3.99
   520  
   521  [[dishes.eggsalad.ingredients]]
   522  name = "Egg"
   523  
   524  [[dishes.eggsalad.ingredients]]
   525  name = "Mayo"
   526  
   527  [[dishes.eggsalad.ingredients]]
   528  name = "Rice"
   529  `
   530  	m := &menu{}
   531  	if _, err := Decode(tomlBlob, m); err != nil {
   532  		t.Fatal(err)
   533  	}
   534  
   535  	if len(m.Dishes) != 2 {
   536  		t.Log("two dishes should be loaded with UnmarshalTOML()")
   537  		t.Errorf("expected %d but got %d", 2, len(m.Dishes))
   538  	}
   539  
   540  	eggSalad := m.Dishes["eggsalad"]
   541  	if _, ok := interface{}(eggSalad).(dish); !ok {
   542  		t.Errorf("expected a dish")
   543  	}
   544  
   545  	if eggSalad.Name != "Egg Salad with rice" {
   546  		t.Errorf("expected the dish to be named 'Egg Salad with rice'")
   547  	}
   548  
   549  	if len(eggSalad.Ingredients) != 3 {
   550  		t.Log("dish should be loaded with UnmarshalTOML()")
   551  		t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients))
   552  	}
   553  
   554  	found := false
   555  	for _, i := range eggSalad.Ingredients {
   556  		if i.Name == "Rice" {
   557  			found = true
   558  			break
   559  		}
   560  	}
   561  	if !found {
   562  		t.Error("Rice was not loaded in UnmarshalTOML()")
   563  	}
   564  
   565  	// test on a value - must be passed as *
   566  	o := menu{}
   567  	if _, err := Decode(tomlBlob, &o); err != nil {
   568  		t.Fatal(err)
   569  	}
   570  
   571  }
   572  
   573  func TestDecodeInlineTable(t *testing.T) {
   574  	input := `
   575  [CookieJar]
   576  Types = {Chocolate = "yummy", Oatmeal = "best ever"}
   577  
   578  [Seasons]
   579  Locations = {NY = {Temp = "not cold", Rating = 4}, MI = {Temp = "freezing", Rating = 9}}
   580  `
   581  	type cookieJar struct {
   582  		Types map[string]string
   583  	}
   584  	type properties struct {
   585  		Temp   string
   586  		Rating int
   587  	}
   588  	type seasons struct {
   589  		Locations map[string]properties
   590  	}
   591  	type wrapper struct {
   592  		CookieJar cookieJar
   593  		Seasons   seasons
   594  	}
   595  	var got wrapper
   596  
   597  	meta, err := Decode(input, &got)
   598  	if err != nil {
   599  		t.Fatal(err)
   600  	}
   601  	want := wrapper{
   602  		CookieJar: cookieJar{
   603  			Types: map[string]string{
   604  				"Chocolate": "yummy",
   605  				"Oatmeal":   "best ever",
   606  			},
   607  		},
   608  		Seasons: seasons{
   609  			Locations: map[string]properties{
   610  				"NY": {
   611  					Temp:   "not cold",
   612  					Rating: 4,
   613  				},
   614  				"MI": {
   615  					Temp:   "freezing",
   616  					Rating: 9,
   617  				},
   618  			},
   619  		},
   620  	}
   621  	if !reflect.DeepEqual(got, want) {
   622  		t.Fatalf("after decode, got:\n\n%#v\n\nwant:\n\n%#v", got, want)
   623  	}
   624  	if len(meta.keys) != 12 {
   625  		t.Errorf("after decode, got %d meta keys; want 12", len(meta.keys))
   626  	}
   627  	if len(meta.keyInfo) != 12 {
   628  		t.Errorf("after decode, got %d meta keyInfo; want 12", len(meta.keyInfo))
   629  	}
   630  }
   631  
   632  func TestDecodeInlineTableArray(t *testing.T) {
   633  	type point struct {
   634  		X, Y, Z int
   635  	}
   636  	var got struct {
   637  		Points []point
   638  	}
   639  	// Example inline table array from the spec.
   640  	const in = `
   641  points = [ { x = 1, y = 2, z = 3 },
   642             { x = 7, y = 8, z = 9 },
   643             { x = 2, y = 4, z = 8 } ]
   644  
   645  `
   646  	if _, err := Decode(in, &got); err != nil {
   647  		t.Fatal(err)
   648  	}
   649  	want := []point{
   650  		{X: 1, Y: 2, Z: 3},
   651  		{X: 7, Y: 8, Z: 9},
   652  		{X: 2, Y: 4, Z: 8},
   653  	}
   654  	if !reflect.DeepEqual(got.Points, want) {
   655  		t.Errorf("got %#v; want %#v", got.Points, want)
   656  	}
   657  }
   658  
   659  type menu struct {
   660  	Dishes map[string]dish
   661  }
   662  
   663  func (m *menu) UnmarshalTOML(p interface{}) error {
   664  	m.Dishes = make(map[string]dish)
   665  	data, _ := p.(map[string]interface{})
   666  	dishes := data["dishes"].(map[string]interface{})
   667  	for n, v := range dishes {
   668  		if d, ok := v.(map[string]interface{}); ok {
   669  			nd := dish{}
   670  			nd.UnmarshalTOML(d)
   671  			m.Dishes[n] = nd
   672  		} else {
   673  			return fmt.Errorf("not a dish")
   674  		}
   675  	}
   676  	return nil
   677  }
   678  
   679  type dish struct {
   680  	Name        string
   681  	Price       float32
   682  	Ingredients []ingredient
   683  }
   684  
   685  func (d *dish) UnmarshalTOML(p interface{}) error {
   686  	data, _ := p.(map[string]interface{})
   687  	d.Name, _ = data["name"].(string)
   688  	d.Price, _ = data["price"].(float32)
   689  	ingredients, _ := data["ingredients"].([]map[string]interface{})
   690  	for _, e := range ingredients {
   691  		n, _ := interface{}(e).(map[string]interface{})
   692  		name, _ := n["name"].(string)
   693  		i := ingredient{name}
   694  		d.Ingredients = append(d.Ingredients, i)
   695  	}
   696  	return nil
   697  }
   698  
   699  type ingredient struct {
   700  	Name string
   701  }
   702  
   703  func TestDecodeSlices(t *testing.T) {
   704  	type (
   705  		T struct {
   706  			Arr []string
   707  			Tbl map[string]interface{}
   708  		}
   709  		M map[string]interface{}
   710  	)
   711  	tests := []struct {
   712  		input    string
   713  		in, want T
   714  	}{
   715  		{"",
   716  			T{}, T{}},
   717  
   718  		// Leave existing values alone.
   719  		{"",
   720  			T{[]string{}, M{"arr": []string{}}},
   721  			T{[]string{}, M{"arr": []string{}}}},
   722  		{"",
   723  			T{[]string{"a"}, M{"arr": []string{"b"}}},
   724  			T{[]string{"a"}, M{"arr": []string{"b"}}}},
   725  
   726  		// Empty array always allocates (see #339)
   727  		{`arr = []
   728  		tbl = {arr = []}`,
   729  			T{},
   730  			T{[]string{}, M{"arr": []interface{}{}}}},
   731  		{`arr = []
   732  		tbl = {}`,
   733  			T{[]string{}, M{}},
   734  			T{[]string{}, M{}}},
   735  
   736  		{`arr = []`,
   737  			T{[]string{"a"}, M{}},
   738  			T{[]string{}, M{}}},
   739  
   740  		{`arr = ["x"]
   741  		 tbl = {arr=["y"]}`,
   742  			T{},
   743  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   744  		{`arr = ["x"]
   745  		 tbl = {arr=["y"]}`,
   746  			T{[]string{}, M{}},
   747  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   748  		{`arr = ["x"]
   749  		tbl = {arr=["y"]}`,
   750  			T{[]string{"a", "b"}, M{"arr": []interface{}{"c", "d"}}},
   751  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   752  	}
   753  
   754  	for _, tt := range tests {
   755  		t.Run("", func(t *testing.T) {
   756  			_, err := Decode(tt.input, &tt.in)
   757  			if err != nil {
   758  				t.Error(err)
   759  			}
   760  			if !reflect.DeepEqual(tt.in, tt.want) {
   761  				t.Errorf("\nhave: %#v\nwant: %#v", tt.in, tt.want)
   762  			}
   763  		})
   764  	}
   765  }
   766  
   767  func TestDecodePrimitive(t *testing.T) {
   768  	type S struct {
   769  		P Primitive
   770  	}
   771  	type T struct {
   772  		S []int
   773  	}
   774  	slicep := func(s []int) *[]int { return &s }
   775  	arrayp := func(a [2]int) *[2]int { return &a }
   776  	mapp := func(m map[string]int) *map[string]int { return &m }
   777  	for i, tt := range []struct {
   778  		v     interface{}
   779  		input string
   780  		want  interface{}
   781  	}{
   782  		// slices
   783  		{slicep(nil), "", slicep(nil)},
   784  		{slicep([]int{}), "", slicep([]int{})},
   785  		{slicep([]int{1, 2, 3}), "", slicep([]int{1, 2, 3})},
   786  		{slicep(nil), "P = [1,2]", slicep([]int{1, 2})},
   787  		{slicep([]int{}), "P = [1,2]", slicep([]int{1, 2})},
   788  		{slicep([]int{1, 2, 3}), "P = [1,2]", slicep([]int{1, 2})},
   789  
   790  		// arrays
   791  		{arrayp([2]int{2, 3}), "", arrayp([2]int{2, 3})},
   792  		{arrayp([2]int{2, 3}), "P = [3,4]", arrayp([2]int{3, 4})},
   793  
   794  		// maps
   795  		{mapp(nil), "", mapp(nil)},
   796  		{mapp(map[string]int{}), "", mapp(map[string]int{})},
   797  		{mapp(map[string]int{"a": 1}), "", mapp(map[string]int{"a": 1})},
   798  		{mapp(nil), "[P]\na = 2", mapp(map[string]int{"a": 2})},
   799  		{mapp(map[string]int{}), "[P]\na = 2", mapp(map[string]int{"a": 2})},
   800  		{mapp(map[string]int{"a": 1, "b": 3}), "[P]\na = 2", mapp(map[string]int{"a": 2, "b": 3})},
   801  
   802  		// structs
   803  		{&T{nil}, "[P]", &T{nil}},
   804  		{&T{[]int{}}, "[P]", &T{[]int{}}},
   805  		{&T{[]int{1, 2, 3}}, "[P]", &T{[]int{1, 2, 3}}},
   806  		{&T{nil}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   807  		{&T{[]int{}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   808  		{&T{[]int{1, 2, 3}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   809  	} {
   810  		var s S
   811  		md, err := Decode(tt.input, &s)
   812  		if err != nil {
   813  			t.Errorf("[%d] Decode error: %s", i, err)
   814  			continue
   815  		}
   816  		if err := md.PrimitiveDecode(s.P, tt.v); err != nil {
   817  			t.Errorf("[%d] PrimitiveDecode error: %s", i, err)
   818  			continue
   819  		}
   820  		if !reflect.DeepEqual(tt.v, tt.want) {
   821  			t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
   822  		}
   823  	}
   824  }
   825  
   826  func TestDecodeDatetime(t *testing.T) {
   827  	// Test here in addition to toml-test to ensure the TZs are correct.
   828  	tz7 := time.FixedZone("", -3600*7)
   829  
   830  	for _, tt := range []struct {
   831  		in   string
   832  		want time.Time
   833  	}{
   834  		// Offset datetime
   835  		{"1979-05-27T07:32:00Z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   836  		{"1979-05-27T07:32:00.999999Z", time.Date(1979, 05, 27, 07, 32, 0, 999999000, time.UTC)},
   837  		{"1979-05-27T00:32:00-07:00", time.Date(1979, 05, 27, 00, 32, 0, 0, tz7)},
   838  		{"1979-05-27T00:32:00.999999-07:00", time.Date(1979, 05, 27, 00, 32, 0, 999999000, tz7)},
   839  		{"1979-05-27T00:32:00.24-07:00", time.Date(1979, 05, 27, 00, 32, 0, 240000000, tz7)},
   840  		{"1979-05-27 07:32:00Z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   841  		{"1979-05-27t07:32:00z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   842  
   843  		// Make sure the space between the datetime and "#" isn't lexed.
   844  		{"1979-05-27T07:32:12-07:00  # c", time.Date(1979, 05, 27, 07, 32, 12, 0, tz7)},
   845  
   846  		// Local times.
   847  		{"1979-05-27T07:32:00", time.Date(1979, 05, 27, 07, 32, 0, 0, internal.LocalDatetime)},
   848  		{"1979-05-27T07:32:00.999999", time.Date(1979, 05, 27, 07, 32, 0, 999999000, internal.LocalDatetime)},
   849  		{"1979-05-27T07:32:00.25", time.Date(1979, 05, 27, 07, 32, 0, 250000000, internal.LocalDatetime)},
   850  		{"1979-05-27", time.Date(1979, 05, 27, 0, 0, 0, 0, internal.LocalDate)},
   851  		{"07:32:00", time.Date(0, 1, 1, 07, 32, 0, 0, internal.LocalTime)},
   852  		{"07:32:00.999999", time.Date(0, 1, 1, 07, 32, 0, 999999000, internal.LocalTime)},
   853  	} {
   854  		t.Run(tt.in, func(t *testing.T) {
   855  			var x struct{ D time.Time }
   856  			input := "d = " + tt.in
   857  			if _, err := Decode(input, &x); err != nil {
   858  				t.Fatalf("got error: %s", err)
   859  			}
   860  
   861  			if h, w := x.D.Format(time.RFC3339Nano), tt.want.Format(time.RFC3339Nano); h != w {
   862  				t.Errorf("\nhave: %s\nwant: %s", h, w)
   863  			}
   864  		})
   865  	}
   866  }
   867  
   868  func TestDecodeTextUnmarshaler(t *testing.T) {
   869  	tests := []struct {
   870  		name string
   871  		t    interface{}
   872  		toml string
   873  		want string
   874  	}{
   875  		{
   876  			"time.Time",
   877  			struct{ Time time.Time }{},
   878  			"Time = 1987-07-05T05:45:00Z",
   879  			"map[Time:1987-07-05 05:45:00 +0000 UTC]",
   880  		},
   881  		{
   882  			"*time.Time",
   883  			struct{ Time *time.Time }{},
   884  			"Time = 1988-07-05T05:45:00Z",
   885  			"map[Time:1988-07-05 05:45:00 +0000 UTC]",
   886  		},
   887  		{
   888  			"map[string]time.Time",
   889  			struct{ Times map[string]time.Time }{},
   890  			"Times.one = 1989-07-05T05:45:00Z\nTimes.two = 1990-07-05T05:45:00Z",
   891  			"map[Times:map[one:1989-07-05 05:45:00 +0000 UTC two:1990-07-05 05:45:00 +0000 UTC]]",
   892  		},
   893  		{
   894  			"map[string]*time.Time",
   895  			struct{ Times map[string]*time.Time }{},
   896  			"Times.one = 1989-07-05T05:45:00Z\nTimes.two = 1990-07-05T05:45:00Z",
   897  			"map[Times:map[one:1989-07-05 05:45:00 +0000 UTC two:1990-07-05 05:45:00 +0000 UTC]]",
   898  		},
   899  	}
   900  
   901  	for _, tt := range tests {
   902  		t.Run(tt.name, func(t *testing.T) {
   903  			_, err := Decode(tt.toml, &tt.t)
   904  			if err != nil {
   905  				t.Fatal(err)
   906  			}
   907  
   908  			have := fmt.Sprintf("%v", tt.t)
   909  			if have != tt.want {
   910  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   911  			}
   912  		})
   913  	}
   914  }
   915  
   916  func TestDecodeDuration(t *testing.T) {
   917  	tests := []struct {
   918  		in                  interface{}
   919  		toml, want, wantErr string
   920  	}{
   921  		{&struct{ T time.Duration }{}, `t = "0s"`,
   922  			"&{0s}", ""},
   923  		{&struct{ T time.Duration }{}, `t = "5m4s"`,
   924  			"&{5m4s}", ""},
   925  		{&struct{ T time.Duration }{}, `t = "4.000000002s"`,
   926  			"&{4.000000002s}", ""},
   927  
   928  		{&struct{ T time.Duration }{}, `t = 0`,
   929  			"&{0s}", ""},
   930  		{&struct{ T time.Duration }{}, `t = 12345678`,
   931  			"&{12.345678ms}", ""},
   932  
   933  		{&struct{ T *time.Duration }{}, `T = "5s"`,
   934  			"&{5s}", ""},
   935  		{&struct{ T *time.Duration }{}, `T = 5`,
   936  			"&{5ns}", ""},
   937  
   938  		{&struct{ T map[string]time.Duration }{}, `T.dur = "5s"`,
   939  			"&{map[dur:5s]}", ""},
   940  		{&struct{ T map[string]*time.Duration }{}, `T.dur = "5s"`,
   941  			"&{map[dur:5s]}", ""},
   942  
   943  		{&struct{ T []time.Duration }{}, `T = ["5s"]`,
   944  			"&{[5s]}", ""},
   945  		{&struct{ T []*time.Duration }{}, `T = ["5s"]`,
   946  			"&{[5s]}", ""},
   947  
   948  		{&struct{ T time.Duration }{}, `t = "99 bottles of beer"`, "&{0s}", `invalid duration: "99 bottles of beer"`},
   949  		{&struct{ T time.Duration }{}, `t = "one bottle of beer"`, "&{0s}", `invalid duration: "one bottle of beer"`},
   950  		{&struct{ T time.Duration }{}, `t = 1.2`, "&{0s}", "incompatible types:"},
   951  		{&struct{ T time.Duration }{}, `t = {}`, "&{0s}", "incompatible types:"},
   952  		{&struct{ T time.Duration }{}, `t = []`, "&{0s}", "incompatible types:"},
   953  	}
   954  
   955  	for _, tt := range tests {
   956  		t.Run("", func(t *testing.T) {
   957  			_, err := Decode(tt.toml, tt.in)
   958  			if !errorContains(err, tt.wantErr) {
   959  				t.Fatal(err)
   960  			}
   961  
   962  			have := fmt.Sprintf("%s", tt.in)
   963  			if have != tt.want {
   964  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   965  			}
   966  		})
   967  	}
   968  }
   969  
   970  func TestDecodeJSONNumber(t *testing.T) {
   971  	tests := []struct {
   972  		in                  interface{}
   973  		toml, want, wantErr string
   974  	}{
   975  		{&struct{ D json.Number }{}, `D = 2`, "&{2}", ""},
   976  		{&struct{ D json.Number }{}, `D = 2.002`, "&{2.002}", ""},
   977  		{&struct{ D *json.Number }{}, `D = 2`, "&{2}", ""},
   978  		{&struct{ D *json.Number }{}, `D = 2.002`, "&{2.002}", ""},
   979  		{&struct{ D []json.Number }{}, `D = [2, 3.03]`, "&{[2 3.03]}", ""},
   980  		{&struct{ D []*json.Number }{}, `D = [2, 3.03]`, "&{[2 3.03]}", ""},
   981  		{&struct{ D map[string]json.Number }{}, `D = {a=2, b=3.03}`, "&{map[a:2 b:3.03]}", ""},
   982  		{&struct{ D map[string]*json.Number }{}, `D = {a=2, b=3.03}`, "&{map[a:2 b:3.03]}", ""},
   983  
   984  		{&struct{ D json.Number }{}, `D = {}`, "&{}", "incompatible types"},
   985  		{&struct{ D json.Number }{}, `D = []`, "&{}", "incompatible types"},
   986  		{&struct{ D json.Number }{}, `D = "2"`, "&{}", "incompatible types"},
   987  	}
   988  
   989  	for _, tt := range tests {
   990  		t.Run("", func(t *testing.T) {
   991  			_, err := Decode(tt.toml, tt.in)
   992  			if !errorContains(err, tt.wantErr) {
   993  				t.Fatal(err)
   994  			}
   995  
   996  			have := fmt.Sprintf("%s", tt.in)
   997  			if have != tt.want {
   998  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   999  			}
  1000  		})
  1001  	}
  1002  }
  1003  
  1004  func TestMetaDotConflict(t *testing.T) {
  1005  	var m map[string]interface{}
  1006  	meta, err := Decode(`
  1007  		"a.b" = "str"
  1008  		a.b   = 1
  1009  		""    = 2
  1010  	`, &m)
  1011  	if err != nil {
  1012  		t.Fatal(err)
  1013  	}
  1014  
  1015  	want := `"a.b"=String; a.b=Integer; ""=Integer`
  1016  	have := ""
  1017  	for i, k := range meta.Keys() {
  1018  		if i > 0 {
  1019  			have += "; "
  1020  		}
  1021  		have += k.String() + "=" + meta.Type(k...)
  1022  	}
  1023  	if have != want {
  1024  		t.Errorf("\nhave: %s\nwant: %s", have, want)
  1025  	}
  1026  }
  1027  
  1028  type (
  1029  	Outer struct {
  1030  		Int   *InnerInt
  1031  		Enum  *Enum
  1032  		Slice *InnerArrayString
  1033  	}
  1034  	Enum             int
  1035  	InnerString      struct{ value string }
  1036  	InnerInt         struct{ value int }
  1037  	InnerBool        struct{ value bool }
  1038  	InnerArrayString struct{ value []string }
  1039  )
  1040  
  1041  const (
  1042  	NoValue Enum = iota
  1043  	OtherValue
  1044  )
  1045  
  1046  func (e *Enum) Value() string {
  1047  	switch *e {
  1048  	case OtherValue:
  1049  		return "OTHER_VALUE"
  1050  	}
  1051  	return ""
  1052  }
  1053  
  1054  func (e *Enum) MarshalTOML() ([]byte, error) {
  1055  	return []byte(`"` + e.Value() + `"`), nil
  1056  }
  1057  
  1058  func (e *Enum) UnmarshalTOML(value interface{}) error {
  1059  	sValue, ok := value.(string)
  1060  	if !ok {
  1061  		return fmt.Errorf("value %v is not a string type", value)
  1062  	}
  1063  	for _, enum := range []Enum{NoValue, OtherValue} {
  1064  		if enum.Value() == sValue {
  1065  			*e = enum
  1066  			return nil
  1067  		}
  1068  	}
  1069  	return errors.New("invalid enum value")
  1070  }
  1071  
  1072  func (i *InnerInt) MarshalTOML() ([]byte, error) {
  1073  	return []byte(strconv.Itoa(i.value)), nil
  1074  }
  1075  func (i *InnerInt) UnmarshalTOML(value interface{}) error {
  1076  	iValue, ok := value.(int64)
  1077  	if !ok {
  1078  		return fmt.Errorf("value %v is not a int type", value)
  1079  	}
  1080  	i.value = int(iValue)
  1081  	return nil
  1082  }
  1083  
  1084  func (as *InnerArrayString) MarshalTOML() ([]byte, error) {
  1085  	return []byte("[\"" + strings.Join(as.value, "\", \"") + "\"]"), nil
  1086  }
  1087  
  1088  func (as *InnerArrayString) UnmarshalTOML(value interface{}) error {
  1089  	if value != nil {
  1090  		asValue, ok := value.([]interface{})
  1091  		if !ok {
  1092  			return fmt.Errorf("value %v is not a [] type", value)
  1093  		}
  1094  		as.value = []string{}
  1095  		for _, value := range asValue {
  1096  			as.value = append(as.value, value.(string))
  1097  		}
  1098  	}
  1099  	return nil
  1100  }
  1101  
  1102  // Test for #341
  1103  func TestCustomEncode(t *testing.T) {
  1104  	enum := OtherValue
  1105  	outer := Outer{
  1106  		Int:   &InnerInt{value: 10},
  1107  		Enum:  &enum,
  1108  		Slice: &InnerArrayString{value: []string{"text1", "text2"}},
  1109  	}
  1110  
  1111  	var buf bytes.Buffer
  1112  	err := NewEncoder(&buf).Encode(outer)
  1113  	if err != nil {
  1114  		t.Errorf("Encode failed: %s", err)
  1115  	}
  1116  
  1117  	have := strings.TrimSpace(buf.String())
  1118  	want := strings.ReplaceAll(strings.TrimSpace(`
  1119  		Int = 10
  1120  		Enum = "OTHER_VALUE"
  1121  		Slice = ["text1", "text2"]
  1122  	`), "\t", "")
  1123  	if want != have {
  1124  		t.Errorf("\nhave: %s\nwant: %s\n", have, want)
  1125  	}
  1126  }
  1127  
  1128  // Test for #341
  1129  func TestCustomDecode(t *testing.T) {
  1130  	var outer Outer
  1131  	_, err := Decode(`
  1132  		Int = 10
  1133  		Enum = "OTHER_VALUE"
  1134  		Slice = ["text1", "text2"]
  1135  	`, &outer)
  1136  	if err != nil {
  1137  		t.Fatalf("Decode failed: %s", err)
  1138  	}
  1139  
  1140  	if outer.Int.value != 10 {
  1141  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Int.value, 10)
  1142  	}
  1143  	if *outer.Enum != OtherValue {
  1144  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Enum, OtherValue)
  1145  	}
  1146  	if fmt.Sprint(outer.Slice.value) != fmt.Sprint([]string{"text1", "text2"}) {
  1147  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Slice.value, []string{"text1", "text2"})
  1148  	}
  1149  }
  1150  
  1151  // TODO: this should be improved for v2:
  1152  // https://github.com/BurntSushi/toml/issues/384
  1153  func TestDecodeDoubleTags(t *testing.T) {
  1154  	var s struct {
  1155  		A int `toml:"a"`
  1156  		B int `toml:"a"`
  1157  		C int `toml:"c"`
  1158  	}
  1159  	_, err := Decode(`
  1160  		a = 1
  1161  		b = 2
  1162  		c = 3
  1163  	`, &s)
  1164  	if err != nil {
  1165  		t.Fatal(err)
  1166  	}
  1167  
  1168  	want := `{0 0 3}`
  1169  	have := fmt.Sprintf("%v", s)
  1170  	if want != have {
  1171  		t.Errorf("\nhave: %s\nwant: %s\n", have, want)
  1172  	}
  1173  }
  1174  
  1175  func TestMetaKeys(t *testing.T) {
  1176  	tests := []struct {
  1177  		in   string
  1178  		want []Key
  1179  	}{
  1180  		{"", []Key{}},
  1181  		{"b=1\na=1", []Key{Key{"b"}, Key{"a"}}},
  1182  		{"a.b=1\na.a=1", []Key{Key{"a", "b"}, Key{"a", "a"}}}, // TODO: should include "a"
  1183  		{"[tbl]\na=1", []Key{Key{"tbl"}, Key{"tbl", "a"}}},
  1184  		{"[tbl]\na.a=1", []Key{Key{"tbl"}, Key{"tbl", "a", "a"}}}, // TODO: should include "a.a"
  1185  		{"tbl={a=1}", []Key{Key{"tbl"}, Key{"tbl", "a"}}},
  1186  		{"tbl={a={b=1}}", []Key{Key{"tbl"}, Key{"tbl", "a"}, Key{"tbl", "a", "b"}}},
  1187  	}
  1188  
  1189  	for _, tt := range tests {
  1190  		t.Run("", func(t *testing.T) {
  1191  			var x interface{}
  1192  			meta, err := Decode(tt.in, &x)
  1193  			if err != nil {
  1194  				t.Fatal(err)
  1195  			}
  1196  
  1197  			have := meta.Keys()
  1198  			if !reflect.DeepEqual(tt.want, have) {
  1199  				t.Errorf("\nhave: %s\nwant: %s\n", have, tt.want)
  1200  			}
  1201  		})
  1202  	}
  1203  }
  1204  
  1205  func TestDecodeParallel(t *testing.T) {
  1206  	doc, err := os.ReadFile("testdata/ja-JP.toml")
  1207  	if err != nil {
  1208  		t.Fatal(err)
  1209  	}
  1210  
  1211  	var wg sync.WaitGroup
  1212  	for i := 0; i < 10; i++ {
  1213  		wg.Add(1)
  1214  		go func() {
  1215  			defer wg.Done()
  1216  			err := Unmarshal(doc, new(map[string]interface{}))
  1217  			if err != nil {
  1218  				t.Fatal(err)
  1219  			}
  1220  		}()
  1221  	}
  1222  	wg.Wait()
  1223  }
  1224  
  1225  // errorContains checks if the error message in have contains the text in
  1226  // want.
  1227  //
  1228  // This is safe when have is nil. Use an empty string for want if you want to
  1229  // test that err is nil.
  1230  func errorContains(have error, want string) bool {
  1231  	if have == nil {
  1232  		return want == ""
  1233  	}
  1234  	if want == "" {
  1235  		return false
  1236  	}
  1237  	return strings.Contains(have.Error(), want)
  1238  }
  1239  

View as plain text