mirror of
				https://github.com/goccy/go-yaml.git
				synced 2025-10-31 13:21:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3122 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			3122 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package yaml_test
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"math"
 | |
| 	"net"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/goccy/go-yaml"
 | |
| 	"github.com/goccy/go-yaml/ast"
 | |
| 	"github.com/goccy/go-yaml/internal/errors"
 | |
| 	"github.com/goccy/go-yaml/parser"
 | |
| )
 | |
| 
 | |
| type Child struct {
 | |
| 	B int
 | |
| 	C int `yaml:"-"`
 | |
| }
 | |
| 
 | |
| func TestDecoder(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		source string
 | |
| 		value  interface{}
 | |
| 	}{
 | |
| 		{
 | |
| 			"null\n",
 | |
| 			(*struct{})(nil),
 | |
| 		},
 | |
| 		{
 | |
| 			"v: hi\n",
 | |
| 			map[string]string{"v": "hi"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: \"true\"\n",
 | |
| 			map[string]string{"v": "true"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: \"false\"\n",
 | |
| 			map[string]string{"v": "false"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: true\n",
 | |
| 			map[string]interface{}{"v": true},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: true\n",
 | |
| 			map[string]string{"v": "true"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 10\n",
 | |
| 			map[string]string{"v": "10"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -10\n",
 | |
| 			map[string]string{"v": "-10"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.234\n",
 | |
| 			map[string]string{"v": "1.234"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: \" foo\"\n",
 | |
| 			map[string]string{"v": " foo"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: \"foo \"\n",
 | |
| 			map[string]string{"v": "foo "},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: \" foo \"\n",
 | |
| 			map[string]string{"v": " foo "},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: false\n",
 | |
| 			map[string]bool{"v": false},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 10\n",
 | |
| 			map[string]int{"v": 10},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 10",
 | |
| 			map[string]interface{}{"v": 10},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0b10",
 | |
| 			map[string]interface{}{"v": 2},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -0b101010",
 | |
| 			map[string]interface{}{"v": -42},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -0b1000000000000000000000000000000000000000000000000000000000000000",
 | |
| 			map[string]interface{}{"v": int64(-9223372036854775808)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0xA",
 | |
| 			map[string]interface{}{"v": 10},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .1",
 | |
| 			map[string]interface{}{"v": 0.1},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -.1",
 | |
| 			map[string]interface{}{"v": -0.1},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -10\n",
 | |
| 			map[string]int{"v": -10},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 4294967296\n",
 | |
| 			map[string]int64{"v": int64(4294967296)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0.1\n",
 | |
| 			map[string]interface{}{"v": 0.1},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0.99\n",
 | |
| 			map[string]float32{"v": 0.99},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -0.1\n",
 | |
| 			map[string]float64{"v": -0.1},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 6.8523e+5",
 | |
| 			map[string]interface{}{"v": 6.8523e+5},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 685.230_15e+03",
 | |
| 			map[string]interface{}{"v": 685.23015e+03},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 685_230.15",
 | |
| 			map[string]interface{}{"v": 685230.15},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 685_230.15",
 | |
| 			map[string]float64{"v": 685230.15},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 685230",
 | |
| 			map[string]interface{}{"v": 685230},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: +685_230",
 | |
| 			map[string]interface{}{"v": 685230},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 02472256",
 | |
| 			map[string]interface{}{"v": 685230},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0x_0A_74_AE",
 | |
| 			map[string]interface{}{"v": 685230},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0b1010_0111_0100_1010_1110",
 | |
| 			map[string]interface{}{"v": 685230},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: +685_230",
 | |
| 			map[string]int{"v": 685230},
 | |
| 		},
 | |
| 
 | |
| 		// Bools from spec
 | |
| 		{
 | |
| 			"v: True",
 | |
| 			map[string]interface{}{"v": true},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: TRUE",
 | |
| 			map[string]interface{}{"v": true},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: False",
 | |
| 			map[string]interface{}{"v": false},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: FALSE",
 | |
| 			map[string]interface{}{"v": false},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: y",
 | |
| 			map[string]interface{}{"v": "y"}, // y or yes or Yes is string
 | |
| 		},
 | |
| 		{
 | |
| 			"v: NO",
 | |
| 			map[string]interface{}{"v": "NO"}, // no or No or NO is string
 | |
| 		},
 | |
| 		{
 | |
| 			"v: on",
 | |
| 			map[string]interface{}{"v": "on"}, // on is string
 | |
| 		},
 | |
| 
 | |
| 		// Some cross type conversions
 | |
| 		{
 | |
| 			"v: 42",
 | |
| 			map[string]uint{"v": 42},
 | |
| 		}, {
 | |
| 			"v: 4294967296",
 | |
| 			map[string]uint64{"v": uint64(4294967296)},
 | |
| 		},
 | |
| 
 | |
| 		// int
 | |
| 		{
 | |
| 			"v: 2147483647",
 | |
| 			map[string]int{"v": math.MaxInt32},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -2147483648",
 | |
| 			map[string]int{"v": math.MinInt32},
 | |
| 		},
 | |
| 
 | |
| 		// int64
 | |
| 		{
 | |
| 			"v: 9223372036854775807",
 | |
| 			map[string]int64{"v": math.MaxInt64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0b111111111111111111111111111111111111111111111111111111111111111",
 | |
| 			map[string]int64{"v": math.MaxInt64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -9223372036854775808",
 | |
| 			map[string]int64{"v": math.MinInt64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -0b111111111111111111111111111111111111111111111111111111111111111",
 | |
| 			map[string]int64{"v": -math.MaxInt64},
 | |
| 		},
 | |
| 
 | |
| 		// uint
 | |
| 		{
 | |
| 			"v: 0",
 | |
| 			map[string]uint{"v": 0},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 4294967295",
 | |
| 			map[string]uint{"v": math.MaxUint32},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e3",
 | |
| 			map[string]uint{"v": 1000},
 | |
| 		},
 | |
| 
 | |
| 		// uint64
 | |
| 		{
 | |
| 			"v: 0",
 | |
| 			map[string]uint{"v": 0},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 18446744073709551615",
 | |
| 			map[string]uint64{"v": math.MaxUint64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 0b1111111111111111111111111111111111111111111111111111111111111111",
 | |
| 			map[string]uint64{"v": math.MaxUint64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 9223372036854775807",
 | |
| 			map[string]uint64{"v": math.MaxInt64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e3",
 | |
| 			map[string]uint64{"v": 1000},
 | |
| 		},
 | |
| 
 | |
| 		// float32
 | |
| 		{
 | |
| 			"v: 3.40282346638528859811704183484516925440e+38",
 | |
| 			map[string]float32{"v": math.MaxFloat32},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.401298464324817070923729583289916131280e-45",
 | |
| 			map[string]float32{"v": math.SmallestNonzeroFloat32},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 18446744073709551615",
 | |
| 			map[string]float32{"v": float32(math.MaxUint64)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 18446744073709551616",
 | |
| 			map[string]float32{"v": float32(math.MaxUint64 + 1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e-06",
 | |
| 			map[string]float32{"v": 1e-6},
 | |
| 		},
 | |
| 
 | |
| 		// float64
 | |
| 		{
 | |
| 			"v: 1.797693134862315708145274237317043567981e+308",
 | |
| 			map[string]float64{"v": math.MaxFloat64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 4.940656458412465441765687928682213723651e-324",
 | |
| 			map[string]float64{"v": math.SmallestNonzeroFloat64},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 18446744073709551615",
 | |
| 			map[string]float64{"v": float64(math.MaxUint64)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 18446744073709551616",
 | |
| 			map[string]float64{"v": float64(math.MaxUint64 + 1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e-06",
 | |
| 			map[string]float64{"v": 1e-06},
 | |
| 		},
 | |
| 
 | |
| 		// Timestamps
 | |
| 		{
 | |
| 			// Date only.
 | |
| 			"v: 2015-01-01\n",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			// RFC3339
 | |
| 			"v: 2015-02-24T18:19:39.12Z\n",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, .12e9, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			// RFC3339 with short dates.
 | |
| 			"v: 2015-2-3T3:4:5Z",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 2, 3, 3, 4, 5, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			// ISO8601 lower case t
 | |
| 			"v: 2015-02-24t18:19:39Z\n",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			// space separate, no time zone
 | |
| 			"v: 2015-02-24 18:19:39\n",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 60s\n",
 | |
| 			map[string]time.Duration{"v": time.Minute},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -0.5h\n",
 | |
| 			map[string]time.Duration{"v": -30 * time.Minute},
 | |
| 		},
 | |
| 
 | |
| 		// Single Quoted values.
 | |
| 		{
 | |
| 			`'1': '2'`,
 | |
| 			map[interface{}]interface{}{"1": `2`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '"2"'`,
 | |
| 			map[interface{}]interface{}{"1": `"2"`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': ''''`,
 | |
| 			map[interface{}]interface{}{"1": `'`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '''2'''`,
 | |
| 			map[interface{}]interface{}{"1": `'2'`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': 'B''z'`,
 | |
| 			map[interface{}]interface{}{"1": `B'z`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '\'`,
 | |
| 			map[interface{}]interface{}{"1": `\`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '\\'`,
 | |
| 			map[interface{}]interface{}{"1": `\\`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '\"2\"'`,
 | |
| 			map[interface{}]interface{}{"1": `\"2\"`},
 | |
| 		},
 | |
| 		{
 | |
| 			`'1': '\\"2\\"'`,
 | |
| 			map[interface{}]interface{}{"1": `\\"2\\"`},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': '   1\n    2\n    3'",
 | |
| 			map[interface{}]interface{}{"1": "   1 2 3"},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': '\n    2\n    3'",
 | |
| 			map[interface{}]interface{}{"1": " 2 3"},
 | |
| 		},
 | |
| 
 | |
| 		// Double Quoted values.
 | |
| 		{
 | |
| 			`"1": "2"`,
 | |
| 			map[interface{}]interface{}{"1": `2`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "\"2\""`,
 | |
| 			map[interface{}]interface{}{"1": `"2"`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "\""`,
 | |
| 			map[interface{}]interface{}{"1": `"`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "X\"z"`,
 | |
| 			map[interface{}]interface{}{"1": `X"z`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "\\"`,
 | |
| 			map[interface{}]interface{}{"1": `\`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "\\\\"`,
 | |
| 			map[interface{}]interface{}{"1": `\\`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "\\\"2\\\""`,
 | |
| 			map[interface{}]interface{}{"1": `\"2\"`},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': \"   1\n    2\n    3\"",
 | |
| 			map[interface{}]interface{}{"1": "   1 2 3"},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': \"\n    2\n    3\"",
 | |
| 			map[interface{}]interface{}{"1": " 2 3"},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "a\x2Fb"`,
 | |
| 			map[interface{}]interface{}{"1": `a/b`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "a\u002Fb"`,
 | |
| 			map[interface{}]interface{}{"1": `a/b`},
 | |
| 		},
 | |
| 		{
 | |
| 			`"1": "a\x2Fb\u002Fc\U0000002Fd"`,
 | |
| 			map[interface{}]interface{}{"1": `a/b/c/d`},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': \"2\\n3\"",
 | |
| 			map[interface{}]interface{}{"1": "2\n3"},
 | |
| 		},
 | |
| 		{
 | |
| 			"'1': \"2\\r\\n3\"",
 | |
| 			map[interface{}]interface{}{"1": "2\r\n3"},
 | |
| 		},
 | |
| 
 | |
| 		{
 | |
| 			"a: -b_c",
 | |
| 			map[string]interface{}{"a": "-b_c"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: +b_c",
 | |
| 			map[string]interface{}{"a": "+b_c"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 50cent_of_dollar",
 | |
| 			map[string]interface{}{"a": "50cent_of_dollar"},
 | |
| 		},
 | |
| 
 | |
| 		// Nulls
 | |
| 		{
 | |
| 			"v:",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: ~",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"~: null key",
 | |
| 			map[interface{}]string{nil: "null key"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:",
 | |
| 			map[string]*bool{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: null",
 | |
| 			map[string]*string{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: null",
 | |
| 			map[string]string{"v": ""},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: null",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: Null",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: NULL",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: ~",
 | |
| 			map[string]*string{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: ~",
 | |
| 			map[string]string{"v": ""},
 | |
| 		},
 | |
| 
 | |
| 		{
 | |
| 			"v: .inf\n",
 | |
| 			map[string]interface{}{"v": math.Inf(0)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .Inf\n",
 | |
| 			map[string]interface{}{"v": math.Inf(0)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .INF\n",
 | |
| 			map[string]interface{}{"v": math.Inf(0)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -.inf\n",
 | |
| 			map[string]interface{}{"v": math.Inf(-1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -.Inf\n",
 | |
| 			map[string]interface{}{"v": math.Inf(-1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: -.INF\n",
 | |
| 			map[string]interface{}{"v": math.Inf(-1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .nan\n",
 | |
| 			map[string]interface{}{"v": math.NaN()},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .NaN\n",
 | |
| 			map[string]interface{}{"v": math.NaN()},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: .NAN\n",
 | |
| 			map[string]interface{}{"v": math.NaN()},
 | |
| 		},
 | |
| 
 | |
| 		// Explicit tags.
 | |
| 		{
 | |
| 			"v: !!float '1.1'",
 | |
| 			map[string]interface{}{"v": 1.1},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!float 0",
 | |
| 			map[string]interface{}{"v": float64(0)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!float -1",
 | |
| 			map[string]interface{}{"v": float64(-1)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!null ''",
 | |
| 			map[string]interface{}{"v": nil},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!timestamp \"2015-01-01\"",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!timestamp 2015-01-01",
 | |
| 			map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!bool yes",
 | |
| 			map[string]bool{"v": true},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: !!bool False",
 | |
| 			map[string]bool{"v": false},
 | |
| 		},
 | |
| 
 | |
| 		// Flow sequence
 | |
| 		{
 | |
| 			"v: [A,B]",
 | |
| 			map[string]interface{}{"v": []interface{}{"A", "B"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: [A,B,C,]",
 | |
| 			map[string][]string{"v": {"A", "B", "C"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: [A,1,C]",
 | |
| 			map[string][]string{"v": {"A", "1", "C"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: [A,1,C]",
 | |
| 			map[string]interface{}{"v": []interface{}{"A", 1, "C"}},
 | |
| 		},
 | |
| 
 | |
| 		// Block sequence
 | |
| 		{
 | |
| 			"v:\n - A\n - B",
 | |
| 			map[string]interface{}{"v": []interface{}{"A", "B"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n - A\n - B\n - C",
 | |
| 			map[string][]string{"v": {"A", "B", "C"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n - A\n - 1\n - C",
 | |
| 			map[string][]string{"v": {"A", "1", "C"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n - A\n - 1\n - C",
 | |
| 			map[string]interface{}{"v": []interface{}{"A", 1, "C"}},
 | |
| 		},
 | |
| 
 | |
| 		// Map inside interface with no type hints.
 | |
| 		{
 | |
| 			"a: {b: c}",
 | |
| 			map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
 | |
| 		},
 | |
| 
 | |
| 		{
 | |
| 			"v: \"\"\n",
 | |
| 			map[string]string{"v": ""},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- B\n",
 | |
| 			map[string][]string{"v": {"A", "B"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: '-'\n",
 | |
| 			map[string]string{"a": "-"},
 | |
| 		},
 | |
| 		{
 | |
| 			"123\n",
 | |
| 			123,
 | |
| 		},
 | |
| 		{
 | |
| 			"hello: world\n",
 | |
| 			map[string]string{"hello": "world"},
 | |
| 		},
 | |
| 		{
 | |
| 			"hello: world\r\n",
 | |
| 			map[string]string{"hello": "world"},
 | |
| 		},
 | |
| 		{
 | |
| 			"hello: world\rGo: Gopher",
 | |
| 			map[string]string{"hello": "world", "Go": "Gopher"},
 | |
| 		},
 | |
| 
 | |
| 		// Structs and type conversions.
 | |
| 		{
 | |
| 			"hello: world",
 | |
| 			struct{ Hello string }{"world"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: {b: c}",
 | |
| 			struct{ A struct{ B string } }{struct{ B string }{"c"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: {b: c}",
 | |
| 			struct{ A map[string]string }{map[string]string{"b": "c"}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a:",
 | |
| 			struct{ A map[string]string }{},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1",
 | |
| 			struct{ A int }{1},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1",
 | |
| 			struct{ A float64 }{1},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1.0",
 | |
| 			struct{ A int }{1},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1.0",
 | |
| 			struct{ A uint }{1},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: [1, 2]",
 | |
| 			struct{ A []int }{[]int{1, 2}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: [1, 2]",
 | |
| 			struct{ A [2]int }{[2]int{1, 2}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1",
 | |
| 			struct{ B int }{0},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1",
 | |
| 			struct {
 | |
| 				B int `yaml:"a"`
 | |
| 			}{1},
 | |
| 		},
 | |
| 
 | |
| 		{
 | |
| 			"a: 1\n",
 | |
| 			yaml.MapItem{Key: "a", Value: 1},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1\nb: 2\nc: 3\n",
 | |
| 			yaml.MapSlice{
 | |
| 				{Key: "a", Value: 1},
 | |
| 				{Key: "b", Value: 2},
 | |
| 				{Key: "c", Value: 3},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
 | |
| 			map[string]interface{}{
 | |
| 				"v": []interface{}{
 | |
| 					"A",
 | |
| 					1,
 | |
| 					map[string][]int{
 | |
| 						"B": {2, 3},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a:\n  b: c\n",
 | |
| 			map[string]interface{}{
 | |
| 				"a": map[string]string{
 | |
| 					"b": "c",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: {x: 1}\n",
 | |
| 			map[string]map[string]int{
 | |
| 				"a": {
 | |
| 					"x": 1,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
 | |
| 			map[string]string{
 | |
| 				"t2": "2018-01-09T10:40:47Z",
 | |
| 				"t4": "2098-01-09T10:40:47Z",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: [1, 2]\n",
 | |
| 			map[string][]int{
 | |
| 				"a": {1, 2},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: {b: c, d: e}\n",
 | |
| 			map[string]interface{}{
 | |
| 				"a": map[string]string{
 | |
| 					"b": "c",
 | |
| 					"d": "e",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 3s\n",
 | |
| 			map[string]string{
 | |
| 				"a": "3s",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: <foo>\n",
 | |
| 			map[string]string{"a": "<foo>"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: \"1:1\"\n",
 | |
| 			map[string]string{"a": "1:1"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1.2.3.4\n",
 | |
| 			map[string]string{"a": "1.2.3.4"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 'b: c'\n",
 | |
| 			map[string]string{"a": "b: c"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 'Hello #comment'\n",
 | |
| 			map[string]string{"a": "Hello #comment"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 100.5\n",
 | |
| 			map[string]interface{}{
 | |
| 				"a": 100.5,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: \"\\0\"\n",
 | |
| 			map[string]string{"a": "\x00"},
 | |
| 		},
 | |
| 		{
 | |
| 			"b: 2\na: 1\nd: 4\nc: 3\nsub:\n  e: 5\n",
 | |
| 			map[string]interface{}{
 | |
| 				"b": 2,
 | |
| 				"a": 1,
 | |
| 				"d": 4,
 | |
| 				"c": 3,
 | |
| 				"sub": map[string]int{
 | |
| 					"e": 5,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"       a       :          b        \n",
 | |
| 			map[string]string{"a": "b"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: b # comment\nb: c\n",
 | |
| 			map[string]string{
 | |
| 				"a": "b",
 | |
| 				"b": "c",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"---\na: b\n",
 | |
| 			map[string]string{"a": "b"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: b\n...\n",
 | |
| 			map[string]string{"a": "b"},
 | |
| 		},
 | |
| 		{
 | |
| 			"%YAML 1.2\n---\n",
 | |
| 			(*struct{})(nil),
 | |
| 		},
 | |
| 		{
 | |
| 			"---\n",
 | |
| 			(*struct{})(nil),
 | |
| 		},
 | |
| 		{
 | |
| 			"...",
 | |
| 			(*struct{})(nil),
 | |
| 		},
 | |
| 		{
 | |
| 			"v: go test ./...",
 | |
| 			map[string]string{"v": "go test ./..."},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: echo ---",
 | |
| 			map[string]string{"v": "echo ---"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: |\n  hello\n  ...\n  world\n",
 | |
| 			map[string]string{"v": "hello\n...\nworld\n"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: !!binary gIGC\n",
 | |
| 			map[string]string{"a": "\x80\x81\x82"},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
 | |
| 			map[string]string{"a": strings.Repeat("\x90", 54)},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- |-\n  B\n  C\n",
 | |
| 			map[string][]string{
 | |
| 				"v": {
 | |
| 					"A", "B\nC",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- |-\n  B\n  C\n\n\n",
 | |
| 			map[string][]string{
 | |
| 				"v": {
 | |
| 					"A", "B\nC",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- >-\n  B\n  C\n",
 | |
| 			map[string][]string{
 | |
| 				"v": {
 | |
| 					"A", "B C",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"v:\n- A\n- >-\n  B\n  C\n\n\n",
 | |
| 			map[string][]string{
 | |
| 				"v": {
 | |
| 					"A", "B C",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: b\nc: d\n",
 | |
| 			struct {
 | |
| 				A string
 | |
| 				C string `yaml:"c"`
 | |
| 			}{
 | |
| 				"b", "d",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1\nb: 2\n",
 | |
| 			struct {
 | |
| 				A int
 | |
| 				B int `yaml:"-"`
 | |
| 			}{
 | |
| 				1, 0,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: 1\nb: 2\n",
 | |
| 			struct {
 | |
| 				A     int
 | |
| 				Child `yaml:",inline"`
 | |
| 			}{
 | |
| 				1,
 | |
| 				Child{
 | |
| 					B: 2,
 | |
| 					C: 0,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 
 | |
| 		// Anchors and aliases.
 | |
| 		{
 | |
| 			"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
 | |
| 			struct{ A, B, C, D int }{1, 2, 1, 2},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: &a {c: 1}\nb: *a\n",
 | |
| 			struct {
 | |
| 				A, B struct {
 | |
| 					C int
 | |
| 				}
 | |
| 			}{struct{ C int }{1}, struct{ C int }{1}},
 | |
| 		},
 | |
| 		{
 | |
| 			"a: &a [1, 2]\nb: *a\n",
 | |
| 			struct{ B []int }{[]int{1, 2}},
 | |
| 		},
 | |
| 		{
 | |
| 			"key1: &anchor\n  subkey: *anchor\nkey2: *anchor\n",
 | |
| 			map[string]any{
 | |
| 				"key1": map[string]any{
 | |
| 					"subkey": nil,
 | |
| 				},
 | |
| 				"key2": map[string]any{
 | |
| 					"subkey": nil,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"tags:\n- hello-world\na: foo",
 | |
| 			struct {
 | |
| 				Tags []string
 | |
| 				A    string
 | |
| 			}{Tags: []string{"hello-world"}, A: "foo"},
 | |
| 		},
 | |
| 		{
 | |
| 			"",
 | |
| 			(*struct{})(nil),
 | |
| 		},
 | |
| 		{
 | |
| 			"{}", struct{}{},
 | |
| 		},
 | |
| 		{
 | |
| 			"{a: , b: c}",
 | |
| 			map[string]any{"a": nil, "b": "c"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: /a/{b}",
 | |
| 			map[string]string{"v": "/a/{b}"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1[]{},!%?&*",
 | |
| 			map[string]string{"v": "1[]{},!%?&*"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: user's item",
 | |
| 			map[string]string{"v": "user's item"},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: [1,[2,[3,[4,5],6],7],8]",
 | |
| 			map[string]interface{}{
 | |
| 				"v": []interface{}{
 | |
| 					1,
 | |
| 					[]interface{}{
 | |
| 						2,
 | |
| 						[]interface{}{
 | |
| 							3,
 | |
| 							[]int{4, 5},
 | |
| 							6,
 | |
| 						},
 | |
| 						7,
 | |
| 					},
 | |
| 					8,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: {a: {b: {c: {d: e},f: g},h: i},j: k}",
 | |
| 			map[string]interface{}{
 | |
| 				"v": map[string]interface{}{
 | |
| 					"a": map[string]interface{}{
 | |
| 						"b": map[string]interface{}{
 | |
| 							"c": map[string]string{
 | |
| 								"d": "e",
 | |
| 							},
 | |
| 							"f": "g",
 | |
| 						},
 | |
| 						"h": "i",
 | |
| 					},
 | |
| 					"j": "k",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			`---
 | |
| - a:
 | |
|     b:
 | |
| - c: d
 | |
| `,
 | |
| 			[]map[string]interface{}{
 | |
| 				{
 | |
| 					"a": map[string]interface{}{
 | |
| 						"b": nil,
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					"c": "d",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			`---
 | |
| a:
 | |
|   b:
 | |
| c: d
 | |
| `,
 | |
| 			map[string]interface{}{
 | |
| 				"a": map[string]interface{}{
 | |
| 					"b": nil,
 | |
| 				},
 | |
| 				"c": "d",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			`---
 | |
| a:
 | |
| b:
 | |
| c:
 | |
| `,
 | |
| 			map[string]interface{}{
 | |
| 				"a": nil,
 | |
| 				"b": nil,
 | |
| 				"c": nil,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			`---
 | |
| a: go test ./...
 | |
| b:
 | |
| c:
 | |
| `,
 | |
| 			map[string]interface{}{
 | |
| 				"a": "go test ./...",
 | |
| 				"b": nil,
 | |
| 				"c": nil,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			`---
 | |
| a: |
 | |
|   hello
 | |
|   ...
 | |
|   world
 | |
| b:
 | |
| c:
 | |
| `,
 | |
| 			map[string]interface{}{
 | |
| 				"a": "hello\n...\nworld\n",
 | |
| 				"b": nil,
 | |
| 				"c": nil,
 | |
| 			},
 | |
| 		},
 | |
| 
 | |
| 		// Multi bytes
 | |
| 		{
 | |
| 			"v: あいうえお\nv2: かきくけこ",
 | |
| 			map[string]string{"v": "あいうえお", "v2": "かきくけこ"},
 | |
| 		},
 | |
| 		{
 | |
| 			`
 | |
| - "Fun with \\"
 | |
| - "\" \a \b \e \f"
 | |
| - "\n \r \t \v \0"
 | |
| - "\  \_ \N \L \P \
 | |
|   \x41 \u0041 \U00000041"
 | |
| `,
 | |
| 			[]string{"Fun with \\", "\" \u0007 \b \u001b \f", "\n \r \t \u000b \u0000", "\u0020 \u00a0 \u0085 \u2028 \u2029 A A A"},
 | |
| 		},
 | |
| 		{
 | |
| 			`"\ud83e\udd23"`,
 | |
| 			"🤣",
 | |
| 		},
 | |
| 		{
 | |
| 			`"\uD83D\uDE00\uD83D\uDE01"`,
 | |
| 			"😀😁",
 | |
| 		},
 | |
| 		{
 | |
| 			`"\uD83D\uDE00a\uD83D\uDE01"`,
 | |
| 			"😀a😁",
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.source, func(t *testing.T) {
 | |
| 			buf := bytes.NewBufferString(test.source)
 | |
| 			dec := yaml.NewDecoder(buf)
 | |
| 			typ := reflect.ValueOf(test.value).Type()
 | |
| 			value := reflect.New(typ)
 | |
| 			if err := dec.Decode(value.Interface()); err != nil {
 | |
| 				if err == io.EOF {
 | |
| 					return
 | |
| 				}
 | |
| 				t.Fatalf("%s: %+v", test.source, err)
 | |
| 			}
 | |
| 			actual := fmt.Sprintf("%+v", value.Elem().Interface())
 | |
| 			expect := fmt.Sprintf("%+v", test.value)
 | |
| 			if actual != expect {
 | |
| 				t.Fatalf("failed to test [%s], actual=[%s], expect=[%s]", test.source, actual, expect)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_Invalid(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		src    string
 | |
| 		expect string
 | |
| 	}{
 | |
| 		{
 | |
| 			"*-0",
 | |
| 			`
 | |
| [1:2] could not find alias "-0"
 | |
| >  1 | *-0
 | |
|         ^
 | |
| `,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.src, func(t *testing.T) {
 | |
| 			var v any
 | |
| 			err := yaml.Unmarshal([]byte(test.src), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("cannot catch decode error")
 | |
| 			}
 | |
| 			actual := "\n" + err.Error()
 | |
| 			if test.expect != actual {
 | |
| 				t.Fatalf("expected: [%s] but got [%s]", test.expect, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_ScientificNotation(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		source string
 | |
| 		value  interface{}
 | |
| 	}{
 | |
| 		{
 | |
| 			"v: 1e3",
 | |
| 			map[string]uint{"v": 1000},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e-3",
 | |
| 			map[string]uint{"v": 0},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e3",
 | |
| 			map[string]int{"v": 1000},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e-3",
 | |
| 			map[string]int{"v": 0},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e3",
 | |
| 			map[string]float32{"v": 1000},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.0e3",
 | |
| 			map[string]float64{"v": 1000},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1e-3",
 | |
| 			map[string]float64{"v": 0.001},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.0e-3",
 | |
| 			map[string]float64{"v": 0.001},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.0e+3",
 | |
| 			map[string]float64{"v": 1000},
 | |
| 		},
 | |
| 		{
 | |
| 			"v: 1.0e+3",
 | |
| 			map[string]float64{"v": 1000},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.source, func(t *testing.T) {
 | |
| 			buf := bytes.NewBufferString(test.source)
 | |
| 			dec := yaml.NewDecoder(buf)
 | |
| 			typ := reflect.ValueOf(test.value).Type()
 | |
| 			value := reflect.New(typ)
 | |
| 			if err := dec.Decode(value.Interface()); err != nil {
 | |
| 				if err == io.EOF {
 | |
| 					return
 | |
| 				}
 | |
| 				t.Fatalf("%s: %+v", test.source, err)
 | |
| 			}
 | |
| 			actual := fmt.Sprintf("%+v", value.Elem().Interface())
 | |
| 			expect := fmt.Sprintf("%+v", test.value)
 | |
| 			if actual != expect {
 | |
| 				t.Fatalf("failed to test [%s], actual=[%s], expect=[%s]", test.source, actual, expect)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_TypeConversionError(t *testing.T) {
 | |
| 	t.Run("type conversion for struct", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			A int
 | |
| 			B uint
 | |
| 			C float32
 | |
| 			D bool
 | |
| 		}
 | |
| 		type U struct {
 | |
| 			*T `yaml:",inline"`
 | |
| 		}
 | |
| 		t.Run("string to int", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`a: str`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go struct field T.A of type int"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("string to uint", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`b: str`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go struct field T.B of type uint"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("string to bool", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`d: str`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go struct field T.D of type bool"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("string to int at inline", func(t *testing.T) {
 | |
| 			var v U
 | |
| 			err := yaml.Unmarshal([]byte(`a: str`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go struct field U.T.A of type int"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 	})
 | |
| 	t.Run("type conversion for array", func(t *testing.T) {
 | |
| 		t.Run("string to int", func(t *testing.T) {
 | |
| 			var v map[string][]int
 | |
| 			err := yaml.Unmarshal([]byte(`v: [A,1,C]`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go value of type int"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if len(v) == 0 || len(v["v"]) == 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 			if v["v"][0] != 1 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("string to int", func(t *testing.T) {
 | |
| 			var v map[string][]int
 | |
| 			err := yaml.Unmarshal([]byte("v:\n - A\n - 1\n - C"), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal string into Go value of type int"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if len(v) == 0 || len(v["v"]) == 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 			if v["v"][0] != 1 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 	})
 | |
| 	t.Run("overflow error", func(t *testing.T) {
 | |
| 		t.Run("negative number to uint", func(t *testing.T) {
 | |
| 			var v map[string]uint
 | |
| 			err := yaml.Unmarshal([]byte("v: -42"), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal -42 into Go value of type uint ( overflow )"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if v["v"] != 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("negative number to uint64", func(t *testing.T) {
 | |
| 			var v map[string]uint64
 | |
| 			err := yaml.Unmarshal([]byte("v: -4294967296"), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal -4294967296 into Go value of type uint64 ( overflow )"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if v["v"] != 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("larger number for int32", func(t *testing.T) {
 | |
| 			var v map[string]int32
 | |
| 			err := yaml.Unmarshal([]byte("v: 4294967297"), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal 4294967297 into Go value of type int32 ( overflow )"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if v["v"] != 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("larger number for int8", func(t *testing.T) {
 | |
| 			var v map[string]int8
 | |
| 			err := yaml.Unmarshal([]byte("v: 128"), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal 128 into Go value of type int8 ( overflow )"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 			if v["v"] != 0 {
 | |
| 				t.Fatal("failed to decode value")
 | |
| 			}
 | |
| 		})
 | |
| 	})
 | |
| 	t.Run("type conversion for time", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			A time.Time
 | |
| 			B time.Duration
 | |
| 		}
 | |
| 		t.Run("int to time", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`a: 123`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal uint64 into Go struct field T.A of type time.Time"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("string to duration", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`b: str`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := `time: invalid duration "str"`
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 		t.Run("int to duration", func(t *testing.T) {
 | |
| 			var v T
 | |
| 			err := yaml.Unmarshal([]byte(`b: 10`), &v)
 | |
| 			if err == nil {
 | |
| 				t.Fatal("expected to error")
 | |
| 			}
 | |
| 			msg := "cannot unmarshal uint64 into Go struct field T.B of type time.Duration"
 | |
| 			if !strings.Contains(err.Error(), msg) {
 | |
| 				t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
 | |
| 			}
 | |
| 		})
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDecoder_AnchorReferenceDirs(t *testing.T) {
 | |
| 	buf := bytes.NewBufferString("a: *a\n")
 | |
| 	dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata"))
 | |
| 	var v struct {
 | |
| 		A struct {
 | |
| 			B int
 | |
| 			C string
 | |
| 		}
 | |
| 	}
 | |
| 	if err := dec.Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.A.B != 1 {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| 	if v.A.C != "hello" {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_AnchorReferenceDirsRecursive(t *testing.T) {
 | |
| 	buf := bytes.NewBufferString("a: *a\n")
 | |
| 	dec := yaml.NewDecoder(
 | |
| 		buf,
 | |
| 		yaml.ReferenceDirs("testdata"),
 | |
| 	)
 | |
| 	var v struct {
 | |
| 		A struct {
 | |
| 			B int
 | |
| 			C string
 | |
| 		}
 | |
| 	}
 | |
| 	if err := dec.Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.A.B != 1 {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| 	if v.A.C != "hello" {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_AnchorFiles(t *testing.T) {
 | |
| 	buf := bytes.NewBufferString("a: *a\n")
 | |
| 	dec := yaml.NewDecoder(buf, yaml.ReferenceFiles("testdata/anchor.yml"))
 | |
| 	var v struct {
 | |
| 		A struct {
 | |
| 			B int
 | |
| 			C string
 | |
| 		}
 | |
| 	}
 | |
| 	if err := dec.Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.A.B != 1 {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| 	if v.A.C != "hello" {
 | |
| 		t.Fatal("failed to decode by reference dirs")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecodeWithMergeKey(t *testing.T) {
 | |
| 	yml := `
 | |
| a: &a
 | |
|   b: 1
 | |
|   c: hello
 | |
| items:
 | |
| - <<: *a
 | |
| - <<: *a
 | |
|   c: world
 | |
| `
 | |
| 	type Item struct {
 | |
| 		B int
 | |
| 		C string
 | |
| 	}
 | |
| 	type T struct {
 | |
| 		Items []*Item
 | |
| 	}
 | |
| 	buf := bytes.NewBufferString(yml)
 | |
| 	dec := yaml.NewDecoder(buf, yaml.AllowDuplicateMapKey())
 | |
| 	var v T
 | |
| 	if err := dec.Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if len(v.Items) != 2 {
 | |
| 		t.Fatal("failed to decode with merge key")
 | |
| 	}
 | |
| 	if v.Items[0].B != 1 || v.Items[0].C != "hello" {
 | |
| 		t.Fatal("failed to decode with merge key")
 | |
| 	}
 | |
| 	if v.Items[1].B != 1 || v.Items[1].C != "world" {
 | |
| 		t.Fatal("failed to decode with merge key")
 | |
| 	}
 | |
| 	t.Run("decode with interface{}", func(t *testing.T) {
 | |
| 		buf := bytes.NewBufferString(yml)
 | |
| 		dec := yaml.NewDecoder(buf)
 | |
| 		var v interface{}
 | |
| 		if err := dec.Decode(&v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		items, _ := v.(map[string]interface{})["items"].([]interface{})
 | |
| 		if len(items) != 2 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		b0 := items[0].(map[string]interface{})["b"]
 | |
| 		if _, ok := b0.(uint64); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if b0.(uint64) != 1 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		c0 := items[0].(map[string]interface{})["c"]
 | |
| 		if _, ok := c0.(string); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if c0.(string) != "hello" {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		b1 := items[1].(map[string]interface{})["b"]
 | |
| 		if _, ok := b1.(uint64); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if b1.(uint64) != 1 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		c1 := items[1].(map[string]interface{})["c"]
 | |
| 		if _, ok := c1.(string); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if c1.(string) != "world" {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("decode with map", func(t *testing.T) {
 | |
| 		var v struct {
 | |
| 			Items []map[string]interface{}
 | |
| 		}
 | |
| 		buf := bytes.NewBufferString(yml)
 | |
| 		dec := yaml.NewDecoder(buf)
 | |
| 		if err := dec.Decode(&v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		if len(v.Items) != 2 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		b0 := v.Items[0]["b"]
 | |
| 		if _, ok := b0.(uint64); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if b0.(uint64) != 1 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		c0 := v.Items[0]["c"]
 | |
| 		if _, ok := c0.(string); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if c0.(string) != "hello" {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		b1 := v.Items[1]["b"]
 | |
| 		if _, ok := b1.(uint64); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if b1.(uint64) != 1 {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		c1 := v.Items[1]["c"]
 | |
| 		if _, ok := c1.(string); !ok {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 		if c1.(string) != "world" {
 | |
| 			t.Fatal("failed to decode with merge key")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDecoder_Inline(t *testing.T) {
 | |
| 	type Base struct {
 | |
| 		A int
 | |
| 		B string
 | |
| 	}
 | |
| 	yml := `---
 | |
| a: 1
 | |
| b: hello
 | |
| c: true
 | |
| `
 | |
| 	var v struct {
 | |
| 		*Base `yaml:",inline"`
 | |
| 		C     bool
 | |
| 	}
 | |
| 	if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.A != 1 {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 	if v.B != "hello" {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 	if !v.C {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 
 | |
| 	t.Run("multiple inline with strict", func(t *testing.T) {
 | |
| 		type Base struct {
 | |
| 			A int
 | |
| 			B string
 | |
| 		}
 | |
| 		type Base2 struct {
 | |
| 			Base *Base `yaml:",inline"`
 | |
| 		}
 | |
| 		yml := `---
 | |
| a: 1
 | |
| b: hello
 | |
| `
 | |
| 		var v struct {
 | |
| 			Base2 *Base2 `yaml:",inline"`
 | |
| 		}
 | |
| 		if err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		if v.Base2.Base.A != 1 {
 | |
| 			t.Fatal("failed to decode with inline key")
 | |
| 		}
 | |
| 		if v.Base2.Base.B != "hello" {
 | |
| 			t.Fatal("failed to decode with inline key")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDecoder_InlineAndConflictKey(t *testing.T) {
 | |
| 	type Base struct {
 | |
| 		A int
 | |
| 		B string
 | |
| 	}
 | |
| 	yml := `---
 | |
| a: 1
 | |
| b: hello
 | |
| c: true
 | |
| `
 | |
| 	var v struct {
 | |
| 		*Base `yaml:",inline"`
 | |
| 		A     int
 | |
| 		C     bool
 | |
| 	}
 | |
| 	if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.A != 1 {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 	if v.B != "hello" {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 	if !v.C {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| 	if v.Base.A != 0 {
 | |
| 		t.Fatal("failed to decode with inline key")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_InlineAndWrongTypeStrict(t *testing.T) {
 | |
| 	type Base struct {
 | |
| 		A int
 | |
| 		B string
 | |
| 	}
 | |
| 	yml := `---
 | |
| a: notanint
 | |
| b: hello
 | |
| c: true
 | |
| `
 | |
| 	var v struct {
 | |
| 		*Base `yaml:",inline"`
 | |
| 		C     bool
 | |
| 	}
 | |
| 	err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v)
 | |
| 	if err == nil {
 | |
| 		t.Fatalf("expected error")
 | |
| 	}
 | |
| 
 | |
| 	//TODO: properly check if errors are colored/have source
 | |
| 	t.Logf("%s", err)
 | |
| 	t.Logf("%s", yaml.FormatError(err, true, false))
 | |
| 	t.Logf("%s", yaml.FormatError(err, false, true))
 | |
| 	t.Logf("%s", yaml.FormatError(err, true, true))
 | |
| }
 | |
| 
 | |
| func TestDecoder_InvalidCases(t *testing.T) {
 | |
| 	const src = `---
 | |
| a:
 | |
| - b
 | |
|   c: d
 | |
| `
 | |
| 	var v struct {
 | |
| 		A []string
 | |
| 	}
 | |
| 	err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v)
 | |
| 	if err == nil {
 | |
| 		t.Fatalf("expected error")
 | |
| 	}
 | |
| 
 | |
| 	if err.Error() != yaml.FormatError(err, false, true) {
 | |
| 		t.Logf("err.Error() = %s", err.Error())
 | |
| 		t.Logf("yaml.FormatError(err, false, true) = %s", yaml.FormatError(err, false, true))
 | |
| 		t.Fatal(`err.Error() should match yaml.FormatError(err, false, true)`)
 | |
| 	}
 | |
| 
 | |
| 	//TODO: properly check if errors are colored/have source
 | |
| 	t.Logf("%s", err)
 | |
| 	t.Logf("%s", yaml.FormatError(err, true, false))
 | |
| 	t.Logf("%s", yaml.FormatError(err, false, true))
 | |
| 	t.Logf("%s", yaml.FormatError(err, true, true))
 | |
| }
 | |
| 
 | |
| func TestDecoder_JSONTags(t *testing.T) {
 | |
| 	var v struct {
 | |
| 		A string `json:"a_json"`               // no YAML tag
 | |
| 		B string `json:"b_json" yaml:"b_yaml"` // both tags
 | |
| 	}
 | |
| 
 | |
| 	const src = `---
 | |
| a_json: a_json_value
 | |
| b_json: b_json_value
 | |
| b_yaml: b_yaml_value
 | |
| `
 | |
| 	if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil {
 | |
| 		t.Fatalf(`parsing should succeed: %s`, err)
 | |
| 	}
 | |
| 
 | |
| 	if v.A != "a_json_value" {
 | |
| 		t.Fatalf("v.A should be `a_json_value`, got `%s`", v.A)
 | |
| 	}
 | |
| 
 | |
| 	if v.B != "b_yaml_value" {
 | |
| 		t.Fatalf("v.B should be `b_yaml_value`, got `%s`", v.B)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_DisallowUnknownField(t *testing.T) {
 | |
| 	t.Run("different level keys with same name", func(t *testing.T) {
 | |
| 		var v struct {
 | |
| 			C Child `yaml:"c"`
 | |
| 		}
 | |
| 		yml := `---
 | |
| b: 1
 | |
| c:
 | |
|   b: 1
 | |
| `
 | |
| 
 | |
| 		err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v)
 | |
| 		if err == nil {
 | |
| 			t.Fatalf("error expected")
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("inline", func(t *testing.T) {
 | |
| 		var v struct {
 | |
| 			*Child `yaml:",inline"`
 | |
| 			A      string `yaml:"a"`
 | |
| 		}
 | |
| 		yml := `---
 | |
| a: a
 | |
| b: 1
 | |
| `
 | |
| 
 | |
| 		if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil {
 | |
| 			t.Fatalf(`parsing should succeed: %s`, err)
 | |
| 		}
 | |
| 		if v.A != "a" {
 | |
| 			t.Fatalf("v.A should be `a`, got `%s`", v.A)
 | |
| 		}
 | |
| 		if v.B != 1 {
 | |
| 			t.Fatalf("v.B should be 1, got %d", v.B)
 | |
| 		}
 | |
| 		if v.C != 0 {
 | |
| 			t.Fatalf("v.C should be 0, got %d", v.C)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("list", func(t *testing.T) {
 | |
| 		type C struct {
 | |
| 			Child `yaml:",inline"`
 | |
| 		}
 | |
| 
 | |
| 		var v struct {
 | |
| 			Children []C `yaml:"children"`
 | |
| 		}
 | |
| 
 | |
| 		yml := `---
 | |
| children:
 | |
| - b: 1
 | |
| - b: 2
 | |
| `
 | |
| 
 | |
| 		if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil {
 | |
| 			t.Fatalf(`parsing should succeed: %s`, err)
 | |
| 		}
 | |
| 
 | |
| 		if len(v.Children) != 2 {
 | |
| 			t.Fatalf(`len(v.Children) should be 2, got %d`, len(v.Children))
 | |
| 		}
 | |
| 
 | |
| 		if v.Children[0].B != 1 {
 | |
| 			t.Fatalf(`v.Children[0].B should be 1, got %d`, v.Children[0].B)
 | |
| 		}
 | |
| 
 | |
| 		if v.Children[1].B != 2 {
 | |
| 			t.Fatalf(`v.Children[1].B should be 2, got %d`, v.Children[1].B)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDecoder_AllowDuplicateMapKey(t *testing.T) {
 | |
| 	yml := `
 | |
| a: b
 | |
| a: c
 | |
| `
 | |
| 	t.Run("map", func(t *testing.T) {
 | |
| 		var v map[string]string
 | |
| 		if err := yaml.NewDecoder(strings.NewReader(yml), yaml.AllowDuplicateMapKey()).Decode(&v); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("struct", func(t *testing.T) {
 | |
| 		var v struct {
 | |
| 			A string
 | |
| 		}
 | |
| 		if err := yaml.NewDecoder(strings.NewReader(yml), yaml.AllowDuplicateMapKey()).Decode(&v); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDecoder_DefaultValues(t *testing.T) {
 | |
| 	v := struct {
 | |
| 		A string `yaml:"a"`
 | |
| 		B string `yaml:"b"`
 | |
| 		c string // private
 | |
| 		D struct {
 | |
| 			E string `yaml:"e"`
 | |
| 			F struct {
 | |
| 				G string `yaml:"g"`
 | |
| 			} `yaml:"f"`
 | |
| 			H struct {
 | |
| 				I string `yaml:"i"`
 | |
| 			} `yaml:",inline"`
 | |
| 		} `yaml:"d"`
 | |
| 		J struct {
 | |
| 			K string `yaml:"k"`
 | |
| 			L struct {
 | |
| 				M string `yaml:"m"`
 | |
| 			} `yaml:"l"`
 | |
| 			N struct {
 | |
| 				O string `yaml:"o"`
 | |
| 			} `yaml:",inline"`
 | |
| 		} `yaml:",inline"`
 | |
| 		P struct {
 | |
| 			Q string `yaml:"q"`
 | |
| 			R struct {
 | |
| 				S string `yaml:"s"`
 | |
| 			} `yaml:"r"`
 | |
| 			T struct {
 | |
| 				U string `yaml:"u"`
 | |
| 			} `yaml:",inline"`
 | |
| 		} `yaml:"p"`
 | |
| 		V struct {
 | |
| 			W string `yaml:"w"`
 | |
| 			X struct {
 | |
| 				Y string `yaml:"y"`
 | |
| 			} `yaml:"x"`
 | |
| 			Z struct {
 | |
| 				Ä string `yaml:"ä"`
 | |
| 			} `yaml:",inline"`
 | |
| 		} `yaml:",inline"`
 | |
| 	}{
 | |
| 		B: "defaultBValue",
 | |
| 		c: "defaultCValue",
 | |
| 	}
 | |
| 
 | |
| 	v.D.E = "defaultEValue"
 | |
| 	v.D.F.G = "defaultGValue"
 | |
| 	v.D.H.I = "defaultIValue"
 | |
| 	v.J.K = "defaultKValue"
 | |
| 	v.J.L.M = "defaultMValue"
 | |
| 	v.J.N.O = "defaultOValue"
 | |
| 	v.P.R.S = "defaultSValue"
 | |
| 	v.P.T.U = "defaultUValue"
 | |
| 	v.V.X.Y = "defaultYValue"
 | |
| 	v.V.Z.Ä = "defaultÄValue"
 | |
| 
 | |
| 	const src = `---
 | |
| a: a_value
 | |
| p:
 | |
|    q: q_value
 | |
| w: w_value
 | |
| `
 | |
| 	if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil {
 | |
| 		t.Fatalf(`parsing should succeed: %s`, err)
 | |
| 	}
 | |
| 	if v.A != "a_value" {
 | |
| 		t.Fatalf("v.A should be `a_value`, got `%s`", v.A)
 | |
| 	}
 | |
| 
 | |
| 	if v.B != "defaultBValue" {
 | |
| 		t.Fatalf("v.B should be `defaultValue`, got `%s`", v.B)
 | |
| 	}
 | |
| 
 | |
| 	if v.c != "defaultCValue" {
 | |
| 		t.Fatalf("v.c should be `defaultCValue`, got `%s`", v.c)
 | |
| 	}
 | |
| 
 | |
| 	if v.D.E != "defaultEValue" {
 | |
| 		t.Fatalf("v.D.E should be `defaultEValue`, got `%s`", v.D.E)
 | |
| 	}
 | |
| 
 | |
| 	if v.D.F.G != "defaultGValue" {
 | |
| 		t.Fatalf("v.D.F.G should be `defaultGValue`, got `%s`", v.D.F.G)
 | |
| 	}
 | |
| 
 | |
| 	if v.D.H.I != "defaultIValue" {
 | |
| 		t.Fatalf("v.D.H.I should be `defaultIValue`, got `%s`", v.D.H.I)
 | |
| 	}
 | |
| 
 | |
| 	if v.J.K != "defaultKValue" {
 | |
| 		t.Fatalf("v.J.K should be `defaultKValue`, got `%s`", v.J.K)
 | |
| 	}
 | |
| 
 | |
| 	if v.J.L.M != "defaultMValue" {
 | |
| 		t.Fatalf("v.J.L.M should be `defaultMValue`, got `%s`", v.J.L.M)
 | |
| 	}
 | |
| 
 | |
| 	if v.J.N.O != "defaultOValue" {
 | |
| 		t.Fatalf("v.J.N.O should be `defaultOValue`, got `%s`", v.J.N.O)
 | |
| 	}
 | |
| 
 | |
| 	if v.P.Q != "q_value" {
 | |
| 		t.Fatalf("v.P.Q should be `q_value`, got `%s`", v.P.Q)
 | |
| 	}
 | |
| 
 | |
| 	if v.P.R.S != "defaultSValue" {
 | |
| 		t.Fatalf("v.P.R.S should be `defaultSValue`, got `%s`", v.P.R.S)
 | |
| 	}
 | |
| 
 | |
| 	if v.P.T.U != "defaultUValue" {
 | |
| 		t.Fatalf("v.P.T.U should be `defaultUValue`, got `%s`", v.P.T.U)
 | |
| 	}
 | |
| 
 | |
| 	if v.V.W != "w_value" {
 | |
| 		t.Fatalf("v.V.W should be `w_value`, got `%s`", v.V.W)
 | |
| 	}
 | |
| 
 | |
| 	if v.V.X.Y != "defaultYValue" {
 | |
| 		t.Fatalf("v.V.X.Y should be `defaultYValue`, got `%s`", v.V.X.Y)
 | |
| 	}
 | |
| 
 | |
| 	if v.V.Z.Ä != "defaultÄValue" {
 | |
| 		t.Fatalf("v.V.Z.Ä should be `defaultÄValue`, got `%s`", v.V.Z.Ä)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func ExampleUnmarshal_yAMLTags() {
 | |
| 	yml := `---
 | |
| foo: 1
 | |
| bar: c
 | |
| A: 2
 | |
| B: d
 | |
| `
 | |
| 	var v struct {
 | |
| 		A int    `yaml:"foo" json:"A"`
 | |
| 		B string `yaml:"bar" json:"B"`
 | |
| 	}
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	fmt.Println(v.A)
 | |
| 	fmt.Println(v.B)
 | |
| 	// OUTPUT:
 | |
| 	// 1
 | |
| 	// c
 | |
| }
 | |
| 
 | |
| type useJSONUnmarshalerTest struct {
 | |
| 	s string
 | |
| }
 | |
| 
 | |
| func (t *useJSONUnmarshalerTest) UnmarshalJSON(b []byte) error {
 | |
| 	s, err := strconv.Unquote(string(b))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	t.s = s
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestDecoder_UseJSONUnmarshaler(t *testing.T) {
 | |
| 	var v useJSONUnmarshalerTest
 | |
| 	if err := yaml.UnmarshalWithOptions([]byte(`"a"`), &v, yaml.UseJSONUnmarshaler()); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if v.s != "a" {
 | |
| 		t.Fatalf("unexpected decoded value: %s", v.s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_CustomUnmarshaler(t *testing.T) {
 | |
| 	t.Run("override struct type", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			Foo string `yaml:"foo"`
 | |
| 		}
 | |
| 		src := []byte(`foo: "bar"`)
 | |
| 		var v T
 | |
| 		if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[T](func(dst *T, b []byte) error {
 | |
| 			if !bytes.Equal(src, b) {
 | |
| 				t.Fatalf("failed to get decode target buffer. expected %q but got %q", src, b)
 | |
| 			}
 | |
| 			var v T
 | |
| 			if err := yaml.Unmarshal(b, &v); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			if v.Foo != "bar" {
 | |
| 				t.Fatal("failed to decode")
 | |
| 			}
 | |
| 			dst.Foo = "bazbaz" // assign another value to target
 | |
| 			return nil
 | |
| 		})); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if v.Foo != "bazbaz" {
 | |
| 			t.Fatalf("failed to switch to custom unmarshaler. got: %v", v.Foo)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("override bytes type", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			Foo []byte `yaml:"foo"`
 | |
| 		}
 | |
| 		src := []byte(`foo: "bar"`)
 | |
| 		var v T
 | |
| 		if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[[]byte](func(dst *[]byte, b []byte) error {
 | |
| 			if !bytes.Equal(b, []byte(`"bar"`)) {
 | |
| 				t.Fatalf("failed to get target buffer: %q", b)
 | |
| 			}
 | |
| 			*dst = []byte("bazbaz")
 | |
| 			return nil
 | |
| 		})); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if !bytes.Equal(v.Foo, []byte("bazbaz")) {
 | |
| 			t.Fatalf("failed to switch to custom unmarshaler. got: %q", v.Foo)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type unmarshalContext struct {
 | |
| 	v int
 | |
| }
 | |
| 
 | |
| func (c *unmarshalContext) UnmarshalYAML(ctx context.Context, b []byte) error {
 | |
| 	v, ok := ctx.Value("k").(int)
 | |
| 	if !ok {
 | |
| 		return errors.New("cannot get valid context")
 | |
| 	}
 | |
| 	if v != 1 {
 | |
| 		return errors.New("cannot get valid context")
 | |
| 	}
 | |
| 	if string(b) != "1" {
 | |
| 		return errors.New("cannot get valid bytes")
 | |
| 	}
 | |
| 	c.v = v
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func Test_UnmarshalerContext(t *testing.T) {
 | |
| 	ctx := context.WithValue(context.Background(), "k", 1)
 | |
| 	var v unmarshalContext
 | |
| 	if err := yaml.UnmarshalContext(ctx, []byte(`1`), &v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if v.v != 1 {
 | |
| 		t.Fatal("cannot call UnmarshalYAML")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_DecodeFromNode(t *testing.T) {
 | |
| 	t.Run("has reference", func(t *testing.T) {
 | |
| 		str := `
 | |
| anchor: &map
 | |
|   text: hello
 | |
| map: *map`
 | |
| 		var buf bytes.Buffer
 | |
| 		dec := yaml.NewDecoder(&buf)
 | |
| 		f, err := parser.ParseBytes([]byte(str), 0)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("failed to parse: %s", err)
 | |
| 		}
 | |
| 		type T struct {
 | |
| 			Map map[string]string
 | |
| 		}
 | |
| 		var v T
 | |
| 		if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil {
 | |
| 			t.Fatalf("failed to decode: %s", err)
 | |
| 		}
 | |
| 		actual := fmt.Sprintf("%+v", v)
 | |
| 		expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}})
 | |
| 		if actual != expect {
 | |
| 			t.Fatalf("actual=[%s], expect=[%s]", actual, expect)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("with reference option", func(t *testing.T) {
 | |
| 		anchor := strings.NewReader(`
 | |
| map: &map
 | |
|   text: hello`)
 | |
| 		var buf bytes.Buffer
 | |
| 		dec := yaml.NewDecoder(&buf, yaml.ReferenceReaders(anchor))
 | |
| 		f, err := parser.ParseBytes([]byte("map: *map"), 0)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("failed to parse: %s", err)
 | |
| 		}
 | |
| 		type T struct {
 | |
| 			Map map[string]string
 | |
| 		}
 | |
| 		var v T
 | |
| 		if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil {
 | |
| 			t.Fatalf("failed to decode: %s", err)
 | |
| 		}
 | |
| 		actual := fmt.Sprintf("%+v", v)
 | |
| 		expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}})
 | |
| 		if actual != expect {
 | |
| 			t.Fatalf("actual=[%s], expect=[%s]", actual, expect)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("value is not pointer", func(t *testing.T) {
 | |
| 		var buf bytes.Buffer
 | |
| 		var v bool
 | |
| 		err := yaml.NewDecoder(&buf).DecodeFromNode(nil, v)
 | |
| 		if !errors.Is(err, yaml.ErrDecodeRequiredPointerType) {
 | |
| 			t.Fatalf("unexpected error: %s", err)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func ExampleUnmarshal_jSONTags() {
 | |
| 	yml := `---
 | |
| foo: 1
 | |
| bar: c
 | |
| `
 | |
| 	var v struct {
 | |
| 		A int    `json:"foo"`
 | |
| 		B string `json:"bar"`
 | |
| 	}
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	fmt.Println(v.A)
 | |
| 	fmt.Println(v.B)
 | |
| 	// OUTPUT:
 | |
| 	// 1
 | |
| 	// c
 | |
| }
 | |
| 
 | |
| func ExampleDecoder_Decode_disallowUnknownField() {
 | |
| 	var v struct {
 | |
| 		A string `yaml:"simple"`
 | |
| 		C string `yaml:"complicated"`
 | |
| 	}
 | |
| 
 | |
| 	const src = `---
 | |
| simple: string
 | |
| unknown: string
 | |
| `
 | |
| 	err := yaml.NewDecoder(strings.NewReader(src), yaml.DisallowUnknownField()).Decode(&v)
 | |
| 	fmt.Printf("%v\n", err)
 | |
| 
 | |
| 	// OUTPUT:
 | |
| 	// [3:1] unknown field "unknown"
 | |
| 	//    1 | ---
 | |
| 	//    2 | simple: string
 | |
| 	// >  3 | unknown: string
 | |
| 	//        ^
 | |
| }
 | |
| 
 | |
| func ExampleNodeToValue() {
 | |
| 	f, err := parser.ParseBytes([]byte("text: node example"), 0)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	var v struct {
 | |
| 		Text string `yaml:"text"`
 | |
| 	}
 | |
| 	if err := yaml.NodeToValue(f.Docs[0].Body, &v); err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	fmt.Println(v.Text)
 | |
| 	// OUTPUT:
 | |
| 	// node example
 | |
| }
 | |
| 
 | |
| type unmarshalableYAMLStringValue string
 | |
| 
 | |
| func (v *unmarshalableYAMLStringValue) UnmarshalYAML(b []byte) error {
 | |
| 	var s string
 | |
| 	if err := yaml.Unmarshal(b, &s); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	*v = unmarshalableYAMLStringValue(s)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type unmarshalableTextStringValue string
 | |
| 
 | |
| func (v *unmarshalableTextStringValue) UnmarshalText(b []byte) error {
 | |
| 	*v = unmarshalableTextStringValue(string(b))
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type unmarshalableStringContainer struct {
 | |
| 	A unmarshalableYAMLStringValue `yaml:"a"`
 | |
| 	B unmarshalableTextStringValue `yaml:"b"`
 | |
| }
 | |
| 
 | |
| func TestUnmarshalableString(t *testing.T) {
 | |
| 	t.Run("empty string", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		yml := `
 | |
| a: ""
 | |
| b: ""
 | |
| `
 | |
| 		var container unmarshalableStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.A != "" {
 | |
| 			t.Fatalf("expected empty string, but %q is set", container.A)
 | |
| 		}
 | |
| 		if container.B != "" {
 | |
| 			t.Fatalf("expected empty string, but %q is set", container.B)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("filled string", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		yml := `
 | |
| a: "aaa"
 | |
| b: "bbb"
 | |
| `
 | |
| 		var container unmarshalableStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.A != "aaa" {
 | |
| 			t.Fatalf("expected \"aaa\", but %q is set", container.A)
 | |
| 		}
 | |
| 		if container.B != "bbb" {
 | |
| 			t.Fatalf("expected \"bbb\", but %q is set", container.B)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("single-quoted string", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		yml := `
 | |
| a: 'aaa'
 | |
| b: 'bbb'
 | |
| `
 | |
| 		var container unmarshalableStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.A != "aaa" {
 | |
| 			t.Fatalf("expected \"aaa\", but %q is set", container.A)
 | |
| 		}
 | |
| 		if container.B != "bbb" {
 | |
| 			t.Fatalf("expected \"aaa\", but %q is set", container.B)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("literal", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		yml := `
 | |
| a: |
 | |
|  a
 | |
|  b
 | |
|  c
 | |
| b: |
 | |
|  a
 | |
|  b
 | |
|  c
 | |
| `
 | |
| 		var container unmarshalableStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.A != "a\nb\nc\n" {
 | |
| 			t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.A)
 | |
| 		}
 | |
| 		if container.B != "a\nb\nc\n" {
 | |
| 			t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.B)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("anchor/alias", func(t *testing.T) {
 | |
| 		yml := `
 | |
| a: &x 1
 | |
| b: *x
 | |
| c: &y hello
 | |
| d: *y
 | |
| `
 | |
| 		var v struct {
 | |
| 			A, B, C, D unmarshalableTextStringValue
 | |
| 		}
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if v.A != "1" {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 		if v.B != "1" {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 		if v.C != "hello" {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 		if v.D != "hello" {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("net.IP", func(t *testing.T) {
 | |
| 		yml := `
 | |
| a: &a 127.0.0.1
 | |
| b: *a
 | |
| `
 | |
| 		var v struct {
 | |
| 			A, B net.IP
 | |
| 		}
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if v.A.String() != net.IPv4(127, 0, 0, 1).String() {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 		if v.B.String() != net.IPv4(127, 0, 0, 1).String() {
 | |
| 			t.Fatal("failed to unmarshal")
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("quoted map keys", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		yml := `
 | |
| a:
 | |
|   "b"  : 2
 | |
|   'c': true
 | |
| `
 | |
| 		var v struct {
 | |
| 			A struct {
 | |
| 				B int
 | |
| 				C bool
 | |
| 			}
 | |
| 		}
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if v.A.B != 2 {
 | |
| 			t.Fatalf("expected a.b to equal 2 but was %d", v.A.B)
 | |
| 		}
 | |
| 		if !v.A.C {
 | |
| 			t.Fatal("expected a.c to be true but was false")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type unmarshalablePtrStringContainer struct {
 | |
| 	V *string `yaml:"value"`
 | |
| }
 | |
| 
 | |
| func TestUnmarshalablePtrString(t *testing.T) {
 | |
| 	t.Run("empty string", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalablePtrStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: ""`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V == nil || *container.V != "" {
 | |
| 			t.Fatalf("expected empty string, but %q is set", *container.V)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("null", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalablePtrStringContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V != (*string)(nil) {
 | |
| 			t.Fatalf("expected nil, but %q is set", *container.V)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type unmarshalableIntValue int
 | |
| 
 | |
| func (v *unmarshalableIntValue) UnmarshalYAML(raw []byte) error {
 | |
| 	i, err := strconv.Atoi(string(raw))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	*v = unmarshalableIntValue(i)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type unmarshalableIntContainer struct {
 | |
| 	V unmarshalableIntValue `yaml:"value"`
 | |
| }
 | |
| 
 | |
| func TestUnmarshalableInt(t *testing.T) {
 | |
| 	t.Run("empty int", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalableIntContainer
 | |
| 		if err := yaml.Unmarshal([]byte(``), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V != 0 {
 | |
| 			t.Fatalf("expected empty int, but %d is set", container.V)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("filled int", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalableIntContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V != 9 {
 | |
| 			t.Fatalf("expected 9, but %d is set", container.V)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("filled number", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalableIntContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V != 9 {
 | |
| 			t.Fatalf("expected 9, but %d is set", container.V)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type unmarshalablePtrIntContainer struct {
 | |
| 	V *int `yaml:"value"`
 | |
| }
 | |
| 
 | |
| func TestUnmarshalablePtrInt(t *testing.T) {
 | |
| 	t.Run("empty int", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalablePtrIntContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: 0`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V == nil || *container.V != 0 {
 | |
| 			t.Fatalf("expected 0, but %q is set", *container.V)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("null", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		var container unmarshalablePtrIntContainer
 | |
| 		if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil {
 | |
| 			t.Fatalf("failed to unmarshal %v", err)
 | |
| 		}
 | |
| 		if container.V != (*int)(nil) {
 | |
| 			t.Fatalf("expected nil, but %q is set", *container.V)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type literalContainer struct {
 | |
| 	v string
 | |
| }
 | |
| 
 | |
| func (c *literalContainer) UnmarshalYAML(v []byte) error {
 | |
| 	var lit string
 | |
| 	if err := yaml.Unmarshal(v, &lit); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	c.v = lit
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestDecode_Literal(t *testing.T) {
 | |
| 	yml := `---
 | |
| value: |
 | |
|   {
 | |
|      "key": "value"
 | |
|   }
 | |
| `
 | |
| 	var v map[string]*literalContainer
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		t.Fatalf("failed to unmarshal %+v", err)
 | |
| 	}
 | |
| 	if v["value"] == nil {
 | |
| 		t.Fatal("failed to unmarshal literal with bytes unmarshaler")
 | |
| 	}
 | |
| 	if v["value"].v == "" {
 | |
| 		t.Fatal("failed to unmarshal literal with bytes unmarshaler")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_UseOrderedMap(t *testing.T) {
 | |
| 	yml := `
 | |
| a: b
 | |
| c: d
 | |
| e:
 | |
|   f: g
 | |
|   h: i
 | |
| j: k
 | |
| `
 | |
| 	var v interface{}
 | |
| 	if err := yaml.NewDecoder(strings.NewReader(yml), yaml.UseOrderedMap()).Decode(&v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if _, ok := v.(yaml.MapSlice); !ok {
 | |
| 		t.Fatalf("failed to convert to ordered map: %T", v)
 | |
| 	}
 | |
| 	bytes, err := yaml.Marshal(v)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	if string(yml) != "\n"+string(bytes) {
 | |
| 		t.Fatalf("expected:[%s] actual:[%s]", string(yml), "\n"+string(bytes))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_Stream(t *testing.T) {
 | |
| 	yml := `
 | |
| ---
 | |
| a: b
 | |
| c: d
 | |
| ---
 | |
| e: f
 | |
| g: h
 | |
| ---
 | |
| i: j
 | |
| k: l
 | |
| `
 | |
| 	dec := yaml.NewDecoder(strings.NewReader(yml))
 | |
| 	values := []map[string]string{}
 | |
| 	for {
 | |
| 		var v map[string]string
 | |
| 		if err := dec.Decode(&v); err != nil {
 | |
| 			if err == io.EOF {
 | |
| 				break
 | |
| 			}
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		values = append(values, v)
 | |
| 	}
 | |
| 	if len(values) != 3 {
 | |
| 		t.Fatal("failed to stream decoding")
 | |
| 	}
 | |
| 	if values[0]["a"] != "b" {
 | |
| 		t.Fatal("failed to stream decoding")
 | |
| 	}
 | |
| 	if values[1]["e"] != "f" {
 | |
| 		t.Fatal("failed to stream decoding")
 | |
| 	}
 | |
| 	if values[2]["i"] != "j" {
 | |
| 		t.Fatal("failed to stream decoding")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type unmarshalYAMLWithAliasString string
 | |
| 
 | |
| func (v *unmarshalYAMLWithAliasString) UnmarshalYAML(b []byte) error {
 | |
| 	var s string
 | |
| 	if err := yaml.Unmarshal(b, &s); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	*v = unmarshalYAMLWithAliasString(s)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type unmarshalYAMLWithAliasMap map[string]interface{}
 | |
| 
 | |
| func (v *unmarshalYAMLWithAliasMap) UnmarshalYAML(b []byte) error {
 | |
| 	var m map[string]interface{}
 | |
| 	if err := yaml.Unmarshal(b, &m); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	*v = unmarshalYAMLWithAliasMap(m)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestDecoder_UnmarshalYAMLWithAlias(t *testing.T) {
 | |
| 	type value struct {
 | |
| 		String unmarshalYAMLWithAliasString
 | |
| 		Map    unmarshalYAMLWithAliasMap
 | |
| 	}
 | |
| 	tests := []struct {
 | |
| 		name          string
 | |
| 		yaml          string
 | |
| 		expectedValue value
 | |
| 		err           string
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "ok",
 | |
| 			yaml: `
 | |
| anchors:
 | |
|  w: &w "\"hello\" \"world\""
 | |
|  map: &x
 | |
|    a: b
 | |
|    c: d
 | |
|    d: *w
 | |
| string: *w
 | |
| map:
 | |
|  <<: *x
 | |
|  e: f
 | |
| `,
 | |
| 			expectedValue: value{
 | |
| 				String: unmarshalYAMLWithAliasString(`"hello" "world"`),
 | |
| 				Map: unmarshalYAMLWithAliasMap(map[string]interface{}{
 | |
| 					"a": "b",
 | |
| 					"c": "d",
 | |
| 					"d": `"hello" "world"`,
 | |
| 					"e": "f",
 | |
| 				}),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "unknown alias",
 | |
| 			yaml: `
 | |
| anchors:
 | |
|  w: &w "\"hello\" \"world\""
 | |
|  map: &x
 | |
|    a: b
 | |
|    c: d
 | |
|    d: *w
 | |
| string: *y
 | |
| map:
 | |
|  <<: *z
 | |
|  e: f
 | |
| `,
 | |
| 			err: `could not find alias "y"`,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.name, func(t *testing.T) {
 | |
| 			var v value
 | |
| 			err := yaml.Unmarshal([]byte(test.yaml), &v)
 | |
| 
 | |
| 			if test.err != "" {
 | |
| 				if err == nil {
 | |
| 					t.Fatal("expected to error")
 | |
| 				}
 | |
| 				if !strings.Contains(err.Error(), test.err) {
 | |
| 					t.Fatalf("expected error message: %s to contain: %s", err.Error(), test.err)
 | |
| 				}
 | |
| 			} else {
 | |
| 				if err != nil {
 | |
| 					t.Fatalf("%+v", err)
 | |
| 				}
 | |
| 				if !reflect.DeepEqual(test.expectedValue, v) {
 | |
| 					t.Fatalf("non matching values:\nexpected[%s]\ngot     [%s]", test.expectedValue, v)
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type unmarshalString string
 | |
| 
 | |
| func (u *unmarshalString) UnmarshalYAML(b []byte) error {
 | |
| 	*u = unmarshalString(string(b))
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type unmarshalList struct {
 | |
| 	v []map[string]unmarshalString
 | |
| }
 | |
| 
 | |
| func (u *unmarshalList) UnmarshalYAML(b []byte) error {
 | |
| 	expected := `
 | |
|  - b: c
 | |
|    d: |
 | |
|      hello
 | |
| 
 | |
|      hello
 | |
|    f: g
 | |
|  - h: i`
 | |
| 	actual := "\n" + string(b)
 | |
| 	if expected != actual {
 | |
| 		return fmt.Errorf("unexpected bytes: expected [%q] but got [%q]", expected, actual)
 | |
| 	}
 | |
| 	var v []map[string]unmarshalString
 | |
| 	if err := yaml.Unmarshal(b, &v); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	u.v = v
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestDecoder_UnmarshalBytesWithSeparatedList(t *testing.T) {
 | |
| 	yml := `
 | |
| a:
 | |
|  - b: c
 | |
|    d: |
 | |
|      hello
 | |
| 
 | |
|      hello
 | |
|    f: g
 | |
|  - h: i
 | |
| `
 | |
| 	var v struct {
 | |
| 		A unmarshalList
 | |
| 	}
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if len(v.A.v) != 2 {
 | |
| 		t.Fatalf("failed to unmarshal %+v", v)
 | |
| 	}
 | |
| 	if len(v.A.v[0]) != 3 {
 | |
| 		t.Fatalf("failed to unmarshal %+v", v.A.v[0])
 | |
| 	}
 | |
| 	if len(v.A.v[1]) != 1 {
 | |
| 		t.Fatalf("failed to unmarshal %+v", v.A.v[1])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_LiteralWithNewLine(t *testing.T) {
 | |
| 	type A struct {
 | |
| 		Node     string `yaml:"b"`
 | |
| 		LastNode string `yaml:"last"`
 | |
| 	}
 | |
| 	tests := []A{
 | |
| 		{
 | |
| 			Node: "hello\nworld",
 | |
| 		},
 | |
| 		{
 | |
| 			Node: "hello\nworld\n",
 | |
| 		},
 | |
| 		{
 | |
| 			LastNode: "hello\nworld",
 | |
| 		},
 | |
| 		{
 | |
| 			LastNode: "hello\nworld\n",
 | |
| 		},
 | |
| 	}
 | |
| 	// struct(want) -> Marshal -> Unmarchal -> struct(got)
 | |
| 	for _, want := range tests {
 | |
| 		bytes, _ := yaml.Marshal(want)
 | |
| 		got := A{}
 | |
| 		if err := yaml.Unmarshal(bytes, &got); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if want.Node != got.Node {
 | |
| 			t.Fatalf("expected:%q but got %q", want.Node, got.Node)
 | |
| 		}
 | |
| 		if want.LastNode != got.LastNode {
 | |
| 			t.Fatalf("expected:%q but got %q", want.LastNode, got.LastNode)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_TabCharacterAtRight(t *testing.T) {
 | |
| 	yml := `
 | |
| - a: [2 , 2] 			
 | |
|   b: [2 , 2] 			
 | |
|   c: [2 , 2]`
 | |
| 	var v []map[string][]int
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if len(v) != 1 {
 | |
| 		t.Fatalf("failed to unmarshal %+v", v)
 | |
| 	}
 | |
| 	if len(v[0]) != 3 {
 | |
| 		t.Fatalf("failed to unmarshal %+v", v)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_Canonical(t *testing.T) {
 | |
| 	yml := `
 | |
| !!map {
 | |
|   ? !!str "explicit":!!str "entry",
 | |
|   ? !!str "implicit" : !!str "entry",
 | |
|   ? !!null "" : !!null "",
 | |
| }
 | |
| `
 | |
| 	var v interface{}
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		t.Fatalf("%+v", err)
 | |
| 	}
 | |
| 	m, ok := v.(map[string]interface{})
 | |
| 	if !ok {
 | |
| 		t.Fatalf("failed to decode canonical yaml: %+v", v)
 | |
| 	}
 | |
| 	if m["explicit"] != "entry" {
 | |
| 		t.Fatalf("failed to decode canonical yaml: %+v", m)
 | |
| 	}
 | |
| 	if m["implicit"] != "entry" {
 | |
| 		t.Fatalf("failed to decode canonical yaml: %+v", m)
 | |
| 	}
 | |
| 	if m["null"] != nil {
 | |
| 		t.Fatalf("failed to decode canonical yaml: %+v", m)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_DecodeFromFile(t *testing.T) {
 | |
| 	yml := `
 | |
| a: b
 | |
| c: d
 | |
| `
 | |
| 	file, err := parser.ParseBytes([]byte(yml), 0)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	var v map[string]string
 | |
| 	if err := yaml.NewDecoder(file).Decode(&v); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if len(v) != 2 {
 | |
| 		t.Fatal("failed to decode from ast.File")
 | |
| 	}
 | |
| 	if v["a"] != "b" {
 | |
| 		t.Fatal("failed to decode from ast.File")
 | |
| 	}
 | |
| 	if v["c"] != "d" {
 | |
| 		t.Fatal("failed to decode from ast.File")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoder_DecodeWithNode(t *testing.T) {
 | |
| 	t.Run("abstract node", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			Text ast.Node `yaml:"text"`
 | |
| 		}
 | |
| 		var v T
 | |
| 		if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		expected := "hello"
 | |
| 		got := v.Text.String()
 | |
| 		if expected != got {
 | |
| 			t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("concrete node", func(t *testing.T) {
 | |
| 		type T struct {
 | |
| 			Text *ast.StringNode `yaml:"text"`
 | |
| 		}
 | |
| 		var v T
 | |
| 		if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		expected := "hello"
 | |
| 		got := v.Text.String()
 | |
| 		if expected != got {
 | |
| 			t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestRoundtripAnchorAlias(t *testing.T) {
 | |
| 	t.Run("irreversible", func(t *testing.T) {
 | |
| 		type foo struct {
 | |
| 			K1 string
 | |
| 			K2 string
 | |
| 		}
 | |
| 
 | |
| 		type bar struct {
 | |
| 			K1 string
 | |
| 			K3 string
 | |
| 		}
 | |
| 
 | |
| 		type doc struct {
 | |
| 			Foo foo
 | |
| 			Bar bar
 | |
| 		}
 | |
| 		yml := `
 | |
| foo:
 | |
|  <<: &test-anchor
 | |
|    k1: "One"
 | |
|  k2: "Two"
 | |
| 
 | |
| bar:
 | |
|  <<: *test-anchor
 | |
|  k3: "Three"
 | |
| `
 | |
| 		var v doc
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		bytes, err := yaml.Marshal(v)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		expected := `
 | |
| foo:
 | |
|   k1: One
 | |
|   k2: Two
 | |
| bar:
 | |
|   k1: One
 | |
|   k3: Three
 | |
| `
 | |
| 		got := "\n" + string(bytes)
 | |
| 		if expected != got {
 | |
| 			t.Fatalf("expected:[%s] but got [%s]", expected, got)
 | |
| 		}
 | |
| 	})
 | |
| 	t.Run("reversible", func(t *testing.T) {
 | |
| 		type TestAnchor struct {
 | |
| 			K1 string
 | |
| 		}
 | |
| 		type foo struct {
 | |
| 			*TestAnchor `yaml:",inline,alias"`
 | |
| 			K2          string
 | |
| 		}
 | |
| 		type bar struct {
 | |
| 			*TestAnchor `yaml:",inline,alias"`
 | |
| 			K3          string
 | |
| 		}
 | |
| 		type doc struct {
 | |
| 			TestAnchor *TestAnchor `yaml:"test-anchor,anchor"`
 | |
| 			Foo        foo
 | |
| 			Bar        bar
 | |
| 		}
 | |
| 		yml := `
 | |
| test-anchor: &test-anchor
 | |
|   k1: One
 | |
| foo:
 | |
|   <<: *test-anchor
 | |
|   k2: Two
 | |
| bar:
 | |
|   <<: *test-anchor
 | |
|   k3: Three
 | |
| `
 | |
| 		var v doc
 | |
| 		if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		bytes, err := yaml.Marshal(v)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("%+v", err)
 | |
| 		}
 | |
| 		got := "\n" + string(bytes)
 | |
| 		if yml != got {
 | |
| 			t.Fatalf("expected:[%s] but got [%s]", yml, got)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestUnmarshalMapSliceParallel(t *testing.T) {
 | |
| 	content := `
 | |
| steps:
 | |
|   req0:
 | |
|     desc: Get /users/1
 | |
|     req:
 | |
|       /users/1:
 | |
|         get: nil
 | |
|     test: |
 | |
|       current.res.status == 200
 | |
|   req1:
 | |
|     desc: Get /private
 | |
|     req:
 | |
|       /private:
 | |
|         get: nil
 | |
|     test: |
 | |
|       current.res.status == 403
 | |
|   req2:
 | |
|     desc: Get /users
 | |
|     req:
 | |
|       /users:
 | |
|         get: nil
 | |
|     test: |
 | |
|       current.res.status == 200
 | |
| `
 | |
| 	type mappedSteps struct {
 | |
| 		Steps yaml.MapSlice `yaml:"steps,omitempty"`
 | |
| 	}
 | |
| 	for i := 0; i < 100; i++ {
 | |
| 		t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			for i := 0; i < 10; i++ {
 | |
| 				m := mappedSteps{
 | |
| 					Steps: yaml.MapSlice{},
 | |
| 				}
 | |
| 				if err := yaml.Unmarshal([]byte(content), &m); err != nil {
 | |
| 					t.Fatal(err)
 | |
| 				}
 | |
| 				for _, s := range m.Steps {
 | |
| 					_, ok := s.Value.(map[string]interface{})
 | |
| 					if !ok {
 | |
| 						t.Fatal("unexpected error")
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSameNameInineStruct(t *testing.T) {
 | |
| 	type X struct {
 | |
| 		X float64 `yaml:"x"`
 | |
| 	}
 | |
| 
 | |
| 	type T struct {
 | |
| 		X `yaml:",inline"`
 | |
| 	}
 | |
| 
 | |
| 	var v T
 | |
| 	if err := yaml.Unmarshal([]byte(`x: 0.7`), &v); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if fmt.Sprint(v.X.X) != "0.7" {
 | |
| 		t.Fatalf("failed to decode")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type unmarshableMapKey struct {
 | |
| 	Key string
 | |
| }
 | |
| 
 | |
| func (mk *unmarshableMapKey) UnmarshalYAML(b []byte) error {
 | |
| 	mk.Key = string(b)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestMapKeyCustomUnmarshaler(t *testing.T) {
 | |
| 	var m map[unmarshableMapKey]string
 | |
| 	if err := yaml.Unmarshal([]byte(`key: value`), &m); err != nil {
 | |
| 		t.Fatalf("failed to unmarshal %v", err)
 | |
| 	}
 | |
| 	if len(m) != 1 {
 | |
| 		t.Fatalf("expected 1 element in map, but got %d", len(m))
 | |
| 	}
 | |
| 	val, ok := m[unmarshableMapKey{Key: "key"}]
 | |
| 	if !ok {
 | |
| 		t.Fatal("expected to have element 'key' in map")
 | |
| 	}
 | |
| 	if val != "value" {
 | |
| 		t.Fatalf("expected to have value \"value\", but got %q", val)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecoderPreservesDefaultValues(t *testing.T) {
 | |
| 	type nested struct {
 | |
| 		Val string `yaml:"val"`
 | |
| 	}
 | |
| 
 | |
| 	type test struct {
 | |
| 		First   string `yaml:"first"`
 | |
| 		Default nested `yaml:"nested"`
 | |
| 	}
 | |
| 
 | |
| 	yml := `
 | |
| first: "Test"
 | |
| nested:
 | |
|   # Just some comment here
 | |
| #  val: "default"
 | |
| `
 | |
| 	v := test{Default: nested{Val: "default"}}
 | |
| 	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if v.Default.Val != "default" {
 | |
| 		t.Fatal("decoder doesn't preserve struct defaults")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecodeError(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name   string
 | |
| 		source string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "duplicated map key name with anchor-alias",
 | |
| 			source: "&0: *0\n*0:\n*0:",
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.name, func(t *testing.T) {
 | |
| 			var v any
 | |
| 			if err := yaml.Unmarshal([]byte(test.source), &v); err == nil {
 | |
| 				t.Fatal("cannot catch decode error")
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
