Black Lives Matter. Support the Equal Justice Initiative.

Source file src/database/sql/convert_test.go

Documentation: database/sql

     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  	"strings"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  var someTime = time.Unix(123, 0)
    19  var answer int64 = 42
    20  
    21  type (
    22  	userDefined       float64
    23  	userDefinedSlice  []int
    24  	userDefinedString string
    25  )
    26  
    27  type conversionTest struct {
    28  	s, d interface{} // source and destination
    29  
    30  	// following are used if they're non-zero
    31  	wantint    int64
    32  	wantuint   uint64
    33  	wantstr    string
    34  	wantbytes  []byte
    35  	wantraw    RawBytes
    36  	wantf32    float32
    37  	wantf64    float64
    38  	wanttime   time.Time
    39  	wantbool   bool // used if d is of type *bool
    40  	wanterr    string
    41  	wantiface  interface{}
    42  	wantptr    *int64 // if non-nil, *d's pointed value must be equal to *wantptr
    43  	wantnil    bool   // if true, *d must be *int64(nil)
    44  	wantusrdef userDefined
    45  	wantusrstr userDefinedString
    46  }
    47  
    48  // Target variables for scanning into.
    49  var (
    50  	scanstr    string
    51  	scanbytes  []byte
    52  	scanraw    RawBytes
    53  	scanint    int
    54  	scanint8   int8
    55  	scanint16  int16
    56  	scanint32  int32
    57  	scanuint8  uint8
    58  	scanuint16 uint16
    59  	scanbool   bool
    60  	scanf32    float32
    61  	scanf64    float64
    62  	scantime   time.Time
    63  	scanptr    *int64
    64  	scaniface  interface{}
    65  )
    66  
    67  func conversionTests() []conversionTest {
    68  	// Return a fresh instance to test so "go test -count 2" works correctly.
    69  	return []conversionTest{
    70  		// Exact conversions (destination pointer type matches source type)
    71  		{s: "foo", d: &scanstr, wantstr: "foo"},
    72  		{s: 123, d: &scanint, wantint: 123},
    73  		{s: someTime, d: &scantime, wanttime: someTime},
    74  
    75  		// To strings
    76  		{s: "string", d: &scanstr, wantstr: "string"},
    77  		{s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
    78  		{s: 123, d: &scanstr, wantstr: "123"},
    79  		{s: int8(123), d: &scanstr, wantstr: "123"},
    80  		{s: int64(123), d: &scanstr, wantstr: "123"},
    81  		{s: uint8(123), d: &scanstr, wantstr: "123"},
    82  		{s: uint16(123), d: &scanstr, wantstr: "123"},
    83  		{s: uint32(123), d: &scanstr, wantstr: "123"},
    84  		{s: uint64(123), d: &scanstr, wantstr: "123"},
    85  		{s: 1.5, d: &scanstr, wantstr: "1.5"},
    86  
    87  		// From time.Time:
    88  		{s: time.Unix(1, 0).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01Z"},
    89  		{s: time.Unix(1453874597, 0).In(time.FixedZone("here", -3600*8)), d: &scanstr, wantstr: "2016-01-26T22:03:17-08:00"},
    90  		{s: time.Unix(1, 2).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01.000000002Z"},
    91  		{s: time.Time{}, d: &scanstr, wantstr: "0001-01-01T00:00:00Z"},
    92  		{s: time.Unix(1, 2).UTC(), d: &scanbytes, wantbytes: []byte("1970-01-01T00:00:01.000000002Z")},
    93  		{s: time.Unix(1, 2).UTC(), d: &scaniface, wantiface: time.Unix(1, 2).UTC()},
    94  
    95  		// To []byte
    96  		{s: nil, d: &scanbytes, wantbytes: nil},
    97  		{s: "string", d: &scanbytes, wantbytes: []byte("string")},
    98  		{s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
    99  		{s: 123, d: &scanbytes, wantbytes: []byte("123")},
   100  		{s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
   101  		{s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
   102  		{s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
   103  		{s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
   104  		{s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
   105  		{s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
   106  		{s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
   107  
   108  		// To RawBytes
   109  		{s: nil, d: &scanraw, wantraw: nil},
   110  		{s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
   111  		{s: "string", d: &scanraw, wantraw: RawBytes("string")},
   112  		{s: 123, d: &scanraw, wantraw: RawBytes("123")},
   113  		{s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
   114  		{s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
   115  		{s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
   116  		{s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
   117  		{s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
   118  		{s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
   119  		{s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
   120  		// time.Time has been placed here to check that the RawBytes slice gets
   121  		// correctly reset when calling time.Time.AppendFormat.
   122  		{s: time.Unix(2, 5).UTC(), d: &scanraw, wantraw: RawBytes("1970-01-01T00:00:02.000000005Z")},
   123  
   124  		// Strings to integers
   125  		{s: "255", d: &scanuint8, wantuint: 255},
   126  		{s: "256", d: &scanuint8, wanterr: "converting driver.Value type string (\"256\") to a uint8: value out of range"},
   127  		{s: "256", d: &scanuint16, wantuint: 256},
   128  		{s: "-1", d: &scanint, wantint: -1},
   129  		{s: "foo", d: &scanint, wanterr: "converting driver.Value type string (\"foo\") to a int: invalid syntax"},
   130  
   131  		// int64 to smaller integers
   132  		{s: int64(5), d: &scanuint8, wantuint: 5},
   133  		{s: int64(256), d: &scanuint8, wanterr: "converting driver.Value type int64 (\"256\") to a uint8: value out of range"},
   134  		{s: int64(256), d: &scanuint16, wantuint: 256},
   135  		{s: int64(65536), d: &scanuint16, wanterr: "converting driver.Value type int64 (\"65536\") to a uint16: value out of range"},
   136  
   137  		// True bools
   138  		{s: true, d: &scanbool, wantbool: true},
   139  		{s: "True", d: &scanbool, wantbool: true},
   140  		{s: "TRUE", d: &scanbool, wantbool: true},
   141  		{s: "1", d: &scanbool, wantbool: true},
   142  		{s: 1, d: &scanbool, wantbool: true},
   143  		{s: int64(1), d: &scanbool, wantbool: true},
   144  		{s: uint16(1), d: &scanbool, wantbool: true},
   145  
   146  		// False bools
   147  		{s: false, d: &scanbool, wantbool: false},
   148  		{s: "false", d: &scanbool, wantbool: false},
   149  		{s: "FALSE", d: &scanbool, wantbool: false},
   150  		{s: "0", d: &scanbool, wantbool: false},
   151  		{s: 0, d: &scanbool, wantbool: false},
   152  		{s: int64(0), d: &scanbool, wantbool: false},
   153  		{s: uint16(0), d: &scanbool, wantbool: false},
   154  
   155  		// Not bools
   156  		{s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
   157  		{s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
   158  
   159  		// Floats
   160  		{s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
   161  		{s: int64(1), d: &scanf64, wantf64: float64(1)},
   162  		{s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
   163  		{s: "1.5", d: &scanf32, wantf32: float32(1.5)},
   164  		{s: "1.5", d: &scanf64, wantf64: float64(1.5)},
   165  
   166  		// Pointers
   167  		{s: interface{}(nil), d: &scanptr, wantnil: true},
   168  		{s: int64(42), d: &scanptr, wantptr: &answer},
   169  
   170  		// To interface{}
   171  		{s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
   172  		{s: int64(1), d: &scaniface, wantiface: int64(1)},
   173  		{s: "str", d: &scaniface, wantiface: "str"},
   174  		{s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
   175  		{s: true, d: &scaniface, wantiface: true},
   176  		{s: nil, d: &scaniface},
   177  		{s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
   178  
   179  		// To a user-defined type
   180  		{s: 1.5, d: new(userDefined), wantusrdef: 1.5},
   181  		{s: int64(123), d: new(userDefined), wantusrdef: 123},
   182  		{s: "1.5", d: new(userDefined), wantusrdef: 1.5},
   183  		{s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`},
   184  		{s: "str", d: new(userDefinedString), wantusrstr: "str"},
   185  
   186  		// Other errors
   187  		{s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`},
   188  	}
   189  }
   190  
   191  func intPtrValue(intptr interface{}) interface{} {
   192  	return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
   193  }
   194  
   195  func intValue(intptr interface{}) int64 {
   196  	return reflect.Indirect(reflect.ValueOf(intptr)).Int()
   197  }
   198  
   199  func uintValue(intptr interface{}) uint64 {
   200  	return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
   201  }
   202  
   203  func float64Value(ptr interface{}) float64 {
   204  	return *(ptr.(*float64))
   205  }
   206  
   207  func float32Value(ptr interface{}) float32 {
   208  	return *(ptr.(*float32))
   209  }
   210  
   211  func timeValue(ptr interface{}) time.Time {
   212  	return *(ptr.(*time.Time))
   213  }
   214  
   215  func TestConversions(t *testing.T) {
   216  	for n, ct := range conversionTests() {
   217  		err := convertAssign(ct.d, ct.s)
   218  		errstr := ""
   219  		if err != nil {
   220  			errstr = err.Error()
   221  		}
   222  		errf := func(format string, args ...interface{}) {
   223  			base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
   224  			t.Errorf(base+format, args...)
   225  		}
   226  		if errstr != ct.wanterr {
   227  			errf("got error %q, want error %q", errstr, ct.wanterr)
   228  		}
   229  		if ct.wantstr != "" && ct.wantstr != scanstr {
   230  			errf("want string %q, got %q", ct.wantstr, scanstr)
   231  		}
   232  		if ct.wantbytes != nil && string(ct.wantbytes) != string(scanbytes) {
   233  			errf("want byte %q, got %q", ct.wantbytes, scanbytes)
   234  		}
   235  		if ct.wantraw != nil && string(ct.wantraw) != string(scanraw) {
   236  			errf("want RawBytes %q, got %q", ct.wantraw, scanraw)
   237  		}
   238  		if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
   239  			errf("want int %d, got %d", ct.wantint, intValue(ct.d))
   240  		}
   241  		if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
   242  			errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
   243  		}
   244  		if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
   245  			errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
   246  		}
   247  		if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
   248  			errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
   249  		}
   250  		if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
   251  			errf("want bool %v, got %v", ct.wantbool, *bp)
   252  		}
   253  		if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
   254  			errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
   255  		}
   256  		if ct.wantnil && *ct.d.(**int64) != nil {
   257  			errf("want nil, got %v", intPtrValue(ct.d))
   258  		}
   259  		if ct.wantptr != nil {
   260  			if *ct.d.(**int64) == nil {
   261  				errf("want pointer to %v, got nil", *ct.wantptr)
   262  			} else if *ct.wantptr != intPtrValue(ct.d) {
   263  				errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
   264  			}
   265  		}
   266  		if ifptr, ok := ct.d.(*interface{}); ok {
   267  			if !reflect.DeepEqual(ct.wantiface, scaniface) {
   268  				errf("want interface %#v, got %#v", ct.wantiface, scaniface)
   269  				continue
   270  			}
   271  			if srcBytes, ok := ct.s.([]byte); ok {
   272  				dstBytes := (*ifptr).([]byte)
   273  				if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
   274  					errf("copy into interface{} didn't copy []byte data")
   275  				}
   276  			}
   277  		}
   278  		if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
   279  			errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
   280  		}
   281  		if len(ct.wantusrstr) != 0 && ct.wantusrstr != *ct.d.(*userDefinedString) {
   282  			errf("want userDefined %q, got %q", ct.wantusrstr, *ct.d.(*userDefinedString))
   283  		}
   284  	}
   285  }
   286  
   287  func TestNullString(t *testing.T) {
   288  	var ns NullString
   289  	convertAssign(&ns, []byte("foo"))
   290  	if !ns.Valid {
   291  		t.Errorf("expecting not null")
   292  	}
   293  	if ns.String != "foo" {
   294  		t.Errorf("expecting foo; got %q", ns.String)
   295  	}
   296  	convertAssign(&ns, nil)
   297  	if ns.Valid {
   298  		t.Errorf("expecting null on nil")
   299  	}
   300  	if ns.String != "" {
   301  		t.Errorf("expecting blank on nil; got %q", ns.String)
   302  	}
   303  }
   304  
   305  type valueConverterTest struct {
   306  	c       driver.ValueConverter
   307  	in, out interface{}
   308  	err     string
   309  }
   310  
   311  var valueConverterTests = []valueConverterTest{
   312  	{driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
   313  	{driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
   314  }
   315  
   316  func TestValueConverters(t *testing.T) {
   317  	for i, tt := range valueConverterTests {
   318  		out, err := tt.c.ConvertValue(tt.in)
   319  		goterr := ""
   320  		if err != nil {
   321  			goterr = err.Error()
   322  		}
   323  		if goterr != tt.err {
   324  			t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
   325  				i, tt.c, tt.in, tt.in, goterr, tt.err)
   326  		}
   327  		if tt.err != "" {
   328  			continue
   329  		}
   330  		if !reflect.DeepEqual(out, tt.out) {
   331  			t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
   332  				i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
   333  		}
   334  	}
   335  }
   336  
   337  // Tests that assigning to RawBytes doesn't allocate (and also works).
   338  func TestRawBytesAllocs(t *testing.T) {
   339  	var tests = []struct {
   340  		name string
   341  		in   interface{}
   342  		want string
   343  	}{
   344  		{"uint64", uint64(12345678), "12345678"},
   345  		{"uint32", uint32(1234), "1234"},
   346  		{"uint16", uint16(12), "12"},
   347  		{"uint8", uint8(1), "1"},
   348  		{"uint", uint(123), "123"},
   349  		{"int", int(123), "123"},
   350  		{"int8", int8(1), "1"},
   351  		{"int16", int16(12), "12"},
   352  		{"int32", int32(1234), "1234"},
   353  		{"int64", int64(12345678), "12345678"},
   354  		{"float32", float32(1.5), "1.5"},
   355  		{"float64", float64(64), "64"},
   356  		{"bool", false, "false"},
   357  		{"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"},
   358  	}
   359  
   360  	buf := make(RawBytes, 10)
   361  	test := func(name string, in interface{}, want string) {
   362  		if err := convertAssign(&buf, in); err != nil {
   363  			t.Fatalf("%s: convertAssign = %v", name, err)
   364  		}
   365  		match := len(buf) == len(want)
   366  		if match {
   367  			for i, b := range buf {
   368  				if want[i] != b {
   369  					match = false
   370  					break
   371  				}
   372  			}
   373  		}
   374  		if !match {
   375  			t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
   376  		}
   377  	}
   378  
   379  	n := testing.AllocsPerRun(100, func() {
   380  		for _, tt := range tests {
   381  			test(tt.name, tt.in, tt.want)
   382  		}
   383  	})
   384  
   385  	// The numbers below are only valid for 64-bit interface word sizes,
   386  	// and gc. With 32-bit words there are more convT2E allocs, and
   387  	// with gccgo, only pointers currently go in interface data.
   388  	// So only care on amd64 gc for now.
   389  	measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc"
   390  
   391  	if n > 0.5 && measureAllocs {
   392  		t.Fatalf("allocs = %v; want 0", n)
   393  	}
   394  
   395  	// This one involves a convT2E allocation, string -> interface{}
   396  	n = testing.AllocsPerRun(100, func() {
   397  		test("string", "foo", "foo")
   398  	})
   399  	if n > 1.5 && measureAllocs {
   400  		t.Fatalf("allocs = %v; want max 1", n)
   401  	}
   402  }
   403  
   404  // https://golang.org/issues/13905
   405  func TestUserDefinedBytes(t *testing.T) {
   406  	type userDefinedBytes []byte
   407  	var u userDefinedBytes
   408  	v := []byte("foo")
   409  
   410  	convertAssign(&u, v)
   411  	if &u[0] == &v[0] {
   412  		t.Fatal("userDefinedBytes got potentially dirty driver memory")
   413  	}
   414  }
   415  
   416  type Valuer_V string
   417  
   418  func (v Valuer_V) Value() (driver.Value, error) {
   419  	return strings.ToUpper(string(v)), nil
   420  }
   421  
   422  type Valuer_P string
   423  
   424  func (p *Valuer_P) Value() (driver.Value, error) {
   425  	if p == nil {
   426  		return "nil-to-str", nil
   427  	}
   428  	return strings.ToUpper(string(*p)), nil
   429  }
   430  
   431  func TestDriverArgs(t *testing.T) {
   432  	var nilValuerVPtr *Valuer_V
   433  	var nilValuerPPtr *Valuer_P
   434  	var nilStrPtr *string
   435  	tests := []struct {
   436  		args []interface{}
   437  		want []driver.NamedValue
   438  	}{
   439  		0: {
   440  			args: []interface{}{Valuer_V("foo")},
   441  			want: []driver.NamedValue{
   442  				{
   443  					Ordinal: 1,
   444  					Value:   "FOO",
   445  				},
   446  			},
   447  		},
   448  		1: {
   449  			args: []interface{}{nilValuerVPtr},
   450  			want: []driver.NamedValue{
   451  				{
   452  					Ordinal: 1,
   453  					Value:   nil,
   454  				},
   455  			},
   456  		},
   457  		2: {
   458  			args: []interface{}{nilValuerPPtr},
   459  			want: []driver.NamedValue{
   460  				{
   461  					Ordinal: 1,
   462  					Value:   "nil-to-str",
   463  				},
   464  			},
   465  		},
   466  		3: {
   467  			args: []interface{}{"plain-str"},
   468  			want: []driver.NamedValue{
   469  				{
   470  					Ordinal: 1,
   471  					Value:   "plain-str",
   472  				},
   473  			},
   474  		},
   475  		4: {
   476  			args: []interface{}{nilStrPtr},
   477  			want: []driver.NamedValue{
   478  				{
   479  					Ordinal: 1,
   480  					Value:   nil,
   481  				},
   482  			},
   483  		},
   484  	}
   485  	for i, tt := range tests {
   486  		ds := &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{nil}}
   487  		got, err := driverArgsConnLocked(nil, ds, tt.args)
   488  		if err != nil {
   489  			t.Errorf("test[%d]: %v", i, err)
   490  			continue
   491  		}
   492  		if !reflect.DeepEqual(got, tt.want) {
   493  			t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
   494  		}
   495  	}
   496  }
   497  
   498  type dec struct {
   499  	form        byte
   500  	neg         bool
   501  	coefficient [16]byte
   502  	exponent    int32
   503  }
   504  
   505  func (d dec) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
   506  	coef := make([]byte, 16)
   507  	copy(coef, d.coefficient[:])
   508  	return d.form, d.neg, coef, d.exponent
   509  }
   510  
   511  func (d *dec) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
   512  	switch form {
   513  	default:
   514  		return fmt.Errorf("unknown form %d", form)
   515  	case 1, 2:
   516  		d.form = form
   517  		d.neg = negative
   518  		return nil
   519  	case 0:
   520  	}
   521  	d.form = form
   522  	d.neg = negative
   523  	d.exponent = exponent
   524  
   525  	// This isn't strictly correct, as the extra bytes could be all zero,
   526  	// ignore this for this test.
   527  	if len(coefficient) > 16 {
   528  		return fmt.Errorf("coefficient too large")
   529  	}
   530  	copy(d.coefficient[:], coefficient)
   531  
   532  	return nil
   533  }
   534  
   535  type decFinite struct {
   536  	neg         bool
   537  	coefficient [16]byte
   538  	exponent    int32
   539  }
   540  
   541  func (d decFinite) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
   542  	coef := make([]byte, 16)
   543  	copy(coef, d.coefficient[:])
   544  	return 0, d.neg, coef, d.exponent
   545  }
   546  
   547  func (d *decFinite) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
   548  	switch form {
   549  	default:
   550  		return fmt.Errorf("unknown form %d", form)
   551  	case 1, 2:
   552  		return fmt.Errorf("unsupported form %d", form)
   553  	case 0:
   554  	}
   555  	d.neg = negative
   556  	d.exponent = exponent
   557  
   558  	// This isn't strictly correct, as the extra bytes could be all zero,
   559  	// ignore this for this test.
   560  	if len(coefficient) > 16 {
   561  		return fmt.Errorf("coefficient too large")
   562  	}
   563  	copy(d.coefficient[:], coefficient)
   564  
   565  	return nil
   566  }
   567  
   568  func TestDecimal(t *testing.T) {
   569  	list := []struct {
   570  		name string
   571  		in   decimalDecompose
   572  		out  dec
   573  		err  bool
   574  	}{
   575  		{name: "same", in: dec{exponent: -6}, out: dec{exponent: -6}},
   576  
   577  		// Ensure reflection is not used to assign the value by using different types.
   578  		{name: "diff", in: decFinite{exponent: -6}, out: dec{exponent: -6}},
   579  
   580  		{name: "bad-form", in: dec{form: 200}, err: true},
   581  	}
   582  	for _, item := range list {
   583  		t.Run(item.name, func(t *testing.T) {
   584  			out := dec{}
   585  			err := convertAssign(&out, item.in)
   586  			if item.err {
   587  				if err == nil {
   588  					t.Fatalf("unexpected nil error")
   589  				}
   590  				return
   591  			}
   592  			if err != nil {
   593  				t.Fatalf("unexpected error: %v", err)
   594  			}
   595  			if !reflect.DeepEqual(out, item.out) {
   596  				t.Fatalf("got %#v want %#v", out, item.out)
   597  			}
   598  		})
   599  	}
   600  }
   601  

View as plain text