encode_test.go 12 KB

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