decode_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. package toml
  2. import (
  3. "fmt"
  4. "log"
  5. "reflect"
  6. "testing"
  7. "time"
  8. )
  9. func init() {
  10. log.SetFlags(0)
  11. }
  12. var testSimple = `
  13. age = 250
  14. andrew = "gallant"
  15. kait = "brady"
  16. now = 1987-07-05T05:45:00Z
  17. yesOrNo = true
  18. pi = 3.14
  19. colors = [
  20. ["red", "green", "blue"],
  21. ["cyan", "magenta", "yellow", "black"],
  22. ]
  23. [Annoying.Cats]
  24. plato = "smelly"
  25. cauchy = "stupido"
  26. `
  27. type kitties struct {
  28. Plato string
  29. Cauchy string
  30. }
  31. type simple struct {
  32. Age int
  33. Colors [][]string
  34. Pi float64
  35. YesOrNo bool
  36. Now time.Time
  37. Andrew string
  38. Kait string
  39. Annoying map[string]kitties
  40. }
  41. func TestDecode(t *testing.T) {
  42. var val simple
  43. md, err := Decode(testSimple, &val)
  44. if err != nil {
  45. t.Fatal(err)
  46. }
  47. testf("Is 'Annoying.Cats.plato' defined? %v\n",
  48. md.IsDefined("Annoying", "Cats", "plato"))
  49. testf("Is 'Cats.Stinky' defined? %v\n", md.IsDefined("Cats", "Stinky"))
  50. testf("Type of 'colors'? %s\n\n", md.Type("colors"))
  51. testf("%v\n", val)
  52. }
  53. var tomlTableArrays = `
  54. [[albums]]
  55. name = "Born to Run"
  56. [[albums.songs]]
  57. name = "Jungleland"
  58. [[albums.songs]]
  59. name = "Meeting Across the River"
  60. [[albums]]
  61. name = "Born in the USA"
  62. [[albums.songs]]
  63. name = "Glory Days"
  64. [[albums.songs]]
  65. name = "Dancing in the Dark"
  66. `
  67. type Music struct {
  68. Albums []Album
  69. }
  70. type Album struct {
  71. Name string
  72. Songs []Song
  73. }
  74. type Song struct {
  75. Name string
  76. }
  77. func TestTableArrays(t *testing.T) {
  78. expected := Music{[]Album{
  79. {"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
  80. {"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
  81. }}
  82. var got Music
  83. if _, err := Decode(tomlTableArrays, &got); err != nil {
  84. t.Fatal(err)
  85. }
  86. if !reflect.DeepEqual(expected, got) {
  87. t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
  88. }
  89. }
  90. // Case insensitive matching tests.
  91. // A bit more comprehensive than needed given the current implementation,
  92. // but implementations change.
  93. // Probably still missing demonstrations of some ugly corner cases regarding
  94. // case insensitive matching and multiple fields.
  95. var caseToml = `
  96. tOpString = "string"
  97. tOpInt = 1
  98. tOpFloat = 1.1
  99. tOpBool = true
  100. tOpdate = 2006-01-02T15:04:05Z
  101. tOparray = [ "array" ]
  102. Match = "i should be in Match only"
  103. MatcH = "i should be in MatcH only"
  104. Field = "neat"
  105. FielD = "messy"
  106. once = "just once"
  107. [nEst.eD]
  108. nEstedString = "another string"
  109. `
  110. type Insensitive struct {
  111. TopString string
  112. TopInt int
  113. TopFloat float64
  114. TopBool bool
  115. TopDate time.Time
  116. TopArray []string
  117. Match string
  118. MatcH string
  119. Field string
  120. Once string
  121. OncE string
  122. Nest InsensitiveNest
  123. }
  124. type InsensitiveNest struct {
  125. Ed InsensitiveEd
  126. }
  127. type InsensitiveEd struct {
  128. NestedString string
  129. }
  130. func TestCase(t *testing.T) {
  131. tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5])
  132. if err != nil {
  133. panic(err)
  134. }
  135. expected := Insensitive{
  136. TopString: "string",
  137. TopInt: 1,
  138. TopFloat: 1.1,
  139. TopBool: true,
  140. TopDate: tme,
  141. TopArray: []string{"array"},
  142. MatcH: "i should be in MatcH only",
  143. Match: "i should be in Match only",
  144. Field: "neat", // encoding/json would store "messy" here
  145. Once: "just once",
  146. OncE: "just once", // wait, what?
  147. Nest: InsensitiveNest{
  148. Ed: InsensitiveEd{NestedString: "another string"},
  149. },
  150. }
  151. var got Insensitive
  152. _, err = Decode(caseToml, &got)
  153. if err != nil {
  154. t.Fatal(err)
  155. }
  156. if !reflect.DeepEqual(expected, got) {
  157. t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
  158. }
  159. }
  160. func TestPointers(t *testing.T) {
  161. type Object struct {
  162. Type string
  163. Description string
  164. }
  165. type Dict struct {
  166. NamedObject map[string]*Object
  167. BaseObject *Object
  168. Strptr *string
  169. Strptrs []*string
  170. }
  171. s1, s2, s3 := "blah", "abc", "def"
  172. expected := &Dict{
  173. Strptr: &s1,
  174. Strptrs: []*string{&s2, &s3},
  175. NamedObject: map[string]*Object{
  176. "foo": {"FOO", "fooooo!!!"},
  177. "bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
  178. },
  179. BaseObject: &Object{"BASE", "da base"},
  180. }
  181. ex1 := `
  182. Strptr = "blah"
  183. Strptrs = ["abc", "def"]
  184. [NamedObject.foo]
  185. Type = "FOO"
  186. Description = "fooooo!!!"
  187. [NamedObject.bar]
  188. Type = "BAR"
  189. Description = "ba-ba-ba-ba-barrrr!!!"
  190. [BaseObject]
  191. Type = "BASE"
  192. Description = "da base"
  193. `
  194. dict := new(Dict)
  195. _, err := Decode(ex1, dict)
  196. if err != nil {
  197. t.Errorf("Decode error: %v", err)
  198. }
  199. if !reflect.DeepEqual(expected, dict) {
  200. t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
  201. }
  202. }
  203. func ExamplePrimitiveDecode() {
  204. var md MetaData
  205. var err error
  206. var tomlBlob = `
  207. ranking = ["Springsteen", "J Geils"]
  208. [bands.Springsteen]
  209. started = 1973
  210. albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
  211. [bands.J Geils]
  212. started = 1970
  213. albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
  214. `
  215. type band struct {
  216. Started int
  217. Albums []string
  218. }
  219. type classics struct {
  220. Ranking []string
  221. Bands map[string]Primitive
  222. }
  223. // Do the initial decode. Reflection is delayed on Primitive values.
  224. var music classics
  225. if md, err = Decode(tomlBlob, &music); err != nil {
  226. log.Fatal(err)
  227. }
  228. // MetaData still includes information on Primitive values.
  229. fmt.Printf("Is `bands.Springsteen` defined? %v\n",
  230. md.IsDefined("bands", "Springsteen"))
  231. // Decode primitive data into Go values.
  232. for _, artist := range music.Ranking {
  233. // A band is a primitive value, so we need to decode it to get a
  234. // real `band` value.
  235. primValue := music.Bands[artist]
  236. var aBand band
  237. if err = PrimitiveDecode(primValue, &aBand); err != nil {
  238. log.Fatal(err)
  239. }
  240. fmt.Printf("%s started in %d.\n", artist, aBand.Started)
  241. }
  242. // Output:
  243. // Is `bands.Springsteen` defined? true
  244. // Springsteen started in 1973.
  245. // J Geils started in 1970.
  246. }
  247. func ExampleDecode() {
  248. var tomlBlob = `
  249. # Some comments.
  250. [alpha]
  251. ip = "10.0.0.1"
  252. [alpha.config]
  253. Ports = [ 8001, 8002 ]
  254. Location = "Toronto"
  255. Created = 1987-07-05T05:45:00Z
  256. [beta]
  257. ip = "10.0.0.2"
  258. [beta.config]
  259. Ports = [ 9001, 9002 ]
  260. Location = "New Jersey"
  261. Created = 1887-01-05T05:55:00Z
  262. `
  263. type serverConfig struct {
  264. Ports []int
  265. Location string
  266. Created time.Time
  267. }
  268. type server struct {
  269. IP string `toml:"ip"`
  270. Config serverConfig `toml:"config"`
  271. }
  272. type servers map[string]server
  273. var config servers
  274. if _, err := Decode(tomlBlob, &config); err != nil {
  275. log.Fatal(err)
  276. }
  277. for _, name := range []string{"alpha", "beta"} {
  278. s := config[name]
  279. fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
  280. name, s.IP, s.Config.Location,
  281. s.Config.Created.Format("2006-01-02"))
  282. fmt.Printf("Ports: %v\n", s.Config.Ports)
  283. }
  284. // // Output:
  285. // Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
  286. // Ports: [8001 8002]
  287. // Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
  288. // Ports: [9001 9002]
  289. }