summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHang Su <87964331+ahangsu@users.noreply.github.com>2021-12-22 22:00:45 -0500
committerGitHub <noreply@github.com>2021-12-22 22:00:45 -0500
commit7da1026ae753c83a7d577e765322c0ec26b19fa4 (patch)
tree22b2a6607074c8658e1650e7a79b1bf00510202a
parent4cb424160db19b9412cedf81b248314084af545c (diff)
Fix flaky test in randomized ABI encoding test (#3346)
* update abi encoding test random testcase generator, scale down parameters to avoid flaky test * parameterized test script * add notes to explain why flaky test is eliminated * show more information from self-roundtrip testing * fully utilize require, remove fmt
-rw-r--r--data/abi/abi_encode_test.go138
1 files changed, 96 insertions, 42 deletions
diff --git a/data/abi/abi_encode_test.go b/data/abi/abi_encode_test.go
index a86fe46b9..b3da406d2 100644
--- a/data/abi/abi_encode_test.go
+++ b/data/abi/abi_encode_test.go
@@ -27,17 +27,58 @@ import (
"github.com/stretchr/testify/require"
)
+const (
+ UintStepLength = 8
+ UintBegin = 8
+ UintEnd = 512
+ UintRandomTestPoints = 1000
+ UintTestCaseCount = 200
+ UfixedPrecision = 160
+ UfixedRandomTestPoints = 20
+ TupleMaxLength = 10
+ ByteTestCaseCount = 1 << 8
+ BoolTestCaseCount = 2
+ AddressTestCaseCount = 300
+ StringTestCaseCount = 10
+ StringTestCaseSpecLenCount = 5
+ TakeNum = 10
+ TupleTestCaseCount = 100
+)
+
+/*
+ The set of parameters ensure that the error of byte length >= 2^16 is eliminated.
+
+ i. Consider uint512[] with length 10, the ABI encoding length is: 64 x 10 + 2
+ (2 is introduced from dynamic array length encoding)
+ The motivation here is that, forall ABI type that is non-array/non-tuple like,
+ uint512 gives the longest byte length in ABI encoding
+ (utf-8 string's byte length is at most 42, address byte length is at most 32)
+
+ ii. Consider a tuple of length 10, with all elements uint512[] of length 10.
+ The ABI encoding length is: 10 x 2 + 10 x 642 == 6440
+ (2 is for tuple index to keep track of dynamic type encoding)
+
+ iii. Consider a tuple of length 10, with all elements of tuples mentioned in (ii).
+ The ABI encoding length is: 10 x 2 + 10 x 6440 == 64420
+ This is the end of the generation of nested-tuple test case,
+ no more layers of random tuples will be produced.
+
+ This gives an upper bound for the produced ABI encoding byte length in this test script,
+ and noticing that length 64420 mentioned in (iii) is less than 2^16 == 65536.
+ Assuming that ABI implementation is correct, then the flaky test should not happen again.
+*/
+
func TestEncodeValid(t *testing.T) {
partitiontest.PartitionTest(t)
// encoding test for uint type, iterating through all uint sizes
// randomly pick 1000 valid uint values and check if encoded value match with expected
- for intSize := 8; intSize <= 512; intSize += 8 {
+ for intSize := UintBegin; intSize <= UintEnd; intSize += UintStepLength {
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(intSize))
uintType, err := makeUintType(intSize)
require.NoError(t, err, "make uint type fail")
- for i := 0; i < 1000; i++ {
+ for i := 0; i < UintRandomTestPoints; i++ {
randomInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")
@@ -64,17 +105,17 @@ func TestEncodeValid(t *testing.T) {
// encoding test for ufixed, iterating through all the valid ufixed bitSize and precision
// randomly generate 10 big int values for ufixed numerator and check if encoded value match with expected
// also check if ufixed can fit max numerator (2^bitSize - 1) under specific byte bitSize
- for size := 8; size <= 512; size += 8 {
+ for size := UintBegin; size <= UintEnd; size += UintStepLength {
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(size))
largest := big.NewInt(0).Add(
upperLimit,
big.NewInt(1).Neg(big.NewInt(1)),
)
- for precision := 1; precision <= 160; precision++ {
+ for precision := 1; precision <= UfixedPrecision; precision++ {
typeUfixed, err := makeUfixedType(size, precision)
require.NoError(t, err, "make ufixed type fail")
- for i := 0; i < 10; i++ {
+ for i := 0; i < UfixedRandomTestPoints; i++ {
randomInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")
@@ -96,13 +137,13 @@ func TestEncodeValid(t *testing.T) {
// encoding test for address, since address is 32 byte, it can be considered as 256 bit uint
// randomly generate 1000 uint256 and make address values, check if encoded value match with expected
- upperLimit := big.NewInt(0).Lsh(big.NewInt(1), 256)
- for i := 0; i < 1000; i++ {
+ upperLimit := big.NewInt(0).Lsh(big.NewInt(1), addressByteSize<<3)
+ for i := 0; i < UintRandomTestPoints; i++ {
randomAddrInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")
rand256Bytes := randomAddrInt.Bytes()
- addrBytesExpected := make([]byte, 32-len(rand256Bytes))
+ addrBytesExpected := make([]byte, addressByteSize-len(rand256Bytes))
addrBytesExpected = append(addrBytesExpected, rand256Bytes...)
addrBytesActual, err := addressType.Encode(addrBytesExpected)
@@ -111,7 +152,7 @@ func TestEncodeValid(t *testing.T) {
}
// encoding test for bool values
- for i := 0; i < 2; i++ {
+ for i := 0; i < BoolTestCaseCount; i++ {
boolEncode, err := boolType.Encode(i == 1)
require.NoError(t, err, "bool encode fail")
expected := []byte{0x00}
@@ -122,7 +163,7 @@ func TestEncodeValid(t *testing.T) {
}
// encoding test for byte values
- for i := 0; i < (1 << 8); i++ {
+ for i := 0; i < ByteTestCaseCount; i++ {
byteEncode, err := byteType.Encode(byte(i))
require.NoError(t, err, "byte encode fail")
expected := []byte{byte(i)}
@@ -133,8 +174,8 @@ func TestEncodeValid(t *testing.T) {
// we use `gobberish` to generate random utf-8 symbols
// randomly generate utf-8 str from length 1 to 100, each length draw 10 random strs
// check if encoded ABI str match with expected value
- for length := 1; length <= 100; length++ {
- for i := 0; i < 10; i++ {
+ for length := 1; length <= StringTestCaseCount; length++ {
+ for i := 0; i < StringTestCaseSpecLenCount; i++ {
// generate utf8 strings from `gobberish` at some length
utf8Str := gobberish.GenerateString(length)
// since string is just type alias of `byte[]`, we need to store number of bytes in encoding
@@ -828,35 +869,48 @@ type testUnit struct {
func categorySelfRoundTripTest(t *testing.T, category []testUnit) {
for _, testObj := range category {
abiType, err := TypeOf(testObj.serializedType)
- require.NoError(t, err, "failure to deserialize type")
+ require.NoError(t, err, "failure to deserialize type: "+testObj.serializedType)
encodedValue, err := abiType.Encode(testObj.value)
- require.NoError(t, err, "failure to encode value")
+ require.NoError(t, err,
+ "failure to encode value %#v over type %s", testObj.value, testObj.serializedType,
+ )
actual, err := abiType.Decode(encodedValue)
- require.NoError(t, err, "failure to decode value")
- require.Equal(t, testObj.value, actual, "decoded value not equal to expected")
+ require.NoError(t, err,
+ "failure to decode value %#v for type %s", encodedValue, testObj.serializedType,
+ )
+ require.Equal(t, testObj.value, actual,
+ "decoded value %#v not equal to expected value %#v", actual, testObj.value,
+ )
jsonEncodedValue, err := abiType.MarshalToJSON(testObj.value)
- require.NoError(t, err, "failure to encode value to JSON type")
+ require.NoError(t, err,
+ "failure to encode value %#v to JSON type", testObj.value,
+ )
jsonActual, err := abiType.UnmarshalFromJSON(jsonEncodedValue)
- require.NoError(t, err, "failure to decode JSON value back")
- require.Equal(t, testObj.value, jsonActual, "decode JSON value not equal to expected")
+ require.NoError(t, err,
+ "failure to decode JSON value %s back for type %s",
+ string(jsonEncodedValue), testObj.serializedType,
+ )
+ require.Equal(t, testObj.value, jsonActual,
+ "decode JSON value %s not equal to expected %s", jsonActual, testObj.value,
+ )
}
}
func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
- (*pool)[Uint] = make([]testUnit, 200*64)
- (*pool)[Ufixed] = make([]testUnit, 160*64)
+ (*pool)[Uint] = make([]testUnit, UintTestCaseCount*UintEnd/UintStepLength)
+ (*pool)[Ufixed] = make([]testUnit, UfixedPrecision*UintEnd/UintStepLength)
uintIndex := 0
ufixedIndex := 0
- for bitSize := 8; bitSize <= 512; bitSize += 8 {
+ for bitSize := UintBegin; bitSize <= UintEnd; bitSize += UintStepLength {
max := new(big.Int).Lsh(big.NewInt(1), uint(bitSize))
uintT, err := makeUintType(bitSize)
require.NoError(t, err, "make uint type failure")
uintTstr := uintT.String()
- for j := 0; j < 200; j++ {
+ for j := 0; j < UintTestCaseCount; j++ {
randVal, err := rand.Int(rand.Reader, max)
require.NoError(t, err, "generate random uint, should be no error")
@@ -867,7 +921,7 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
uintIndex++
}
- for precision := 1; precision <= 160; precision++ {
+ for precision := 1; precision <= UfixedPrecision; precision++ {
randVal, err := rand.Int(rand.Reader, max)
require.NoError(t, err, "generate random ufixed, should be no error")
@@ -884,33 +938,33 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
categorySelfRoundTripTest(t, (*pool)[Uint])
categorySelfRoundTripTest(t, (*pool)[Ufixed])
- (*pool)[Byte] = make([]testUnit, 1<<8)
- for i := 0; i < (1 << 8); i++ {
+ (*pool)[Byte] = make([]testUnit, ByteTestCaseCount)
+ for i := 0; i < ByteTestCaseCount; i++ {
(*pool)[Byte][i] = testUnit{serializedType: byteType.String(), value: byte(i)}
}
categorySelfRoundTripTest(t, (*pool)[Byte])
- (*pool)[Bool] = make([]testUnit, 2)
+ (*pool)[Bool] = make([]testUnit, BoolTestCaseCount)
(*pool)[Bool][0] = testUnit{serializedType: boolType.String(), value: false}
(*pool)[Bool][1] = testUnit{serializedType: boolType.String(), value: true}
categorySelfRoundTripTest(t, (*pool)[Bool])
- maxAddress := new(big.Int).Lsh(big.NewInt(1), 256)
- (*pool)[Address] = make([]testUnit, 300)
- for i := 0; i < 300; i++ {
+ maxAddress := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3)
+ (*pool)[Address] = make([]testUnit, AddressTestCaseCount)
+ for i := 0; i < AddressTestCaseCount; i++ {
randAddrVal, err := rand.Int(rand.Reader, maxAddress)
require.NoError(t, err, "generate random value for address, should be no error")
addrBytes := randAddrVal.Bytes()
- remainBytes := make([]byte, 32-len(addrBytes))
+ remainBytes := make([]byte, addressByteSize-len(addrBytes))
addrBytes = append(remainBytes, addrBytes...)
(*pool)[Address][i] = testUnit{serializedType: addressType.String(), value: addrBytes}
}
categorySelfRoundTripTest(t, (*pool)[Address])
- (*pool)[String] = make([]testUnit, 400)
+ (*pool)[String] = make([]testUnit, StringTestCaseCount*StringTestCaseSpecLenCount)
stringIndex := 0
- for length := 1; length <= 100; length++ {
- for i := 0; i < 4; i++ {
+ for length := 1; length <= StringTestCaseCount; length++ {
+ for i := 0; i < StringTestCaseSpecLenCount; i++ {
(*pool)[String][stringIndex] = testUnit{
serializedType: stringType.String(),
value: gobberish.GenerateString(length),
@@ -945,21 +999,21 @@ func takeSomeFromCategoryAndGenerateArray(
}
func addArrayRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
- for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += 200 {
- takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, 20, pool)
+ for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += UintTestCaseCount {
+ takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, TakeNum, pool)
}
- takeSomeFromCategoryAndGenerateArray(t, Byte, 0, 20, pool)
- takeSomeFromCategoryAndGenerateArray(t, Address, 0, 20, pool)
- takeSomeFromCategoryAndGenerateArray(t, String, 0, 20, pool)
- takeSomeFromCategoryAndGenerateArray(t, Bool, 0, 20, pool)
+ takeSomeFromCategoryAndGenerateArray(t, Byte, 0, TakeNum, pool)
+ takeSomeFromCategoryAndGenerateArray(t, Address, 0, TakeNum, pool)
+ takeSomeFromCategoryAndGenerateArray(t, String, 0, TakeNum, pool)
+ takeSomeFromCategoryAndGenerateArray(t, Bool, 0, TakeNum, pool)
categorySelfRoundTripTest(t, (*pool)[ArrayStatic])
categorySelfRoundTripTest(t, (*pool)[ArrayDynamic])
}
func addTupleRandomValues(t *testing.T, slotRange BaseType, pool *map[BaseType][]testUnit) {
- for i := 0; i < 100; i++ {
- tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(20))
+ for i := 0; i < TupleTestCaseCount; i++ {
+ tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(TupleMaxLength))
require.NoError(t, err, "generate random tuple length should not return error")
tupleLen := tupleLenBig.Int64() + 1
testUnits := make([]testUnit, tupleLen)