encode_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. package toml
  2. import (
  3. "bytes"
  4. "fmt"
  5. "log"
  6. "net"
  7. "testing"
  8. "time"
  9. )
  10. func TestEncodeRoundTrip(t *testing.T) {
  11. type Config struct {
  12. Age int
  13. Cats []string
  14. Pi float64
  15. Perfection []int
  16. DOB time.Time
  17. Ipaddress net.IP
  18. }
  19. var inputs = Config{
  20. 13,
  21. []string{"one", "two", "three"},
  22. 3.145,
  23. []int{11, 2, 3, 4},
  24. time.Now(),
  25. net.ParseIP("192.168.59.254"),
  26. }
  27. var firstBuffer bytes.Buffer
  28. e := NewEncoder(&firstBuffer)
  29. err := e.Encode(inputs)
  30. if err != nil {
  31. t.Fatal(err)
  32. }
  33. var outputs Config
  34. if _, err := Decode(firstBuffer.String(), &outputs); err != nil {
  35. log.Printf("Could not decode:\n-----\n%s\n-----\n",
  36. firstBuffer.String())
  37. t.Fatal(err)
  38. }
  39. // could test each value individually, but I'm lazy
  40. var secondBuffer bytes.Buffer
  41. e2 := NewEncoder(&secondBuffer)
  42. err = e2.Encode(outputs)
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. if firstBuffer.String() != secondBuffer.String() {
  47. t.Error(
  48. firstBuffer.String(),
  49. "\n\n is not identical to\n\n",
  50. secondBuffer.String())
  51. }
  52. }
  53. // XXX(burntsushi)
  54. // I think these tests probably should be removed. They are good, but they
  55. // ought to be obsolete by toml-test.
  56. func TestEncode(t *testing.T) {
  57. type Embedded struct {
  58. Int int `toml:"_int"`
  59. }
  60. type NonStruct int
  61. date := time.Date(2014, 5, 11, 20, 30, 40, 0, time.FixedZone("IST", 3600))
  62. dateStr := "2014-05-11T19:30:40Z"
  63. tests := map[string]struct {
  64. input interface{}
  65. wantOutput string
  66. wantError error
  67. }{
  68. "bool field": {
  69. input: struct {
  70. BoolTrue bool
  71. BoolFalse bool
  72. }{true, false},
  73. wantOutput: "BoolTrue = true\nBoolFalse = false\n",
  74. },
  75. "int fields": {
  76. input: struct {
  77. Int int
  78. Int8 int8
  79. Int16 int16
  80. Int32 int32
  81. Int64 int64
  82. }{1, 2, 3, 4, 5},
  83. wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n",
  84. },
  85. "uint fields": {
  86. input: struct {
  87. Uint uint
  88. Uint8 uint8
  89. Uint16 uint16
  90. Uint32 uint32
  91. Uint64 uint64
  92. }{1, 2, 3, 4, 5},
  93. wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" +
  94. "\nUint64 = 5\n",
  95. },
  96. "float fields": {
  97. input: struct {
  98. Float32 float32
  99. Float64 float64
  100. }{1.5, 2.5},
  101. wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n",
  102. },
  103. "string field": {
  104. input: struct{ String string }{"foo"},
  105. wantOutput: "String = \"foo\"\n",
  106. },
  107. "string field and unexported field": {
  108. input: struct {
  109. String string
  110. unexported int
  111. }{"foo", 0},
  112. wantOutput: "String = \"foo\"\n",
  113. },
  114. "datetime field in UTC": {
  115. input: struct{ Date time.Time }{date},
  116. wantOutput: fmt.Sprintf("Date = %s\n", dateStr),
  117. },
  118. "datetime field as primitive": {
  119. // Using a map here to fail if isStructOrMap() returns true for
  120. // time.Time.
  121. input: map[string]interface{}{
  122. "Date": date,
  123. "Int": 1,
  124. },
  125. wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr),
  126. },
  127. "array fields": {
  128. input: struct {
  129. IntArray0 [0]int
  130. IntArray3 [3]int
  131. }{[0]int{}, [3]int{1, 2, 3}},
  132. wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n",
  133. },
  134. "slice fields": {
  135. input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{
  136. nil, []int{}, []int{1, 2, 3},
  137. },
  138. wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n",
  139. },
  140. "datetime slices": {
  141. input: struct{ DatetimeSlice []time.Time }{
  142. []time.Time{date, date},
  143. },
  144. wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n",
  145. dateStr, dateStr),
  146. },
  147. "nested arrays and slices": {
  148. input: struct {
  149. SliceOfArrays [][2]int
  150. ArrayOfSlices [2][]int
  151. SliceOfArraysOfSlices [][2][]int
  152. ArrayOfSlicesOfArrays [2][][2]int
  153. SliceOfMixedArrays [][2]interface{}
  154. ArrayOfMixedSlices [2][]interface{}
  155. }{
  156. [][2]int{{1, 2}, {3, 4}},
  157. [2][]int{{1, 2}, {3, 4}},
  158. [][2][]int{
  159. {
  160. {1, 2}, {3, 4},
  161. },
  162. {
  163. {5, 6}, {7, 8},
  164. },
  165. },
  166. [2][][2]int{
  167. {
  168. {1, 2}, {3, 4},
  169. },
  170. {
  171. {5, 6}, {7, 8},
  172. },
  173. },
  174. [][2]interface{}{
  175. {1, 2}, {"a", "b"},
  176. },
  177. [2][]interface{}{
  178. {1, 2}, {"a", "b"},
  179. },
  180. },
  181. wantOutput: `SliceOfArrays = [[1, 2], [3, 4]]
  182. ArrayOfSlices = [[1, 2], [3, 4]]
  183. SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
  184. ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
  185. SliceOfMixedArrays = [[1, 2], ["a", "b"]]
  186. ArrayOfMixedSlices = [[1, 2], ["a", "b"]]
  187. `,
  188. },
  189. "empty slice": {
  190. input: struct{ Empty []interface{} }{[]interface{}{}},
  191. wantOutput: "Empty = []\n",
  192. },
  193. "(error) slice with element type mismatch (string and integer)": {
  194. input: struct{ Mixed []interface{} }{[]interface{}{1, "a"}},
  195. wantError: errArrayMixedElementTypes,
  196. },
  197. "(error) slice with element type mismatch (integer and float)": {
  198. input: struct{ Mixed []interface{} }{[]interface{}{1, 2.5}},
  199. wantError: errArrayMixedElementTypes,
  200. },
  201. "slice with elems of differing Go types, same TOML types": {
  202. input: struct {
  203. MixedInts []interface{}
  204. MixedFloats []interface{}
  205. }{
  206. []interface{}{
  207. int(1), int8(2), int16(3), int32(4), int64(5),
  208. uint(1), uint8(2), uint16(3), uint32(4), uint64(5),
  209. },
  210. []interface{}{float32(1.5), float64(2.5)},
  211. },
  212. wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" +
  213. "MixedFloats = [1.5, 2.5]\n",
  214. },
  215. "(error) slice w/ element type mismatch (one is nested array)": {
  216. input: struct{ Mixed []interface{} }{
  217. []interface{}{1, []interface{}{2}},
  218. },
  219. wantError: errArrayMixedElementTypes,
  220. },
  221. "(error) slice with 1 nil element": {
  222. input: struct{ NilElement1 []interface{} }{[]interface{}{nil}},
  223. wantError: errArrayNilElement,
  224. },
  225. "(error) slice with 1 nil element (and other non-nil elements)": {
  226. input: struct{ NilElement []interface{} }{
  227. []interface{}{1, nil},
  228. },
  229. wantError: errArrayNilElement,
  230. },
  231. "simple map": {
  232. input: map[string]int{"a": 1, "b": 2},
  233. wantOutput: "a = 1\nb = 2\n",
  234. },
  235. "map with interface{} value type": {
  236. input: map[string]interface{}{"a": 1, "b": "c"},
  237. wantOutput: "a = 1\nb = \"c\"\n",
  238. },
  239. "map with interface{} value type, some of which are structs": {
  240. input: map[string]interface{}{
  241. "a": struct{ Int int }{2},
  242. "b": 1,
  243. },
  244. wantOutput: "b = 1\n\n[a]\n Int = 2\n",
  245. },
  246. "nested map": {
  247. input: map[string]map[string]int{
  248. "a": {"b": 1},
  249. "c": {"d": 2},
  250. },
  251. wantOutput: "[a]\n b = 1\n\n[c]\n d = 2\n",
  252. },
  253. "nested struct": {
  254. input: struct{ Struct struct{ Int int } }{
  255. struct{ Int int }{1},
  256. },
  257. wantOutput: "[Struct]\n Int = 1\n",
  258. },
  259. "nested struct and non-struct field": {
  260. input: struct {
  261. Struct struct{ Int int }
  262. Bool bool
  263. }{struct{ Int int }{1}, true},
  264. wantOutput: "Bool = true\n\n[Struct]\n Int = 1\n",
  265. },
  266. "2 nested structs": {
  267. input: struct{ Struct1, Struct2 struct{ Int int } }{
  268. struct{ Int int }{1}, struct{ Int int }{2},
  269. },
  270. wantOutput: "[Struct1]\n Int = 1\n\n[Struct2]\n Int = 2\n",
  271. },
  272. "deeply nested structs": {
  273. input: struct {
  274. Struct1, Struct2 struct{ Struct3 *struct{ Int int } }
  275. }{
  276. struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}},
  277. struct{ Struct3 *struct{ Int int } }{nil},
  278. },
  279. wantOutput: "[Struct1]\n [Struct1.Struct3]\n Int = 1" +
  280. "\n\n[Struct2]\n",
  281. },
  282. "nested struct with nil struct elem": {
  283. input: struct {
  284. Struct struct{ Inner *struct{ Int int } }
  285. }{
  286. struct{ Inner *struct{ Int int } }{nil},
  287. },
  288. wantOutput: "[Struct]\n",
  289. },
  290. "nested struct with no fields": {
  291. input: struct {
  292. Struct struct{ Inner struct{} }
  293. }{
  294. struct{ Inner struct{} }{struct{}{}},
  295. },
  296. wantOutput: "[Struct]\n [Struct.Inner]\n",
  297. },
  298. "struct with tags": {
  299. input: struct {
  300. Struct struct {
  301. Int int `toml:"_int"`
  302. } `toml:"_struct"`
  303. Bool bool `toml:"_bool"`
  304. }{
  305. struct {
  306. Int int `toml:"_int"`
  307. }{1}, true,
  308. },
  309. wantOutput: "_bool = true\n\n[_struct]\n _int = 1\n",
  310. },
  311. "embedded struct": {
  312. input: struct{ Embedded }{Embedded{1}},
  313. wantOutput: "_int = 1\n",
  314. },
  315. "embedded *struct": {
  316. input: struct{ *Embedded }{&Embedded{1}},
  317. wantOutput: "_int = 1\n",
  318. },
  319. "nested embedded struct": {
  320. input: struct {
  321. Struct struct{ Embedded } `toml:"_struct"`
  322. }{struct{ Embedded }{Embedded{1}}},
  323. wantOutput: "[_struct]\n _int = 1\n",
  324. },
  325. "nested embedded *struct": {
  326. input: struct {
  327. Struct struct{ *Embedded } `toml:"_struct"`
  328. }{struct{ *Embedded }{&Embedded{1}}},
  329. wantOutput: "[_struct]\n _int = 1\n",
  330. },
  331. "array of tables": {
  332. input: struct {
  333. Structs []*struct{ Int int } `toml:"struct"`
  334. }{
  335. []*struct{ Int int }{{1}, {3}},
  336. },
  337. wantOutput: "[[struct]]\n Int = 1\n\n[[struct]]\n Int = 3\n",
  338. },
  339. "array of tables order": {
  340. input: map[string]interface{}{
  341. "map": map[string]interface{}{
  342. "zero": 5,
  343. "arr": []map[string]int{
  344. map[string]int{
  345. "friend": 5,
  346. },
  347. },
  348. },
  349. },
  350. wantOutput: "[map]\n zero = 5\n\n [[map.arr]]\n friend = 5\n",
  351. },
  352. "(error) top-level slice": {
  353. input: []struct{ Int int }{{1}, {2}, {3}},
  354. wantError: errNoKey,
  355. },
  356. "(error) slice of slice": {
  357. input: struct {
  358. Slices [][]struct{ Int int }
  359. }{
  360. [][]struct{ Int int }{{{1}}, {{2}}, {{3}}},
  361. },
  362. wantError: errArrayNoTable,
  363. },
  364. "(error) map no string key": {
  365. input: map[int]string{1: ""},
  366. wantError: errNonString,
  367. },
  368. "(error) anonymous non-struct": {
  369. input: struct{ NonStruct }{5},
  370. wantError: errAnonNonStruct,
  371. },
  372. "(error) empty key name": {
  373. input: map[string]int{"": 1},
  374. wantError: errAnything,
  375. },
  376. "(error) empty map name": {
  377. input: map[string]interface{}{
  378. "": map[string]int{"v": 1},
  379. },
  380. wantError: errAnything,
  381. },
  382. }
  383. for label, test := range tests {
  384. encodeExpected(t, label, test.input, test.wantOutput, test.wantError)
  385. }
  386. }
  387. func TestEncodeNestedTableArrays(t *testing.T) {
  388. type song struct {
  389. Name string `toml:"name"`
  390. }
  391. type album struct {
  392. Name string `toml:"name"`
  393. Songs []song `toml:"songs"`
  394. }
  395. type springsteen struct {
  396. Albums []album `toml:"albums"`
  397. }
  398. value := springsteen{
  399. []album{
  400. {"Born to Run",
  401. []song{{"Jungleland"}, {"Meeting Across the River"}}},
  402. {"Born in the USA",
  403. []song{{"Glory Days"}, {"Dancing in the Dark"}}},
  404. },
  405. }
  406. expected := `[[albums]]
  407. name = "Born to Run"
  408. [[albums.songs]]
  409. name = "Jungleland"
  410. [[albums.songs]]
  411. name = "Meeting Across the River"
  412. [[albums]]
  413. name = "Born in the USA"
  414. [[albums.songs]]
  415. name = "Glory Days"
  416. [[albums.songs]]
  417. name = "Dancing in the Dark"
  418. `
  419. encodeExpected(t, "nested table arrays", value, expected, nil)
  420. }
  421. func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) {
  422. type Alpha struct {
  423. V int
  424. }
  425. type Beta struct {
  426. V int
  427. }
  428. type Conf struct {
  429. V int
  430. A Alpha
  431. B []Beta
  432. }
  433. val := Conf{
  434. V: 1,
  435. A: Alpha{2},
  436. B: []Beta{{3}},
  437. }
  438. expected := "V = 1\n\n[A]\n V = 2\n\n[[B]]\n V = 3\n"
  439. encodeExpected(t, "array hash with normal hash order", val, expected, nil)
  440. }
  441. func TestEncodeWithOmitEmpty(t *testing.T) {
  442. type simple struct {
  443. User string `toml:"user"`
  444. Pass string `toml:"password,omitempty"`
  445. }
  446. value := simple{"Testing", ""}
  447. expected := fmt.Sprintf("user = %q\n", value.User)
  448. encodeExpected(t, "simple with omitempty, is empty", value, expected, nil)
  449. value.Pass = "some password"
  450. expected = fmt.Sprintf("user = %q\npassword = %q\n", value.User, value.Pass)
  451. encodeExpected(t, "simple with omitempty, not empty", value, expected, nil)
  452. }
  453. func TestEncodeWithOmitZero(t *testing.T) {
  454. type simple struct {
  455. Number int `toml:"number,omitzero"`
  456. Real float64 `toml:"real,omitzero"`
  457. Unsigned uint `toml:"unsigned,omitzero"`
  458. }
  459. value := simple{0, 0.0, uint(0)}
  460. expected := ""
  461. encodeExpected(t, "simple with omitzero, all zero", value, expected, nil)
  462. value.Number = 10
  463. value.Real = 20
  464. value.Unsigned = 5
  465. expected = `number = 10
  466. real = 20.0
  467. unsigned = 5
  468. `
  469. encodeExpected(t, "simple with omitzero, non-zero", value, expected, nil)
  470. }
  471. func encodeExpected(
  472. t *testing.T, label string, val interface{}, wantStr string, wantErr error,
  473. ) {
  474. var buf bytes.Buffer
  475. enc := NewEncoder(&buf)
  476. err := enc.Encode(val)
  477. if err != wantErr {
  478. if wantErr != nil {
  479. if wantErr == errAnything && err != nil {
  480. return
  481. }
  482. t.Errorf("%s: want Encode error %v, got %v", label, wantErr, err)
  483. } else {
  484. t.Errorf("%s: Encode failed: %s", label, err)
  485. }
  486. }
  487. if err != nil {
  488. return
  489. }
  490. if got := buf.String(); wantStr != got {
  491. t.Errorf("%s: want\n-----\n%q\n-----\nbut got\n-----\n%q\n-----\n",
  492. label, wantStr, got)
  493. }
  494. }
  495. func ExampleEncoder_Encode() {
  496. date, _ := time.Parse(time.RFC822, "14 Mar 10 18:00 UTC")
  497. var config = map[string]interface{}{
  498. "date": date,
  499. "counts": []int{1, 1, 2, 3, 5, 8},
  500. "hash": map[string]string{
  501. "key1": "val1",
  502. "key2": "val2",
  503. },
  504. }
  505. buf := new(bytes.Buffer)
  506. if err := NewEncoder(buf).Encode(config); err != nil {
  507. log.Fatal(err)
  508. }
  509. fmt.Println(buf.String())
  510. // Output:
  511. // counts = [1, 1, 2, 3, 5, 8]
  512. // date = 2010-03-14T18:00:00Z
  513. //
  514. // [hash]
  515. // key1 = "val1"
  516. // key2 = "val2"
  517. }