2019-10-18 00:00:58 +09:00
|
|
|
package yaml_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/goccy/go-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: false\n",
|
|
|
|
map[string]bool{"v": false},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: 10\n",
|
|
|
|
map[string]int{"v": 10},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: -10\n",
|
|
|
|
map[string]int{"v": -10},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: 4294967296\n",
|
|
|
|
map[string]int{"v": 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: .inf\n",
|
|
|
|
map[string]interface{}{"v": math.Inf(0)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: -.inf\n",
|
|
|
|
map[string]interface{}{"v": math.Inf(-1)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: .nan\n",
|
|
|
|
map[string]interface{}{"v": math.NaN()},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: null\n",
|
2019-10-24 17:18:03 +09:00
|
|
|
map[string]interface{}{"v": nil},
|
2019-10-18 00:00:58 +09:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"v: \"\"\n",
|
|
|
|
map[string]string{"v": ""},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v:\n- A\n- B\n",
|
2019-10-21 01:28:48 +09:00
|
|
|
map[string][]string{"v": {"A", "B"}},
|
2019-10-18 00:00:58 +09:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"a: '-'\n",
|
|
|
|
map[string]string{"a": "-"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"123\n",
|
|
|
|
123,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"hello: world\n",
|
|
|
|
map[string]string{"hello": "world"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
|
|
|
|
map[string]interface{}{
|
|
|
|
"v": []interface{}{
|
|
|
|
"A",
|
|
|
|
1,
|
|
|
|
map[string][]int{
|
2019-10-21 01:28:48 +09:00
|
|
|
"B": {2, 3},
|
2019-10-18 00:00:58 +09:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a:\n b: c\n",
|
|
|
|
map[string]interface{}{
|
|
|
|
"a": map[string]string{
|
|
|
|
"b": "c",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a: {x: 1}\n",
|
|
|
|
map[string]map[string]int{
|
2019-10-21 01:28:48 +09:00
|
|
|
"a": {
|
2019-10-18 00:00:58 +09:00
|
|
|
"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{
|
2019-10-21 01:28:48 +09:00
|
|
|
"a": {1, 2},
|
2019-10-18 00:00:58 +09:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"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": "\\0"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"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),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"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{
|
2019-10-21 01:28:48 +09:00
|
|
|
"v": {
|
2019-10-18 00:00:58 +09:00
|
|
|
"A", "B\nC",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"v:\n- A\n- >-\n B\n C\n",
|
|
|
|
map[string][]string{
|
2019-10-21 01:28:48 +09:00
|
|
|
"v": {
|
2019-10-18 00:00:58 +09:00
|
|
|
"A", "B C",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-10-18 12:55:38 +09:00
|
|
|
{
|
|
|
|
"a: b\nc: d\n",
|
2019-10-18 13:05:36 +09:00
|
|
|
struct {
|
2019-10-18 12:55:38 +09:00
|
|
|
A string
|
|
|
|
C string `yaml:"c"`
|
|
|
|
}{
|
|
|
|
"b", "d",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a: 1\nb: 2\n",
|
2019-10-18 13:05:36 +09:00
|
|
|
struct {
|
2019-10-18 12:55:38 +09:00
|
|
|
A int
|
|
|
|
B int `yaml:"-"`
|
|
|
|
}{
|
|
|
|
1, 0,
|
|
|
|
},
|
|
|
|
},
|
2019-10-18 14:27:49 +09:00
|
|
|
{
|
|
|
|
"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}},
|
|
|
|
},
|
2019-10-26 18:39:15 +09:00
|
|
|
{
|
|
|
|
"tags:\n- hello-world\na: foo",
|
|
|
|
struct {
|
|
|
|
Tags []string
|
2019-10-28 22:12:23 +09:00
|
|
|
A string
|
2019-10-26 18:39:15 +09:00
|
|
|
}{Tags: []string{"hello-world"}, A: "foo"},
|
|
|
|
},
|
2019-10-29 16:22:55 +09:00
|
|
|
{
|
|
|
|
"",
|
|
|
|
(*struct{})(nil),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"{}", struct{}{},
|
|
|
|
},
|
2019-10-18 00:00:58 +09:00
|
|
|
}
|
|
|
|
for _, test := range tests {
|
|
|
|
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 {
|
2019-10-23 03:21:42 +09:00
|
|
|
t.Fatalf("%s: %+v", test.source, err)
|
2019-10-18 00:00:58 +09:00
|
|
|
}
|
|
|
|
actual := fmt.Sprintf("%v", value.Elem().Interface())
|
|
|
|
expect := fmt.Sprintf("%v", test.value)
|
|
|
|
if actual != expect {
|
|
|
|
t.Fatal("failed to test. ", actual, expect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-18 15:20:09 +09:00
|
|
|
|
|
|
|
func TestDecodeByAnchorOfOtherFile(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)
|
|
|
|
}
|
|
|
|
}
|
2019-10-21 14:54:26 +09:00
|
|
|
|
|
|
|
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)
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2019-10-24 11:07:40 +09:00
|
|
|
|
2019-10-28 22:12:23 +09:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 22:44:21 +09:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:07:40 +09:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2019-10-24 15:28:41 +09:00
|
|
|
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)`)
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:07:40 +09:00
|
|
|
//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))
|
|
|
|
}
|