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  }