github.com/lmittmann/w3@v0.20.0/internal/abi/tuple_test.go (about) 1 package abi 2 3 import ( 4 "errors" 5 "maps" 6 "math/big" 7 "reflect" 8 "slices" 9 "strconv" 10 "testing" 11 12 "github.com/ethereum/go-ethereum/accounts/abi" 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/google/go-cmp/cmp" 15 "github.com/google/go-cmp/cmp/cmpopts" 16 "github.com/lmittmann/w3/internal" 17 ) 18 19 func TestTupleMap(t *testing.T) { 20 tests := []struct { 21 Tuples []any 22 WantTuples map[string]reflect.Type 23 WantErr error 24 }{ 25 { 26 Tuples: []any{Tuple1{}}, 27 WantTuples: map[string]reflect.Type{ 28 "Tuple1": reflect.TypeOf(Tuple1{}), 29 }, 30 }, 31 { 32 Tuples: []any{Tuple1{}, Tuple1{}}, 33 WantErr: errors.New("duplicate tuple definition: Tuple1"), 34 }, 35 { 36 Tuples: []any{123}, // int instead of struct 37 WantErr: errors.New("expected struct, got int"), 38 }, 39 { 40 Tuples: []any{"hello"}, // string instead of struct 41 WantErr: errors.New("expected struct, got string"), 42 }, 43 { 44 Tuples: []any{Tuple1{}, Tuple2{}}, 45 WantTuples: map[string]reflect.Type{ 46 "Tuple1": reflect.TypeOf(Tuple1{}), 47 "Tuple2": reflect.TypeOf(Tuple2{}), 48 }, 49 }, 50 } 51 52 for i, test := range tests { 53 t.Run(strconv.Itoa(i), func(t *testing.T) { 54 gotTuples, gotErr := tupleMap(test.Tuples...) 55 if diff := cmp.Diff(test.WantErr, gotErr, 56 internal.EquateErrors(), 57 ); diff != "" { 58 t.Fatalf("Err (-want, +got):\n%s", diff) 59 } 60 61 want := slices.Sorted(maps.Keys(test.WantTuples)) 62 got := slices.Sorted(maps.Keys(gotTuples)) 63 if diff := cmp.Diff(want, got); diff != "" { 64 t.Fatalf("Tuples (-want, +got):\n%s", diff) 65 } 66 }) 67 } 68 } 69 70 type Tuple1 struct { 71 Arg0 *big.Int 72 } 73 74 type Tuple2 struct { 75 Arg0 *big.Int 76 Arg1 *big.Int `abitype:"int256"` 77 } 78 79 func TestTypeOfField(t *testing.T) { 80 tests := []struct { 81 Field reflect.StructField 82 Want *abi.Type 83 WantErr error 84 }{ 85 // default types 86 { 87 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[bool]()}, 88 Want: &abi.Type{T: abi.BoolTy}, 89 }, 90 { 91 Field: reflect.StructField{Name: "TestUint8", Type: reflect.TypeFor[uint8]()}, 92 Want: &abi.Type{T: abi.UintTy, Size: 8}, 93 }, 94 { 95 Field: reflect.StructField{Name: "TestUint16", Type: reflect.TypeFor[uint16]()}, 96 Want: &abi.Type{T: abi.UintTy, Size: 16}, 97 }, 98 { 99 Field: reflect.StructField{Name: "TestUint32", Type: reflect.TypeFor[uint32]()}, 100 Want: &abi.Type{T: abi.UintTy, Size: 32}, 101 }, 102 { 103 Field: reflect.StructField{Name: "TestUint64", Type: reflect.TypeFor[uint64]()}, 104 Want: &abi.Type{T: abi.UintTy, Size: 64}, 105 }, 106 { 107 Field: reflect.StructField{Name: "TestInt8", Type: reflect.TypeFor[int8]()}, 108 Want: &abi.Type{T: abi.IntTy, Size: 8}, 109 }, 110 { 111 Field: reflect.StructField{Name: "TestInt16", Type: reflect.TypeFor[int16]()}, 112 Want: &abi.Type{T: abi.IntTy, Size: 16}, 113 }, 114 { 115 Field: reflect.StructField{Name: "TestInt32", Type: reflect.TypeFor[int32]()}, 116 Want: &abi.Type{T: abi.IntTy, Size: 32}, 117 }, 118 { 119 Field: reflect.StructField{Name: "TestInt64", Type: reflect.TypeFor[int64]()}, 120 Want: &abi.Type{T: abi.IntTy, Size: 64}, 121 }, 122 { 123 Field: reflect.StructField{Name: "TestBigInt", Type: reflect.TypeFor[*big.Int]()}, 124 Want: &abi.Type{T: abi.UintTy, Size: 256}, 125 }, 126 { 127 Field: reflect.StructField{Name: "TestAddress", Type: reflect.TypeFor[common.Address]()}, 128 Want: &abi.Type{T: abi.AddressTy, Size: 20}, 129 }, 130 { 131 Field: reflect.StructField{Name: "TestHash", Type: reflect.TypeFor[common.Hash]()}, 132 Want: &abi.Type{T: abi.FixedBytesTy, Size: 32}, 133 }, 134 { 135 Field: reflect.StructField{Name: "TestBytes", Type: reflect.TypeFor[[]byte]()}, 136 Want: &abi.Type{T: abi.BytesTy}, 137 }, 138 { 139 Field: reflect.StructField{Name: "TestString", Type: reflect.TypeFor[string]()}, 140 Want: &abi.Type{T: abi.StringTy}, 141 }, 142 143 // slice types 144 { 145 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[]bool]()}, 146 Want: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}}, 147 }, 148 { 149 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1]bool]()}, 150 Want: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1}, 151 }, 152 { 153 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[][]bool]()}, 154 Want: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}}}, 155 }, 156 { 157 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1][]bool]()}, 158 Want: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}}, Size: 1}, 159 }, 160 { 161 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[][1]bool]()}, 162 Want: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1}}, 163 }, 164 { 165 Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1][1]bool]()}, 166 Want: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1}, Size: 1}, 167 }, 168 169 // tagged types 170 { 171 Field: reflect.StructField{Name: "TestTagUint256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"uint256"`}, 172 Want: &abi.Type{T: abi.UintTy, Size: 256}, 173 }, 174 { 175 Field: reflect.StructField{Name: "TestTagUint256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"uint16"`}, 176 Want: &abi.Type{T: abi.UintTy, Size: 16}, 177 }, 178 { 179 Field: reflect.StructField{Name: "TestTagInt256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"int256"`}, 180 Want: &abi.Type{T: abi.IntTy, Size: 256}, 181 }, 182 { 183 Field: reflect.StructField{Name: "TestTagAddress", Type: reflect.TypeFor[common.Address](), Tag: `abitype:"address"`}, 184 Want: &abi.Type{T: abi.AddressTy, Size: 20}, 185 }, 186 { 187 Field: reflect.StructField{Name: "TestTagBytes32", Type: reflect.TypeFor[[32]byte](), Tag: `abitype:"bytes32"`}, 188 Want: &abi.Type{T: abi.FixedBytesTy, Size: 32}, 189 }, 190 { 191 Field: reflect.StructField{Name: "TestTagBytes32Hash", Type: reflect.TypeFor[common.Hash](), Tag: `abitype:"bytes32"`}, 192 WantErr: errors.New(`tagged type "bytes32" does not match type common.Hash`), 193 }, 194 { 195 Field: reflect.StructField{Name: "TestUnknownTag", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"unknown"`}, 196 WantErr: errors.New(`unknown abi type "unknown"`), 197 }, 198 { 199 Field: reflect.StructField{Name: "TestIncompatible", Type: reflect.TypeFor[string](), Tag: `abitype:"uint256"`}, 200 WantErr: errors.New(`tagged type "uint256" does not match type string`), 201 }, 202 { 203 Field: reflect.StructField{Name: "TestUnsupported", Type: reflect.TypeFor[float64]()}, 204 WantErr: errors.New(`unknown type "float64"`), 205 }, 206 } 207 208 for i, test := range tests { 209 t.Run(strconv.Itoa(i), func(t *testing.T) { 210 got, err := typeOfField(test.Field) 211 if diff := cmp.Diff(test.WantErr, err, 212 internal.EquateErrors(), 213 ); diff != "" { 214 t.Fatalf("Err: (-want +got):\n%s", diff) 215 } 216 if diff := cmp.Diff(test.Want, got, 217 cmpopts.IgnoreUnexported(abi.Type{}), 218 ); diff != "" { 219 t.Errorf("(-want +got):\n%s", diff) 220 } 221 }) 222 } 223 } 224 225 func TestToCamelCase(t *testing.T) { 226 tests := []struct { 227 Input string 228 Want string 229 }{ 230 {"", ""}, 231 {"test", "test"}, 232 {"TEST", "test"}, 233 {"Test", "test"}, 234 {"testCase", "testCase"}, 235 {"TestCase", "testCase"}, 236 {"testID", "testId"}, 237 {"TestID", "testId"}, 238 } 239 240 for i, test := range tests { 241 t.Run(strconv.Itoa(i), func(t *testing.T) { 242 got := toCamelCase(test.Input) 243 if test.Want != got { 244 t.Fatalf("want %q, got %q", test.Want, got) 245 } 246 }) 247 } 248 }