github.com/elves/Elvish@v0.12.0/eval/builtin_fn_test.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/elves/elvish/eval/vals" 8 ) 9 10 func TestReflectBuiltinFnCall(t *testing.T) { 11 theFrame := new(Frame) 12 theOptions := map[string]interface{}{} 13 14 var f Callable 15 callGood := func(fm *Frame, args []interface{}, opts map[string]interface{}) { 16 err := f.Call(fm, args, opts) 17 if err != nil { 18 t.Errorf("Failed to call f: %v", err) 19 } 20 } 21 callBad := func(fm *Frame, args []interface{}, opts map[string]interface{}) { 22 err := f.Call(fm, args, opts) 23 if err == nil { 24 t.Errorf("Calling f didn't return error") 25 } 26 } 27 28 // *Frame parameter gets the Frame. 29 f = NewBuiltinFn("f", func(f *Frame) { 30 if f != theFrame { 31 t.Errorf("*Frame parameter doesn't get current frame") 32 } 33 }) 34 callGood(theFrame, nil, theOptions) 35 36 // Options parameter gets options. 37 f = NewBuiltinFn("f", func(opts RawOptions) { 38 if opts["foo"] != "bar" { 39 t.Errorf("Options parameter doesn't get options") 40 } 41 }) 42 callGood(theFrame, nil, RawOptions{"foo": "bar"}) 43 44 // Combination of Frame and Options. 45 f = NewBuiltinFn("f", func(f *Frame, opts RawOptions) { 46 if f != theFrame { 47 t.Errorf("*Frame parameter doesn't get current frame") 48 } 49 if opts["foo"] != "bar" { 50 t.Errorf("Options parameter doesn't get options") 51 } 52 }) 53 callGood(theFrame, nil, RawOptions{"foo": "bar"}) 54 55 // Argument passing. 56 f = NewBuiltinFn("f", func(x, y string) { 57 if x != "lorem" { 58 t.Errorf("Argument x not passed") 59 } 60 if y != "ipsum" { 61 t.Errorf("Argument y not passed") 62 } 63 }) 64 callGood(theFrame, []interface{}{"lorem", "ipsum"}, theOptions) 65 66 // Variadic arguments. 67 f = NewBuiltinFn("f", func(x ...string) { 68 if len(x) != 2 || x[0] != "lorem" || x[1] != "ipsum" { 69 t.Errorf("Variadic argument not passed") 70 } 71 }) 72 callGood(theFrame, []interface{}{"lorem", "ipsum"}, theOptions) 73 74 // Conversion into int and float64. 75 f = NewBuiltinFn("f", func(i int, f float64) { 76 if i != 314 { 77 t.Errorf("Integer argument i not passed") 78 } 79 if f != 1.25 { 80 t.Errorf("Float argument f not passed") 81 } 82 }) 83 callGood(theFrame, []interface{}{"314", "1.25"}, theOptions) 84 85 // Conversion of supplied inputs. 86 f = NewBuiltinFn("f", func(i Inputs) { 87 var values []interface{} 88 i(func(x interface{}) { 89 values = append(values, x) 90 }) 91 if len(values) != 2 || values[0] != "foo" || values[1] != "bar" { 92 t.Errorf("Inputs parameter didn't get supplied inputs") 93 } 94 }) 95 callGood(theFrame, []interface{}{vals.MakeList("foo", "bar")}, theOptions) 96 97 // Conversion of implicit inputs. 98 inFrame := &Frame{ports: make([]*Port, 3)} 99 ch := make(chan interface{}, 10) 100 ch <- "foo" 101 ch <- "bar" 102 close(ch) 103 inFrame.ports[0] = &Port{Chan: ch} 104 f = NewBuiltinFn("f", func(i Inputs) { 105 var values []interface{} 106 i(func(x interface{}) { 107 values = append(values, x) 108 }) 109 if len(values) != 2 || values[0] != "foo" || values[1] != "bar" { 110 t.Errorf("Inputs parameter didn't get implicit inputs") 111 } 112 }) 113 callGood(inFrame, []interface{}{vals.MakeList("foo", "bar")}, theOptions) 114 115 // Outputting of return values. 116 outFrame := &Frame{ports: make([]*Port, 3)} 117 ch = make(chan interface{}, 10) 118 outFrame.ports[1] = &Port{Chan: ch} 119 f = NewBuiltinFn("f", func() string { return "ret" }) 120 callGood(outFrame, nil, theOptions) 121 select { 122 case ret := <-ch: 123 if ret != "ret" { 124 t.Errorf("Output is not the same as return value") 125 } 126 default: 127 t.Errorf("Return value is not outputted") 128 } 129 130 // Conversion of return values. 131 f = NewBuiltinFn("f", func() int { return 314 }) 132 callGood(outFrame, nil, theOptions) 133 select { 134 case ret := <-ch: 135 if ret != "314" { 136 t.Errorf("Return value is not converted to string") 137 } 138 default: 139 t.Errorf("Return value is not outputted") 140 } 141 142 // Passing of error return value. 143 theError := errors.New("the error") 144 f = NewBuiltinFn("f", func() (string, error) { 145 return "x", theError 146 }) 147 if f.Call(outFrame, nil, theOptions) != theError { 148 t.Errorf("Returned error is not passed") 149 } 150 select { 151 case <-ch: 152 t.Errorf("Return value is outputted when error is not nil") 153 default: 154 } 155 156 // Too many arguments. 157 f = NewBuiltinFn("f", func() { 158 t.Errorf("Function called when there are too many arguments") 159 }) 160 callBad(theFrame, []interface{}{"x"}, theOptions) 161 162 // Too few arguments. 163 f = NewBuiltinFn("f", func(x string) { 164 t.Errorf("Function called when there are too few arguments") 165 }) 166 callBad(theFrame, nil, theOptions) 167 f = NewBuiltinFn("f", func(x string, y ...string) { 168 t.Errorf("Function called when there are too few arguments") 169 }) 170 callBad(theFrame, nil, theOptions) 171 172 // Options when the function does not accept options. 173 f = NewBuiltinFn("f", func() { 174 t.Errorf("Function called when there are extra options") 175 }) 176 callBad(theFrame, nil, RawOptions{"foo": "bar"}) 177 178 // Wrong argument type. 179 f = NewBuiltinFn("f", func(x string) { 180 t.Errorf("Function called when arguments have wrong type") 181 }) 182 callBad(theFrame, []interface{}{1}, theOptions) 183 184 // Wrong argument type: cannot convert to int. 185 f = NewBuiltinFn("f", func(x int) { 186 t.Errorf("Function called when arguments have wrong type") 187 }) 188 callBad(theFrame, []interface{}{"x"}, theOptions) 189 190 // Wrong argument type: cannot convert to float64. 191 f = NewBuiltinFn("f", func(x float64) { 192 t.Errorf("Function called when arguments have wrong type") 193 }) 194 callBad(theFrame, []interface{}{"x"}, theOptions) 195 }