struct_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // Copyright 2014 Unknwon
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package ini
  15. import (
  16. "bytes"
  17. "fmt"
  18. "strings"
  19. "testing"
  20. "time"
  21. . "github.com/smartystreets/goconvey/convey"
  22. )
  23. type testNested struct {
  24. Cities []string `delim:"|"`
  25. Visits []time.Time
  26. Years []int
  27. Numbers []int64
  28. Ages []uint
  29. Populations []uint64
  30. Coordinates []float64
  31. Note string
  32. Unused int `ini:"-"`
  33. }
  34. type testEmbeded struct {
  35. GPA float64
  36. }
  37. type testStruct struct {
  38. Name string `ini:"NAME"`
  39. Age int
  40. Male bool
  41. Money float64
  42. Born time.Time
  43. Time time.Duration `ini:"Duration"`
  44. Others testNested
  45. *testEmbeded `ini:"grade"`
  46. Unused int `ini:"-"`
  47. Unsigned uint
  48. Omitted bool `ini:"omitthis,omitempty"`
  49. }
  50. const _CONF_DATA_STRUCT = `
  51. NAME = Unknwon
  52. Age = 21
  53. Male = true
  54. Money = 1.25
  55. Born = 1993-10-07T20:17:05Z
  56. Duration = 2h45m
  57. Unsigned = 3
  58. omitthis = true
  59. [Others]
  60. Cities = HangZhou|Boston
  61. Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
  62. Years = 1993,1994
  63. Numbers = 10010,10086
  64. Ages = 18,19
  65. Populations = 12345678,98765432
  66. Coordinates = 192.168,10.11
  67. Note = Hello world!
  68. [grade]
  69. GPA = 2.8
  70. [foo.bar]
  71. Here = there
  72. When = then
  73. `
  74. type unsupport struct {
  75. Byte byte
  76. }
  77. type unsupport2 struct {
  78. Others struct {
  79. Cities byte
  80. }
  81. }
  82. type unsupport3 struct {
  83. Cities byte
  84. }
  85. type unsupport4 struct {
  86. *unsupport3 `ini:"Others"`
  87. }
  88. type defaultValue struct {
  89. Name string
  90. Age int
  91. Male bool
  92. Money float64
  93. Born time.Time
  94. Cities []string
  95. }
  96. type fooBar struct {
  97. Here, When string
  98. }
  99. const _INVALID_DATA_CONF_STRUCT = `
  100. Name =
  101. Age = age
  102. Male = 123
  103. Money = money
  104. Born = nil
  105. Cities =
  106. `
  107. func Test_Struct(t *testing.T) {
  108. Convey("Map to struct", t, func() {
  109. Convey("Map file to struct", func() {
  110. ts := new(testStruct)
  111. So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
  112. So(ts.Name, ShouldEqual, "Unknwon")
  113. So(ts.Age, ShouldEqual, 21)
  114. So(ts.Male, ShouldBeTrue)
  115. So(ts.Money, ShouldEqual, 1.25)
  116. So(ts.Unsigned, ShouldEqual, 3)
  117. t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
  118. So(err, ShouldBeNil)
  119. So(ts.Born.String(), ShouldEqual, t.String())
  120. dur, err := time.ParseDuration("2h45m")
  121. So(err, ShouldBeNil)
  122. So(ts.Time.Seconds(), ShouldEqual, dur.Seconds())
  123. So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston")
  124. So(ts.Others.Visits[0].String(), ShouldEqual, t.String())
  125. So(fmt.Sprint(ts.Others.Years), ShouldEqual, "[1993 1994]")
  126. So(fmt.Sprint(ts.Others.Numbers), ShouldEqual, "[10010 10086]")
  127. So(fmt.Sprint(ts.Others.Ages), ShouldEqual, "[18 19]")
  128. So(fmt.Sprint(ts.Others.Populations), ShouldEqual, "[12345678 98765432]")
  129. So(fmt.Sprint(ts.Others.Coordinates), ShouldEqual, "[192.168 10.11]")
  130. So(ts.Others.Note, ShouldEqual, "Hello world!")
  131. So(ts.testEmbeded.GPA, ShouldEqual, 2.8)
  132. })
  133. Convey("Map section to struct", func() {
  134. foobar := new(fooBar)
  135. f, err := Load([]byte(_CONF_DATA_STRUCT))
  136. So(err, ShouldBeNil)
  137. So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil)
  138. So(foobar.Here, ShouldEqual, "there")
  139. So(foobar.When, ShouldEqual, "then")
  140. })
  141. Convey("Map to non-pointer struct", func() {
  142. cfg, err := Load([]byte(_CONF_DATA_STRUCT))
  143. So(err, ShouldBeNil)
  144. So(cfg, ShouldNotBeNil)
  145. So(cfg.MapTo(testStruct{}), ShouldNotBeNil)
  146. })
  147. Convey("Map to unsupported type", func() {
  148. cfg, err := Load([]byte(_CONF_DATA_STRUCT))
  149. So(err, ShouldBeNil)
  150. So(cfg, ShouldNotBeNil)
  151. cfg.NameMapper = func(raw string) string {
  152. if raw == "Byte" {
  153. return "NAME"
  154. }
  155. return raw
  156. }
  157. So(cfg.MapTo(&unsupport{}), ShouldNotBeNil)
  158. So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil)
  159. So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil)
  160. })
  161. Convey("Map to omitempty field", func() {
  162. ts := new(testStruct)
  163. So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
  164. So(ts.Omitted, ShouldEqual, true)
  165. })
  166. Convey("Map from invalid data source", func() {
  167. So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
  168. })
  169. Convey("Map to wrong types and gain default values", func() {
  170. cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT))
  171. So(err, ShouldBeNil)
  172. t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
  173. So(err, ShouldBeNil)
  174. dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}}
  175. So(cfg.MapTo(dv), ShouldBeNil)
  176. So(dv.Name, ShouldEqual, "Joe")
  177. So(dv.Age, ShouldEqual, 10)
  178. So(dv.Male, ShouldBeTrue)
  179. So(dv.Money, ShouldEqual, 1.25)
  180. So(dv.Born.String(), ShouldEqual, t.String())
  181. So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston")
  182. })
  183. })
  184. Convey("Reflect from struct", t, func() {
  185. type Embeded struct {
  186. Dates []time.Time `delim:"|"`
  187. Places []string
  188. Years []int
  189. Numbers []int64
  190. Ages []uint
  191. Populations []uint64
  192. Coordinates []float64
  193. None []int
  194. }
  195. type Author struct {
  196. Name string `ini:"NAME"`
  197. Male bool
  198. Age int
  199. Height uint
  200. GPA float64
  201. Date time.Time
  202. NeverMind string `ini:"-"`
  203. *Embeded `ini:"infos"`
  204. }
  205. t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
  206. So(err, ShouldBeNil)
  207. a := &Author{"Unknwon", true, 21, 100, 2.8, t, "",
  208. &Embeded{
  209. []time.Time{t, t},
  210. []string{"HangZhou", "Boston"},
  211. []int{1993, 1994},
  212. []int64{10010, 10086},
  213. []uint{18, 19},
  214. []uint64{12345678, 98765432},
  215. []float64{192.168, 10.11},
  216. []int{},
  217. }}
  218. cfg := Empty()
  219. So(ReflectFrom(cfg, a), ShouldBeNil)
  220. var buf bytes.Buffer
  221. _, err = cfg.WriteTo(&buf)
  222. So(err, ShouldBeNil)
  223. So(buf.String(), ShouldEqual, `NAME = Unknwon
  224. Male = true
  225. Age = 21
  226. Height = 100
  227. GPA = 2.8
  228. Date = 1993-10-07T20:17:05Z
  229. [infos]
  230. Dates = 1993-10-07T20:17:05Z|1993-10-07T20:17:05Z
  231. Places = HangZhou,Boston
  232. Years = 1993,1994
  233. Numbers = 10010,10086
  234. Ages = 18,19
  235. Populations = 12345678,98765432
  236. Coordinates = 192.168,10.11
  237. None =
  238. `)
  239. Convey("Reflect from non-point struct", func() {
  240. So(ReflectFrom(cfg, Author{}), ShouldNotBeNil)
  241. })
  242. Convey("Reflect from struct with omitempty", func() {
  243. cfg := Empty()
  244. type SpecialStruct struct {
  245. FirstName string `ini:"first_name"`
  246. LastName string `ini:"last_name"`
  247. JustOmitMe string `ini:"omitempty"`
  248. LastLogin time.Time `ini:"last_login,omitempty"`
  249. LastLogin2 time.Time `ini:",omitempty"`
  250. NotEmpty int `ini:"omitempty"`
  251. }
  252. So(ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil)
  253. var buf bytes.Buffer
  254. _, err = cfg.WriteTo(&buf)
  255. So(buf.String(), ShouldEqual, `first_name = John
  256. last_name = Doe
  257. omitempty = 9
  258. `)
  259. })
  260. })
  261. }
  262. type testMapper struct {
  263. PackageName string
  264. }
  265. func Test_NameGetter(t *testing.T) {
  266. Convey("Test name mappers", t, func() {
  267. So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
  268. cfg, err := Load([]byte("PACKAGE_NAME=ini"))
  269. So(err, ShouldBeNil)
  270. So(cfg, ShouldNotBeNil)
  271. cfg.NameMapper = AllCapsUnderscore
  272. tg := new(testMapper)
  273. So(cfg.MapTo(tg), ShouldBeNil)
  274. So(tg.PackageName, ShouldEqual, "ini")
  275. })
  276. }