scan_test.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright 2012 Gary Burd
  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 redis_test
  15. import (
  16. "fmt"
  17. "github.com/garyburd/redigo/redis"
  18. "math"
  19. "reflect"
  20. "testing"
  21. )
  22. var scanConversionTests = []struct {
  23. src interface{}
  24. dest interface{}
  25. }{
  26. {[]byte("-inf"), math.Inf(-1)},
  27. {[]byte("+inf"), math.Inf(1)},
  28. {[]byte("0"), float64(0)},
  29. {[]byte("3.14159"), float64(3.14159)},
  30. {[]byte("3.14"), float32(3.14)},
  31. {[]byte("-100"), int(-100)},
  32. {[]byte("101"), int(101)},
  33. {int64(102), int(102)},
  34. {[]byte("103"), uint(103)},
  35. {int64(104), uint(104)},
  36. {[]byte("105"), int8(105)},
  37. {int64(106), int8(106)},
  38. {[]byte("107"), uint8(107)},
  39. {int64(108), uint8(108)},
  40. {[]byte("0"), false},
  41. {int64(0), false},
  42. {[]byte("f"), false},
  43. {[]byte("1"), true},
  44. {int64(1), true},
  45. {[]byte("t"), true},
  46. {[]byte("hello"), "hello"},
  47. {[]byte("world"), []byte("world")},
  48. {[]interface{}{[]byte("foo")}, []string{"foo"}},
  49. {[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
  50. {[]interface{}{[]byte("1")}, []int{1}},
  51. {[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
  52. {[]interface{}{[]byte("1")}, []byte{1}},
  53. {[]interface{}{[]byte("1")}, []bool{true}},
  54. }
  55. func TestScanConversion(t *testing.T) {
  56. for _, tt := range scanConversionTests {
  57. values := []interface{}{tt.src}
  58. dest := reflect.New(reflect.TypeOf(tt.dest))
  59. values, err := redis.Scan(values, dest.Interface())
  60. if err != nil {
  61. t.Errorf("Scan(%v) returned error %v", tt, err)
  62. continue
  63. }
  64. if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
  65. t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
  66. }
  67. }
  68. }
  69. var scanConversionErrorTests = []struct {
  70. src interface{}
  71. dest interface{}
  72. }{
  73. {[]byte("1234"), byte(0)},
  74. {int64(1234), byte(0)},
  75. {[]byte("-1"), byte(0)},
  76. {int64(-1), byte(0)},
  77. {[]byte("junk"), false},
  78. {redis.Error("blah"), false},
  79. }
  80. func TestScanConversionError(t *testing.T) {
  81. for _, tt := range scanConversionErrorTests {
  82. values := []interface{}{tt.src}
  83. dest := reflect.New(reflect.TypeOf(tt.dest))
  84. values, err := redis.Scan(values, dest.Interface())
  85. if err == nil {
  86. t.Errorf("Scan(%v) did not return error", tt)
  87. }
  88. }
  89. }
  90. func ExampleScan() {
  91. c, err := dial()
  92. if err != nil {
  93. panic(err)
  94. }
  95. defer c.Close()
  96. c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
  97. c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
  98. c.Send("HMSET", "album:3", "title", "Beat")
  99. c.Send("LPUSH", "albums", "1")
  100. c.Send("LPUSH", "albums", "2")
  101. c.Send("LPUSH", "albums", "3")
  102. values, err := redis.Values(c.Do("SORT", "albums",
  103. "BY", "album:*->rating",
  104. "GET", "album:*->title",
  105. "GET", "album:*->rating"))
  106. if err != nil {
  107. panic(err)
  108. }
  109. for len(values) > 0 {
  110. var title string
  111. rating := -1 // initialize to illegal value to detect nil.
  112. values, err = redis.Scan(values, &title, &rating)
  113. if err != nil {
  114. panic(err)
  115. }
  116. if rating == -1 {
  117. fmt.Println(title, "not-rated")
  118. } else {
  119. fmt.Println(title, rating)
  120. }
  121. }
  122. // Output:
  123. // Beat not-rated
  124. // Earthbound 1
  125. // Red 5
  126. }
  127. type s0 struct {
  128. X int
  129. Y int `redis:"y"`
  130. Bt bool
  131. }
  132. type s1 struct {
  133. X int `redis:"-"`
  134. I int `redis:"i"`
  135. U uint `redis:"u"`
  136. S string `redis:"s"`
  137. P []byte `redis:"p"`
  138. B bool `redis:"b"`
  139. Bt bool
  140. Bf bool
  141. s0
  142. }
  143. var scanStructTests = []struct {
  144. title string
  145. reply []string
  146. value interface{}
  147. }{
  148. {"basic",
  149. []string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
  150. &s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
  151. },
  152. }
  153. func TestScanStruct(t *testing.T) {
  154. for _, tt := range scanStructTests {
  155. var reply []interface{}
  156. for _, v := range tt.reply {
  157. reply = append(reply, []byte(v))
  158. }
  159. value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())
  160. if err := redis.ScanStruct(reply, value.Interface()); err != nil {
  161. t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
  162. }
  163. if !reflect.DeepEqual(value.Interface(), tt.value) {
  164. t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
  165. }
  166. }
  167. }
  168. func TestBadScanStructArgs(t *testing.T) {
  169. x := []interface{}{"A", "b"}
  170. test := func(v interface{}) {
  171. if err := redis.ScanStruct(x, v); err == nil {
  172. t.Errorf("Expect error for ScanStruct(%T, %T)", x, v)
  173. }
  174. }
  175. test(nil)
  176. var v0 *struct{}
  177. test(v0)
  178. var v1 int
  179. test(&v1)
  180. x = x[:1]
  181. v2 := struct{ A string }{}
  182. test(&v2)
  183. }
  184. var scanSliceTests = []struct {
  185. src []interface{}
  186. fieldNames []string
  187. ok bool
  188. dest interface{}
  189. }{
  190. {
  191. []interface{}{[]byte("1"), nil, []byte("-1")},
  192. nil,
  193. true,
  194. []int{1, 0, -1},
  195. },
  196. {
  197. []interface{}{[]byte("1"), nil, []byte("2")},
  198. nil,
  199. true,
  200. []uint{1, 0, 2},
  201. },
  202. {
  203. []interface{}{[]byte("-1")},
  204. nil,
  205. false,
  206. []uint{1},
  207. },
  208. {
  209. []interface{}{[]byte("hello"), nil, []byte("world")},
  210. nil,
  211. true,
  212. [][]byte{[]byte("hello"), nil, []byte("world")},
  213. },
  214. {
  215. []interface{}{[]byte("hello"), nil, []byte("world")},
  216. nil,
  217. true,
  218. []string{"hello", "", "world"},
  219. },
  220. {
  221. []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
  222. nil,
  223. true,
  224. []struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
  225. },
  226. {
  227. []interface{}{[]byte("a1"), []byte("b1")},
  228. nil,
  229. false,
  230. []struct{ A, B, C string }{{"a1", "b1", ""}},
  231. },
  232. {
  233. []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
  234. nil,
  235. true,
  236. []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
  237. },
  238. {
  239. []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
  240. []string{"A", "B"},
  241. true,
  242. []struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}},
  243. },
  244. }
  245. func TestScanSlice(t *testing.T) {
  246. for _, tt := range scanSliceTests {
  247. typ := reflect.ValueOf(tt.dest).Type()
  248. dest := reflect.New(typ)
  249. err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...)
  250. if tt.ok != (err == nil) {
  251. t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err)
  252. continue
  253. }
  254. if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) {
  255. t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest)
  256. }
  257. }
  258. }
  259. func ExampleScanSlice() {
  260. c, err := dial()
  261. if err != nil {
  262. panic(err)
  263. }
  264. defer c.Close()
  265. c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
  266. c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
  267. c.Send("HMSET", "album:3", "title", "Beat", "rating", 4)
  268. c.Send("LPUSH", "albums", "1")
  269. c.Send("LPUSH", "albums", "2")
  270. c.Send("LPUSH", "albums", "3")
  271. values, err := redis.Values(c.Do("SORT", "albums",
  272. "BY", "album:*->rating",
  273. "GET", "album:*->title",
  274. "GET", "album:*->rating"))
  275. if err != nil {
  276. panic(err)
  277. }
  278. var albums []struct {
  279. Title string
  280. Rating int
  281. }
  282. if err := redis.ScanSlice(values, &albums); err != nil {
  283. panic(err)
  284. }
  285. fmt.Printf("%v\n", albums)
  286. // Output:
  287. // [{Earthbound 1} {Beat 4} {Red 5}]
  288. }
  289. var argsTests = []struct {
  290. title string
  291. actual redis.Args
  292. expected redis.Args
  293. }{
  294. {"struct ptr",
  295. redis.Args{}.AddFlat(&struct {
  296. I int `redis:"i"`
  297. U uint `redis:"u"`
  298. S string `redis:"s"`
  299. P []byte `redis:"p"`
  300. Bt bool
  301. Bf bool
  302. }{
  303. -1234, 5678, "hello", []byte("world"), true, false,
  304. }),
  305. redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "Bt", true, "Bf", false},
  306. },
  307. {"struct",
  308. redis.Args{}.AddFlat(struct{ I int }{123}),
  309. redis.Args{"I", 123},
  310. },
  311. {"slice",
  312. redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
  313. redis.Args{1, "a", "b", "c", 2},
  314. },
  315. }
  316. func TestArgs(t *testing.T) {
  317. for _, tt := range argsTests {
  318. if !reflect.DeepEqual(tt.actual, tt.expected) {
  319. t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
  320. }
  321. }
  322. }
  323. func ExampleArgs() {
  324. c, err := dial()
  325. if err != nil {
  326. panic(err)
  327. }
  328. defer c.Close()
  329. var p1, p2 struct {
  330. Title string `redis:"title"`
  331. Author string `redis:"author"`
  332. Body string `redis:"body"`
  333. }
  334. p1.Title = "Example"
  335. p1.Author = "Gary"
  336. p1.Body = "Hello"
  337. if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
  338. panic(err)
  339. }
  340. m := map[string]string{
  341. "title": "Example2",
  342. "author": "Steve",
  343. "body": "Map",
  344. }
  345. if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
  346. panic(err)
  347. }
  348. for _, id := range []string{"id1", "id2"} {
  349. v, err := redis.Values(c.Do("HGETALL", id))
  350. if err != nil {
  351. panic(err)
  352. }
  353. if err := redis.ScanStruct(v, &p2); err != nil {
  354. panic(err)
  355. }
  356. fmt.Printf("%+v\n", p2)
  357. }
  358. // Output:
  359. // {Title:Example Author:Gary Body:Hello}
  360. // {Title:Example2 Author:Steve Body:Map}
  361. }