encode_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. //
  2. // Copyright (c) 2011-2019 Canonical Ltd
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. package yaml_test
  16. import (
  17. "bytes"
  18. "fmt"
  19. "math"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "net"
  24. "os"
  25. . "gopkg.in/check.v1"
  26. "gopkg.in/niemeyer/ynext.v3"
  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. // Struct inlining as a pointer
  280. {
  281. &struct {
  282. A int
  283. C *inlineB `yaml:",inline"`
  284. }{1, &inlineB{2, inlineC{3}}},
  285. "a: 1\nb: 2\nc: 3\n",
  286. }, {
  287. &struct {
  288. A int
  289. C *inlineB `yaml:",inline"`
  290. }{1, nil},
  291. "a: 1\n",
  292. }, {
  293. &struct {
  294. A int
  295. D *inlineD `yaml:",inline"`
  296. }{1, &inlineD{&inlineC{3}, 4}},
  297. "a: 1\nc: 3\nd: 4\n",
  298. },
  299. // Map inlining
  300. {
  301. &struct {
  302. A int
  303. C map[string]int `yaml:",inline"`
  304. }{1, map[string]int{"b": 2, "c": 3}},
  305. "a: 1\nb: 2\nc: 3\n",
  306. },
  307. // Duration
  308. {
  309. map[string]time.Duration{"a": 3 * time.Second},
  310. "a: 3s\n",
  311. },
  312. // Issue #24: bug in map merging logic.
  313. {
  314. map[string]string{"a": "<foo>"},
  315. "a: <foo>\n",
  316. },
  317. // Issue #34: marshal unsupported base 60 floats quoted for compatibility
  318. // with old YAML 1.1 parsers.
  319. {
  320. map[string]string{"a": "1:1"},
  321. "a: \"1:1\"\n",
  322. },
  323. // Binary data.
  324. {
  325. map[string]string{"a": "\x00"},
  326. "a: \"\\0\"\n",
  327. }, {
  328. map[string]string{"a": "\x80\x81\x82"},
  329. "a: !!binary gIGC\n",
  330. }, {
  331. map[string]string{"a": strings.Repeat("\x90", 54)},
  332. "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
  333. },
  334. // Encode unicode as utf-8 rather than in escaped form.
  335. {
  336. map[string]string{"a": "你好"},
  337. "a: 你好\n",
  338. },
  339. // Support encoding.TextMarshaler.
  340. {
  341. map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
  342. "a: 1.2.3.4\n",
  343. },
  344. // time.Time gets a timestamp tag.
  345. {
  346. map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
  347. "a: 2015-02-24T18:19:39Z\n",
  348. },
  349. {
  350. map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))},
  351. "a: 2015-02-24T18:19:39Z\n",
  352. },
  353. {
  354. // This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag.
  355. map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))},
  356. "a: 2015-02-24T18:19:39.123456789-03:00\n",
  357. },
  358. // Ensure timestamp-like strings are quoted.
  359. {
  360. map[string]string{"a": "2015-02-24T18:19:39Z"},
  361. "a: \"2015-02-24T18:19:39Z\"\n",
  362. },
  363. // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
  364. {
  365. map[string]string{"a": "b: c"},
  366. "a: 'b: c'\n",
  367. },
  368. // Containing hash mark ('#') in string should be quoted
  369. {
  370. map[string]string{"a": "Hello #comment"},
  371. "a: 'Hello #comment'\n",
  372. },
  373. {
  374. map[string]string{"a": "你好 #comment"},
  375. "a: '你好 #comment'\n",
  376. },
  377. }
  378. func (s *S) TestMarshal(c *C) {
  379. defer os.Setenv("TZ", os.Getenv("TZ"))
  380. os.Setenv("TZ", "UTC")
  381. for i, item := range marshalTests {
  382. c.Logf("test %d: %q", i, item.data)
  383. data, err := yaml.Marshal(item.value)
  384. c.Assert(err, IsNil)
  385. c.Assert(string(data), Equals, item.data)
  386. }
  387. }
  388. func (s *S) TestEncoderSingleDocument(c *C) {
  389. for i, item := range marshalTests {
  390. c.Logf("test %d. %q", i, item.data)
  391. var buf bytes.Buffer
  392. enc := yaml.NewEncoder(&buf)
  393. err := enc.Encode(item.value)
  394. c.Assert(err, Equals, nil)
  395. err = enc.Close()
  396. c.Assert(err, Equals, nil)
  397. c.Assert(buf.String(), Equals, item.data)
  398. }
  399. }
  400. func (s *S) TestEncoderMultipleDocuments(c *C) {
  401. var buf bytes.Buffer
  402. enc := yaml.NewEncoder(&buf)
  403. err := enc.Encode(map[string]string{"a": "b"})
  404. c.Assert(err, Equals, nil)
  405. err = enc.Encode(map[string]string{"c": "d"})
  406. c.Assert(err, Equals, nil)
  407. err = enc.Close()
  408. c.Assert(err, Equals, nil)
  409. c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n")
  410. }
  411. func (s *S) TestEncoderWriteError(c *C) {
  412. enc := yaml.NewEncoder(errorWriter{})
  413. err := enc.Encode(map[string]string{"a": "b"})
  414. c.Assert(err, ErrorMatches, `yaml: write error: some write error`) // Data not flushed yet
  415. }
  416. type errorWriter struct{}
  417. func (errorWriter) Write([]byte) (int, error) {
  418. return 0, fmt.Errorf("some write error")
  419. }
  420. var marshalErrorTests = []struct {
  421. value interface{}
  422. error string
  423. panic string
  424. }{{
  425. value: &struct {
  426. B int
  427. inlineB ",inline"
  428. }{1, inlineB{2, inlineC{3}}},
  429. panic: `duplicated key 'b' in struct struct \{ B int; .*`,
  430. }, {
  431. value: &struct {
  432. A int
  433. B map[string]int ",inline"
  434. }{1, map[string]int{"a": 2}},
  435. panic: `cannot have key "a" in inlined map: conflicts with struct field`,
  436. }}
  437. func (s *S) TestMarshalErrors(c *C) {
  438. for _, item := range marshalErrorTests {
  439. if item.panic != "" {
  440. c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
  441. } else {
  442. _, err := yaml.Marshal(item.value)
  443. c.Assert(err, ErrorMatches, item.error)
  444. }
  445. }
  446. }
  447. func (s *S) TestMarshalTypeCache(c *C) {
  448. var data []byte
  449. var err error
  450. func() {
  451. type T struct{ A int }
  452. data, err = yaml.Marshal(&T{})
  453. c.Assert(err, IsNil)
  454. }()
  455. func() {
  456. type T struct{ B int }
  457. data, err = yaml.Marshal(&T{})
  458. c.Assert(err, IsNil)
  459. }()
  460. c.Assert(string(data), Equals, "b: 0\n")
  461. }
  462. var marshalerTests = []struct {
  463. data string
  464. value interface{}
  465. }{
  466. {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
  467. {"_:\n- 1\n- A\n", []interface{}{1, "A"}},
  468. {"_: 10\n", 10},
  469. {"_: null\n", nil},
  470. {"_: BAR!\n", "BAR!"},
  471. }
  472. type marshalerType struct {
  473. value interface{}
  474. }
  475. func (o marshalerType) MarshalText() ([]byte, error) {
  476. panic("MarshalText called on type with MarshalYAML")
  477. }
  478. func (o marshalerType) MarshalYAML() (interface{}, error) {
  479. return o.value, nil
  480. }
  481. type marshalerValue struct {
  482. Field marshalerType "_"
  483. }
  484. func (s *S) TestMarshaler(c *C) {
  485. for _, item := range marshalerTests {
  486. obj := &marshalerValue{}
  487. obj.Field.value = item.value
  488. data, err := yaml.Marshal(obj)
  489. c.Assert(err, IsNil)
  490. c.Assert(string(data), Equals, string(item.data))
  491. }
  492. }
  493. func (s *S) TestMarshalerWholeDocument(c *C) {
  494. obj := &marshalerType{}
  495. obj.value = map[string]string{"hello": "world!"}
  496. data, err := yaml.Marshal(obj)
  497. c.Assert(err, IsNil)
  498. c.Assert(string(data), Equals, "hello: world!\n")
  499. }
  500. type failingMarshaler struct{}
  501. func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
  502. return nil, failingErr
  503. }
  504. func (s *S) TestMarshalerError(c *C) {
  505. _, err := yaml.Marshal(&failingMarshaler{})
  506. c.Assert(err, Equals, failingErr)
  507. }
  508. func (s *S) TestSetIndent(c *C) {
  509. var buf bytes.Buffer
  510. enc := yaml.NewEncoder(&buf)
  511. enc.SetIndent(8)
  512. err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}})
  513. c.Assert(err, Equals, nil)
  514. err = enc.Close()
  515. c.Assert(err, Equals, nil)
  516. c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n")
  517. }
  518. func (s *S) TestSortedOutput(c *C) {
  519. order := []interface{}{
  520. false,
  521. true,
  522. 1,
  523. uint(1),
  524. 1.0,
  525. 1.1,
  526. 1.2,
  527. 2,
  528. uint(2),
  529. 2.0,
  530. 2.1,
  531. "",
  532. ".1",
  533. ".2",
  534. ".a",
  535. "1",
  536. "2",
  537. "a!10",
  538. "a/0001",
  539. "a/002",
  540. "a/3",
  541. "a/10",
  542. "a/11",
  543. "a/0012",
  544. "a/100",
  545. "a~10",
  546. "ab/1",
  547. "b/1",
  548. "b/01",
  549. "b/2",
  550. "b/02",
  551. "b/3",
  552. "b/03",
  553. "b1",
  554. "b01",
  555. "b3",
  556. "c2.10",
  557. "c10.2",
  558. "d1",
  559. "d7",
  560. "d7abc",
  561. "d12",
  562. "d12a",
  563. }
  564. m := make(map[interface{}]int)
  565. for _, k := range order {
  566. m[k] = 1
  567. }
  568. data, err := yaml.Marshal(m)
  569. c.Assert(err, IsNil)
  570. out := "\n" + string(data)
  571. last := 0
  572. for i, k := range order {
  573. repr := fmt.Sprint(k)
  574. if s, ok := k.(string); ok {
  575. if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
  576. repr = `"` + repr + `"`
  577. }
  578. }
  579. index := strings.Index(out, "\n"+repr+":")
  580. if index == -1 {
  581. c.Fatalf("%#v is not in the output: %#v", k, out)
  582. }
  583. if index < last {
  584. c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
  585. }
  586. last = index
  587. }
  588. }
  589. func newTime(t time.Time) *time.Time {
  590. return &t
  591. }