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  }