github.com/ava-labs/subnet-evm@v0.6.4/accounts/abi/reflect_test.go (about)

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