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
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
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
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
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
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
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
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
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
828 tz7 := time.FixedZone("", -3600*7)
829
830 for _, tt := range []struct {
831 in string
832 want time.Time
833 }{
834
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
844 {"1979-05-27T07:32:12-07:00 # c", time.Date(1979, 05, 27, 07, 32, 12, 0, tz7)},
845
846
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
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
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
1152
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"}}},
1183 {"[tbl]\na=1", []Key{Key{"tbl"}, Key{"tbl", "a"}}},
1184 {"[tbl]\na.a=1", []Key{Key{"tbl"}, Key{"tbl", "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
1226
1227
1228
1229
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