scan_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. var scanConversionErrorTests = []struct {
  56. src interface{}
  57. dest interface{}
  58. }{
  59. {[]byte("1234"), byte(0)},
  60. {int64(1234), byte(0)},
  61. {[]byte("-1"), byte(0)},
  62. {int64(-1), byte(0)},
  63. {[]byte("junk"), false},
  64. {redis.Error("blah"), false},
  65. }
  66. func TestScanConversion(t *testing.T) {
  67. for _, tt := range scanConversionTests {
  68. values := []interface{}{tt.src}
  69. dest := reflect.New(reflect.TypeOf(tt.dest))
  70. values, err := redis.Scan(values, dest.Interface())
  71. if err != nil {
  72. t.Errorf("Scan(%v) returned error %v", tt, err)
  73. continue
  74. }
  75. if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
  76. t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
  77. }
  78. }
  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 argsTests = []struct {
  185. title string
  186. actual redis.Args
  187. expected redis.Args
  188. }{
  189. {"struct ptr",
  190. redis.Args{}.AddFlat(&struct {
  191. I int `redis:"i"`
  192. U uint `redis:"u"`
  193. S string `redis:"s"`
  194. P []byte `redis:"p"`
  195. Bt bool
  196. Bf bool
  197. }{
  198. -1234, 5678, "hello", []byte("world"), true, false,
  199. }),
  200. redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "Bt", true, "Bf", false},
  201. },
  202. {"struct",
  203. redis.Args{}.AddFlat(struct{ I int }{123}),
  204. redis.Args{"I", 123},
  205. },
  206. {"slice",
  207. redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
  208. redis.Args{1, "a", "b", "c", 2},
  209. },
  210. }
  211. func TestArgs(t *testing.T) {
  212. for _, tt := range argsTests {
  213. if !reflect.DeepEqual(tt.actual, tt.expected) {
  214. t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
  215. }
  216. }
  217. }
  218. func ExampleArgs() {
  219. c, err := dial()
  220. if err != nil {
  221. panic(err)
  222. }
  223. defer c.Close()
  224. var p1, p2 struct {
  225. Title string `redis:"title"`
  226. Author string `redis:"author"`
  227. Body string `redis:"body"`
  228. }
  229. p1.Title = "Example"
  230. p1.Author = "Gary"
  231. p1.Body = "Hello"
  232. if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
  233. panic(err)
  234. }
  235. m := map[string]string{
  236. "title": "Example2",
  237. "author": "Steve",
  238. "body": "Map",
  239. }
  240. if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
  241. panic(err)
  242. }
  243. for _, id := range []string{"id1", "id2"} {
  244. v, err := redis.Values(c.Do("HGETALL", id))
  245. if err != nil {
  246. panic(err)
  247. }
  248. if err := redis.ScanStruct(v, &p2); err != nil {
  249. panic(err)
  250. }
  251. fmt.Printf("%+v\n", p2)
  252. }
  253. // Output:
  254. // {Title:Example Author:Gary Body:Hello}
  255. // {Title:Example2 Author:Steve Body:Map}
  256. }