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 }