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