Skip to content

Commit 7da1026

Browse files
authored
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
1 parent 4cb4241 commit 7da1026

File tree

1 file changed

+96
-42
lines changed

1 file changed

+96
-42
lines changed

data/abi/abi_encode_test.go

Lines changed: 96 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,58 @@ import (
2727
"github.com/stretchr/testify/require"
2828
)
2929

30+
const (
31+
UintStepLength = 8
32+
UintBegin = 8
33+
UintEnd = 512
34+
UintRandomTestPoints = 1000
35+
UintTestCaseCount = 200
36+
UfixedPrecision = 160
37+
UfixedRandomTestPoints = 20
38+
TupleMaxLength = 10
39+
ByteTestCaseCount = 1 << 8
40+
BoolTestCaseCount = 2
41+
AddressTestCaseCount = 300
42+
StringTestCaseCount = 10
43+
StringTestCaseSpecLenCount = 5
44+
TakeNum = 10
45+
TupleTestCaseCount = 100
46+
)
47+
48+
/*
49+
The set of parameters ensure that the error of byte length >= 2^16 is eliminated.
50+
51+
i. Consider uint512[] with length 10, the ABI encoding length is: 64 x 10 + 2
52+
(2 is introduced from dynamic array length encoding)
53+
The motivation here is that, forall ABI type that is non-array/non-tuple like,
54+
uint512 gives the longest byte length in ABI encoding
55+
(utf-8 string's byte length is at most 42, address byte length is at most 32)
56+
57+
ii. Consider a tuple of length 10, with all elements uint512[] of length 10.
58+
The ABI encoding length is: 10 x 2 + 10 x 642 == 6440
59+
(2 is for tuple index to keep track of dynamic type encoding)
60+
61+
iii. Consider a tuple of length 10, with all elements of tuples mentioned in (ii).
62+
The ABI encoding length is: 10 x 2 + 10 x 6440 == 64420
63+
This is the end of the generation of nested-tuple test case,
64+
no more layers of random tuples will be produced.
65+
66+
This gives an upper bound for the produced ABI encoding byte length in this test script,
67+
and noticing that length 64420 mentioned in (iii) is less than 2^16 == 65536.
68+
Assuming that ABI implementation is correct, then the flaky test should not happen again.
69+
*/
70+
3071
func TestEncodeValid(t *testing.T) {
3172
partitiontest.PartitionTest(t)
3273

3374
// encoding test for uint type, iterating through all uint sizes
3475
// randomly pick 1000 valid uint values and check if encoded value match with expected
35-
for intSize := 8; intSize <= 512; intSize += 8 {
76+
for intSize := UintBegin; intSize <= UintEnd; intSize += UintStepLength {
3677
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(intSize))
3778
uintType, err := makeUintType(intSize)
3879
require.NoError(t, err, "make uint type fail")
3980

40-
for i := 0; i < 1000; i++ {
81+
for i := 0; i < UintRandomTestPoints; i++ {
4182
randomInt, err := rand.Int(rand.Reader, upperLimit)
4283
require.NoError(t, err, "cryptographic random int init fail")
4384

@@ -64,17 +105,17 @@ func TestEncodeValid(t *testing.T) {
64105
// encoding test for ufixed, iterating through all the valid ufixed bitSize and precision
65106
// randomly generate 10 big int values for ufixed numerator and check if encoded value match with expected
66107
// also check if ufixed can fit max numerator (2^bitSize - 1) under specific byte bitSize
67-
for size := 8; size <= 512; size += 8 {
108+
for size := UintBegin; size <= UintEnd; size += UintStepLength {
68109
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(size))
69110
largest := big.NewInt(0).Add(
70111
upperLimit,
71112
big.NewInt(1).Neg(big.NewInt(1)),
72113
)
73-
for precision := 1; precision <= 160; precision++ {
114+
for precision := 1; precision <= UfixedPrecision; precision++ {
74115
typeUfixed, err := makeUfixedType(size, precision)
75116
require.NoError(t, err, "make ufixed type fail")
76117

77-
for i := 0; i < 10; i++ {
118+
for i := 0; i < UfixedRandomTestPoints; i++ {
78119
randomInt, err := rand.Int(rand.Reader, upperLimit)
79120
require.NoError(t, err, "cryptographic random int init fail")
80121

@@ -96,13 +137,13 @@ func TestEncodeValid(t *testing.T) {
96137

97138
// encoding test for address, since address is 32 byte, it can be considered as 256 bit uint
98139
// randomly generate 1000 uint256 and make address values, check if encoded value match with expected
99-
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), 256)
100-
for i := 0; i < 1000; i++ {
140+
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), addressByteSize<<3)
141+
for i := 0; i < UintRandomTestPoints; i++ {
101142
randomAddrInt, err := rand.Int(rand.Reader, upperLimit)
102143
require.NoError(t, err, "cryptographic random int init fail")
103144

104145
rand256Bytes := randomAddrInt.Bytes()
105-
addrBytesExpected := make([]byte, 32-len(rand256Bytes))
146+
addrBytesExpected := make([]byte, addressByteSize-len(rand256Bytes))
106147
addrBytesExpected = append(addrBytesExpected, rand256Bytes...)
107148

108149
addrBytesActual, err := addressType.Encode(addrBytesExpected)
@@ -111,7 +152,7 @@ func TestEncodeValid(t *testing.T) {
111152
}
112153

113154
// encoding test for bool values
114-
for i := 0; i < 2; i++ {
155+
for i := 0; i < BoolTestCaseCount; i++ {
115156
boolEncode, err := boolType.Encode(i == 1)
116157
require.NoError(t, err, "bool encode fail")
117158
expected := []byte{0x00}
@@ -122,7 +163,7 @@ func TestEncodeValid(t *testing.T) {
122163
}
123164

124165
// encoding test for byte values
125-
for i := 0; i < (1 << 8); i++ {
166+
for i := 0; i < ByteTestCaseCount; i++ {
126167
byteEncode, err := byteType.Encode(byte(i))
127168
require.NoError(t, err, "byte encode fail")
128169
expected := []byte{byte(i)}
@@ -133,8 +174,8 @@ func TestEncodeValid(t *testing.T) {
133174
// we use `gobberish` to generate random utf-8 symbols
134175
// randomly generate utf-8 str from length 1 to 100, each length draw 10 random strs
135176
// check if encoded ABI str match with expected value
136-
for length := 1; length <= 100; length++ {
137-
for i := 0; i < 10; i++ {
177+
for length := 1; length <= StringTestCaseCount; length++ {
178+
for i := 0; i < StringTestCaseSpecLenCount; i++ {
138179
// generate utf8 strings from `gobberish` at some length
139180
utf8Str := gobberish.GenerateString(length)
140181
// since string is just type alias of `byte[]`, we need to store number of bytes in encoding
@@ -828,35 +869,48 @@ type testUnit struct {
828869
func categorySelfRoundTripTest(t *testing.T, category []testUnit) {
829870
for _, testObj := range category {
830871
abiType, err := TypeOf(testObj.serializedType)
831-
require.NoError(t, err, "failure to deserialize type")
872+
require.NoError(t, err, "failure to deserialize type: "+testObj.serializedType)
832873
encodedValue, err := abiType.Encode(testObj.value)
833-
require.NoError(t, err, "failure to encode value")
874+
require.NoError(t, err,
875+
"failure to encode value %#v over type %s", testObj.value, testObj.serializedType,
876+
)
834877
actual, err := abiType.Decode(encodedValue)
835-
require.NoError(t, err, "failure to decode value")
836-
require.Equal(t, testObj.value, actual, "decoded value not equal to expected")
878+
require.NoError(t, err,
879+
"failure to decode value %#v for type %s", encodedValue, testObj.serializedType,
880+
)
881+
require.Equal(t, testObj.value, actual,
882+
"decoded value %#v not equal to expected value %#v", actual, testObj.value,
883+
)
837884
jsonEncodedValue, err := abiType.MarshalToJSON(testObj.value)
838-
require.NoError(t, err, "failure to encode value to JSON type")
885+
require.NoError(t, err,
886+
"failure to encode value %#v to JSON type", testObj.value,
887+
)
839888
jsonActual, err := abiType.UnmarshalFromJSON(jsonEncodedValue)
840-
require.NoError(t, err, "failure to decode JSON value back")
841-
require.Equal(t, testObj.value, jsonActual, "decode JSON value not equal to expected")
889+
require.NoError(t, err,
890+
"failure to decode JSON value %s back for type %s",
891+
string(jsonEncodedValue), testObj.serializedType,
892+
)
893+
require.Equal(t, testObj.value, jsonActual,
894+
"decode JSON value %s not equal to expected %s", jsonActual, testObj.value,
895+
)
842896
}
843897
}
844898

845899
func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
846-
(*pool)[Uint] = make([]testUnit, 200*64)
847-
(*pool)[Ufixed] = make([]testUnit, 160*64)
900+
(*pool)[Uint] = make([]testUnit, UintTestCaseCount*UintEnd/UintStepLength)
901+
(*pool)[Ufixed] = make([]testUnit, UfixedPrecision*UintEnd/UintStepLength)
848902

849903
uintIndex := 0
850904
ufixedIndex := 0
851905

852-
for bitSize := 8; bitSize <= 512; bitSize += 8 {
906+
for bitSize := UintBegin; bitSize <= UintEnd; bitSize += UintStepLength {
853907
max := new(big.Int).Lsh(big.NewInt(1), uint(bitSize))
854908

855909
uintT, err := makeUintType(bitSize)
856910
require.NoError(t, err, "make uint type failure")
857911
uintTstr := uintT.String()
858912

859-
for j := 0; j < 200; j++ {
913+
for j := 0; j < UintTestCaseCount; j++ {
860914
randVal, err := rand.Int(rand.Reader, max)
861915
require.NoError(t, err, "generate random uint, should be no error")
862916

@@ -867,7 +921,7 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
867921
uintIndex++
868922
}
869923

870-
for precision := 1; precision <= 160; precision++ {
924+
for precision := 1; precision <= UfixedPrecision; precision++ {
871925
randVal, err := rand.Int(rand.Reader, max)
872926
require.NoError(t, err, "generate random ufixed, should be no error")
873927

@@ -884,33 +938,33 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
884938
categorySelfRoundTripTest(t, (*pool)[Uint])
885939
categorySelfRoundTripTest(t, (*pool)[Ufixed])
886940

887-
(*pool)[Byte] = make([]testUnit, 1<<8)
888-
for i := 0; i < (1 << 8); i++ {
941+
(*pool)[Byte] = make([]testUnit, ByteTestCaseCount)
942+
for i := 0; i < ByteTestCaseCount; i++ {
889943
(*pool)[Byte][i] = testUnit{serializedType: byteType.String(), value: byte(i)}
890944
}
891945
categorySelfRoundTripTest(t, (*pool)[Byte])
892946

893-
(*pool)[Bool] = make([]testUnit, 2)
947+
(*pool)[Bool] = make([]testUnit, BoolTestCaseCount)
894948
(*pool)[Bool][0] = testUnit{serializedType: boolType.String(), value: false}
895949
(*pool)[Bool][1] = testUnit{serializedType: boolType.String(), value: true}
896950
categorySelfRoundTripTest(t, (*pool)[Bool])
897951

898-
maxAddress := new(big.Int).Lsh(big.NewInt(1), 256)
899-
(*pool)[Address] = make([]testUnit, 300)
900-
for i := 0; i < 300; i++ {
952+
maxAddress := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3)
953+
(*pool)[Address] = make([]testUnit, AddressTestCaseCount)
954+
for i := 0; i < AddressTestCaseCount; i++ {
901955
randAddrVal, err := rand.Int(rand.Reader, maxAddress)
902956
require.NoError(t, err, "generate random value for address, should be no error")
903957
addrBytes := randAddrVal.Bytes()
904-
remainBytes := make([]byte, 32-len(addrBytes))
958+
remainBytes := make([]byte, addressByteSize-len(addrBytes))
905959
addrBytes = append(remainBytes, addrBytes...)
906960
(*pool)[Address][i] = testUnit{serializedType: addressType.String(), value: addrBytes}
907961
}
908962
categorySelfRoundTripTest(t, (*pool)[Address])
909963

910-
(*pool)[String] = make([]testUnit, 400)
964+
(*pool)[String] = make([]testUnit, StringTestCaseCount*StringTestCaseSpecLenCount)
911965
stringIndex := 0
912-
for length := 1; length <= 100; length++ {
913-
for i := 0; i < 4; i++ {
966+
for length := 1; length <= StringTestCaseCount; length++ {
967+
for i := 0; i < StringTestCaseSpecLenCount; i++ {
914968
(*pool)[String][stringIndex] = testUnit{
915969
serializedType: stringType.String(),
916970
value: gobberish.GenerateString(length),
@@ -945,21 +999,21 @@ func takeSomeFromCategoryAndGenerateArray(
945999
}
9461000

9471001
func addArrayRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
948-
for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += 200 {
949-
takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, 20, pool)
1002+
for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += UintTestCaseCount {
1003+
takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, TakeNum, pool)
9501004
}
951-
takeSomeFromCategoryAndGenerateArray(t, Byte, 0, 20, pool)
952-
takeSomeFromCategoryAndGenerateArray(t, Address, 0, 20, pool)
953-
takeSomeFromCategoryAndGenerateArray(t, String, 0, 20, pool)
954-
takeSomeFromCategoryAndGenerateArray(t, Bool, 0, 20, pool)
1005+
takeSomeFromCategoryAndGenerateArray(t, Byte, 0, TakeNum, pool)
1006+
takeSomeFromCategoryAndGenerateArray(t, Address, 0, TakeNum, pool)
1007+
takeSomeFromCategoryAndGenerateArray(t, String, 0, TakeNum, pool)
1008+
takeSomeFromCategoryAndGenerateArray(t, Bool, 0, TakeNum, pool)
9551009

9561010
categorySelfRoundTripTest(t, (*pool)[ArrayStatic])
9571011
categorySelfRoundTripTest(t, (*pool)[ArrayDynamic])
9581012
}
9591013

9601014
func addTupleRandomValues(t *testing.T, slotRange BaseType, pool *map[BaseType][]testUnit) {
961-
for i := 0; i < 100; i++ {
962-
tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(20))
1015+
for i := 0; i < TupleTestCaseCount; i++ {
1016+
tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(TupleMaxLength))
9631017
require.NoError(t, err, "generate random tuple length should not return error")
9641018
tupleLen := tupleLenBig.Int64() + 1
9651019
testUnits := make([]testUnit, tupleLen)

0 commit comments

Comments
 (0)