encode_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. package yaml_test
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. "strings"
  7. "time"
  8. . "gopkg.in/check.v1"
  9. "gopkg.in/yaml.v2"
  10. "net"
  11. "os"
  12. )
  13. var marshalIntTest = 123
  14. var marshalTests = []struct {
  15. value interface{}
  16. data string
  17. }{
  18. {
  19. nil,
  20. "null\n",
  21. }, {
  22. &struct{}{},
  23. "{}\n",
  24. }, {
  25. map[string]string{"v": "hi"},
  26. "v: hi\n",
  27. }, {
  28. map[string]interface{}{"v": "hi"},
  29. "v: hi\n",
  30. }, {
  31. map[string]string{"v": "true"},
  32. "v: \"true\"\n",
  33. }, {
  34. map[string]string{"v": "false"},
  35. "v: \"false\"\n",
  36. }, {
  37. map[string]interface{}{"v": true},
  38. "v: true\n",
  39. }, {
  40. map[string]interface{}{"v": false},
  41. "v: false\n",
  42. }, {
  43. map[string]interface{}{"v": 10},
  44. "v: 10\n",
  45. }, {
  46. map[string]interface{}{"v": -10},
  47. "v: -10\n",
  48. }, {
  49. map[string]uint{"v": 42},
  50. "v: 42\n",
  51. }, {
  52. map[string]interface{}{"v": int64(4294967296)},
  53. "v: 4294967296\n",
  54. }, {
  55. map[string]int64{"v": int64(4294967296)},
  56. "v: 4294967296\n",
  57. }, {
  58. map[string]uint64{"v": 4294967296},
  59. "v: 4294967296\n",
  60. }, {
  61. map[string]interface{}{"v": "10"},
  62. "v: \"10\"\n",
  63. }, {
  64. map[string]interface{}{"v": 0.1},
  65. "v: 0.1\n",
  66. }, {
  67. map[string]interface{}{"v": float64(0.1)},
  68. "v: 0.1\n",
  69. }, {
  70. map[string]interface{}{"v": -0.1},
  71. "v: -0.1\n",
  72. }, {
  73. map[string]interface{}{"v": math.Inf(+1)},
  74. "v: .inf\n",
  75. }, {
  76. map[string]interface{}{"v": math.Inf(-1)},
  77. "v: -.inf\n",
  78. }, {
  79. map[string]interface{}{"v": math.NaN()},
  80. "v: .nan\n",
  81. }, {
  82. map[string]interface{}{"v": nil},
  83. "v: null\n",
  84. }, {
  85. map[string]interface{}{"v": ""},
  86. "v: \"\"\n",
  87. }, {
  88. map[string][]string{"v": []string{"A", "B"}},
  89. "v:\n- A\n- B\n",
  90. }, {
  91. map[string][]string{"v": []string{"A", "B\nC"}},
  92. "v:\n- A\n- |-\n B\n C\n",
  93. }, {
  94. map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
  95. "v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
  96. }, {
  97. map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
  98. "a:\n b: c\n",
  99. }, {
  100. map[string]interface{}{"a": "-"},
  101. "a: '-'\n",
  102. },
  103. // Simple values.
  104. {
  105. &marshalIntTest,
  106. "123\n",
  107. },
  108. // Structures
  109. {
  110. &struct{ Hello string }{"world"},
  111. "hello: world\n",
  112. }, {
  113. &struct {
  114. A struct {
  115. B string
  116. }
  117. }{struct{ B string }{"c"}},
  118. "a:\n b: c\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. }{},
  132. "a: null\n",
  133. }, {
  134. &struct{ A int }{1},
  135. "a: 1\n",
  136. }, {
  137. &struct{ A []int }{[]int{1, 2}},
  138. "a:\n- 1\n- 2\n",
  139. }, {
  140. &struct {
  141. B int "a"
  142. }{1},
  143. "a: 1\n",
  144. }, {
  145. &struct{ A bool }{true},
  146. "a: true\n",
  147. },
  148. // Conditional flag
  149. {
  150. &struct {
  151. A int "a,omitempty"
  152. B int "b,omitempty"
  153. }{1, 0},
  154. "a: 1\n",
  155. }, {
  156. &struct {
  157. A int "a,omitempty"
  158. B int "b,omitempty"
  159. }{0, 0},
  160. "{}\n",
  161. }, {
  162. &struct {
  163. A *struct{ X, y int } "a,omitempty,flow"
  164. }{&struct{ X, y int }{1, 2}},
  165. "a: {x: 1}\n",
  166. }, {
  167. &struct {
  168. A *struct{ X, y int } "a,omitempty,flow"
  169. }{nil},
  170. "{}\n",
  171. }, {
  172. &struct {
  173. A *struct{ X, y int } "a,omitempty,flow"
  174. }{&struct{ X, y int }{}},
  175. "a: {x: 0}\n",
  176. }, {
  177. &struct {
  178. A struct{ X, y int } "a,omitempty,flow"
  179. }{struct{ X, y int }{1, 2}},
  180. "a: {x: 1}\n",
  181. }, {
  182. &struct {
  183. A struct{ X, y int } "a,omitempty,flow"
  184. }{struct{ X, y int }{0, 1}},
  185. "{}\n",
  186. }, {
  187. &struct {
  188. A float64 "a,omitempty"
  189. B float64 "b,omitempty"
  190. }{1, 0},
  191. "a: 1\n",
  192. },
  193. // Flow flag
  194. {
  195. &struct {
  196. A []int "a,flow"
  197. }{[]int{1, 2}},
  198. "a: [1, 2]\n",
  199. }, {
  200. &struct {
  201. A map[string]string "a,flow"
  202. }{map[string]string{"b": "c", "d": "e"}},
  203. "a: {b: c, d: e}\n",
  204. }, {
  205. &struct {
  206. A struct {
  207. B, D string
  208. } "a,flow"
  209. }{struct{ B, D string }{"c", "e"}},
  210. "a: {b: c, d: e}\n",
  211. },
  212. // Unexported field
  213. {
  214. &struct {
  215. u int
  216. A int
  217. }{0, 1},
  218. "a: 1\n",
  219. },
  220. // Ignored field
  221. {
  222. &struct {
  223. A int
  224. B int "-"
  225. }{1, 2},
  226. "a: 1\n",
  227. },
  228. // Struct inlining
  229. {
  230. &struct {
  231. A int
  232. C inlineB `yaml:",inline"`
  233. }{1, inlineB{2, inlineC{3}}},
  234. "a: 1\nb: 2\nc: 3\n",
  235. },
  236. // Map inlining
  237. {
  238. &struct {
  239. A int
  240. C map[string]int `yaml:",inline"`
  241. }{1, map[string]int{"b": 2, "c": 3}},
  242. "a: 1\nb: 2\nc: 3\n",
  243. },
  244. // Duration
  245. {
  246. map[string]time.Duration{"a": 3 * time.Second},
  247. "a: 3s\n",
  248. },
  249. // Issue #24: bug in map merging logic.
  250. {
  251. map[string]string{"a": "<foo>"},
  252. "a: <foo>\n",
  253. },
  254. // Issue #34: marshal unsupported base 60 floats quoted for compatibility
  255. // with old YAML 1.1 parsers.
  256. {
  257. map[string]string{"a": "1:1"},
  258. "a: \"1:1\"\n",
  259. },
  260. // Binary data.
  261. {
  262. map[string]string{"a": "\x00"},
  263. "a: \"\\0\"\n",
  264. }, {
  265. map[string]string{"a": "\x80\x81\x82"},
  266. "a: !!binary gIGC\n",
  267. }, {
  268. map[string]string{"a": strings.Repeat("\x90", 54)},
  269. "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
  270. },
  271. // Ordered maps.
  272. {
  273. &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
  274. "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n",
  275. },
  276. // Encode unicode as utf-8 rather than in escaped form.
  277. {
  278. map[string]string{"a": "你好"},
  279. "a: 你好\n",
  280. },
  281. // Support encoding.TextMarshaler.
  282. {
  283. map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
  284. "a: 1.2.3.4\n",
  285. },
  286. {
  287. map[string]time.Time{"a": time.Unix(1424801979, 0)},
  288. "a: 2015-02-24T18:19:39Z\n",
  289. },
  290. // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
  291. {
  292. map[string]string{"a": "b: c"},
  293. "a: 'b: c'\n",
  294. },
  295. // Containing hash mark ('#') in string should be quoted
  296. {
  297. map[string]string{"a": "Hello #comment"},
  298. "a: 'Hello #comment'\n",
  299. },
  300. {
  301. map[string]string{"a": "你好 #comment"},
  302. "a: '你好 #comment'\n",
  303. },
  304. }
  305. func (s *S) TestMarshal(c *C) {
  306. defer os.Setenv("TZ", os.Getenv("TZ"))
  307. os.Setenv("TZ", "UTC")
  308. for _, item := range marshalTests {
  309. data, err := yaml.Marshal(item.value)
  310. c.Assert(err, IsNil)
  311. c.Assert(string(data), Equals, item.data)
  312. }
  313. }
  314. var marshalErrorTests = []struct {
  315. value interface{}
  316. error string
  317. panic string
  318. }{{
  319. value: &struct {
  320. B int
  321. inlineB ",inline"
  322. }{1, inlineB{2, inlineC{3}}},
  323. panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
  324. }, {
  325. value: &struct {
  326. A int
  327. B map[string]int ",inline"
  328. }{1, map[string]int{"a": 2}},
  329. panic: `Can't have key "a" in inlined map; conflicts with struct field`,
  330. }}
  331. func (s *S) TestMarshalErrors(c *C) {
  332. for _, item := range marshalErrorTests {
  333. if item.panic != "" {
  334. c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
  335. } else {
  336. _, err := yaml.Marshal(item.value)
  337. c.Assert(err, ErrorMatches, item.error)
  338. }
  339. }
  340. }
  341. func (s *S) TestMarshalTypeCache(c *C) {
  342. var data []byte
  343. var err error
  344. func() {
  345. type T struct{ A int }
  346. data, err = yaml.Marshal(&T{})
  347. c.Assert(err, IsNil)
  348. }()
  349. func() {
  350. type T struct{ B int }
  351. data, err = yaml.Marshal(&T{})
  352. c.Assert(err, IsNil)
  353. }()
  354. c.Assert(string(data), Equals, "b: 0\n")
  355. }
  356. var marshalerTests = []struct {
  357. data string
  358. value interface{}
  359. }{
  360. {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
  361. {"_:\n- 1\n- A\n", []interface{}{1, "A"}},
  362. {"_: 10\n", 10},
  363. {"_: null\n", nil},
  364. {"_: BAR!\n", "BAR!"},
  365. }
  366. type marshalerType struct {
  367. value interface{}
  368. }
  369. func (o marshalerType) MarshalText() ([]byte, error) {
  370. panic("MarshalText called on type with MarshalYAML")
  371. }
  372. func (o marshalerType) MarshalYAML() (interface{}, error) {
  373. return o.value, nil
  374. }
  375. type marshalerValue struct {
  376. Field marshalerType "_"
  377. }
  378. func (s *S) TestMarshaler(c *C) {
  379. for _, item := range marshalerTests {
  380. obj := &marshalerValue{}
  381. obj.Field.value = item.value
  382. data, err := yaml.Marshal(obj)
  383. c.Assert(err, IsNil)
  384. c.Assert(string(data), Equals, string(item.data))
  385. }
  386. }
  387. func (s *S) TestMarshalerWholeDocument(c *C) {
  388. obj := &marshalerType{}
  389. obj.value = map[string]string{"hello": "world!"}
  390. data, err := yaml.Marshal(obj)
  391. c.Assert(err, IsNil)
  392. c.Assert(string(data), Equals, "hello: world!\n")
  393. }
  394. type failingMarshaler struct{}
  395. func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
  396. return nil, failingErr
  397. }
  398. func (s *S) TestMarshalerError(c *C) {
  399. _, err := yaml.Marshal(&failingMarshaler{})
  400. c.Assert(err, Equals, failingErr)
  401. }
  402. func (s *S) TestSortedOutput(c *C) {
  403. order := []interface{}{
  404. false,
  405. true,
  406. 1,
  407. uint(1),
  408. 1.0,
  409. 1.1,
  410. 1.2,
  411. 2,
  412. uint(2),
  413. 2.0,
  414. 2.1,
  415. "",
  416. ".1",
  417. ".2",
  418. ".a",
  419. "1",
  420. "2",
  421. "a!10",
  422. "a/2",
  423. "a/10",
  424. "a~10",
  425. "ab/1",
  426. "b/1",
  427. "b/01",
  428. "b/2",
  429. "b/02",
  430. "b/3",
  431. "b/03",
  432. "b1",
  433. "b01",
  434. "b3",
  435. "c2.10",
  436. "c10.2",
  437. "d1",
  438. "d12",
  439. "d12a",
  440. }
  441. m := make(map[interface{}]int)
  442. for _, k := range order {
  443. m[k] = 1
  444. }
  445. data, err := yaml.Marshal(m)
  446. c.Assert(err, IsNil)
  447. out := "\n" + string(data)
  448. last := 0
  449. for i, k := range order {
  450. repr := fmt.Sprint(k)
  451. if s, ok := k.(string); ok {
  452. if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
  453. repr = `"` + repr + `"`
  454. }
  455. }
  456. index := strings.Index(out, "\n"+repr+":")
  457. if index == -1 {
  458. c.Fatalf("%#v is not in the output: %#v", k, out)
  459. }
  460. if index < last {
  461. c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
  462. }
  463. last = index
  464. }
  465. }