github.com/ethereum/go-ethereum@v1.14.3/accounts/abi/reflect_test.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package abi 18 19 import ( 20 "math/big" 21 "reflect" 22 "testing" 23 ) 24 25 type reflectTest struct { 26 name string 27 args []string 28 struc interface{} 29 want map[string]string 30 err string 31 } 32 33 var reflectTests = []reflectTest{ 34 { 35 name: "OneToOneCorrespondence", 36 args: []string{"fieldA"}, 37 struc: struct { 38 FieldA int `abi:"fieldA"` 39 }{}, 40 want: map[string]string{ 41 "fieldA": "FieldA", 42 }, 43 }, 44 { 45 name: "MissingFieldsInStruct", 46 args: []string{"fieldA", "fieldB"}, 47 struc: struct { 48 FieldA int `abi:"fieldA"` 49 }{}, 50 want: map[string]string{ 51 "fieldA": "FieldA", 52 }, 53 }, 54 { 55 name: "MoreFieldsInStructThanArgs", 56 args: []string{"fieldA"}, 57 struc: struct { 58 FieldA int `abi:"fieldA"` 59 FieldB int 60 }{}, 61 want: map[string]string{ 62 "fieldA": "FieldA", 63 }, 64 }, 65 { 66 name: "MissingFieldInArgs", 67 args: []string{"fieldA"}, 68 struc: struct { 69 FieldA int `abi:"fieldA"` 70 FieldB int `abi:"fieldB"` 71 }{}, 72 err: "struct: abi tag 'fieldB' defined but not found in abi", 73 }, 74 { 75 name: "NoAbiDescriptor", 76 args: []string{"fieldA"}, 77 struc: struct { 78 FieldA int 79 }{}, 80 want: map[string]string{ 81 "fieldA": "FieldA", 82 }, 83 }, 84 { 85 name: "NoArgs", 86 args: []string{}, 87 struc: struct { 88 FieldA int `abi:"fieldA"` 89 }{}, 90 err: "struct: abi tag 'fieldA' defined but not found in abi", 91 }, 92 { 93 name: "DifferentName", 94 args: []string{"fieldB"}, 95 struc: struct { 96 FieldA int `abi:"fieldB"` 97 }{}, 98 want: map[string]string{ 99 "fieldB": "FieldA", 100 }, 101 }, 102 { 103 name: "DifferentName", 104 args: []string{"fieldB"}, 105 struc: struct { 106 FieldA int `abi:"fieldB"` 107 }{}, 108 want: map[string]string{ 109 "fieldB": "FieldA", 110 }, 111 }, 112 { 113 name: "MultipleFields", 114 args: []string{"fieldA", "fieldB"}, 115 struc: struct { 116 FieldA int `abi:"fieldA"` 117 FieldB int `abi:"fieldB"` 118 }{}, 119 want: map[string]string{ 120 "fieldA": "FieldA", 121 "fieldB": "FieldB", 122 }, 123 }, 124 { 125 name: "MultipleFieldsABIMissing", 126 args: []string{"fieldA", "fieldB"}, 127 struc: struct { 128 FieldA int `abi:"fieldA"` 129 FieldB int 130 }{}, 131 want: map[string]string{ 132 "fieldA": "FieldA", 133 "fieldB": "FieldB", 134 }, 135 }, 136 { 137 name: "NameConflict", 138 args: []string{"fieldB"}, 139 struc: struct { 140 FieldA int `abi:"fieldB"` 141 FieldB int 142 }{}, 143 err: "abi: multiple variables maps to the same abi field 'fieldB'", 144 }, 145 { 146 name: "Underscored", 147 args: []string{"_"}, 148 struc: struct { 149 FieldA int 150 }{}, 151 err: "abi: purely underscored output cannot unpack to struct", 152 }, 153 { 154 name: "DoubleMapping", 155 args: []string{"fieldB", "fieldC", "fieldA"}, 156 struc: struct { 157 FieldA int `abi:"fieldC"` 158 FieldB int 159 }{}, 160 err: "abi: multiple outputs mapping to the same struct field 'FieldA'", 161 }, 162 { 163 name: "AlreadyMapped", 164 args: []string{"fieldB", "fieldB"}, 165 struc: struct { 166 FieldB int `abi:"fieldB"` 167 }{}, 168 err: "struct: abi tag in 'FieldB' already mapped", 169 }, 170 } 171 172 func TestReflectNameToStruct(t *testing.T) { 173 t.Parallel() 174 for _, test := range reflectTests { 175 test := test 176 t.Run(test.name, func(t *testing.T) { 177 t.Parallel() 178 m, err := mapArgNamesToStructFields(test.args, reflect.ValueOf(test.struc)) 179 if len(test.err) > 0 { 180 if err == nil || err.Error() != test.err { 181 t.Fatalf("Invalid error: expected %v, got %v", test.err, err) 182 } 183 } else { 184 if err != nil { 185 t.Fatalf("Unexpected error: %v", err) 186 } 187 for fname := range test.want { 188 if m[fname] != test.want[fname] { 189 t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname]) 190 } 191 } 192 } 193 }) 194 } 195 } 196 197 func TestConvertType(t *testing.T) { 198 t.Parallel() 199 // Test Basic Struct 200 type T struct { 201 X *big.Int 202 Y *big.Int 203 } 204 // Create on-the-fly structure 205 var fields []reflect.StructField 206 fields = append(fields, reflect.StructField{ 207 Name: "X", 208 Type: reflect.TypeOf(new(big.Int)), 209 Tag: "json:\"" + "x" + "\"", 210 }) 211 fields = append(fields, reflect.StructField{ 212 Name: "Y", 213 Type: reflect.TypeOf(new(big.Int)), 214 Tag: "json:\"" + "y" + "\"", 215 }) 216 val := reflect.New(reflect.StructOf(fields)) 217 val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1))) 218 val.Elem().Field(1).Set(reflect.ValueOf(big.NewInt(2))) 219 // ConvertType 220 out := *ConvertType(val.Interface(), new(T)).(*T) 221 if out.X.Cmp(big.NewInt(1)) != 0 { 222 t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1)) 223 } 224 if out.Y.Cmp(big.NewInt(2)) != 0 { 225 t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2)) 226 } 227 // Slice Type 228 val2 := reflect.MakeSlice(reflect.SliceOf(reflect.StructOf(fields)), 2, 2) 229 val2.Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) 230 val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) 231 val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) 232 val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) 233 out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T) 234 if out2[0].X.Cmp(big.NewInt(1)) != 0 { 235 t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) 236 } 237 if out2[0].Y.Cmp(big.NewInt(2)) != 0 { 238 t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) 239 } 240 if out2[1].X.Cmp(big.NewInt(3)) != 0 { 241 t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) 242 } 243 if out2[1].Y.Cmp(big.NewInt(4)) != 0 { 244 t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) 245 } 246 // Array Type 247 val3 := reflect.New(reflect.ArrayOf(2, reflect.StructOf(fields))) 248 val3.Elem().Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) 249 val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) 250 val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) 251 val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) 252 out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T) 253 if out3[0].X.Cmp(big.NewInt(1)) != 0 { 254 t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) 255 } 256 if out3[0].Y.Cmp(big.NewInt(2)) != 0 { 257 t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) 258 } 259 if out3[1].X.Cmp(big.NewInt(3)) != 0 { 260 t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) 261 } 262 if out3[1].Y.Cmp(big.NewInt(4)) != 0 { 263 t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) 264 } 265 }