diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/github.com/goccy/go-yaml@v1.9.6/encode_test.go | 1475 |
1 files changed, 1475 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.9.6/encode_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.9.6/encode_test.go new file mode 100644 index 0000000..cfc3d8e --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.9.6/encode_test.go @@ -0,0 +1,1475 @@ +package yaml_test + +import ( + "bytes" + "context" + "fmt" + "math" + "reflect" + "strconv" + "testing" + "time" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" +) + +var zero = 0 +var emptyStr = "" + +func TestEncoder(t *testing.T) { + tests := []struct { + source string + value interface{} + options []yaml.EncodeOption + }{ + { + "null\n", + (*struct{})(nil), + nil, + }, + { + "v: hi\n", + map[string]string{"v": "hi"}, + nil, + }, + { + "v: \"true\"\n", + map[string]string{"v": "true"}, + nil, + }, + { + "v: \"false\"\n", + map[string]string{"v": "false"}, + nil, + }, + { + "v: true\n", + map[string]interface{}{"v": true}, + nil, + }, + { + "v: false\n", + map[string]bool{"v": false}, + nil, + }, + { + "v: 10\n", + map[string]int{"v": 10}, + nil, + }, + { + "v: -10\n", + map[string]int{"v": -10}, + nil, + }, + { + "v: 4294967296\n", + map[string]int64{"v": int64(4294967296)}, + nil, + }, + { + "v: 0.1\n", + map[string]interface{}{"v": 0.1}, + nil, + }, + { + "v: 0.99\n", + map[string]float32{"v": 0.99}, + nil, + }, + { + "v: 0.123456789\n", + map[string]float64{"v": 0.123456789}, + nil, + }, + { + "v: -0.1\n", + map[string]float64{"v": -0.1}, + nil, + }, + { + "v: 1.0\n", + map[string]float64{"v": 1.0}, + nil, + }, + { + "v: 1e+06\n", + map[string]float64{"v": 1000000}, + nil, + }, + { + "v: .inf\n", + map[string]interface{}{"v": math.Inf(0)}, + nil, + }, + { + "v: -.inf\n", + map[string]interface{}{"v": math.Inf(-1)}, + nil, + }, + { + "v: .nan\n", + map[string]interface{}{"v": math.NaN()}, + nil, + }, + { + "v: null\n", + map[string]interface{}{"v": nil}, + nil, + }, + { + "v: \"\"\n", + map[string]string{"v": ""}, + nil, + }, + { + "v:\n- A\n- B\n", + map[string][]string{"v": {"A", "B"}}, + nil, + }, + { + "v:\n - A\n - B\n", + map[string][]string{"v": {"A", "B"}}, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "v:\n- A\n- B\n", + map[string][2]string{"v": {"A", "B"}}, + nil, + }, + { + "v:\n - A\n - B\n", + map[string][2]string{"v": {"A", "B"}}, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "a: -\n", + map[string]string{"a": "-"}, + nil, + }, + { + "123\n", + 123, + nil, + }, + { + "hello: world\n", + map[string]string{"hello": "world"}, + nil, + }, + { + "hello: |\n hello\n world\n", + map[string]string{"hello": "hello\nworld\n"}, + nil, + }, + { + "hello: |-\n hello\n world\n", + map[string]string{"hello": "hello\nworld"}, + nil, + }, + { + "hello: |+\n hello\n world\n\n", + map[string]string{"hello": "hello\nworld\n\n"}, + nil, + }, + { + "hello:\n hello: |\n hello\n world\n", + map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}}, + nil, + }, + { + "hello: |\r hello\r world\n", + map[string]string{"hello": "hello\rworld\r"}, + nil, + }, + { + "hello: |\r\n hello\r\n world\n", + map[string]string{"hello": "hello\r\nworld\r\n"}, + nil, + }, + { + "v: |-\n username: hello\n password: hello123\n", + map[string]interface{}{"v": "username: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(true), + }, + }, + { + "v: |-\n # comment\n username: hello\n password: hello123\n", + map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(true), + }, + }, + { + "v: \"# comment\\nusername: hello\\npassword: hello123\"\n", + map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(false), + }, + }, + { + "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}, + }, + }, + }, + nil, + }, + { + "v:\n - A\n - 1\n - B:\n - 2\n - 3\n - 2\n", + map[string]interface{}{ + "v": []interface{}{ + "A", + 1, + map[string][]int{ + "B": {2, 3}, + }, + 2, + }, + }, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "a:\n b: c\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + }, + }, + nil, + }, + { + "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", + }, + nil, + }, + { + "a:\n b: c\n d: e\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + "d": "e", + }, + }, + nil, + }, + { + "a: 3s\n", + map[string]string{ + "a": "3s", + }, + nil, + }, + { + "a: <foo>\n", + map[string]string{"a": "<foo>"}, + nil, + }, + { + "a: \"1:1\"\n", + map[string]string{"a": "1:1"}, + nil, + }, + { + "a: 1.2.3.4\n", + map[string]string{"a": "1.2.3.4"}, + nil, + }, + { + "a: \"b: c\"\n", + map[string]string{"a": "b: c"}, + nil, + }, + { + "a: \"Hello #comment\"\n", + map[string]string{"a": "Hello #comment"}, + nil, + }, + { + "a: 100.5\n", + map[string]interface{}{ + "a": 100.5, + }, + nil, + }, + { + "a: \"\\\\0\"\n", + map[string]string{"a": "\\0"}, + nil, + }, + { + "a: 1\nb: 2\nc: 3\nd: 4\nsub:\n e: 5\n", + map[string]interface{}{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "sub": map[string]int{ + "e": 5, + }, + }, + nil, + }, + { + "a: 1\nb: []\n", + struct { + A int + B []string + }{ + 1, ([]string)(nil), + }, + nil, + }, + { + "a: 1\nb: []\n", + struct { + A int + B []string + }{ + 1, []string{}, + }, + nil, + }, + { + "a: {}\n", + struct { + A map[string]interface{} + }{ + map[string]interface{}{}, + }, + nil, + }, + { + "a: b\nc: d\n", + struct { + A string + C string `yaml:"c"` + }{ + "b", "d", + }, + nil, + }, + { + "a: 1\n", + struct { + A int + B int `yaml:"-"` + }{ + 1, 0, + }, + nil, + }, + { + "a: \"\"\n", + struct { + A string + }{ + "", + }, + nil, + }, + { + "a: null\n", + struct { + A *string + }{ + nil, + }, + nil, + }, + { + "a: \"\"\n", + struct { + A *string + }{ + &emptyStr, + }, + nil, + }, + { + "a: null\n", + struct { + A *int + }{ + nil, + }, + nil, + }, + { + "a: 0\n", + struct { + A *int + }{ + &zero, + }, + nil, + }, + + // Conditional flag + { + "a: 1\n", + struct { + A int `yaml:"a,omitempty"` + B int `yaml:"b,omitempty"` + }{1, 0}, + nil, + }, + { + "{}\n", + struct { + A int `yaml:"a,omitempty"` + B int `yaml:"b,omitempty"` + }{0, 0}, + nil, + }, + + { + "a:\n y: \"\"\n", + struct { + A *struct { + X string `yaml:"x,omitempty"` + Y string + } + }{&struct { + X string `yaml:"x,omitempty"` + Y string + }{}}, + nil, + }, + + { + "a: {}\n", + struct { + A *struct { + X string `yaml:"x,omitempty"` + Y string `yaml:"y,omitempty"` + } + }{&struct { + X string `yaml:"x,omitempty"` + Y string `yaml:"y,omitempty"` + }{}}, + nil, + }, + + { + "a: {x: 1}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{&struct{ X, y int }{1, 2}}, + nil, + }, + + { + "{}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{nil}, + nil, + }, + + { + "a: {x: 0}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{&struct{ X, y int }{}}, + nil, + }, + + { + "a: {x: 1}\n", + struct { + A struct{ X, y int } `yaml:"a,omitempty,flow"` + }{struct{ X, y int }{1, 2}}, + nil, + }, + { + "{}\n", + struct { + A struct{ X, y int } `yaml:"a,omitempty,flow"` + }{struct{ X, y int }{0, 1}}, + nil, + }, + { + "a: 1.0\n", + struct { + A float64 `yaml:"a,omitempty"` + B float64 `yaml:"b,omitempty"` + }{1, 0}, + nil, + }, + { + "a: 1\n", + struct { + A int + B []string `yaml:"b,omitempty"` + }{ + 1, []string{}, + }, + nil, + }, + + // Flow flag + { + "a: [1, 2]\n", + struct { + A []int `yaml:"a,flow"` + }{[]int{1, 2}}, + nil, + }, + { + "a: {b: c, d: e}\n", + &struct { + A map[string]string `yaml:"a,flow"` + }{map[string]string{"b": "c", "d": "e"}}, + nil, + }, + { + "a: {b: c, d: e}\n", + struct { + A struct { + B, D string + } `yaml:"a,flow"` + }{struct{ B, D string }{"c", "e"}}, + nil, + }, + // Quoting in flow mode + { + `a: [b, "c,d", e]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c,d", "e"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c]", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c]", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c}", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c}", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c\"", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", `c"`, "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c'", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c'", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + // No quoting in non-flow mode + { + "a:\n- b\n- c,d\n- e\n", + struct { + A []string `yaml:"a"` + }{[]string{"b", "c,d", "e"}}, + nil, + }, + { + `a: [b, "c]", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c]", "d"}}, + nil, + }, + { + `a: [b, "c}", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c}", "d"}}, + nil, + }, + { + `a: [b, "c\"", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", `c"`, "d"}}, + nil, + }, + { + `a: [b, "c'", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c'", "d"}}, + nil, + }, + + // Multi bytes + { + "v: あいうえお\nv2: かきくけこ\n", + map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, + nil, + }, + + // time value + { + "v: 0001-01-01T00:00:00Z\n", + map[string]time.Time{"v": time.Time{}}, + nil, + }, + { + "v: 0001-01-01T00:00:00Z\n", + map[string]*time.Time{"v": &time.Time{}}, + nil, + }, + { + "v: null\n", + map[string]*time.Time{"v": nil}, + nil, + }, + { + "v: 30s\n", + map[string]time.Duration{"v": 30 * time.Second}, + nil, + }, + // Quote style + { + `v: '\'a\'b'` + "\n", + map[string]string{"v": `'a'b`}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(true), + }, + }, + { + `v: "'a'b"` + "\n", + map[string]string{"v": `'a'b`}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + } + for _, test := range tests { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, test.options...) + if err := enc.Encode(test.value); err != nil { + t.Fatalf("%+v", err) + } + if test.source != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", test.source, buf.String()) + } + } +} + +func TestEncodeStructIncludeMap(t *testing.T) { + type U struct { + M map[string]string + } + type T struct { + A U + } + bytes, err := yaml.Marshal(T{ + A: U{ + M: map[string]string{"x": "y"}, + }, + }) + if err != nil { + t.Fatalf("%+v", err) + } + expect := "a:\n m:\n x: y\n" + actual := string(bytes) + if actual != expect { + t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) + } +} + +func TestEncodeDefinedTypeKeyMap(t *testing.T) { + type K string + type U struct { + M map[K]string + } + bytes, err := yaml.Marshal(U{ + M: map[K]string{K("x"): "y"}, + }) + if err != nil { + t.Fatalf("%+v", err) + } + expect := "m:\n x: y\n" + actual := string(bytes) + if actual != expect { + t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) + } +} + +func TestEncodeWithAnchorAndAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + A int + B string + } + var v struct { + A *T `yaml:"a,anchor=c"` + B *T `yaml:"b,alias=c"` + } + v.A = &T{A: 1, B: "hello"} + v.B = v.A + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := "a: &c\n a: 1\n b: hello\nb: *c\n" + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithAutoAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor=a"` + B *T `yaml:"b,anchor=b"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A + v.D = v.B + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := `a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithImplicitAnchorAndAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A + v.D = v.B + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := `a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithMerge(t *testing.T) { + type Person struct { + *Person `yaml:",omitempty,inline,alias"` + Name string `yaml:",omitempty"` + Age int `yaml:",omitempty"` + } + defaultPerson := &Person{ + Name: "John Smith", + Age: 20, + } + people := []*Person{ + { + Person: defaultPerson, + Name: "Ken", + Age: 10, + }, + { + Person: defaultPerson, + }, + } + var doc struct { + Default *Person `yaml:"default,anchor"` + People []*Person `yaml:"people"` + } + doc.Default = defaultPerson + doc.People = people + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(doc); err != nil { + t.Fatalf("%+v", err) + } + expect := `default: &default + name: John Smith + age: 20 +people: +- <<: *default + name: Ken + age: 10 +- <<: *default +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithNestedYAML(t *testing.T) { + // Represents objects containing stringified YAML, and special chars + tests := []struct { + value interface{} + // If true, expects a different result between when using forced literal style or not + expectDifferent bool + }{ + { + value: map[string]interface{}{"v": "# comment\nname: hello\npassword: hello123\nspecial: \":ghost:\"\ntext: |\n nested multiline!"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "# comment\n"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "\n"}, + }, + } + + for _, test := range tests { + yamlBytesForced, err := yaml.MarshalWithOptions(test.value, yaml.UseLiteralStyleIfMultiline(true)) + if err != nil { + t.Fatalf("%+v", err) + } + + // Convert it back for proper equality testing + var unmarshaled interface{} + + if err := yaml.Unmarshal(yamlBytesForced, &unmarshaled); err != nil { + t.Fatalf("%+v", err) + } + + if !reflect.DeepEqual(test.value, unmarshaled) { + t.Fatalf("expected %v(%T). but actual %v(%T)", test.value, test.value, unmarshaled, unmarshaled) + } + + if test.expectDifferent { + yamlBytesNotForced, err := yaml.MarshalWithOptions(test.value) + if err != nil { + t.Fatalf("%+v", err) + } + + if string(yamlBytesForced) == string(yamlBytesNotForced) { + t.Fatalf("expected different strings when force literal style is not enabled. forced: %s, not forced: %s", string(yamlBytesForced), string(yamlBytesNotForced)) + } + } + } +} + +func TestEncoder_Inline(t *testing.T) { + type base struct { + A int + B string + } + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(struct { + *base `yaml:",inline"` + C bool + }{ + base: &base{ + A: 1, + B: "hello", + }, + C: true, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +a: 1 +b: hello +c: true +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_InlineAndConflictKey(t *testing.T) { + type base struct { + A int + B string + } + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(struct { + *base `yaml:",inline"` + A int // conflict + C bool + }{ + base: &base{ + A: 1, + B: "hello", + }, + A: 0, // default value + C: true, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +b: hello +a: 0 +c: true +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_Flow(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, yaml.Flow(true)) + var v struct { + A int + B string + C struct { + D int + E string + } + F []int + } + v.A = 1 + v.B = "hello" + v.C.D = 3 + v.C.E = "world" + v.F = []int{1, 2} + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +{a: 1, b: hello, c: {d: 3, e: world}, f: [1, 2]} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_FlowRecursive(t *testing.T) { + var v struct { + M map[string][]int `yaml:",flow"` + } + v.M = map[string][]int{ + "test": []int{1, 2, 3}, + } + var buf bytes.Buffer + if err := yaml.NewEncoder(&buf).Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +m: {test: [1, 2, 3]} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_JSON(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, yaml.JSON()) + type st struct { + I int8 + S string + F float32 + } + if err := enc.Encode(struct { + I int + U uint + S string + F float64 + Struct *st + Slice []int + Map map[string]interface{} + Time time.Time + Duration time.Duration + }{ + I: -10, + U: 10, + S: "hello", + F: 3.14, + Struct: &st{ + I: 2, + S: "world", + F: 1.23, + }, + Slice: []int{1, 2, 3, 4, 5}, + Map: map[string]interface{}{ + "a": 1, + "b": 1.23, + "c": "json", + }, + Time: time.Time{}, + Duration: 5 * time.Minute, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +{"i": -10, "u": 10, "s": "hello", "f": 3.14, "struct": {"i": 2, "s": "world", "f": 1.23}, "slice": [1, 2, 3, 4, 5], "map": {"a": 1, "b": 1.23, "c": "json"}, "time": "0001-01-01T00:00:00Z", "duration": "5m0s"} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("JSON style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_MarshalAnchor(t *testing.T) { + type Host struct { + Hostname string + Username string + Password string + } + type HostDecl struct { + Host *Host `yaml:",anchor"` + } + type Queue struct { + Name string `yaml:","` + *Host `yaml:",alias"` + } + var doc struct { + Hosts []*HostDecl `yaml:"hosts"` + Queues []*Queue `yaml:"queues"` + } + host1 := &Host{ + Hostname: "host1.example.com", + Username: "userA", + Password: "pass1", + } + host2 := &Host{ + Hostname: "host2.example.com", + Username: "userB", + Password: "pass2", + } + doc.Hosts = []*HostDecl{ + { + Host: host1, + }, + { + Host: host2, + }, + } + doc.Queues = []*Queue{ + { + Name: "queue", + Host: host1, + }, { + Name: "queue2", + Host: host2, + }, + } + hostIdx := 1 + opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { + if _, ok := value.(*Host); ok { + nameNode := anchor.Name.(*ast.StringNode) + nameNode.Value = fmt.Sprintf("host%d", hostIdx) + hostIdx++ + } + return nil + }) + + var buf bytes.Buffer + if err := yaml.NewEncoder(&buf, opt).Encode(doc); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +hosts: +- host: &host1 + hostname: host1.example.com + username: userA + password: pass1 +- host: &host2 + hostname: host2.example.com + username: userB + password: pass2 +queues: +- name: queue + host: *host1 +- name: queue2 + host: *host2 +` + if "\n"+buf.String() != expect { + t.Fatalf("unexpected output. %s", buf.String()) + } +} + +type useJSONMarshalerTest struct{} + +func (t useJSONMarshalerTest) MarshalJSON() ([]byte, error) { + return []byte(`{"a":[1, 2, 3]}`), nil +} + +func TestEncoder_UseJSONMarshaler(t *testing.T) { + got, err := yaml.MarshalWithOptions(useJSONMarshalerTest{}, yaml.UseJSONMarshaler()) + if err != nil { + t.Fatal(err) + } + expected := ` +a: +- 1 +- 2 +- 3 +` + if expected != "\n"+string(got) { + t.Fatalf("failed to use json marshaler. expected [%q] but got [%q]", expected, string(got)) + } +} + +func TestEncoder_MultipleDocuments(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(1); err != nil { + t.Fatalf("failed to encode: %s", err) + } + if err := enc.Encode(2); err != nil { + t.Fatalf("failed to encode: %s", err) + } + if actual, expect := buf.String(), "1\n---\n2\n"; actual != expect { + t.Errorf("expect:\n%s\nactual\n%s\n", expect, actual) + } +} + +func Example_Marshal_Node() { + type T struct { + Text ast.Node `yaml:"text"` + } + stringNode, err := yaml.ValueToNode("node example") + if err != nil { + panic(err) + } + bytes, err := yaml.Marshal(T{Text: stringNode}) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // text: node example +} + +func Example_Marshal_ExplicitAnchorAlias() { + type T struct { + A int + B string + } + var v struct { + C *T `yaml:"c,anchor=x"` + D *T `yaml:"d,alias=x"` + } + v.C = &T{A: 1, B: "hello"} + v.D = v.C + bytes, err := yaml.Marshal(v) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // c: &x + // a: 1 + // b: hello + // d: *x +} + +func Example_Marshal_ImplicitAnchorAlias() { + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A // C has same pointer address to A + v.D = v.B // D has same pointer address to B + bytes, err := yaml.Marshal(v) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // a: &a + // i: 1 + // s: hello + // b: &b + // i: 2 + // s: world + // c: *a + // d: *b +} + +type tMarshal []string + +func (t *tMarshal) MarshalYAML() ([]byte, error) { + var buf bytes.Buffer + buf.WriteString("tags:\n") + for i, v := range *t { + if i == 0 { + fmt.Fprintf(&buf, "- %s\n", v) + } else { + fmt.Fprintf(&buf, " %s\n", v) + } + } + return buf.Bytes(), nil +} +func Test_Marshaler(t *testing.T) { + const expected = `- hello-world +` + + // sanity check + var l []string + if err := yaml.Unmarshal([]byte(expected), &l); err != nil { + t.Fatalf("failed to parse string: %s", err) + } + + buf, err := yaml.Marshal(tMarshal{"hello-world"}) + if err != nil { + t.Fatalf("failed to marshal: %s", err) + } + + if string(buf) != expected { + t.Fatalf("expected '%s', got '%s'", expected, buf) + } + + t.Logf("%s", buf) +} + +type marshalContext struct{} + +func (c *marshalContext) MarshalYAML(ctx context.Context) ([]byte, error) { + v, ok := ctx.Value("k").(int) + if !ok { + return nil, fmt.Errorf("cannot get valid context") + } + if v != 1 { + return nil, fmt.Errorf("cannot get valid context") + } + return []byte("1"), nil +} + +func Test_MarshalerContext(t *testing.T) { + ctx := context.WithValue(context.Background(), "k", 1) + bytes, err := yaml.MarshalContext(ctx, &marshalContext{}) + if err != nil { + t.Fatalf("%+v", err) + } + if string(bytes) != "1\n" { + t.Fatalf("failed marshal: %q", string(bytes)) + } +} + +type SlowMarshaler struct { + A string + B int +} +type FastMarshaler struct { + A string + B int +} +type TextMarshaler int64 +type TextMarshalerContainer struct { + Field TextMarshaler `yaml:"field"` +} + +func (v SlowMarshaler) MarshalYAML() ([]byte, error) { + var buf bytes.Buffer + buf.WriteString("tags:\n") + buf.WriteString("- slow-marshaler\n") + buf.WriteString("a: " + v.A + "\n") + buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n") + return buf.Bytes(), nil +} + +func (v FastMarshaler) MarshalYAML() (interface{}, error) { + return yaml.MapSlice{ + {"tags", []string{"fast-marshaler"}}, + {"a", v.A}, + {"b", v.B}, + }, nil +} + +func (t TextMarshaler) MarshalText() ([]byte, error) { + return []byte(strconv.FormatInt(int64(t), 8)), nil +} + +func Example_MarshalYAML() { + var slow SlowMarshaler + slow.A = "Hello slow poke" + slow.B = 100 + buf, err := yaml.Marshal(slow) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + + var fast FastMarshaler + fast.A = "Hello speed demon" + fast.B = 100 + buf, err = yaml.Marshal(fast) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + + text := TextMarshalerContainer{ + Field: 11, + } + buf, err = yaml.Marshal(text) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + // OUTPUT: + // tags: + // - slow-marshaler + // a: Hello slow poke + // b: 100 + // + // tags: + // - fast-marshaler + // a: Hello speed demon + // b: 100 + // + // field: 13 +} + +func TestMarshalIndentWithMultipleText(t *testing.T) { + t.Run("depth1", func(t *testing.T) { + b, err := yaml.MarshalWithOptions(map[string]interface{}{ + "key": []string{`line1 +line2 +line3`}, + }, yaml.Indent(2)) + if err != nil { + t.Fatal(err) + } + got := string(b) + expected := `key: +- |- + line1 + line2 + line3 +` + if expected != got { + t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) + } + }) + t.Run("depth2", func(t *testing.T) { + b, err := yaml.MarshalWithOptions(map[string]interface{}{ + "key": map[string]interface{}{ + "key2": []string{`line1 +line2 +line3`}, + }, + }, yaml.Indent(2)) + if err != nil { + t.Fatal(err) + } + got := string(b) + expected := `key: + key2: + - |- + line1 + line2 + line3 +` + if expected != got { + t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) + } + }) +} + +type bytesMarshaler struct{} + +func (b *bytesMarshaler) MarshalYAML() ([]byte, error) { + return yaml.Marshal(map[string]interface{}{"d": "foo"}) +} + +func TestBytesMarshaler(t *testing.T) { + b, err := yaml.Marshal(map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": &bytesMarshaler{}, + }, + }, + }) + if err != nil { + t.Fatal(err) + } + expected := ` +a: + b: + c: + d: foo +` + got := "\n" + string(b) + if expected != got { + t.Fatalf("failed to encode. expected %s but got %s", expected, got) + } +} |