elvish/pkg/eval/go_fn_test.go

170 lines
4.7 KiB
Go

package eval_test
import (
"errors"
"math/big"
"reflect"
"testing"
. "src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/errs"
. "src.elv.sh/pkg/eval/evaltest"
"src.elv.sh/pkg/eval/vals"
)
type someOptions struct {
Foo string
Bar string
}
func (o *someOptions) SetDefaultOptions() { o.Bar = "default" }
//lint:ignore ST1012 test code
var anError = errors.New("an error")
type namedSlice []string
func TestGoFn_RawOptions(t *testing.T) {
Test(t,
That("f").DoesNothing().
WithSetup(f(func() {})),
// RawOptions
That("f &foo=bar").DoesNothing().
WithSetup(f(func(opts RawOptions) {
if opts["foo"] != "bar" {
t.Errorf("RawOptions parameter doesn't get options")
}
})),
// Options when the function does not accept options.
That("f &foo=bar").Throws(ErrNoOptAccepted).
WithSetup(f(func() {
t.Errorf("Function called when there are extra options")
})),
// Parsed options
That("f &foo=bar").DoesNothing().
WithSetup(f(func(opts someOptions) {
if opts.Foo != "bar" {
t.Errorf("ScanOptions parameter doesn't get options")
}
if opts.Bar != "default" {
t.Errorf("ScanOptions parameter doesn't use default value")
}
})),
// Invalid option; regression test for #958.
That("f &bad=bar").Throws(UnknownOption{"bad"}).
WithSetup(f(func(opts someOptions) {
t.Errorf("function called when there are invalid options")
})),
// Invalid option type; regression test for #958.
That("f &foo=[]").Throws(ErrorWithType(vals.WrongType{})).
WithSetup(f(func(opts someOptions) {
t.Errorf("function called when there are invalid options")
})),
// Argument
That("f lorem ipsum").DoesNothing().
WithSetup(f(func(x, y string) {
if x != "lorem" {
t.Errorf("Argument x not passed")
}
if y != "ipsum" {
t.Errorf("Argument y not passed")
}
})),
// Variadic arguments
That("f lorem ipsum").DoesNothing().
WithSetup(f(func(args ...string) {
wantArgs := []string{"lorem", "ipsum"}
if !reflect.DeepEqual(args, wantArgs) {
t.Errorf("got args %v, want %v", args, wantArgs)
}
})),
// Argument conversion
That("f 314 1.25").DoesNothing().
WithSetup(f(func(i int, f float64) {
if i != 314 {
t.Errorf("Integer argument i not passed")
}
if f != 1.25 {
t.Errorf("Float argument f not passed")
}
})),
// Inputs
That("f [foo bar]").DoesNothing().
WithSetup(f(testInputs(t, "foo", "bar"))),
That("f [foo bar]").DoesNothing().
WithSetup(f(testInputs(t, "foo", "bar"))),
// Too many arguments
That("f x").
Throws(errs.ArityMismatch{What: "arguments",
ValidLow: 0, ValidHigh: 0, Actual: 1}).
WithSetup(f(func() {
t.Errorf("Function called when there are too many arguments")
})),
// Too few arguments
That("f").
Throws(errs.ArityMismatch{What: "arguments",
ValidLow: 1, ValidHigh: 1, Actual: 0}).
WithSetup(f(func(x string) {
t.Errorf("Function called when there are too few arguments")
})),
That("f").
Throws(errs.ArityMismatch{What: "arguments",
ValidLow: 1, ValidHigh: -1, Actual: 0}).
WithSetup(f(func(x string, y ...string) {
t.Errorf("Function called when there are too few arguments")
})),
// Wrong argument type
That("f (num 1)").Throws(ErrorWithType(WrongArgType{})).
WithSetup(f(func(x string) {
t.Errorf("Function called when arguments have wrong type")
})),
That("f str").Throws(ErrorWithType(WrongArgType{})).
WithSetup(f(func(x int) {
t.Errorf("Function called when arguments have wrong type")
})),
// Return value
That("f").Puts("foo").
WithSetup(f(func() string { return "foo" })),
// Return value conversion
That("f").Puts(314).
WithSetup(f(func() *big.Int { return big.NewInt(314) })),
// Slice and array return value
That("f").Puts("foo", "bar").
WithSetup(f(func() []string { return []string{"foo", "bar"} })),
That("f").Puts("foo", "bar").
WithSetup(f(func() [2]string { return [2]string{"foo", "bar"} })),
// Named types with underlying slice type treated as a single value
That("f").Puts(namedSlice{"foo", "bar"}).
WithSetup(f(func() namedSlice { return namedSlice{"foo", "bar"} })),
// Error return value
That("f").Throws(anError).
WithSetup(f(func() (string, error) { return "x", anError })),
That("f").DoesNothing().
WithSetup(f(func() error { return nil })),
)
}
func f(body any) func(*Evaler) {
return func(ev *Evaler) {
ev.ExtendGlobal(BuildNs().AddGoFn("f", body))
}
}
func testInputs(t *testing.T, wantValues ...any) func(Inputs) {
return func(i Inputs) {
t.Helper()
var values []any
i(func(x any) {
values = append(values, x)
})
wantValues := []any{"foo", "bar"}
if !reflect.DeepEqual(values, wantValues) {
t.Errorf("Inputs parameter didn't get supplied inputs")
}
}
}