diff options
author | Hang Su <87964331+ahangsu@users.noreply.github.com> | 2021-12-22 22:00:45 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-22 22:00:45 -0500 |
commit | 7da1026ae753c83a7d577e765322c0ec26b19fa4 (patch) | |
tree | 22b2a6607074c8658e1650e7a79b1bf00510202a | |
parent | 4cb424160db19b9412cedf81b248314084af545c (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.go | 138 |
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) |