| package yaml_test |
| |
| import ( |
| "errors" |
| . "gopkg.in/check.v1" |
| "gopkg.in/yaml.v2" |
| "math" |
| "net" |
| "reflect" |
| "strings" |
| "time" |
| ) |
| |
| var unmarshalIntTest = 123 |
| |
| var unmarshalTests = []struct { |
| data string |
| value interface{} |
| }{ |
| { |
| "", |
| &struct{}{}, |
| }, { |
| "{}", &struct{}{}, |
| }, { |
| "v: hi", |
| map[string]string{"v": "hi"}, |
| }, { |
| "v: hi", map[string]interface{}{"v": "hi"}, |
| }, { |
| "v: true", |
| map[string]string{"v": "true"}, |
| }, { |
| "v: true", |
| map[string]interface{}{"v": true}, |
| }, { |
| "v: 10", |
| map[string]interface{}{"v": 10}, |
| }, { |
| "v: 0b10", |
| map[string]interface{}{"v": 2}, |
| }, { |
| "v: 0xA", |
| map[string]interface{}{"v": 10}, |
| }, { |
| "v: 4294967296", |
| map[string]int64{"v": 4294967296}, |
| }, { |
| "v: 0.1", |
| map[string]interface{}{"v": 0.1}, |
| }, { |
| "v: .1", |
| map[string]interface{}{"v": 0.1}, |
| }, { |
| "v: .Inf", |
| map[string]interface{}{"v": math.Inf(+1)}, |
| }, { |
| "v: -.Inf", |
| map[string]interface{}{"v": math.Inf(-1)}, |
| }, { |
| "v: -10", |
| map[string]interface{}{"v": -10}, |
| }, { |
| "v: -.1", |
| map[string]interface{}{"v": -0.1}, |
| }, |
| |
| // Simple values. |
| { |
| "123", |
| &unmarshalIntTest, |
| }, |
| |
| // Floats from spec |
| { |
| "canonical: 6.8523e+5", |
| map[string]interface{}{"canonical": 6.8523e+5}, |
| }, { |
| "expo: 685.230_15e+03", |
| map[string]interface{}{"expo": 685.23015e+03}, |
| }, { |
| "fixed: 685_230.15", |
| map[string]interface{}{"fixed": 685230.15}, |
| }, { |
| "neginf: -.inf", |
| map[string]interface{}{"neginf": math.Inf(-1)}, |
| }, { |
| "fixed: 685_230.15", |
| map[string]float64{"fixed": 685230.15}, |
| }, |
| //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported |
| //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. |
| |
| // Bools from spec |
| { |
| "canonical: y", |
| map[string]interface{}{"canonical": true}, |
| }, { |
| "answer: NO", |
| map[string]interface{}{"answer": false}, |
| }, { |
| "logical: True", |
| map[string]interface{}{"logical": true}, |
| }, { |
| "option: on", |
| map[string]interface{}{"option": true}, |
| }, { |
| "option: on", |
| map[string]bool{"option": true}, |
| }, |
| // Ints from spec |
| { |
| "canonical: 685230", |
| map[string]interface{}{"canonical": 685230}, |
| }, { |
| "decimal: +685_230", |
| map[string]interface{}{"decimal": 685230}, |
| }, { |
| "octal: 02472256", |
| map[string]interface{}{"octal": 685230}, |
| }, { |
| "hexa: 0x_0A_74_AE", |
| map[string]interface{}{"hexa": 685230}, |
| }, { |
| "bin: 0b1010_0111_0100_1010_1110", |
| map[string]interface{}{"bin": 685230}, |
| }, { |
| "bin: -0b101010", |
| map[string]interface{}{"bin": -42}, |
| }, { |
| "decimal: +685_230", |
| map[string]int{"decimal": 685230}, |
| }, |
| |
| //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported |
| |
| // Nulls from spec |
| { |
| "empty:", |
| map[string]interface{}{"empty": nil}, |
| }, { |
| "canonical: ~", |
| map[string]interface{}{"canonical": nil}, |
| }, { |
| "english: null", |
| map[string]interface{}{"english": nil}, |
| }, { |
| "~: null key", |
| map[interface{}]string{nil: "null key"}, |
| }, { |
| "empty:", |
| map[string]*bool{"empty": nil}, |
| }, |
| |
| // Flow sequence |
| { |
| "seq: [A,B]", |
| map[string]interface{}{"seq": []interface{}{"A", "B"}}, |
| }, { |
| "seq: [A,B,C,]", |
| map[string][]string{"seq": []string{"A", "B", "C"}}, |
| }, { |
| "seq: [A,1,C]", |
| map[string][]string{"seq": []string{"A", "1", "C"}}, |
| }, { |
| "seq: [A,1,C]", |
| map[string][]int{"seq": []int{1}}, |
| }, { |
| "seq: [A,1,C]", |
| map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, |
| }, |
| // Block sequence |
| { |
| "seq:\n - A\n - B", |
| map[string]interface{}{"seq": []interface{}{"A", "B"}}, |
| }, { |
| "seq:\n - A\n - B\n - C", |
| map[string][]string{"seq": []string{"A", "B", "C"}}, |
| }, { |
| "seq:\n - A\n - 1\n - C", |
| map[string][]string{"seq": []string{"A", "1", "C"}}, |
| }, { |
| "seq:\n - A\n - 1\n - C", |
| map[string][]int{"seq": []int{1}}, |
| }, { |
| "seq:\n - A\n - 1\n - C", |
| map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, |
| }, |
| |
| // Literal block scalar |
| { |
| "scalar: | # Comment\n\n literal\n\n \ttext\n\n", |
| map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, |
| }, |
| |
| // Folded block scalar |
| { |
| "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", |
| map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, |
| }, |
| |
| // Map inside interface with no type hints. |
| { |
| "a: {b: c}", |
| map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, |
| }, |
| |
| // 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 *struct{ B string } }{&struct{ B string }{"c"}}, |
| }, { |
| "a: {b: c}", |
| &struct{ A map[string]string }{map[string]string{"b": "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", |
| &struct{ B int }{0}, |
| }, { |
| "a: 1", |
| &struct { |
| B int "a" |
| }{1}, |
| }, { |
| "a: y", |
| &struct{ A bool }{true}, |
| }, |
| |
| // Some cross type conversions |
| { |
| "v: 42", |
| map[string]uint{"v": 42}, |
| }, { |
| "v: -42", |
| map[string]uint{}, |
| }, { |
| "v: 4294967296", |
| map[string]uint64{"v": 4294967296}, |
| }, { |
| "v: -4294967296", |
| map[string]uint64{}, |
| }, |
| |
| // int |
| { |
| "int_max: 2147483647", |
| map[string]int{"int_max": math.MaxInt32}, |
| }, |
| { |
| "int_min: -2147483648", |
| map[string]int{"int_min": math.MinInt32}, |
| }, |
| { |
| "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 |
| map[string]int{}, |
| }, |
| |
| // int64 |
| { |
| "int64_max: 9223372036854775807", |
| map[string]int64{"int64_max": math.MaxInt64}, |
| }, |
| { |
| "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", |
| map[string]int64{"int64_max_base2": math.MaxInt64}, |
| }, |
| { |
| "int64_min: -9223372036854775808", |
| map[string]int64{"int64_min": math.MinInt64}, |
| }, |
| { |
| "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", |
| map[string]int64{"int64_neg_base2": -math.MaxInt64}, |
| }, |
| { |
| "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 |
| map[string]int64{}, |
| }, |
| |
| // uint |
| { |
| "uint_min: 0", |
| map[string]uint{"uint_min": 0}, |
| }, |
| { |
| "uint_max: 4294967295", |
| map[string]uint{"uint_max": math.MaxUint32}, |
| }, |
| { |
| "uint_underflow: -1", |
| map[string]uint{}, |
| }, |
| |
| // uint64 |
| { |
| "uint64_min: 0", |
| map[string]uint{"uint64_min": 0}, |
| }, |
| { |
| "uint64_max: 18446744073709551615", |
| map[string]uint64{"uint64_max": math.MaxUint64}, |
| }, |
| { |
| "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", |
| map[string]uint64{"uint64_max_base2": math.MaxUint64}, |
| }, |
| { |
| "uint64_maxint64: 9223372036854775807", |
| map[string]uint64{"uint64_maxint64": math.MaxInt64}, |
| }, |
| { |
| "uint64_underflow: -1", |
| map[string]uint64{}, |
| }, |
| |
| // float32 |
| { |
| "float32_max: 3.40282346638528859811704183484516925440e+38", |
| map[string]float32{"float32_max": math.MaxFloat32}, |
| }, |
| { |
| "float32_nonzero: 1.401298464324817070923729583289916131280e-45", |
| map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32}, |
| }, |
| { |
| "float32_maxuint64: 18446744073709551615", |
| map[string]float32{"float32_maxuint64": float32(math.MaxUint64)}, |
| }, |
| { |
| "float32_maxuint64+1: 18446744073709551616", |
| map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)}, |
| }, |
| |
| // float64 |
| { |
| "float64_max: 1.797693134862315708145274237317043567981e+308", |
| map[string]float64{"float64_max": math.MaxFloat64}, |
| }, |
| { |
| "float64_nonzero: 4.940656458412465441765687928682213723651e-324", |
| map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64}, |
| }, |
| { |
| "float64_maxuint64: 18446744073709551615", |
| map[string]float64{"float64_maxuint64": float64(math.MaxUint64)}, |
| }, |
| { |
| "float64_maxuint64+1: 18446744073709551616", |
| map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)}, |
| }, |
| |
| // Overflow cases. |
| { |
| "v: 4294967297", |
| map[string]int32{}, |
| }, { |
| "v: 128", |
| map[string]int8{}, |
| }, |
| |
| // Quoted values. |
| { |
| "'1': '\"2\"'", |
| map[interface{}]interface{}{"1": "\"2\""}, |
| }, { |
| "v:\n- A\n- 'B\n\n C'\n", |
| map[string][]string{"v": []string{"A", "B\nC"}}, |
| }, |
| |
| // Explicit tags. |
| { |
| "v: !!float '1.1'", |
| map[string]interface{}{"v": 1.1}, |
| }, { |
| "v: !!null ''", |
| map[string]interface{}{"v": nil}, |
| }, { |
| "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", |
| map[string]interface{}{"v": 1}, |
| }, |
| |
| // 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", |
| &struct { |
| A, B struct { |
| C int |
| } |
| }{struct{ C int }{1}, struct{ C int }{1}}, |
| }, { |
| "a: &a [1, 2]\nb: *a", |
| &struct{ B []int }{[]int{1, 2}}, |
| }, { |
| "b: *a\na: &a {c: 1}", |
| &struct { |
| A, B struct { |
| C int |
| } |
| }{struct{ C int }{1}, struct{ C int }{1}}, |
| }, |
| |
| // Bug #1133337 |
| { |
| "foo: ''", |
| map[string]*string{"foo": new(string)}, |
| }, { |
| "foo: null", |
| map[string]string{"foo": ""}, |
| }, { |
| "foo: null", |
| map[string]interface{}{"foo": nil}, |
| }, |
| |
| // Ignored field |
| { |
| "a: 1\nb: 2\n", |
| &struct { |
| A int |
| B int "-" |
| }{1, 0}, |
| }, |
| |
| // Bug #1191981 |
| { |
| "" + |
| "%YAML 1.1\n" + |
| "--- !!str\n" + |
| `"Generic line break (no glyph)\n\` + "\n" + |
| ` Generic line break (glyphed)\n\` + "\n" + |
| ` Line separator\u2028\` + "\n" + |
| ` Paragraph separator\u2029"` + "\n", |
| "" + |
| "Generic line break (no glyph)\n" + |
| "Generic line break (glyphed)\n" + |
| "Line separator\u2028Paragraph separator\u2029", |
| }, |
| |
| // Struct inlining |
| { |
| "a: 1\nb: 2\nc: 3\n", |
| &struct { |
| A int |
| C inlineB `yaml:",inline"` |
| }{1, inlineB{2, inlineC{3}}}, |
| }, |
| |
| // Map inlining |
| { |
| "a: 1\nb: 2\nc: 3\n", |
| &struct { |
| A int |
| C map[string]int `yaml:",inline"` |
| }{1, map[string]int{"b": 2, "c": 3}}, |
| }, |
| |
| // bug 1243827 |
| { |
| "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"}, |
| }, |
| |
| // Duration |
| { |
| "a: 3s", |
| map[string]time.Duration{"a": 3 * time.Second}, |
| }, |
| |
| // Issue #24. |
| { |
| "a: <foo>", |
| map[string]string{"a": "<foo>"}, |
| }, |
| |
| // Base 60 floats are obsolete and unsupported. |
| { |
| "a: 1:1\n", |
| map[string]string{"a": "1:1"}, |
| }, |
| |
| // Binary data. |
| { |
| "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)}, |
| }, { |
| "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", |
| map[string]string{"a": strings.Repeat("\x00", 52)}, |
| }, |
| |
| // Ordered maps. |
| { |
| "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", |
| &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, |
| }, |
| |
| // Issue #39. |
| { |
| "a:\n b:\n c: d\n", |
| map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}}, |
| }, |
| |
| // Custom map type. |
| { |
| "a: {b: c}", |
| M{"a": M{"b": "c"}}, |
| }, |
| |
| // Support encoding.TextUnmarshaler. |
| { |
| "a: 1.2.3.4\n", |
| map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, |
| }, |
| { |
| "a: 2015-02-24T18:19:39Z\n", |
| map[string]time.Time{"a": time.Unix(1424801979, 0)}, |
| }, |
| |
| // Encode empty lists as zero-length slices. |
| { |
| "a: []", |
| &struct{ A []int }{[]int{}}, |
| }, |
| |
| // UTF-16-LE |
| { |
| "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", |
| M{"ñoño": "very yes"}, |
| }, |
| // UTF-16-LE with surrogate. |
| { |
| "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", |
| M{"ñoño": "very yes 🟔"}, |
| }, |
| |
| // UTF-16-BE |
| { |
| "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", |
| M{"ñoño": "very yes"}, |
| }, |
| // UTF-16-BE with surrogate. |
| { |
| "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", |
| M{"ñoño": "very yes 🟔"}, |
| }, |
| } |
| |
| type M map[interface{}]interface{} |
| |
| type inlineB struct { |
| B int |
| inlineC `yaml:",inline"` |
| } |
| |
| type inlineC struct { |
| C int |
| } |
| |
| func (s *S) TestUnmarshal(c *C) { |
| for _, item := range unmarshalTests { |
| t := reflect.ValueOf(item.value).Type() |
| var value interface{} |
| switch t.Kind() { |
| case reflect.Map: |
| value = reflect.MakeMap(t).Interface() |
| case reflect.String: |
| value = reflect.New(t).Interface() |
| case reflect.Ptr: |
| value = reflect.New(t.Elem()).Interface() |
| default: |
| c.Fatalf("missing case for %s", t) |
| } |
| err := yaml.Unmarshal([]byte(item.data), value) |
| if _, ok := err.(*yaml.TypeError); !ok { |
| c.Assert(err, IsNil) |
| } |
| if t.Kind() == reflect.String { |
| c.Assert(*value.(*string), Equals, item.value) |
| } else { |
| c.Assert(value, DeepEquals, item.value) |
| } |
| } |
| } |
| |
| func (s *S) TestUnmarshalNaN(c *C) { |
| value := map[string]interface{}{} |
| err := yaml.Unmarshal([]byte("notanum: .NaN"), &value) |
| c.Assert(err, IsNil) |
| c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) |
| } |
| |
| var unmarshalErrorTests = []struct { |
| data, error string |
| }{ |
| {"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"}, |
| {"v: [A,", "yaml: line 1: did not find expected node content"}, |
| {"v:\n- [A,", "yaml: line 2: did not find expected node content"}, |
| {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, |
| {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, |
| {"value: -", "yaml: block sequence entries are not allowed in this context"}, |
| {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, |
| {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, |
| {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, |
| } |
| |
| func (s *S) TestUnmarshalErrors(c *C) { |
| for _, item := range unmarshalErrorTests { |
| var value interface{} |
| err := yaml.Unmarshal([]byte(item.data), &value) |
| c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) |
| } |
| } |
| |
| var unmarshalerTests = []struct { |
| data, tag string |
| value interface{} |
| }{ |
| {"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}}, |
| {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, |
| {"_: 10", "!!int", 10}, |
| {"_: null", "!!null", nil}, |
| {`_: BAR!`, "!!str", "BAR!"}, |
| {`_: "BAR!"`, "!!str", "BAR!"}, |
| {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, |
| } |
| |
| var unmarshalerResult = map[int]error{} |
| |
| type unmarshalerType struct { |
| value interface{} |
| } |
| |
| func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error { |
| if err := unmarshal(&o.value); err != nil { |
| return err |
| } |
| if i, ok := o.value.(int); ok { |
| if result, ok := unmarshalerResult[i]; ok { |
| return result |
| } |
| } |
| return nil |
| } |
| |
| type unmarshalerPointer struct { |
| Field *unmarshalerType "_" |
| } |
| |
| type unmarshalerValue struct { |
| Field unmarshalerType "_" |
| } |
| |
| func (s *S) TestUnmarshalerPointerField(c *C) { |
| for _, item := range unmarshalerTests { |
| obj := &unmarshalerPointer{} |
| err := yaml.Unmarshal([]byte(item.data), obj) |
| c.Assert(err, IsNil) |
| if item.value == nil { |
| c.Assert(obj.Field, IsNil) |
| } else { |
| c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) |
| c.Assert(obj.Field.value, DeepEquals, item.value) |
| } |
| } |
| } |
| |
| func (s *S) TestUnmarshalerValueField(c *C) { |
| for _, item := range unmarshalerTests { |
| obj := &unmarshalerValue{} |
| err := yaml.Unmarshal([]byte(item.data), obj) |
| c.Assert(err, IsNil) |
| c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) |
| c.Assert(obj.Field.value, DeepEquals, item.value) |
| } |
| } |
| |
| func (s *S) TestUnmarshalerWholeDocument(c *C) { |
| obj := &unmarshalerType{} |
| err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj) |
| c.Assert(err, IsNil) |
| value, ok := obj.value.(map[interface{}]interface{}) |
| c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value)) |
| c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value) |
| } |
| |
| func (s *S) TestUnmarshalerTypeError(c *C) { |
| unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} |
| unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} |
| defer func() { |
| delete(unmarshalerResult, 2) |
| delete(unmarshalerResult, 4) |
| }() |
| |
| type T struct { |
| Before int |
| After int |
| M map[string]*unmarshalerType |
| } |
| var v T |
| data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` |
| err := yaml.Unmarshal([]byte(data), &v) |
| c.Assert(err, ErrorMatches, ""+ |
| "yaml: unmarshal errors:\n"+ |
| " line 1: cannot unmarshal !!str `A` into int\n"+ |
| " foo\n"+ |
| " bar\n"+ |
| " line 1: cannot unmarshal !!str `B` into int") |
| c.Assert(v.M["abc"], NotNil) |
| c.Assert(v.M["def"], IsNil) |
| c.Assert(v.M["ghi"], NotNil) |
| c.Assert(v.M["jkl"], IsNil) |
| |
| c.Assert(v.M["abc"].value, Equals, 1) |
| c.Assert(v.M["ghi"].value, Equals, 3) |
| } |
| |
| type proxyTypeError struct{} |
| |
| func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error { |
| var s string |
| var a int32 |
| var b int64 |
| if err := unmarshal(&s); err != nil { |
| panic(err) |
| } |
| if s == "a" { |
| if err := unmarshal(&b); err == nil { |
| panic("should have failed") |
| } |
| return unmarshal(&a) |
| } |
| if err := unmarshal(&a); err == nil { |
| panic("should have failed") |
| } |
| return unmarshal(&b) |
| } |
| |
| func (s *S) TestUnmarshalerTypeErrorProxying(c *C) { |
| type T struct { |
| Before int |
| After int |
| M map[string]*proxyTypeError |
| } |
| var v T |
| data := `{before: A, m: {abc: a, def: b}, after: B}` |
| err := yaml.Unmarshal([]byte(data), &v) |
| c.Assert(err, ErrorMatches, ""+ |
| "yaml: unmarshal errors:\n"+ |
| " line 1: cannot unmarshal !!str `A` into int\n"+ |
| " line 1: cannot unmarshal !!str `a` into int32\n"+ |
| " line 1: cannot unmarshal !!str `b` into int64\n"+ |
| " line 1: cannot unmarshal !!str `B` into int") |
| } |
| |
| type failingUnmarshaler struct{} |
| |
| var failingErr = errors.New("failingErr") |
| |
| func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { |
| return failingErr |
| } |
| |
| func (s *S) TestUnmarshalerError(c *C) { |
| err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{}) |
| c.Assert(err, Equals, failingErr) |
| } |
| |
| type sliceUnmarshaler []int |
| |
| func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { |
| var slice []int |
| err := unmarshal(&slice) |
| if err == nil { |
| *su = slice |
| return nil |
| } |
| |
| var intVal int |
| err = unmarshal(&intVal) |
| if err == nil { |
| *su = []int{intVal} |
| return nil |
| } |
| |
| return err |
| } |
| |
| func (s *S) TestUnmarshalerRetry(c *C) { |
| var su sliceUnmarshaler |
| err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su) |
| c.Assert(err, IsNil) |
| c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3})) |
| |
| err = yaml.Unmarshal([]byte("1"), &su) |
| c.Assert(err, IsNil) |
| c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1})) |
| } |
| |
| // From http://yaml.org/type/merge.html |
| var mergeTests = ` |
| anchors: |
| list: |
| - &CENTER { "x": 1, "y": 2 } |
| - &LEFT { "x": 0, "y": 2 } |
| - &BIG { "r": 10 } |
| - &SMALL { "r": 1 } |
| |
| # All the following maps are equal: |
| |
| plain: |
| # Explicit keys |
| "x": 1 |
| "y": 2 |
| "r": 10 |
| label: center/big |
| |
| mergeOne: |
| # Merge one map |
| << : *CENTER |
| "r": 10 |
| label: center/big |
| |
| mergeMultiple: |
| # Merge multiple maps |
| << : [ *CENTER, *BIG ] |
| label: center/big |
| |
| override: |
| # Override |
| << : [ *BIG, *LEFT, *SMALL ] |
| "x": 1 |
| label: center/big |
| |
| shortTag: |
| # Explicit short merge tag |
| !!merge "<<" : [ *CENTER, *BIG ] |
| label: center/big |
| |
| longTag: |
| # Explicit merge long tag |
| !<tag:yaml.org,2002:merge> "<<" : [ *CENTER, *BIG ] |
| label: center/big |
| |
| inlineMap: |
| # Inlined map |
| << : {"x": 1, "y": 2, "r": 10} |
| label: center/big |
| |
| inlineSequenceMap: |
| # Inlined map in sequence |
| << : [ *CENTER, {"r": 10} ] |
| label: center/big |
| ` |
| |
| func (s *S) TestMerge(c *C) { |
| var want = map[interface{}]interface{}{ |
| "x": 1, |
| "y": 2, |
| "r": 10, |
| "label": "center/big", |
| } |
| |
| var m map[interface{}]interface{} |
| err := yaml.Unmarshal([]byte(mergeTests), &m) |
| c.Assert(err, IsNil) |
| for name, test := range m { |
| if name == "anchors" { |
| continue |
| } |
| c.Assert(test, DeepEquals, want, Commentf("test %q failed", name)) |
| } |
| } |
| |
| func (s *S) TestMergeStruct(c *C) { |
| type Data struct { |
| X, Y, R int |
| Label string |
| } |
| want := Data{1, 2, 10, "center/big"} |
| |
| var m map[string]Data |
| err := yaml.Unmarshal([]byte(mergeTests), &m) |
| c.Assert(err, IsNil) |
| for name, test := range m { |
| if name == "anchors" { |
| continue |
| } |
| c.Assert(test, Equals, want, Commentf("test %q failed", name)) |
| } |
| } |
| |
| var unmarshalNullTests = []func() interface{}{ |
| func() interface{} { var v interface{}; v = "v"; return &v }, |
| func() interface{} { var s = "s"; return &s }, |
| func() interface{} { var s = "s"; sptr := &s; return &sptr }, |
| func() interface{} { var i = 1; return &i }, |
| func() interface{} { var i = 1; iptr := &i; return &iptr }, |
| func() interface{} { m := map[string]int{"s": 1}; return &m }, |
| func() interface{} { m := map[string]int{"s": 1}; return m }, |
| } |
| |
| func (s *S) TestUnmarshalNull(c *C) { |
| for _, test := range unmarshalNullTests { |
| item := test() |
| zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface() |
| err := yaml.Unmarshal([]byte("null"), item) |
| c.Assert(err, IsNil) |
| if reflect.TypeOf(item).Kind() == reflect.Map { |
| c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface()) |
| } else { |
| c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero) |
| } |
| } |
| } |
| |
| func (s *S) TestUnmarshalSliceOnPreset(c *C) { |
| // Issue #48. |
| v := struct{ A []int }{[]int{1}} |
| yaml.Unmarshal([]byte("a: [2]"), &v) |
| c.Assert(v.A, DeepEquals, []int{2}) |
| } |
| |
| //var data []byte |
| //func init() { |
| // var err error |
| // data, err = ioutil.ReadFile("/tmp/file.yaml") |
| // if err != nil { |
| // panic(err) |
| // } |
| //} |
| // |
| //func (s *S) BenchmarkUnmarshal(c *C) { |
| // var err error |
| // for i := 0; i < c.N; i++ { |
| // var v map[string]interface{} |
| // err = yaml.Unmarshal(data, &v) |
| // } |
| // if err != nil { |
| // panic(err) |
| // } |
| //} |
| // |
| //func (s *S) BenchmarkMarshal(c *C) { |
| // var v map[string]interface{} |
| // yaml.Unmarshal(data, &v) |
| // c.ResetTimer() |
| // for i := 0; i < c.N; i++ { |
| // yaml.Marshal(&v) |
| // } |
| //} |