github.com/amazechain/amc@v0.1.3/internal/avm/abi/reflect_test.go (about)

     1  // Copyright 2023 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain 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 AmazeChain 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 AmazeChain 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: "OneToOneCorrespondance",
    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  	for _, test := range reflectTests {
   174  		t.Run(test.name, func(t *testing.T) {
   175  			m, err := mapArgNamesToStructFields(test.args, reflect.ValueOf(test.struc))
   176  			if len(test.err) > 0 {
   177  				if err == nil || err.Error() != test.err {
   178  					t.Fatalf("Invalid error: expected %v, got %v", test.err, err)
   179  				}
   180  			} else {
   181  				if err != nil {
   182  					t.Fatalf("Unexpected error: %v", err)
   183  				}
   184  				for fname := range test.want {
   185  					if m[fname] != test.want[fname] {
   186  						t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
   187  					}
   188  				}
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  func TestConvertType(t *testing.T) {
   195  	// Test Basic Struct
   196  	type T struct {
   197  		X *big.Int
   198  		Y *big.Int
   199  	}
   200  	// Create on-the-fly structure
   201  	var fields []reflect.StructField
   202  	fields = append(fields, reflect.StructField{
   203  		Name: "X",
   204  		Type: reflect.TypeOf(new(big.Int)),
   205  		Tag:  "json:\"" + "x" + "\"",
   206  	})
   207  	fields = append(fields, reflect.StructField{
   208  		Name: "Y",
   209  		Type: reflect.TypeOf(new(big.Int)),
   210  		Tag:  "json:\"" + "y" + "\"",
   211  	})
   212  	val := reflect.New(reflect.StructOf(fields))
   213  	val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1)))
   214  	val.Elem().Field(1).Set(reflect.ValueOf(big.NewInt(2)))
   215  	// ConvertType
   216  	out := *ConvertType(val.Interface(), new(T)).(*T)
   217  	if out.X.Cmp(big.NewInt(1)) != 0 {
   218  		t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
   219  	}
   220  	if out.Y.Cmp(big.NewInt(2)) != 0 {
   221  		t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2))
   222  	}
   223  	// Slice Type
   224  	val2 := reflect.MakeSlice(reflect.SliceOf(reflect.StructOf(fields)), 2, 2)
   225  	val2.Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1)))
   226  	val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
   227  	val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
   228  	val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
   229  	out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
   230  	if out2[0].X.Cmp(big.NewInt(1)) != 0 {
   231  		t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
   232  	}
   233  	if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
   234  		t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
   235  	}
   236  	if out2[1].X.Cmp(big.NewInt(3)) != 0 {
   237  		t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
   238  	}
   239  	if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
   240  		t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
   241  	}
   242  	// Array Type
   243  	val3 := reflect.New(reflect.ArrayOf(2, reflect.StructOf(fields)))
   244  	val3.Elem().Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1)))
   245  	val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
   246  	val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
   247  	val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
   248  	out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
   249  	if out3[0].X.Cmp(big.NewInt(1)) != 0 {
   250  		t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
   251  	}
   252  	if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
   253  		t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
   254  	}
   255  	if out3[1].X.Cmp(big.NewInt(3)) != 0 {
   256  		t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
   257  	}
   258  	if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
   259  		t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
   260  	}
   261  }