encode_test.go 12 KB

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