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