1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package yaml_test
17
18 import (
19 "bytes"
20 "fmt"
21 "math"
22 "strconv"
23 "strings"
24 "time"
25
26 "net"
27 "os"
28
29 . "gopkg.in/check.v1"
30 "gopkg.in/yaml.v3"
31 )
32
33 var marshalIntTest = 123
34
35 var marshalTests = []struct {
36 value interface{}
37 data string
38 }{
39 {
40 nil,
41 "null\n",
42 }, {
43 (*marshalerType)(nil),
44 "null\n",
45 }, {
46 &struct{}{},
47 "{}\n",
48 }, {
49 map[string]string{"v": "hi"},
50 "v: hi\n",
51 }, {
52 map[string]interface{}{"v": "hi"},
53 "v: hi\n",
54 }, {
55 map[string]string{"v": "true"},
56 "v: \"true\"\n",
57 }, {
58 map[string]string{"v": "false"},
59 "v: \"false\"\n",
60 }, {
61 map[string]interface{}{"v": true},
62 "v: true\n",
63 }, {
64 map[string]interface{}{"v": false},
65 "v: false\n",
66 }, {
67 map[string]interface{}{"v": 10},
68 "v: 10\n",
69 }, {
70 map[string]interface{}{"v": -10},
71 "v: -10\n",
72 }, {
73 map[string]uint{"v": 42},
74 "v: 42\n",
75 }, {
76 map[string]interface{}{"v": int64(4294967296)},
77 "v: 4294967296\n",
78 }, {
79 map[string]int64{"v": int64(4294967296)},
80 "v: 4294967296\n",
81 }, {
82 map[string]uint64{"v": 4294967296},
83 "v: 4294967296\n",
84 }, {
85 map[string]interface{}{"v": "10"},
86 "v: \"10\"\n",
87 }, {
88 map[string]interface{}{"v": 0.1},
89 "v: 0.1\n",
90 }, {
91 map[string]interface{}{"v": float64(0.1)},
92 "v: 0.1\n",
93 }, {
94 map[string]interface{}{"v": float32(0.99)},
95 "v: 0.99\n",
96 }, {
97 map[string]interface{}{"v": -0.1},
98 "v: -0.1\n",
99 }, {
100 map[string]interface{}{"v": math.Inf(+1)},
101 "v: .inf\n",
102 }, {
103 map[string]interface{}{"v": math.Inf(-1)},
104 "v: -.inf\n",
105 }, {
106 map[string]interface{}{"v": math.NaN()},
107 "v: .nan\n",
108 }, {
109 map[string]interface{}{"v": nil},
110 "v: null\n",
111 }, {
112 map[string]interface{}{"v": ""},
113 "v: \"\"\n",
114 }, {
115 map[string][]string{"v": []string{"A", "B"}},
116 "v:\n - A\n - B\n",
117 }, {
118 map[string][]string{"v": []string{"A", "B\nC"}},
119 "v:\n - A\n - |-\n B\n C\n",
120 }, {
121 map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
122 "v:\n - A\n - 1\n - B:\n - 2\n - 3\n",
123 }, {
124 map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
125 "a:\n b: c\n",
126 }, {
127 map[string]interface{}{"a": "-"},
128 "a: '-'\n",
129 },
130
131
132 {
133 &marshalIntTest,
134 "123\n",
135 },
136
137
138 {
139 &struct{ Hello string }{"world"},
140 "hello: world\n",
141 }, {
142 &struct {
143 A struct {
144 B string
145 }
146 }{struct{ B string }{"c"}},
147 "a:\n b: c\n",
148 }, {
149 &struct {
150 A *struct {
151 B string
152 }
153 }{&struct{ B string }{"c"}},
154 "a:\n b: c\n",
155 }, {
156 &struct {
157 A *struct {
158 B string
159 }
160 }{},
161 "a: null\n",
162 }, {
163 &struct{ A int }{1},
164 "a: 1\n",
165 }, {
166 &struct{ A []int }{[]int{1, 2}},
167 "a:\n - 1\n - 2\n",
168 }, {
169 &struct{ A [2]int }{[2]int{1, 2}},
170 "a:\n - 1\n - 2\n",
171 }, {
172 &struct {
173 B int "a"
174 }{1},
175 "a: 1\n",
176 }, {
177 &struct{ A bool }{true},
178 "a: true\n",
179 }, {
180 &struct{ A string }{"true"},
181 "a: \"true\"\n",
182 }, {
183 &struct{ A string }{"off"},
184 "a: \"off\"\n",
185 },
186
187
188 {
189 &struct {
190 A int "a,omitempty"
191 B int "b,omitempty"
192 }{1, 0},
193 "a: 1\n",
194 }, {
195 &struct {
196 A int "a,omitempty"
197 B int "b,omitempty"
198 }{0, 0},
199 "{}\n",
200 }, {
201 &struct {
202 A *struct{ X, y int } "a,omitempty,flow"
203 }{&struct{ X, y int }{1, 2}},
204 "a: {x: 1}\n",
205 }, {
206 &struct {
207 A *struct{ X, y int } "a,omitempty,flow"
208 }{nil},
209 "{}\n",
210 }, {
211 &struct {
212 A *struct{ X, y int } "a,omitempty,flow"
213 }{&struct{ X, y int }{}},
214 "a: {x: 0}\n",
215 }, {
216 &struct {
217 A struct{ X, y int } "a,omitempty,flow"
218 }{struct{ X, y int }{1, 2}},
219 "a: {x: 1}\n",
220 }, {
221 &struct {
222 A struct{ X, y int } "a,omitempty,flow"
223 }{struct{ X, y int }{0, 1}},
224 "{}\n",
225 }, {
226 &struct {
227 A float64 "a,omitempty"
228 B float64 "b,omitempty"
229 }{1, 0},
230 "a: 1\n",
231 },
232 {
233 &struct {
234 T1 time.Time "t1,omitempty"
235 T2 time.Time "t2,omitempty"
236 T3 *time.Time "t3,omitempty"
237 T4 *time.Time "t4,omitempty"
238 }{
239 T2: time.Date(2018, 1, 9, 10, 40, 47, 0, time.UTC),
240 T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)),
241 },
242 "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
243 },
244
245 {
246 map[string]yaml.Marshaler{
247 "a": nil,
248 },
249 "a: null\n",
250 },
251
252
253 {
254 &struct {
255 A []int "a,flow"
256 }{[]int{1, 2}},
257 "a: [1, 2]\n",
258 }, {
259 &struct {
260 A map[string]string "a,flow"
261 }{map[string]string{"b": "c", "d": "e"}},
262 "a: {b: c, d: e}\n",
263 }, {
264 &struct {
265 A struct {
266 B, D string
267 } "a,flow"
268 }{struct{ B, D string }{"c", "e"}},
269 "a: {b: c, d: e}\n",
270 }, {
271 &struct {
272 A string "a,flow"
273 }{"b\nc"},
274 "a: \"b\\nc\"\n",
275 },
276
277
278 {
279 &struct {
280 u int
281 A int
282 }{0, 1},
283 "a: 1\n",
284 },
285
286
287 {
288 &struct {
289 A int
290 B int "-"
291 }{1, 2},
292 "a: 1\n",
293 },
294
295
296 {
297 &struct {
298 A int
299 C inlineB `yaml:",inline"`
300 }{1, inlineB{2, inlineC{3}}},
301 "a: 1\nb: 2\nc: 3\n",
302 },
303
304 {
305 &struct {
306 A int
307 C *inlineB `yaml:",inline"`
308 }{1, &inlineB{2, inlineC{3}}},
309 "a: 1\nb: 2\nc: 3\n",
310 }, {
311 &struct {
312 A int
313 C *inlineB `yaml:",inline"`
314 }{1, nil},
315 "a: 1\n",
316 }, {
317 &struct {
318 A int
319 D *inlineD `yaml:",inline"`
320 }{1, &inlineD{&inlineC{3}, 4}},
321 "a: 1\nc: 3\nd: 4\n",
322 },
323
324
325 {
326 &struct {
327 A int
328 C map[string]int `yaml:",inline"`
329 }{1, map[string]int{"b": 2, "c": 3}},
330 "a: 1\nb: 2\nc: 3\n",
331 },
332
333
334 {
335 map[string]time.Duration{"a": 3 * time.Second},
336 "a: 3s\n",
337 },
338
339
340 {
341 map[string]string{"a": "<foo>"},
342 "a: <foo>\n",
343 },
344
345
346
347 {
348 map[string]string{"a": "1:1"},
349 "a: \"1:1\"\n",
350 },
351
352
353 {
354 map[string]string{"a": "\x00"},
355 "a: \"\\0\"\n",
356 }, {
357 map[string]string{"a": "\x80\x81\x82"},
358 "a: !!binary gIGC\n",
359 }, {
360 map[string]string{"a": strings.Repeat("\x90", 54)},
361 "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
362 },
363
364
365 {
366 map[string]string{"a": "你好"},
367 "a: 你好\n",
368 },
369
370
371 {
372 map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
373 "a: 1.2.3.4\n",
374 },
375
376 {
377 map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
378 "a: 2015-02-24T18:19:39Z\n",
379 },
380 {
381 map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))},
382 "a: 2015-02-24T18:19:39Z\n",
383 },
384 {
385
386 map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))},
387 "a: 2015-02-24T18:19:39.123456789-03:00\n",
388 },
389
390 {
391 map[string]string{"a": "2015-02-24T18:19:39Z"},
392 "a: \"2015-02-24T18:19:39Z\"\n",
393 },
394
395
396 {
397 map[string]string{"a": "b: c"},
398 "a: 'b: c'\n",
399 },
400
401
402 {
403 map[string]string{"a": "Hello #comment"},
404 "a: 'Hello #comment'\n",
405 },
406 {
407 map[string]string{"a": "你好 #comment"},
408 "a: '你好 #comment'\n",
409 },
410
411
412 {
413 &marshalerType{marshalerType{true}},
414 "true\n",
415 }, {
416 &marshalerType{&marshalerType{true}},
417 "true\n",
418 },
419
420
421 {
422 map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}},
423 "a:\n b:\n - c: 1\n d: 2\n",
424 },
425
426
427 {
428 map[string]string{"a": "\tB\n\tC\n"},
429 "a: |\n \tB\n \tC\n",
430 },
431
432
433 {
434 map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "},
435 "a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n",
436 },
437
438
439 {
440 &struct {
441 Value yaml.Node
442 }{
443 yaml.Node{
444 Kind: yaml.ScalarNode,
445 Tag: "!!str",
446 Value: "foo",
447 Style: yaml.SingleQuotedStyle,
448 },
449 },
450 "value: 'foo'\n",
451 }, {
452 yaml.Node{
453 Kind: yaml.ScalarNode,
454 Tag: "!!str",
455 Value: "foo",
456 Style: yaml.SingleQuotedStyle,
457 },
458 "'foo'\n",
459 },
460
461
462 {
463 &struct {
464 Value yaml.Node
465 }{
466 yaml.Node{
467 Kind: yaml.ScalarNode,
468 Style: yaml.TaggedStyle,
469 Value: "foo",
470 Tag: "!!str",
471 },
472 },
473 "value: !!str foo\n",
474 }, {
475 &struct {
476 Value yaml.Node
477 }{
478 yaml.Node{
479 Kind: yaml.MappingNode,
480 Style: yaml.TaggedStyle,
481 Tag: "!!map",
482 },
483 },
484 "value: !!map {}\n",
485 }, {
486 &struct {
487 Value yaml.Node
488 }{
489 yaml.Node{
490 Kind: yaml.SequenceNode,
491 Style: yaml.TaggedStyle,
492 Tag: "!!seq",
493 },
494 },
495 "value: !!seq []\n",
496 },
497 }
498
499 func (s *S) TestMarshal(c *C) {
500 defer os.Setenv("TZ", os.Getenv("TZ"))
501 os.Setenv("TZ", "UTC")
502 for i, item := range marshalTests {
503 c.Logf("test %d: %q", i, item.data)
504 data, err := yaml.Marshal(item.value)
505 c.Assert(err, IsNil)
506 c.Assert(string(data), Equals, item.data)
507 }
508 }
509
510 func (s *S) TestEncoderSingleDocument(c *C) {
511 for i, item := range marshalTests {
512 c.Logf("test %d. %q", i, item.data)
513 var buf bytes.Buffer
514 enc := yaml.NewEncoder(&buf)
515 err := enc.Encode(item.value)
516 c.Assert(err, Equals, nil)
517 err = enc.Close()
518 c.Assert(err, Equals, nil)
519 c.Assert(buf.String(), Equals, item.data)
520 }
521 }
522
523 func (s *S) TestEncoderMultipleDocuments(c *C) {
524 var buf bytes.Buffer
525 enc := yaml.NewEncoder(&buf)
526 err := enc.Encode(map[string]string{"a": "b"})
527 c.Assert(err, Equals, nil)
528 err = enc.Encode(map[string]string{"c": "d"})
529 c.Assert(err, Equals, nil)
530 err = enc.Close()
531 c.Assert(err, Equals, nil)
532 c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n")
533 }
534
535 func (s *S) TestEncoderWriteError(c *C) {
536 enc := yaml.NewEncoder(errorWriter{})
537 err := enc.Encode(map[string]string{"a": "b"})
538 c.Assert(err, ErrorMatches, `yaml: write error: some write error`)
539 }
540
541 type errorWriter struct{}
542
543 func (errorWriter) Write([]byte) (int, error) {
544 return 0, fmt.Errorf("some write error")
545 }
546
547 var marshalErrorTests = []struct {
548 value interface{}
549 error string
550 panic string
551 }{{
552 value: &struct {
553 B int
554 inlineB ",inline"
555 }{1, inlineB{2, inlineC{3}}},
556 panic: `duplicated key 'b' in struct struct \{ B int; .*`,
557 }, {
558 value: &struct {
559 A int
560 B map[string]int ",inline"
561 }{1, map[string]int{"a": 2}},
562 panic: `cannot have key "a" in inlined map: conflicts with struct field`,
563 }}
564
565 func (s *S) TestMarshalErrors(c *C) {
566 for _, item := range marshalErrorTests {
567 if item.panic != "" {
568 c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
569 } else {
570 _, err := yaml.Marshal(item.value)
571 c.Assert(err, ErrorMatches, item.error)
572 }
573 }
574 }
575
576 func (s *S) TestMarshalTypeCache(c *C) {
577 var data []byte
578 var err error
579 func() {
580 type T struct{ A int }
581 data, err = yaml.Marshal(&T{})
582 c.Assert(err, IsNil)
583 }()
584 func() {
585 type T struct{ B int }
586 data, err = yaml.Marshal(&T{})
587 c.Assert(err, IsNil)
588 }()
589 c.Assert(string(data), Equals, "b: 0\n")
590 }
591
592 var marshalerTests = []struct {
593 data string
594 value interface{}
595 }{
596 {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
597 {"_:\n - 1\n - A\n", []interface{}{1, "A"}},
598 {"_: 10\n", 10},
599 {"_: null\n", nil},
600 {"_: BAR!\n", "BAR!"},
601 }
602
603 type marshalerType struct {
604 value interface{}
605 }
606
607 func (o marshalerType) MarshalText() ([]byte, error) {
608 panic("MarshalText called on type with MarshalYAML")
609 }
610
611 func (o marshalerType) MarshalYAML() (interface{}, error) {
612 return o.value, nil
613 }
614
615 type marshalerValue struct {
616 Field marshalerType "_"
617 }
618
619 func (s *S) TestMarshaler(c *C) {
620 for _, item := range marshalerTests {
621 obj := &marshalerValue{}
622 obj.Field.value = item.value
623 data, err := yaml.Marshal(obj)
624 c.Assert(err, IsNil)
625 c.Assert(string(data), Equals, string(item.data))
626 }
627 }
628
629 func (s *S) TestMarshalerWholeDocument(c *C) {
630 obj := &marshalerType{}
631 obj.value = map[string]string{"hello": "world!"}
632 data, err := yaml.Marshal(obj)
633 c.Assert(err, IsNil)
634 c.Assert(string(data), Equals, "hello: world!\n")
635 }
636
637 type failingMarshaler struct{}
638
639 func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
640 return nil, failingErr
641 }
642
643 func (s *S) TestMarshalerError(c *C) {
644 _, err := yaml.Marshal(&failingMarshaler{})
645 c.Assert(err, Equals, failingErr)
646 }
647
648 func (s *S) TestSetIndent(c *C) {
649 var buf bytes.Buffer
650 enc := yaml.NewEncoder(&buf)
651 enc.SetIndent(8)
652 err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}})
653 c.Assert(err, Equals, nil)
654 err = enc.Close()
655 c.Assert(err, Equals, nil)
656 c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n")
657 }
658
659 func (s *S) TestSortedOutput(c *C) {
660 order := []interface{}{
661 false,
662 true,
663 1,
664 uint(1),
665 1.0,
666 1.1,
667 1.2,
668 2,
669 uint(2),
670 2.0,
671 2.1,
672 "",
673 ".1",
674 ".2",
675 ".a",
676 "1",
677 "2",
678 "a!10",
679 "a/0001",
680 "a/002",
681 "a/3",
682 "a/10",
683 "a/11",
684 "a/0012",
685 "a/100",
686 "a~10",
687 "ab/1",
688 "b/1",
689 "b/01",
690 "b/2",
691 "b/02",
692 "b/3",
693 "b/03",
694 "b1",
695 "b01",
696 "b3",
697 "c2.10",
698 "c10.2",
699 "d1",
700 "d7",
701 "d7abc",
702 "d12",
703 "d12a",
704 "e2b",
705 "e4b",
706 "e21a",
707 }
708 m := make(map[interface{}]int)
709 for _, k := range order {
710 m[k] = 1
711 }
712 data, err := yaml.Marshal(m)
713 c.Assert(err, IsNil)
714 out := "\n" + string(data)
715 last := 0
716 for i, k := range order {
717 repr := fmt.Sprint(k)
718 if s, ok := k.(string); ok {
719 if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
720 repr = `"` + repr + `"`
721 }
722 }
723 index := strings.Index(out, "\n"+repr+":")
724 if index == -1 {
725 c.Fatalf("%#v is not in the output: %#v", k, out)
726 }
727 if index < last {
728 c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
729 }
730 last = index
731 }
732 }
733
734 func newTime(t time.Time) *time.Time {
735 return &t
736 }
737
View as plain text