package luar import ( "reflect" "testing" "github.com/yuin/gopher-lua" ) func Test_func_variableargs(t *testing.T) { L := lua.NewState() defer L.Close() fn := func(str string, extra ...int) { switch str { case "a": if len(extra) != 3 || extra[0] != 1 || extra[1] != 2 || extra[2] != 3 { t.Fatalf("unexpected variable arguments: %v", extra) } case "b": if len(extra) != 0 { t.Fatalf("unexpected variable arguments: %v", extra) } case "c": if len(extra) != 1 || extra[0] != 4 { t.Fatalf("unexpected variable arguments: %v", extra) } } } L.SetGlobal("fn", New(L, fn)) testReturn(t, L, `return fn("a", 1, 2, 3)`) testReturn(t, L, `return fn("b")`) testReturn(t, L, `return fn("c", 4)`) } func Test_func_structarg(t *testing.T) { L := lua.NewState() defer L.Close() tim := &StructTestPerson{ Name: "Tim", } fn := func(p *StructTestPerson) string { return "Hello, " + p.Name } L.SetGlobal("person", New(L, tim)) L.SetGlobal("getHello", New(L, fn)) testReturn(t, L, `return getHello(person)`, "Hello, Tim") } func Test_func_nilreference(t *testing.T) { L := lua.NewState() defer L.Close() var fn func() L.SetGlobal("fn", New(L, fn)) testReturn(t, L, `return fn`, "nil") } func Test_func_arrayarg(t *testing.T) { L := lua.NewState() defer L.Close() arr := [3]int{1, 2, 3} fn := func(val [3]int) { if val != arr { t.Fatalf("expecting %v, got %v", arr, val) } } L.SetGlobal("fn", New(L, fn)) L.SetGlobal("arr", New(L, arr)) testReturn(t, L, `return fn(arr)`) } func Test_func_luareturntype(t *testing.T) { L := lua.NewState() defer L.Close() fn := func(x ...float64) *lua.LTable { tbl := L.NewTable() for i := len(x) - 1; i >= 0; i-- { tbl.Insert(len(x)-i, lua.LNumber(x[i])) } return tbl } L.SetGlobal("fn", New(L, fn)) testReturn( t, L, ` local t = {} for _, x in ipairs(fn(1, 2, 3)) do table.insert(t, x) end return t[1], t[2], t[3]`, "3", "2", "1", ) testReturn( t, L, ` local t = {} for _, x in ipairs(fn()) do table.insert(t, x) end return t[1]`, "nil", ) } type TestLuaFuncRef struct { F1 *lua.LFunction } func Test_func_luafuncref(t *testing.T) { L := lua.NewState() defer L.Close() e := &TestLuaFuncRef{} L.SetGlobal("e", New(L, e)) testReturn( t, L, ` e.F1 = function(str) return "Hello World", 123 end `, ) L.Push(e.F1) L.Call(0, 2) if L.GetTop() != 2 || L.Get(1).String() != "Hello World" || L.Get(2).String() != "123" { t.Fatal("incorrect return values") } } type TestFuncCall struct { Fn func(a string) (string, int) Fn2 func(a string, b ...int) string } func Test_func_luafunccall(t *testing.T) { L := lua.NewState() defer L.Close() e := &TestFuncCall{} L.SetGlobal("x", New(L, e)) testReturn( t, L, ` i = 0 x.Fn = function(str) i = i + 1 return ">" .. str .. "<", i end x.Fn2 = function(str, a, b, c) if type(a) == "number" and type(b) == "number" and type(c) == "number" then return str end return "" end `, ) if str, i := e.Fn("A"); str != ">A<" || i != 1 { t.Fatal("unexpected return values") } if str, i := e.Fn("B"); str != ">B<" || i != 2 { t.Fatal("unexpected return values") } if val := e.Fn2("hello", 1, 2); val != "" { t.Fatal("unexpected return value") } if val := e.Fn2("hello", 1, 2, 3); val != "hello" { t.Fatal("unexpected return value") } if L.GetTop() != 0 { t.Fatalf("expecting GetTop to return 0, got %d", L.GetTop()) } } func Test_func_argerror(t *testing.T) { L := lua.NewState() defer L.Close() fn := func(v uint8) { } L.SetGlobal("fn", New(L, fn)) testError(t, L, `fn("hello world")`, "bad argument #1 to fn (cannot use hello world (type lua.LString) as type uint8)") } func Test_func_conversionstack(t *testing.T) { L := lua.NewState() defer L.Close() var fn interface{} L.SetGlobal("fn", New(L, &fn)) if err := L.DoString(`_ = fn ^ function() return "hello", true, 123 end`); err != nil { t.Fatal(err) } callable, ok := fn.(func(...interface{}) []interface{}) if !ok { t.Fatal("invalid function signature") } values := callable() expected := []interface{}{ string("hello"), bool(true), float64(123), } if !reflect.DeepEqual(expected, values) { t.Fatalf("expected return %#v, got %#v", expected, values) } }