github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/builtin_fn_test.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/u-root/u-root/cmds/core/elvish/eval/vals" 8 ) 9 10 func TestReflectBuiltinFnCall(t *testing.T) { 11 called := false 12 theFrame := new(Frame) 13 theOptions := map[string]interface{}{} 14 theError := errors.New("the error") 15 16 var f Callable 17 callGood := func(fm *Frame, args []interface{}, opts map[string]interface{}) { 18 err := f.Call(fm, args, opts) 19 if err != nil { 20 t.Errorf("Failed to call f: %v", err) 21 } 22 } 23 callBad := func(fm *Frame, args []interface{}, opts map[string]interface{}) { 24 err := f.Call(fm, args, opts) 25 if err == nil { 26 t.Errorf("Calling f didn't return error") 27 } 28 } 29 callBad2 := func(fm *Frame, args []interface{}, opts map[string]interface{}) { 30 err := f.Call(fm, args, opts) 31 if err == nil { 32 t.Errorf("Calling f didn't return error") 33 } 34 if err != theError { 35 t.Errorf("Calling f didn't return the right error") 36 } 37 } 38 39 // func() 40 called = false 41 f = NewBuiltinFn("f1", func() { 42 called = true 43 }) 44 callGood(theFrame, nil, theOptions) 45 if !called { 46 t.Errorf("not called") 47 } 48 49 // func(float64) 50 called = false 51 f = NewBuiltinFn("f2", func(a float64) { 52 called = true 53 if a != 123.456 { 54 t.Errorf("arg1 not passed") 55 } 56 }) 57 callGood(theFrame, []interface{}{"123.456"}, theOptions) 58 if !called { 59 t.Errorf("not called") 60 } 61 62 // func(int) 63 called = false 64 f = NewBuiltinFn("f3", func(a int) { 65 called = true 66 if a != 123 { 67 t.Errorf("arg1 not passed") 68 } 69 }) 70 callGood(theFrame, []interface{}{"123"}, theOptions) 71 if !called { 72 t.Errorf("not called") 73 } 74 75 // func(...int) error 76 f = NewBuiltinFn("f4", func(va ...int) error { 77 if va[0] != 123 { 78 t.Errorf("arg1 not passed") 79 } 80 if va[1] != 456 { 81 t.Errorf("arg2 not passed") 82 } 83 return theError 84 }) 85 callBad2(theFrame, []interface{}{"123", "456"}, theOptions) 86 87 // func() error 88 f = NewBuiltinFn("f5", func() error { 89 called = true 90 return theError 91 }) 92 callBad2(theFrame, nil, theOptions) 93 94 // func(*eval.Frame) 95 called = false 96 f = NewBuiltinFn("f10", func(f *Frame) { 97 called = true 98 if f != theFrame { 99 t.Errorf("*Frame parameter doesn't get current frame") 100 } 101 }) 102 callGood(theFrame, nil, theOptions) 103 if !called { 104 t.Errorf("not called") 105 } 106 107 // func(*eval.Frame, ...interface {}) error 108 f = NewBuiltinFn("f12", func(f *Frame, va ...interface{}) error { 109 if f != theFrame { 110 t.Errorf("*Frame parameter doesn't get current frame") 111 } 112 if va[0].(int) != 123 { 113 t.Errorf("arg1 not passed") 114 } 115 if va[1].(string) != "abc" { 116 t.Errorf("arg2 not passed") 117 } 118 if va[2].(error) != theError { 119 t.Errorf("arg3 not passed") 120 } 121 return theError 122 }) 123 callBad2(theFrame, []interface{}{123, "abc", theError}, theOptions) 124 125 // func(*eval.Frame, interface {}, interface {}, interface {}) 126 called = false 127 f = NewBuiltinFn("f12", func(f *Frame, a1 interface{}, a2 interface{}, a3 interface{}) { 128 called = true 129 if f != theFrame { 130 t.Errorf("*Frame parameter doesn't get current frame") 131 } 132 if a1.(int) != 123 { 133 t.Errorf("arg1 not passed") 134 } 135 if a2.(string) != "abc" { 136 t.Errorf("arg2 not passed") 137 } 138 if a3.(error) != theError { 139 t.Errorf("arg3 not passed") 140 } 141 }) 142 callGood(theFrame, []interface{}{123, "abc", theError}, theOptions) 143 if !called { 144 t.Errorf("not called") 145 } 146 147 // func(*eval.Frame, ...int) error 148 f = NewBuiltinFn("f13", func(f *Frame, va ...int) error { 149 if f != theFrame { 150 t.Errorf("*Frame parameter doesn't get current frame") 151 } 152 if va[0] != 123 { 153 t.Errorf("arg1 not passed") 154 } 155 return theError 156 }) 157 callBad2(theFrame, []interface{}{"123"}, theOptions) 158 159 // func(*eval.Frame, ...string) error 160 f = NewBuiltinFn("f13", func(f *Frame, va ...string) error { 161 if f != theFrame { 162 t.Errorf("*Frame parameter doesn't get current frame") 163 } 164 if va[0] != "abc" { 165 t.Errorf("arg1 not passed") 166 } 167 return theError 168 }) 169 callBad2(theFrame, []interface{}{"abc"}, theOptions) 170 171 // func(*eval.Frame, string) 172 called = false 173 f = NewBuiltinFn("f14", func(f *Frame, s string) { 174 called = true 175 if f != theFrame { 176 t.Errorf("*Frame parameter doesn't get current frame") 177 } 178 if s != "abc" { 179 t.Errorf("arg1 not passed") 180 } 181 }) 182 callGood(theFrame, []interface{}{"abc"}, theOptions) 183 if !called { 184 t.Errorf("not called") 185 } 186 187 // func(*eval.Frame, string) error 188 f = NewBuiltinFn("f15", func(f *Frame, s string) error { 189 if f != theFrame { 190 t.Errorf("*Frame parameter doesn't get current frame") 191 } 192 if s != "abc" { 193 t.Errorf("arg1 not passed") 194 } 195 return theError 196 }) 197 callBad2(theFrame, []interface{}{"abc"}, theOptions) 198 199 // Options parameter gets options. 200 called = false 201 f = NewBuiltinFn("f20", func(opts RawOptions) { 202 called = true 203 if opts["foo"] != "bar" { 204 t.Errorf("Options parameter doesn't get options") 205 } 206 }) 207 callGood(theFrame, nil, RawOptions{"foo": "bar"}) 208 if !called { 209 t.Errorf("not called") 210 } 211 212 // Combination of Frame and Options. 213 called = false 214 f = NewBuiltinFn("f30", func(f *Frame, opts RawOptions) { 215 called = true 216 if f != theFrame { 217 t.Errorf("*Frame parameter doesn't get current frame") 218 } 219 if opts["foo"] != "bar" { 220 t.Errorf("Options parameter doesn't get options") 221 } 222 }) 223 callGood(theFrame, nil, RawOptions{"foo": "bar"}) 224 if !called { 225 t.Errorf("not called") 226 } 227 228 // func(*eval.Frame, eval.RawOptions, eval.Callable, eval.Callable) 229 called = false 230 theCallable1 := &BuiltinFn{} 231 theCallable2 := &BuiltinFn{} 232 f = NewBuiltinFn("f35", func(f *Frame, opts RawOptions, a1 Callable, a2 Callable) { 233 called = true 234 if f != theFrame { 235 t.Errorf("*Frame parameter doesn't get current frame") 236 } 237 if opts["foo"] != "bar" { 238 t.Errorf("Options parameter doesn't get options") 239 } 240 if a1 != theCallable1 { 241 t.Errorf("arg1 not passed") 242 } 243 if a2 != theCallable2 { 244 t.Errorf("arg2 not passed") 245 } 246 }) 247 callGood(theFrame, []interface{}{theCallable1, theCallable2}, RawOptions{"foo": "bar"}) 248 if !called { 249 t.Errorf("not called") 250 } 251 252 // Argument passing. 253 called = false 254 f = NewBuiltinFn("f40", func(x string) { 255 called = true 256 if x != "lorem" { 257 t.Errorf("Argument x not passed") 258 } 259 }) 260 callGood(theFrame, []interface{}{"lorem"}, theOptions) 261 if !called { 262 t.Errorf("not called") 263 } 264 265 // Variadic arguments. 266 called = false 267 f = NewBuiltinFn("f50", func(f *Frame, x ...int) { 268 called = true 269 if len(x) != 2 || x[0] != 123 || x[1] != 456 { 270 t.Errorf("Variadic argument not passed") 271 } 272 }) 273 callGood(theFrame, []interface{}{"123", "456"}, theOptions) 274 if !called { 275 t.Errorf("not called") 276 } 277 278 // Conversion into int and float64. 279 called = false 280 f = NewBuiltinFn("f60", func(i int, f float64) { 281 called = true 282 if i != 314 { 283 t.Errorf("Integer argument i not passed") 284 } 285 if f != 1.25 { 286 t.Errorf("Float argument f not passed") 287 } 288 }) 289 callGood(theFrame, []interface{}{"314", "1.25"}, theOptions) 290 if !called { 291 t.Errorf("not called") 292 } 293 294 // func(string, ...string) 295 called = false 296 f = NewBuiltinFn("f65", func(a string, va ...string) { 297 called = true 298 if a != "lorem" { 299 t.Errorf("arg1 not passed") 300 } 301 if va[0] != "ipsum" { 302 t.Errorf("arg2 not passed") 303 } 304 }) 305 callGood(theFrame, []interface{}{"lorem", "ipsum"}, theOptions) 306 if !called { 307 t.Errorf("not called") 308 } 309 310 // Conversion of supplied inputs. 311 called = false 312 f = NewBuiltinFn("f70", func(i Inputs) { 313 called = true 314 var values []interface{} 315 i(func(x interface{}) { 316 values = append(values, x) 317 }) 318 if len(values) != 2 || values[0] != "foo" || values[1] != "bar" { 319 t.Errorf("Inputs parameter didn't get supplied inputs") 320 } 321 }) 322 callGood(theFrame, []interface{}{vals.MakeList("foo", "bar")}, theOptions) 323 if !called { 324 t.Errorf("not called") 325 } 326 327 // Conversion of implicit inputs. 328 inFrame := &Frame{ports: make([]*Port, 3)} 329 ch := make(chan interface{}, 10) 330 ch <- "foo" 331 ch <- "bar" 332 close(ch) 333 inFrame.ports[0] = &Port{Chan: ch} 334 called = false 335 f = NewBuiltinFn("f80", func(f *Frame, opts RawOptions, s string, i Inputs) { 336 called = true 337 var values []interface{} 338 i(func(x interface{}) { 339 values = append(values, x) 340 }) 341 if s != "s" { 342 t.Errorf("Explicit argument not passed") 343 } 344 if len(values) != 2 || values[0] != "foo" || values[1] != "bar" { 345 t.Errorf("Inputs parameter didn't get implicit inputs") 346 } 347 }) 348 callGood(inFrame, []interface{}{"s", vals.MakeList("foo", "bar")}, theOptions) 349 if !called { 350 t.Errorf("not called") 351 } 352 353 // Outputting of return values. 354 outFrame := &Frame{ports: make([]*Port, 3)} 355 ch = make(chan interface{}, 10) 356 outFrame.ports[1] = &Port{Chan: ch} 357 f = NewBuiltinFn("f90", func(s string) string { return s + "-ret" }) 358 callGood(outFrame, []interface{}{"arg"}, theOptions) 359 select { 360 case ret := <-ch: 361 if ret != "arg-ret" { 362 t.Errorf("Output is not the same as return value") 363 } 364 default: 365 t.Errorf("Return value is not outputted") 366 } 367 368 // Conversion of return values. 369 f = NewBuiltinFn("f100", func() int { return 314 }) 370 callGood(outFrame, nil, theOptions) 371 select { 372 case ret := <-ch: 373 if ret != "314" { 374 t.Errorf("Return value is not converted to string") 375 } 376 default: 377 t.Errorf("Return value is not outputted") 378 } 379 380 // Passing of error return value. 381 f = NewBuiltinFn("f110", func() error { 382 return theError 383 }) 384 if f.Call(outFrame, nil, theOptions) != theError { 385 t.Errorf("Returned error is not passed") 386 } 387 select { 388 case <-ch: 389 t.Errorf("Return value is outputted when error is not nil") 390 default: 391 } 392 393 // Too many arguments. 394 f = NewBuiltinFn("f120", func() { 395 t.Errorf("Function called when there are too many arguments") 396 }) 397 callBad(theFrame, []interface{}{"x"}, theOptions) 398 399 // Too few arguments. 400 f = NewBuiltinFn("f130", func(x string) { 401 t.Errorf("Function called when there are too few arguments") 402 }) 403 callBad(theFrame, nil, theOptions) 404 f = NewBuiltinFn("f140", func(x string, y ...string) { 405 t.Errorf("Function called when there are too few arguments") 406 }) 407 callBad(theFrame, nil, theOptions) 408 409 // Options when the function does not accept options. 410 f = NewBuiltinFn("f150", func() { 411 t.Errorf("Function called when there are extra options") 412 }) 413 callBad(theFrame, nil, RawOptions{"foo": "bar"}) 414 415 // Wrong argument type. 416 f = NewBuiltinFn("f160", func(x string) { 417 t.Errorf("Function called when arguments have wrong type") 418 }) 419 callBad(theFrame, []interface{}{1}, theOptions) 420 421 // Wrong argument type: cannot convert to int. 422 f = NewBuiltinFn("f170", func(x int) { 423 t.Errorf("Function called when arguments have wrong type") 424 }) 425 callBad(theFrame, []interface{}{"x"}, theOptions) 426 427 // Wrong argument type: cannot convert to float64. 428 f = NewBuiltinFn("f180", func(x float64) { 429 t.Errorf("Function called when arguments have wrong type") 430 }) 431 callBad(theFrame, []interface{}{"x"}, theOptions) 432 }