encode_test.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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.v1"
  10. )
  11. var marshalIntTest = 123
  12. var marshalTests = []struct {
  13. value interface{}
  14. data string
  15. }{
  16. {
  17. nil,
  18. "null\n",
  19. }, {
  20. &struct{}{},
  21. "{}\n",
  22. }, {
  23. map[string]string{"v": "hi"},
  24. "v: hi\n",
  25. }, {
  26. map[string]interface{}{"v": "hi"},
  27. "v: hi\n",
  28. }, {
  29. map[string]string{"v": "true"},
  30. "v: \"true\"\n",
  31. }, {
  32. map[string]string{"v": "false"},
  33. "v: \"false\"\n",
  34. }, {
  35. map[string]interface{}{"v": true},
  36. "v: true\n",
  37. }, {
  38. map[string]interface{}{"v": false},
  39. "v: false\n",
  40. }, {
  41. map[string]interface{}{"v": 10},
  42. "v: 10\n",
  43. }, {
  44. map[string]interface{}{"v": -10},
  45. "v: -10\n",
  46. }, {
  47. map[string]uint{"v": 42},
  48. "v: 42\n",
  49. }, {
  50. map[string]interface{}{"v": int64(4294967296)},
  51. "v: 4294967296\n",
  52. }, {
  53. map[string]int64{"v": int64(4294967296)},
  54. "v: 4294967296\n",
  55. }, {
  56. map[string]uint64{"v": 4294967296},
  57. "v: 4294967296\n",
  58. }, {
  59. map[string]interface{}{"v": "10"},
  60. "v: \"10\"\n",
  61. }, {
  62. map[string]interface{}{"v": 0.1},
  63. "v: 0.1\n",
  64. }, {
  65. map[string]interface{}{"v": float64(0.1)},
  66. "v: 0.1\n",
  67. }, {
  68. map[string]interface{}{"v": -0.1},
  69. "v: -0.1\n",
  70. }, {
  71. map[string]interface{}{"v": math.Inf(+1)},
  72. "v: .inf\n",
  73. }, {
  74. map[string]interface{}{"v": math.Inf(-1)},
  75. "v: -.inf\n",
  76. }, {
  77. map[string]interface{}{"v": math.NaN()},
  78. "v: .nan\n",
  79. }, {
  80. map[string]interface{}{"v": nil},
  81. "v: null\n",
  82. }, {
  83. map[string]interface{}{"v": ""},
  84. "v: \"\"\n",
  85. }, {
  86. map[string][]string{"v": []string{"A", "B"}},
  87. "v:\n- A\n- B\n",
  88. }, {
  89. map[string][]string{"v": []string{"A", "B\nC"}},
  90. "v:\n- A\n- |-\n B\n C\n",
  91. }, {
  92. map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
  93. "v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
  94. }, {
  95. map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
  96. "a:\n b: c\n",
  97. }, {
  98. map[string]interface{}{"a": "-"},
  99. "a: '-'\n",
  100. },
  101. // Simple values.
  102. {
  103. &marshalIntTest,
  104. "123\n",
  105. },
  106. // Structures
  107. {
  108. &struct{ Hello string }{"world"},
  109. "hello: world\n",
  110. }, {
  111. &struct {
  112. A struct {
  113. B string
  114. }
  115. }{struct{ B string }{"c"}},
  116. "a:\n b: c\n",
  117. }, {
  118. &struct {
  119. A *struct {
  120. B string
  121. }
  122. }{&struct{ B string }{"c"}},
  123. "a:\n b: c\n",
  124. }, {
  125. &struct {
  126. A *struct {
  127. B string
  128. }
  129. }{},
  130. "a: null\n",
  131. }, {
  132. &struct{ A int }{1},
  133. "a: 1\n",
  134. }, {
  135. &struct{ A []int }{[]int{1, 2}},
  136. "a:\n- 1\n- 2\n",
  137. }, {
  138. &struct {
  139. B int "a"
  140. }{1},
  141. "a: 1\n",
  142. }, {
  143. &struct{ A bool }{true},
  144. "a: true\n",
  145. },
  146. // Conditional flag
  147. {
  148. &struct {
  149. A int "a,omitempty"
  150. B int "b,omitempty"
  151. }{1, 0},
  152. "a: 1\n",
  153. }, {
  154. &struct {
  155. A int "a,omitempty"
  156. B int "b,omitempty"
  157. }{0, 0},
  158. "{}\n",
  159. }, {
  160. &struct {
  161. A *struct{ X int } "a,omitempty"
  162. B int "b,omitempty"
  163. }{nil, 0},
  164. "{}\n",
  165. },
  166. // Flow flag
  167. {
  168. &struct {
  169. A []int "a,flow"
  170. }{[]int{1, 2}},
  171. "a: [1, 2]\n",
  172. }, {
  173. &struct {
  174. A map[string]string "a,flow"
  175. }{map[string]string{"b": "c", "d": "e"}},
  176. "a: {b: c, d: e}\n",
  177. }, {
  178. &struct {
  179. A struct {
  180. B, D string
  181. } "a,flow"
  182. }{struct{ B, D string }{"c", "e"}},
  183. "a: {b: c, d: e}\n",
  184. },
  185. // Unexported field
  186. {
  187. &struct {
  188. u int
  189. A int
  190. }{0, 1},
  191. "a: 1\n",
  192. },
  193. // Ignored field
  194. {
  195. &struct {
  196. A int
  197. B int "-"
  198. }{1, 2},
  199. "a: 1\n",
  200. },
  201. // Struct inlining
  202. {
  203. &struct {
  204. A int
  205. C inlineB `yaml:",inline"`
  206. }{1, inlineB{2, inlineC{3}}},
  207. "a: 1\nb: 2\nc: 3\n",
  208. },
  209. // Duration
  210. {
  211. map[string]time.Duration{"a": 3 * time.Second},
  212. "a: 3s\n",
  213. },
  214. // Issue #24: bug in map merging logic.
  215. {
  216. map[string]string{"a": "<foo>"},
  217. "a: <foo>\n",
  218. },
  219. // Issue #34: marshal unsupported base 60 floats quoted for compatibility
  220. // with old YAML 1.1 parsers.
  221. {
  222. map[string]string{"a": "1:1"},
  223. "a: \"1:1\"\n",
  224. },
  225. // Binary data.
  226. {
  227. map[string]string{"a": "\x00"},
  228. "a: \"\\0\"\n",
  229. }, {
  230. map[string]string{"a": "\x80\x81\x82"},
  231. "a: !!binary gIGC\n",
  232. }, {
  233. map[string]string{"a": strings.Repeat("\x90", 54)},
  234. "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
  235. }, {
  236. map[string]interface{}{"a": typeWithGetter{"!!str", "\x80\x81\x82"}},
  237. "a: !!binary gIGC\n",
  238. },
  239. // Escaping of tags.
  240. {
  241. map[string]interface{}{"a": typeWithGetter{"foo!bar", 1}},
  242. "a: !<foo%21bar> 1\n",
  243. },
  244. }
  245. func (s *S) TestMarshal(c *C) {
  246. for _, item := range marshalTests {
  247. data, err := yaml.Marshal(item.value)
  248. c.Assert(err, IsNil)
  249. c.Assert(string(data), Equals, item.data)
  250. }
  251. }
  252. var marshalErrorTests = []struct {
  253. value interface{}
  254. error string
  255. panic string
  256. }{{
  257. value: &struct {
  258. B int
  259. inlineB ",inline"
  260. }{1, inlineB{2, inlineC{3}}},
  261. panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
  262. }, {
  263. value: typeWithGetter{"!!binary", "\x80"},
  264. error: "YAML error: explicitly tagged !!binary data must be base64-encoded",
  265. }, {
  266. value: typeWithGetter{"!!float", "\x80"},
  267. error: `YAML error: cannot marshal invalid UTF-8 data as !!float`,
  268. }}
  269. func (s *S) TestMarshalErrors(c *C) {
  270. for _, item := range marshalErrorTests {
  271. if item.panic != "" {
  272. c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
  273. } else {
  274. _, err := yaml.Marshal(item.value)
  275. c.Assert(err, ErrorMatches, item.error)
  276. }
  277. }
  278. }
  279. var marshalTaggedIfaceTest interface{} = &struct{ A string }{"B"}
  280. var getterTests = []struct {
  281. data, tag string
  282. value interface{}
  283. }{
  284. {"_:\n hi: there\n", "", map[interface{}]interface{}{"hi": "there"}},
  285. {"_:\n- 1\n- A\n", "", []interface{}{1, "A"}},
  286. {"_: 10\n", "", 10},
  287. {"_: null\n", "", nil},
  288. {"_: !foo BAR!\n", "!foo", "BAR!"},
  289. {"_: !foo 1\n", "!foo", "1"},
  290. {"_: !foo '\"1\"'\n", "!foo", "\"1\""},
  291. {"_: !foo 1.1\n", "!foo", 1.1},
  292. {"_: !foo 1\n", "!foo", 1},
  293. {"_: !foo 1\n", "!foo", uint(1)},
  294. {"_: !foo true\n", "!foo", true},
  295. {"_: !foo\n- A\n- B\n", "!foo", []string{"A", "B"}},
  296. {"_: !foo\n A: B\n", "!foo", map[string]string{"A": "B"}},
  297. {"_: !foo\n a: B\n", "!foo", &marshalTaggedIfaceTest},
  298. }
  299. func (s *S) TestMarshalTypeCache(c *C) {
  300. var data []byte
  301. var err error
  302. func() {
  303. type T struct{ A int }
  304. data, err = yaml.Marshal(&T{})
  305. c.Assert(err, IsNil)
  306. }()
  307. func() {
  308. type T struct{ B int }
  309. data, err = yaml.Marshal(&T{})
  310. c.Assert(err, IsNil)
  311. }()
  312. c.Assert(string(data), Equals, "b: 0\n")
  313. }
  314. type typeWithGetter struct {
  315. tag string
  316. value interface{}
  317. }
  318. func (o typeWithGetter) GetYAML() (tag string, value interface{}) {
  319. return o.tag, o.value
  320. }
  321. type typeWithGetterField struct {
  322. Field typeWithGetter "_"
  323. }
  324. func (s *S) TestMashalWithGetter(c *C) {
  325. for _, item := range getterTests {
  326. obj := &typeWithGetterField{}
  327. obj.Field.tag = item.tag
  328. obj.Field.value = item.value
  329. data, err := yaml.Marshal(obj)
  330. c.Assert(err, IsNil)
  331. c.Assert(string(data), Equals, string(item.data))
  332. }
  333. }
  334. func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) {
  335. obj := &typeWithGetter{}
  336. obj.tag = ""
  337. obj.value = map[string]string{"hello": "world!"}
  338. data, err := yaml.Marshal(obj)
  339. c.Assert(err, IsNil)
  340. c.Assert(string(data), Equals, "hello: world!\n")
  341. }
  342. func (s *S) TestSortedOutput(c *C) {
  343. order := []interface{}{
  344. false,
  345. true,
  346. 1,
  347. uint(1),
  348. 1.0,
  349. 1.1,
  350. 1.2,
  351. 2,
  352. uint(2),
  353. 2.0,
  354. 2.1,
  355. "",
  356. ".1",
  357. ".2",
  358. ".a",
  359. "1",
  360. "2",
  361. "a!10",
  362. "a/2",
  363. "a/10",
  364. "a~10",
  365. "ab/1",
  366. "b/1",
  367. "b/01",
  368. "b/2",
  369. "b/02",
  370. "b/3",
  371. "b/03",
  372. "b1",
  373. "b01",
  374. "b3",
  375. "c2.10",
  376. "c10.2",
  377. "d1",
  378. "d12",
  379. "d12a",
  380. }
  381. m := make(map[interface{}]int)
  382. for _, k := range order {
  383. m[k] = 1
  384. }
  385. data, err := yaml.Marshal(m)
  386. c.Assert(err, IsNil)
  387. out := "\n" + string(data)
  388. last := 0
  389. for i, k := range order {
  390. repr := fmt.Sprint(k)
  391. if s, ok := k.(string); ok {
  392. if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
  393. repr = `"` + repr + `"`
  394. }
  395. }
  396. index := strings.Index(out, "\n"+repr+":")
  397. if index == -1 {
  398. c.Fatalf("%#v is not in the output: %#v", k, out)
  399. }
  400. if index < last {
  401. c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
  402. }
  403. last = index
  404. }
  405. }