cosmos-sdk/types/int_test.go

518 lines
13 KiB
Go

package types
import (
"math/big"
"math/rand"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
func TestFromInt64(t *testing.T) {
t.Parallel()
for n := 0; n < 20; n++ {
r := rand.Int63()
require.Equal(t, r, NewInt(r).Int64())
}
}
func TestFromUint64(t *testing.T) {
t.Parallel()
for n := 0; n < 20; n++ {
r := rand.Uint64()
require.True(t, NewIntFromUint64(r).IsUint64())
require.Equal(t, r, NewIntFromUint64(r).Uint64())
}
}
func TestIntPanic(t *testing.T) {
t.Parallel()
// Max Int = 2^255-1 = 5.789e+76
// Min Int = -(2^255-1) = -5.789e+76
require.NotPanics(t, func() { NewIntWithDecimal(1, 76) })
i1 := NewIntWithDecimal(1, 76)
require.NotPanics(t, func() { NewIntWithDecimal(2, 76) })
i2 := NewIntWithDecimal(2, 76)
require.NotPanics(t, func() { NewIntWithDecimal(3, 76) })
i3 := NewIntWithDecimal(3, 76)
require.Panics(t, func() { NewIntWithDecimal(6, 76) })
require.Panics(t, func() { NewIntWithDecimal(9, 80) })
// Overflow check
require.NotPanics(t, func() { i1.Add(i1) })
require.NotPanics(t, func() { i2.Add(i2) })
require.Panics(t, func() { i3.Add(i3) })
require.NotPanics(t, func() { i1.Sub(i1.Neg()) })
require.NotPanics(t, func() { i2.Sub(i2.Neg()) })
require.Panics(t, func() { i3.Sub(i3.Neg()) })
require.Panics(t, func() { i1.Mul(i1) })
require.Panics(t, func() { i2.Mul(i2) })
require.Panics(t, func() { i3.Mul(i3) })
require.Panics(t, func() { i1.Neg().Mul(i1.Neg()) })
require.Panics(t, func() { i2.Neg().Mul(i2.Neg()) })
require.Panics(t, func() { i3.Neg().Mul(i3.Neg()) })
// Underflow check
i3n := i3.Neg()
require.NotPanics(t, func() { i3n.Sub(i1) })
require.NotPanics(t, func() { i3n.Sub(i2) })
require.Panics(t, func() { i3n.Sub(i3) })
require.NotPanics(t, func() { i3n.Add(i1.Neg()) })
require.NotPanics(t, func() { i3n.Add(i2.Neg()) })
require.Panics(t, func() { i3n.Add(i3.Neg()) })
require.Panics(t, func() { i1.Mul(i1.Neg()) })
require.Panics(t, func() { i2.Mul(i2.Neg()) })
require.Panics(t, func() { i3.Mul(i3.Neg()) })
// Bound check
intmax := NewIntFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil), big.NewInt(1)))
intmin := intmax.Neg()
require.NotPanics(t, func() { intmax.Add(ZeroInt()) })
require.NotPanics(t, func() { intmin.Sub(ZeroInt()) })
require.Panics(t, func() { intmax.Add(OneInt()) })
require.Panics(t, func() { intmin.Sub(OneInt()) })
// Division-by-zero check
require.Panics(t, func() { i1.Quo(NewInt(0)) })
}
// Tests below uses randomness
// Since we are using *big.Int as underlying value
// and (U/)Int is immutable value(see TestImmutability(U/)Int)
// it is safe to use randomness in the tests
func TestIdentInt(t *testing.T) {
t.Parallel()
for d := 0; d < 1000; d++ {
n := rand.Int63()
i := NewInt(n)
ifromstr, ok := NewIntFromString(strconv.FormatInt(n, 10))
require.True(t, ok)
cases := []int64{
i.Int64(),
i.BigInt().Int64(),
ifromstr.Int64(),
NewIntFromBigInt(big.NewInt(n)).Int64(),
NewIntWithDecimal(n, 0).Int64(),
}
for tcnum, tc := range cases {
require.Equal(t, n, tc, "Int is modified during conversion. tc #%d", tcnum)
}
}
}
func minint(i1, i2 int64) int64 {
if i1 < i2 {
return i1
}
return i2
}
func maxint(i1, i2 int64) int64 {
if i1 > i2 {
return i1
}
return i2
}
func TestArithInt(t *testing.T) {
t.Parallel()
for d := 0; d < 1000; d++ {
n1 := int64(rand.Int31())
i1 := NewInt(n1)
n2 := int64(rand.Int31())
i2 := NewInt(n2)
cases := []struct {
ires Int
nres int64
}{
{i1.Add(i2), n1 + n2},
{i1.Sub(i2), n1 - n2},
{i1.Mul(i2), n1 * n2},
{i1.Quo(i2), n1 / n2},
{i1.AddRaw(n2), n1 + n2},
{i1.SubRaw(n2), n1 - n2},
{i1.MulRaw(n2), n1 * n2},
{i1.QuoRaw(n2), n1 / n2},
{MinInt(i1, i2), minint(n1, n2)},
{MaxInt(i1, i2), maxint(n1, n2)},
{i1.Neg(), -n1},
}
for tcnum, tc := range cases {
require.Equal(t, tc.nres, tc.ires.Int64(), "Int arithmetic operation does not match with int64 operation. tc #%d", tcnum)
}
}
}
func TestCompInt(t *testing.T) {
t.Parallel()
for d := 0; d < 1000; d++ {
n1 := int64(rand.Int31())
i1 := NewInt(n1)
n2 := int64(rand.Int31())
i2 := NewInt(n2)
cases := []struct {
ires bool
nres bool
}{
{i1.Equal(i2), n1 == n2},
{i1.GT(i2), n1 > n2},
{i1.LT(i2), n1 < n2},
{i1.LTE(i2), n1 <= n2},
}
for tcnum, tc := range cases {
require.Equal(t, tc.nres, tc.ires, "Int comparison operation does not match with int64 operation. tc #%d", tcnum)
}
}
}
func minuint(i1, i2 uint64) uint64 {
if i1 < i2 {
return i1
}
return i2
}
func maxuint(i1, i2 uint64) uint64 {
if i1 > i2 {
return i1
}
return i2
}
func randint() Int {
return NewInt(rand.Int63())
}
func TestImmutabilityAllInt(t *testing.T) {
t.Parallel()
ops := []func(*Int){
func(i *Int) { _ = i.Add(randint()) },
func(i *Int) { _ = i.Sub(randint()) },
func(i *Int) { _ = i.Mul(randint()) },
func(i *Int) { _ = i.Quo(randint()) },
func(i *Int) { _ = i.AddRaw(rand.Int63()) },
func(i *Int) { _ = i.SubRaw(rand.Int63()) },
func(i *Int) { _ = i.MulRaw(rand.Int63()) },
func(i *Int) { _ = i.QuoRaw(rand.Int63()) },
func(i *Int) { _ = i.Neg() },
func(i *Int) { _ = i.IsZero() },
func(i *Int) { _ = i.Sign() },
func(i *Int) { _ = i.Equal(randint()) },
func(i *Int) { _ = i.GT(randint()) },
func(i *Int) { _ = i.LT(randint()) },
func(i *Int) { _ = i.String() },
}
for i := 0; i < 1000; i++ {
n := rand.Int63()
ni := NewInt(n)
for opnum, op := range ops {
op(&ni)
require.Equal(t, n, ni.Int64(), "Int is modified by operation. tc #%d", opnum)
require.Equal(t, NewInt(n), ni, "Int is modified by operation. tc #%d", opnum)
}
}
}
type intop func(Int, *big.Int) (Int, *big.Int)
func intarith(uifn func(Int, Int) Int, bifn func(*big.Int, *big.Int, *big.Int) *big.Int) intop {
return func(ui Int, bi *big.Int) (Int, *big.Int) {
r := rand.Int63()
br := new(big.Int).SetInt64(r)
return uifn(ui, NewInt(r)), bifn(new(big.Int), bi, br)
}
}
func intarithraw(uifn func(Int, int64) Int, bifn func(*big.Int, *big.Int, *big.Int) *big.Int) intop {
return func(ui Int, bi *big.Int) (Int, *big.Int) {
r := rand.Int63()
br := new(big.Int).SetInt64(r)
return uifn(ui, r), bifn(new(big.Int), bi, br)
}
}
func TestImmutabilityArithInt(t *testing.T) {
t.Parallel()
size := 500
ops := []intop{
intarith(Int.Add, (*big.Int).Add),
intarith(Int.Sub, (*big.Int).Sub),
intarith(Int.Mul, (*big.Int).Mul),
intarith(Int.Quo, (*big.Int).Quo),
intarithraw(Int.AddRaw, (*big.Int).Add),
intarithraw(Int.SubRaw, (*big.Int).Sub),
intarithraw(Int.MulRaw, (*big.Int).Mul),
intarithraw(Int.QuoRaw, (*big.Int).Quo),
}
for i := 0; i < 100; i++ {
uis := make([]Int, size)
bis := make([]*big.Int, size)
n := rand.Int63()
ui := NewInt(n)
bi := new(big.Int).SetInt64(n)
for j := 0; j < size; j++ {
op := ops[rand.Intn(len(ops))]
uis[j], bis[j] = op(ui, bi)
}
for j := 0; j < size; j++ {
require.Equal(t, 0, bis[j].Cmp(uis[j].BigInt()), "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String())
require.Equal(t, NewIntFromBigInt(bis[j]), uis[j], "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String())
require.True(t, uis[j].i != bis[j], "Pointer addresses are equal. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String())
}
}
}
func TestEncodingRandom(t *testing.T) {
for i := 0; i < 1000; i++ {
n := rand.Int63()
ni := NewInt(n)
var ri Int
str, err := ni.Marshal()
require.Nil(t, err)
err = (&ri).Unmarshal(str)
require.Nil(t, err)
require.Equal(t, ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String())
require.True(t, ni.i != ri.i, "pointer addresses are equal; tc #%d", i)
bz, err := ni.MarshalJSON()
require.Nil(t, err)
err = (&ri).UnmarshalJSON(bz)
require.Nil(t, err)
require.Equal(t, ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String())
require.True(t, ni.i != ri.i, "pointer addresses are equal; tc #%d", i)
}
for i := 0; i < 1000; i++ {
n := rand.Uint64()
ni := NewUint(n)
var ri Uint
str, err := ni.Marshal()
require.Nil(t, err)
err = (&ri).Unmarshal(str)
require.Nil(t, err)
require.Equal(t, ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String())
require.True(t, ni.i != ri.i, "pointer addresses are equal; tc #%d", i)
bz, err := ni.MarshalJSON()
require.Nil(t, err)
err = (&ri).UnmarshalJSON(bz)
require.Nil(t, err)
require.Equal(t, ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String())
require.True(t, ni.i != ri.i, "pointer addresses are equal; tc #%d", i)
}
}
func TestEncodingTableInt(t *testing.T) {
t.Parallel()
var i Int
cases := []struct {
i Int
jsonBz []byte
rawBz []byte
}{
{
NewInt(0),
[]byte("\"0\""),
[]byte{0x30},
},
{
NewInt(100),
[]byte("\"100\""),
[]byte{0x31, 0x30, 0x30},
},
{
NewInt(-100),
[]byte("\"-100\""),
[]byte{0x2d, 0x31, 0x30, 0x30},
},
{
NewInt(51842),
[]byte("\"51842\""),
[]byte{0x35, 0x31, 0x38, 0x34, 0x32},
},
{
NewInt(-51842),
[]byte("\"-51842\""),
[]byte{0x2d, 0x35, 0x31, 0x38, 0x34, 0x32},
},
{
NewInt(19513368),
[]byte("\"19513368\""),
[]byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38},
},
{
NewInt(-19513368),
[]byte("\"-19513368\""),
[]byte{0x2d, 0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38},
},
{
NewInt(999999999999),
[]byte("\"999999999999\""),
[]byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39},
},
{
NewInt(-999999999999),
[]byte("\"-999999999999\""),
[]byte{0x2d, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39},
},
}
for tcnum, tc := range cases {
bz, err := tc.i.MarshalJSON()
require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum)
err = (&i).UnmarshalJSON(bz)
require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum)
bz, err = tc.i.Marshal()
require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum)
err = (&i).Unmarshal(bz)
require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum)
}
}
func TestEncodingTableUint(t *testing.T) {
t.Parallel()
var i Uint
cases := []struct {
i Uint
jsonBz []byte
rawBz []byte
}{
{
NewUint(0),
[]byte("\"0\""),
[]byte{0x30},
},
{
NewUint(100),
[]byte("\"100\""),
[]byte{0x31, 0x30, 0x30},
},
{
NewUint(51842),
[]byte("\"51842\""),
[]byte{0x35, 0x31, 0x38, 0x34, 0x32},
},
{
NewUint(19513368),
[]byte("\"19513368\""),
[]byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38},
},
{
NewUint(999999999999),
[]byte("\"999999999999\""),
[]byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39},
},
}
for tcnum, tc := range cases {
bz, err := tc.i.MarshalJSON()
require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum)
err = (&i).UnmarshalJSON(bz)
require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum)
bz, err = tc.i.Marshal()
require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum)
err = (&i).Unmarshal(bz)
require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err)
require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum)
}
}
func TestSerializationOverflow(t *testing.T) {
t.Parallel()
bx, _ := new(big.Int).SetString("91888242871839275229946405745257275988696311157297823662689937894645226298583", 10)
x := Int{bx}
y := new(Int)
bz, err := x.Marshal()
require.NoError(t, err)
// require deserialization to fail due to overflow
err = y.Unmarshal(bz)
require.Error(t, err)
// require JSON deserialization to fail due to overflow
bz, err = x.MarshalJSON()
require.NoError(t, err)
err = y.UnmarshalJSON(bz)
require.Error(t, err)
}
func TestIntMod(t *testing.T) {
t.Parallel()
tests := []struct {
name string
x int64
y int64
ret int64
wantPanic bool
}{
{"3 % 10", 3, 10, 3, false},
{"10 % 3", 10, 3, 1, false},
{"4 % 2", 4, 2, 0, false},
{"2 % 0", 2, 0, 0, true},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if tt.wantPanic {
require.Panics(t, func() { NewInt(tt.x).Mod(NewInt(tt.y)) })
require.Panics(t, func() { NewInt(tt.x).ModRaw(tt.y) })
return
}
require.True(t, NewInt(tt.x).Mod(NewInt(tt.y)).Equal(NewInt(tt.ret)))
require.True(t, NewInt(tt.x).ModRaw(tt.y).Equal(NewInt(tt.ret)))
})
}
}
func TestIntEq(t *testing.T) {
require.True(IntEq(t, ZeroInt(), ZeroInt()))
require.False(IntEq(t, OneInt(), ZeroInt()))
}