Source file src/database/sql/convert_test.go

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

View as plain text