tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/duplex/fn/call_test.go (about)

     1  package fn
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  )
     8  
     9  func fatal(err error, t *testing.T) {
    10  	t.Helper()
    11  	if err != nil {
    12  		t.Fatal(err)
    13  	}
    14  }
    15  
    16  func callParseReturn(fn interface{}, args []reflect.Value) ([]any, error) {
    17  	ret := reflect.ValueOf(fn).Call(args)
    18  	return ParseReturn(ret)
    19  }
    20  
    21  func values(args ...any) []reflect.Value {
    22  	r := make([]reflect.Value, len(args))
    23  	for i, a := range args {
    24  		r[i] = reflect.ValueOf(a)
    25  	}
    26  	return r
    27  }
    28  
    29  func equal(expected, actual []any) bool {
    30  	if len(expected) == 0 {
    31  		return len(actual) == 0
    32  	}
    33  	return reflect.DeepEqual(expected, actual)
    34  }
    35  
    36  // func TestCallCatchPanic(t *testing.T) {
    37  // 	ret, err := Call(func() { panic("catch me!") }, nil)
    38  // 	if ret != nil {
    39  // 		t.Errorf("expected nil return, got: %v", ret)
    40  // 	}
    41  // 	if err == nil || !strings.Contains(err.Error(), "catch me!") {
    42  // 		t.Errorf("expected to panic info in an error, got: %v", err)
    43  // 	}
    44  // }
    45  
    46  func TestCallArgStructSlice(t *testing.T) {
    47  	type arg struct{ V int }
    48  
    49  	acutal, err := Call(func(vs []arg) int {
    50  		sum := 0
    51  		for _, v := range vs {
    52  			sum += v.V
    53  		}
    54  		return sum
    55  	}, []any{[]arg{{1}, {2}, {3}}})
    56  	if err != nil {
    57  		t.Fatalf("unexpected error: %v", err)
    58  	}
    59  	expected := []any{int(6)}
    60  	if !reflect.DeepEqual(expected, acutal) {
    61  		t.Errorf("expected %#v, got %#v", expected, acutal)
    62  	}
    63  }
    64  
    65  func TestParseReturn(t *testing.T) {
    66  	tests := []struct {
    67  		name        string
    68  		fn          interface{}
    69  		args        []reflect.Value
    70  		expected    []any
    71  		expectedErr bool
    72  	}{
    73  		{"no return values", func() {}, nil, nil, false},
    74  		{
    75  			"single value return", func(i int) int { return i * 2 }, values(int(21)),
    76  			[]any{int(42)}, false,
    77  		},
    78  		{
    79  			"multiple value return", func(i int) (int, float64) {
    80  				return i * 2, float64(i) / 2
    81  			}, values(int(21)),
    82  			[]any{int(42), float64(10.5)}, false,
    83  		},
    84  
    85  		{"return nil error", func() error { return nil }, nil, nil, false},
    86  		{"return non-nil error", func() error { return fmt.Errorf("an error") }, nil, nil, true},
    87  
    88  		{
    89  			"single value with nil error", func() (int, error) { return 42, nil }, nil,
    90  			[]any{int(42)}, false,
    91  		},
    92  		{
    93  			"single value with non-nil error", func() (int, error) { return 42, fmt.Errorf("an error") }, nil,
    94  			nil, true,
    95  		},
    96  
    97  		{
    98  			"multiple value with nil error", func() (int, float64, error) { return 42, 0.5, nil }, nil,
    99  			[]any{int(42), float64(0.5)}, false,
   100  		},
   101  		{
   102  			"multiple value with non-nil error", func() (int, float64, error) { return 42, 0.5, fmt.Errorf("an error") }, nil,
   103  			nil, true,
   104  		},
   105  
   106  		{
   107  			"return error as value", func() any { return fmt.Errorf("an error") }, nil,
   108  			[]any{fmt.Errorf("an error")}, false,
   109  		},
   110  	}
   111  	for _, td := range tests {
   112  		t.Run(td.name, func(t *testing.T) {
   113  			actual, err := callParseReturn(td.fn, td.args)
   114  			if !td.expectedErr {
   115  				fatal(err, t)
   116  			} else if err == nil {
   117  				t.Fatalf("expected an error")
   118  			}
   119  			if !equal(td.expected, actual) {
   120  				t.Errorf("expected: %v\ngot: %v", td.expected, actual)
   121  			}
   122  		})
   123  	}
   124  }