mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 19:39:44 +08:00
refactor: convertArgs
This commit is contained in:
@@ -8,91 +8,25 @@ import (
|
||||
)
|
||||
|
||||
// CallFunc calls function with arguments
|
||||
// it is used when calling go plugin or builtin functions
|
||||
func CallFunc(fn reflect.Value, args ...interface{}) (interface{}, error) {
|
||||
fnArgsNum := fn.Type().NumIn()
|
||||
|
||||
// function expect 0 argument
|
||||
if fnArgsNum == 0 {
|
||||
if len(args) > 0 {
|
||||
return nil, fmt.Errorf("function expect 0 argument, but got %d", len(args))
|
||||
}
|
||||
return call(fn, nil)
|
||||
}
|
||||
|
||||
// fnArgsNum > 0: function expect more than 0 arguments
|
||||
// function last argument is not slice
|
||||
if fn.Type().In(fnArgsNum-1).Kind() != reflect.Slice {
|
||||
// function arguments should match exactly
|
||||
if fnArgsNum != len(args) {
|
||||
return nil, fmt.Errorf("function expect %d arguments, but got %d", fnArgsNum, len(args))
|
||||
}
|
||||
// prepare arguments
|
||||
argumentsValue, err := convertFirstNArgs(fn, fnArgsNum, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return call(fn, argumentsValue)
|
||||
}
|
||||
|
||||
// function last argument is slice
|
||||
// e.g.
|
||||
// ...interface{}
|
||||
// a, b string, c ...interface{}
|
||||
// []int
|
||||
// a, b string, c []int
|
||||
|
||||
// arguments number should >= fnArgsNum-1
|
||||
if len(args) < fnArgsNum-1 {
|
||||
return nil, fmt.Errorf("function expect at least %d arguments, but got %d", fnArgsNum-1, len(args))
|
||||
}
|
||||
|
||||
// prepare first n-1 arguments
|
||||
argumentsValue, err := convertFirstNArgs(fn, fnArgsNum-1, args...)
|
||||
argumentsValue, err := convertArgs(fn, args...)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("convert arguments failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// prepare last slice argument
|
||||
fnLastSliceElemType := fn.Type().In(fnArgsNum - 1).Elem() // slice element type
|
||||
for i := fnArgsNum - 1; i < len(args); i++ { // loop last len(args)-fnArgsNum+1 arguments
|
||||
argValue := reflect.ValueOf(args[i])
|
||||
argValueType := reflect.TypeOf(args[i])
|
||||
|
||||
// check if slice element type match
|
||||
if fnLastSliceElemType == argValueType {
|
||||
argumentsValue = append(argumentsValue, argValue)
|
||||
continue
|
||||
}
|
||||
|
||||
// last argument is also slice, e.g. []int
|
||||
if argValueType.Kind() == reflect.Slice {
|
||||
if argValueType.Elem() != fnLastSliceElemType {
|
||||
err := fmt.Errorf("function argument %d's slice element type is not match, expect %v, actual %v",
|
||||
i, fnLastSliceElemType, argValueType)
|
||||
return nil, err
|
||||
}
|
||||
argumentsValue = append(argumentsValue, argValue)
|
||||
continue
|
||||
}
|
||||
|
||||
// type not match, check if convertible
|
||||
if !argValueType.ConvertibleTo(fnLastSliceElemType) {
|
||||
// function argument type not match and not convertible
|
||||
err := fmt.Errorf("function argument %d's type is neither match nor convertible, expect %v, actual %v",
|
||||
i, fnLastSliceElemType, argValueType)
|
||||
return nil, err
|
||||
}
|
||||
// convert argument to expect type
|
||||
argumentsValue = append(argumentsValue, argValue.Convert(fnLastSliceElemType))
|
||||
}
|
||||
|
||||
return call(fn, argumentsValue)
|
||||
}
|
||||
|
||||
func convertFirstNArgs(fn reflect.Value, n int, args ...interface{}) ([]reflect.Value, error) {
|
||||
argumentsValue := make([]reflect.Value, n)
|
||||
for index := 0; index < n; index++ {
|
||||
func convertArgs(fn reflect.Value, args ...interface{}) ([]reflect.Value, error) {
|
||||
fnArgsNum := fn.Type().NumIn()
|
||||
|
||||
// function arguments should match exactly if function's last argument is not slice
|
||||
if len(args) != fnArgsNum && (fnArgsNum == 0 || fn.Type().In(fnArgsNum-1).Kind() != reflect.Slice) {
|
||||
return nil, fmt.Errorf("function expect %d arguments, but got %d", fnArgsNum, len(args))
|
||||
}
|
||||
|
||||
argumentsValue := make([]reflect.Value, len(args))
|
||||
for index := 0; index < len(args); index++ {
|
||||
argument := args[index]
|
||||
if argument == nil {
|
||||
argumentsValue[index] = reflect.Zero(fn.Type().In(index))
|
||||
@@ -100,9 +34,27 @@ func convertFirstNArgs(fn reflect.Value, n int, args ...interface{}) ([]reflect.
|
||||
}
|
||||
|
||||
argumentValue := reflect.ValueOf(argument)
|
||||
expectArgumentType := fn.Type().In(index)
|
||||
actualArgumentType := reflect.TypeOf(argument)
|
||||
|
||||
var expectArgumentType reflect.Type
|
||||
if (index == fnArgsNum-1 && fn.Type().In(fnArgsNum-1).Kind() == reflect.Slice) || index > fnArgsNum-1 {
|
||||
// last fn argument is slice
|
||||
expectArgumentType = fn.Type().In(fnArgsNum - 1).Elem() // slice element type
|
||||
|
||||
// last argument is also slice, e.g. []int
|
||||
if actualArgumentType.Kind() == reflect.Slice {
|
||||
if actualArgumentType.Elem() != expectArgumentType {
|
||||
err := fmt.Errorf("function argument %d's slice element type is not match, expect %v, actual %v",
|
||||
index, expectArgumentType, actualArgumentType)
|
||||
return nil, err
|
||||
}
|
||||
argumentsValue[index] = argumentValue
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
expectArgumentType = fn.Type().In(index)
|
||||
}
|
||||
|
||||
// type match
|
||||
if expectArgumentType == actualArgumentType {
|
||||
argumentsValue[index] = argumentValue
|
||||
@@ -114,7 +66,6 @@ func convertFirstNArgs(fn reflect.Value, n int, args ...interface{}) ([]reflect.
|
||||
// function argument type not match and not convertible
|
||||
err := fmt.Errorf("function argument %d's type is neither match nor convertible, expect %v, actual %v",
|
||||
index, expectArgumentType, actualArgumentType)
|
||||
log.Error().Err(err).Msg("convert arguments failed")
|
||||
return nil, err
|
||||
}
|
||||
// convert argument to expect type
|
||||
|
||||
@@ -168,8 +168,30 @@ func TestCallFuncComplex(t *testing.T) {
|
||||
args: []interface{}{1, 2, []int{3, 4}},
|
||||
expVal: 10,
|
||||
},
|
||||
|
||||
// FIXME: return more than 2 values
|
||||
}
|
||||
|
||||
for _, p := range params {
|
||||
fn := reflect.ValueOf(p.f)
|
||||
val, err := CallFunc(fn, p.args...)
|
||||
if !assert.Equal(t, p.expErr, err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !assert.Equal(t, p.expVal, val) {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCallFuncAbnormal(t *testing.T) {
|
||||
params := []data{
|
||||
// return more than 2 values
|
||||
{
|
||||
f: func() (int, int, error) { return 1, 2, nil },
|
||||
args: []interface{}{},
|
||||
expVal: nil,
|
||||
expErr: fmt.Errorf("function should return at most 2 values"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, p := range params {
|
||||
|
||||
Reference in New Issue
Block a user