github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/database/sql/convert_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sql 6 7 import ( 8 "database/sql/driver" 9 "fmt" 10 "reflect" 11 "runtime" 12 "testing" 13 "time" 14 ) 15 16 var someTime = time.Unix(123, 0) 17 var answer int64 = 42 18 19 type userDefined float64 20 21 type userDefinedSlice []int 22 23 type conversionTest struct { 24 s, d interface{} // source and destination 25 26 // following are used if they're non-zero 27 wantint int64 28 wantuint uint64 29 wantstr string 30 wantbytes []byte 31 wantraw RawBytes 32 wantf32 float32 33 wantf64 float64 34 wanttime time.Time 35 wantbool bool // used if d is of type *bool 36 wanterr string 37 wantiface interface{} 38 wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr 39 wantnil bool // if true, *d must be *int64(nil) 40 wantusrdef userDefined 41 } 42 43 // Target variables for scanning into. 44 var ( 45 scanstr string 46 scanbytes []byte 47 scanraw RawBytes 48 scanint int 49 scanint8 int8 50 scanint16 int16 51 scanint32 int32 52 scanuint8 uint8 53 scanuint16 uint16 54 scanbool bool 55 scanf32 float32 56 scanf64 float64 57 scantime time.Time 58 scanptr *int64 59 scaniface interface{} 60 ) 61 62 var conversionTests = []conversionTest{ 63 // Exact conversions (destination pointer type matches source type) 64 {s: "foo", d: &scanstr, wantstr: "foo"}, 65 {s: 123, d: &scanint, wantint: 123}, 66 {s: someTime, d: &scantime, wanttime: someTime}, 67 68 // To strings 69 {s: "string", d: &scanstr, wantstr: "string"}, 70 {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, 71 {s: 123, d: &scanstr, wantstr: "123"}, 72 {s: int8(123), d: &scanstr, wantstr: "123"}, 73 {s: int64(123), d: &scanstr, wantstr: "123"}, 74 {s: uint8(123), d: &scanstr, wantstr: "123"}, 75 {s: uint16(123), d: &scanstr, wantstr: "123"}, 76 {s: uint32(123), d: &scanstr, wantstr: "123"}, 77 {s: uint64(123), d: &scanstr, wantstr: "123"}, 78 {s: 1.5, d: &scanstr, wantstr: "1.5"}, 79 80 // To []byte 81 {s: nil, d: &scanbytes, wantbytes: nil}, 82 {s: "string", d: &scanbytes, wantbytes: []byte("string")}, 83 {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")}, 84 {s: 123, d: &scanbytes, wantbytes: []byte("123")}, 85 {s: int8(123), d: &scanbytes, wantbytes: []byte("123")}, 86 {s: int64(123), d: &scanbytes, wantbytes: []byte("123")}, 87 {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")}, 88 {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")}, 89 {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")}, 90 {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")}, 91 {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")}, 92 93 // To RawBytes 94 {s: nil, d: &scanraw, wantraw: nil}, 95 {s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")}, 96 {s: 123, d: &scanraw, wantraw: RawBytes("123")}, 97 {s: int8(123), d: &scanraw, wantraw: RawBytes("123")}, 98 {s: int64(123), d: &scanraw, wantraw: RawBytes("123")}, 99 {s: uint8(123), d: &scanraw, wantraw: RawBytes("123")}, 100 {s: uint16(123), d: &scanraw, wantraw: RawBytes("123")}, 101 {s: uint32(123), d: &scanraw, wantraw: RawBytes("123")}, 102 {s: uint64(123), d: &scanraw, wantraw: RawBytes("123")}, 103 {s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")}, 104 105 // Strings to integers 106 {s: "255", d: &scanuint8, wantuint: 255}, 107 {s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`}, 108 {s: "256", d: &scanuint16, wantuint: 256}, 109 {s: "-1", d: &scanint, wantint: -1}, 110 {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`}, 111 112 // True bools 113 {s: true, d: &scanbool, wantbool: true}, 114 {s: "True", d: &scanbool, wantbool: true}, 115 {s: "TRUE", d: &scanbool, wantbool: true}, 116 {s: "1", d: &scanbool, wantbool: true}, 117 {s: 1, d: &scanbool, wantbool: true}, 118 {s: int64(1), d: &scanbool, wantbool: true}, 119 {s: uint16(1), d: &scanbool, wantbool: true}, 120 121 // False bools 122 {s: false, d: &scanbool, wantbool: false}, 123 {s: "false", d: &scanbool, wantbool: false}, 124 {s: "FALSE", d: &scanbool, wantbool: false}, 125 {s: "0", d: &scanbool, wantbool: false}, 126 {s: 0, d: &scanbool, wantbool: false}, 127 {s: int64(0), d: &scanbool, wantbool: false}, 128 {s: uint16(0), d: &scanbool, wantbool: false}, 129 130 // Not bools 131 {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, 132 {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, 133 134 // Floats 135 {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, 136 {s: int64(1), d: &scanf64, wantf64: float64(1)}, 137 {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, 138 {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, 139 {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, 140 141 // Pointers 142 {s: interface{}(nil), d: &scanptr, wantnil: true}, 143 {s: int64(42), d: &scanptr, wantptr: &answer}, 144 145 // To interface{} 146 {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, 147 {s: int64(1), d: &scaniface, wantiface: int64(1)}, 148 {s: "str", d: &scaniface, wantiface: "str"}, 149 {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, 150 {s: true, d: &scaniface, wantiface: true}, 151 {s: nil, d: &scaniface}, 152 {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)}, 153 154 // To a user-defined type 155 {s: 1.5, d: new(userDefined), wantusrdef: 1.5}, 156 {s: int64(123), d: new(userDefined), wantusrdef: 123}, 157 {s: "1.5", d: new(userDefined), wantusrdef: 1.5}, 158 {s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported driver -> Scan pair: []uint8 -> *sql.userDefinedSlice`}, 159 } 160 161 func intPtrValue(intptr interface{}) interface{} { 162 return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int() 163 } 164 165 func intValue(intptr interface{}) int64 { 166 return reflect.Indirect(reflect.ValueOf(intptr)).Int() 167 } 168 169 func uintValue(intptr interface{}) uint64 { 170 return reflect.Indirect(reflect.ValueOf(intptr)).Uint() 171 } 172 173 func float64Value(ptr interface{}) float64 { 174 return *(ptr.(*float64)) 175 } 176 177 func float32Value(ptr interface{}) float32 { 178 return *(ptr.(*float32)) 179 } 180 181 func timeValue(ptr interface{}) time.Time { 182 return *(ptr.(*time.Time)) 183 } 184 185 func TestConversions(t *testing.T) { 186 for n, ct := range conversionTests { 187 err := convertAssign(ct.d, ct.s) 188 errstr := "" 189 if err != nil { 190 errstr = err.Error() 191 } 192 errf := func(format string, args ...interface{}) { 193 base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) 194 t.Errorf(base+format, args...) 195 } 196 if errstr != ct.wanterr { 197 errf("got error %q, want error %q", errstr, ct.wanterr) 198 } 199 if ct.wantstr != "" && ct.wantstr != scanstr { 200 errf("want string %q, got %q", ct.wantstr, scanstr) 201 } 202 if ct.wantint != 0 && ct.wantint != intValue(ct.d) { 203 errf("want int %d, got %d", ct.wantint, intValue(ct.d)) 204 } 205 if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { 206 errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) 207 } 208 if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { 209 errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) 210 } 211 if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { 212 errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) 213 } 214 if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { 215 errf("want bool %v, got %v", ct.wantbool, *bp) 216 } 217 if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { 218 errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) 219 } 220 if ct.wantnil && *ct.d.(**int64) != nil { 221 errf("want nil, got %v", intPtrValue(ct.d)) 222 } 223 if ct.wantptr != nil { 224 if *ct.d.(**int64) == nil { 225 errf("want pointer to %v, got nil", *ct.wantptr) 226 } else if *ct.wantptr != intPtrValue(ct.d) { 227 errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d)) 228 } 229 } 230 if ifptr, ok := ct.d.(*interface{}); ok { 231 if !reflect.DeepEqual(ct.wantiface, scaniface) { 232 errf("want interface %#v, got %#v", ct.wantiface, scaniface) 233 continue 234 } 235 if srcBytes, ok := ct.s.([]byte); ok { 236 dstBytes := (*ifptr).([]byte) 237 if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] { 238 errf("copy into interface{} didn't copy []byte data") 239 } 240 } 241 } 242 if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) { 243 errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined)) 244 } 245 } 246 } 247 248 func TestNullString(t *testing.T) { 249 var ns NullString 250 convertAssign(&ns, []byte("foo")) 251 if !ns.Valid { 252 t.Errorf("expecting not null") 253 } 254 if ns.String != "foo" { 255 t.Errorf("expecting foo; got %q", ns.String) 256 } 257 convertAssign(&ns, nil) 258 if ns.Valid { 259 t.Errorf("expecting null on nil") 260 } 261 if ns.String != "" { 262 t.Errorf("expecting blank on nil; got %q", ns.String) 263 } 264 } 265 266 type valueConverterTest struct { 267 c driver.ValueConverter 268 in, out interface{} 269 err string 270 } 271 272 var valueConverterTests = []valueConverterTest{ 273 {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""}, 274 {driver.DefaultParameterConverter, NullString{"", false}, nil, ""}, 275 } 276 277 func TestValueConverters(t *testing.T) { 278 for i, tt := range valueConverterTests { 279 out, err := tt.c.ConvertValue(tt.in) 280 goterr := "" 281 if err != nil { 282 goterr = err.Error() 283 } 284 if goterr != tt.err { 285 t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", 286 i, tt.c, tt.in, tt.in, goterr, tt.err) 287 } 288 if tt.err != "" { 289 continue 290 } 291 if !reflect.DeepEqual(out, tt.out) { 292 t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", 293 i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) 294 } 295 } 296 } 297 298 // Tests that assigning to RawBytes doesn't allocate (and also works). 299 func TestRawBytesAllocs(t *testing.T) { 300 var tests = []struct { 301 name string 302 in interface{} 303 want string 304 }{ 305 {"uint64", uint64(12345678), "12345678"}, 306 {"uint32", uint32(1234), "1234"}, 307 {"uint16", uint16(12), "12"}, 308 {"uint8", uint8(1), "1"}, 309 {"uint", uint(123), "123"}, 310 {"int", int(123), "123"}, 311 {"int8", int8(1), "1"}, 312 {"int16", int16(12), "12"}, 313 {"int32", int32(1234), "1234"}, 314 {"int64", int64(12345678), "12345678"}, 315 {"float32", float32(1.5), "1.5"}, 316 {"float64", float64(64), "64"}, 317 {"bool", false, "false"}, 318 } 319 320 buf := make(RawBytes, 10) 321 test := func(name string, in interface{}, want string) { 322 if err := convertAssign(&buf, in); err != nil { 323 t.Fatalf("%s: convertAssign = %v", name, err) 324 } 325 match := len(buf) == len(want) 326 if match { 327 for i, b := range buf { 328 if want[i] != b { 329 match = false 330 break 331 } 332 } 333 } 334 if !match { 335 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want)) 336 } 337 } 338 339 n := testing.AllocsPerRun(100, func() { 340 for _, tt := range tests { 341 test(tt.name, tt.in, tt.want) 342 } 343 }) 344 345 // The numbers below are only valid for 64-bit interface word sizes, 346 // and gc. With 32-bit words there are more convT2E allocs, and 347 // with gccgo, only pointers currently go in interface data. 348 // So only care on amd64 gc for now. 349 measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc" 350 351 if n > 0.5 && measureAllocs { 352 t.Fatalf("allocs = %v; want 0", n) 353 } 354 355 // This one involves a convT2E allocation, string -> interface{} 356 n = testing.AllocsPerRun(100, func() { 357 test("string", "foo", "foo") 358 }) 359 if n > 1.5 && measureAllocs { 360 t.Fatalf("allocs = %v; want max 1", n) 361 } 362 }