github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/go_fn_test.go (about)

     1  package eval_test
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"reflect"
     7  	"testing"
     8  
     9  	. "github.com/markusbkk/elvish/pkg/eval"
    10  	"github.com/markusbkk/elvish/pkg/eval/errs"
    11  	. "github.com/markusbkk/elvish/pkg/eval/evaltest"
    12  	"github.com/markusbkk/elvish/pkg/eval/vals"
    13  )
    14  
    15  type someOptions struct {
    16  	Foo string
    17  	Bar string
    18  }
    19  
    20  func (o *someOptions) SetDefaultOptions() { o.Bar = "default" }
    21  
    22  //lint:ignore ST1012 test code
    23  var anError = errors.New("an error")
    24  
    25  type namedSlice []string
    26  
    27  func TestGoFn_RawOptions(t *testing.T) {
    28  	Test(t,
    29  		That("f").DoesNothing().
    30  			WithSetup(f(func() {})),
    31  
    32  		// RawOptions
    33  		That("f &foo=bar").DoesNothing().
    34  			WithSetup(f(func(opts RawOptions) {
    35  				if opts["foo"] != "bar" {
    36  					t.Errorf("RawOptions parameter doesn't get options")
    37  				}
    38  			})),
    39  		// Options when the function does not accept options.
    40  		That("f &foo=bar").Throws(ErrNoOptAccepted).
    41  			WithSetup(f(func() {
    42  				t.Errorf("Function called when there are extra options")
    43  			})),
    44  		// Parsed options
    45  		That("f &foo=bar").DoesNothing().
    46  			WithSetup(f(func(opts someOptions) {
    47  				if opts.Foo != "bar" {
    48  					t.Errorf("ScanOptions parameter doesn't get options")
    49  				}
    50  				if opts.Bar != "default" {
    51  					t.Errorf("ScanOptions parameter doesn't use default value")
    52  				}
    53  			})),
    54  		// Invalid option; regression test for #958.
    55  		That("f &bad=bar").Throws(UnknownOption{"bad"}).
    56  			WithSetup(f(func(opts someOptions) {
    57  				t.Errorf("function called when there are invalid options")
    58  			})),
    59  		// Invalid option type; regression test for #958.
    60  		That("f &foo=[]").Throws(ErrorWithType(vals.WrongType{})).
    61  			WithSetup(f(func(opts someOptions) {
    62  				t.Errorf("function called when there are invalid options")
    63  			})),
    64  
    65  		// Argument
    66  		That("f lorem ipsum").DoesNothing().
    67  			WithSetup(f(func(x, y string) {
    68  				if x != "lorem" {
    69  					t.Errorf("Argument x not passed")
    70  				}
    71  				if y != "ipsum" {
    72  					t.Errorf("Argument y not passed")
    73  				}
    74  			})),
    75  		// Variadic arguments
    76  		That("f lorem ipsum").DoesNothing().
    77  			WithSetup(f(func(args ...string) {
    78  				wantArgs := []string{"lorem", "ipsum"}
    79  				if !reflect.DeepEqual(args, wantArgs) {
    80  					t.Errorf("got args %v, want %v", args, wantArgs)
    81  				}
    82  			})),
    83  		// Argument conversion
    84  		That("f 314 1.25").DoesNothing().
    85  			WithSetup(f(func(i int, f float64) {
    86  				if i != 314 {
    87  					t.Errorf("Integer argument i not passed")
    88  				}
    89  				if f != 1.25 {
    90  					t.Errorf("Float argument f not passed")
    91  				}
    92  			})),
    93  		// Inputs
    94  		That("f [foo bar]").DoesNothing().
    95  			WithSetup(f(testInputs(t, "foo", "bar"))),
    96  		That("f [foo bar]").DoesNothing().
    97  			WithSetup(f(testInputs(t, "foo", "bar"))),
    98  		// Too many arguments
    99  		That("f x").
   100  			Throws(errs.ArityMismatch{What: "arguments",
   101  				ValidLow: 0, ValidHigh: 0, Actual: 1}).
   102  			WithSetup(f(func() {
   103  				t.Errorf("Function called when there are too many arguments")
   104  			})),
   105  		// Too few arguments
   106  		That("f").
   107  			Throws(errs.ArityMismatch{What: "arguments",
   108  				ValidLow: 1, ValidHigh: 1, Actual: 0}).
   109  			WithSetup(f(func(x string) {
   110  				t.Errorf("Function called when there are too few arguments")
   111  			})),
   112  		That("f").
   113  			Throws(errs.ArityMismatch{What: "arguments",
   114  				ValidLow: 1, ValidHigh: -1, Actual: 0}).
   115  			WithSetup(f(func(x string, y ...string) {
   116  				t.Errorf("Function called when there are too few arguments")
   117  			})),
   118  		// Wrong argument type
   119  		That("f (num 1)").Throws(ErrorWithType(WrongArgType{})).
   120  			WithSetup(f(func(x string) {
   121  				t.Errorf("Function called when arguments have wrong type")
   122  			})),
   123  		That("f str").Throws(ErrorWithType(WrongArgType{})).
   124  			WithSetup(f(func(x int) {
   125  				t.Errorf("Function called when arguments have wrong type")
   126  			})),
   127  
   128  		// Return value
   129  		That("f").Puts("foo").
   130  			WithSetup(f(func() string { return "foo" })),
   131  		// Return value conversion
   132  		That("f").Puts(314).
   133  			WithSetup(f(func() *big.Int { return big.NewInt(314) })),
   134  		// Slice and array return value
   135  		That("f").Puts("foo", "bar").
   136  			WithSetup(f(func() []string { return []string{"foo", "bar"} })),
   137  		That("f").Puts("foo", "bar").
   138  			WithSetup(f(func() [2]string { return [2]string{"foo", "bar"} })),
   139  		// Named types with underlying slice type treated as a single value
   140  		That("f").Puts(namedSlice{"foo", "bar"}).
   141  			WithSetup(f(func() namedSlice { return namedSlice{"foo", "bar"} })),
   142  
   143  		// Error return value
   144  		That("f").Throws(anError).
   145  			WithSetup(f(func() (string, error) { return "x", anError })),
   146  		That("f").DoesNothing().
   147  			WithSetup(f(func() error { return nil })),
   148  	)
   149  }
   150  
   151  func f(body interface{}) func(*Evaler) {
   152  	return func(ev *Evaler) {
   153  		ev.ExtendGlobal(BuildNs().AddGoFn("f", body))
   154  	}
   155  }
   156  
   157  func testInputs(t *testing.T, wantValues ...interface{}) func(Inputs) {
   158  	return func(i Inputs) {
   159  		t.Helper()
   160  		var values []interface{}
   161  		i(func(x interface{}) {
   162  			values = append(values, x)
   163  		})
   164  		wantValues := []interface{}{"foo", "bar"}
   165  		if !reflect.DeepEqual(values, wantValues) {
   166  			t.Errorf("Inputs parameter didn't get supplied inputs")
   167  		}
   168  	}
   169  }