mirror of https://github.com/ledisdb/ledisdb.git
387 lines
7.4 KiB
Go
387 lines
7.4 KiB
Go
|
// +build lua
|
||
|
|
||
|
package lua
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
type TestStruct struct {
|
||
|
IntField int
|
||
|
StringField string
|
||
|
FloatField float64
|
||
|
}
|
||
|
|
||
|
func TestGoStruct(t *testing.T) {
|
||
|
L := NewState()
|
||
|
L.OpenLibs()
|
||
|
defer L.Close()
|
||
|
|
||
|
ts := &TestStruct{10, "test", 2.3}
|
||
|
|
||
|
L.CheckStack(1)
|
||
|
|
||
|
L.PushGoStruct(ts)
|
||
|
L.SetGlobal("t")
|
||
|
|
||
|
L.GetGlobal("t")
|
||
|
if !L.IsGoStruct(-1) {
|
||
|
t.Fatal("Not go struct")
|
||
|
}
|
||
|
|
||
|
tsr := L.ToGoStruct(-1).(*TestStruct)
|
||
|
if tsr != ts {
|
||
|
t.Fatal("Retrieved something different from what we inserted")
|
||
|
}
|
||
|
|
||
|
L.Pop(1)
|
||
|
|
||
|
L.PushString("This is not a struct")
|
||
|
if L.ToGoStruct(-1) != nil {
|
||
|
t.Fatal("Non-GoStruct value attempted to convert into GoStruct should result in nil")
|
||
|
}
|
||
|
|
||
|
L.Pop(1)
|
||
|
}
|
||
|
|
||
|
func TestCheckStringSuccess(t *testing.T) {
|
||
|
L := NewState()
|
||
|
L.OpenLibs()
|
||
|
defer L.Close()
|
||
|
|
||
|
Test := func(L *State) int {
|
||
|
L.PushString("this is a test")
|
||
|
L.CheckString(-1)
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
L.Register("test", Test)
|
||
|
err := L.DoString("test()")
|
||
|
if err != nil {
|
||
|
t.Fatalf("DoString did return an error: %v\n", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCheckStringFail(t *testing.T) {
|
||
|
L := NewState()
|
||
|
L.OpenLibs()
|
||
|
defer L.Close()
|
||
|
|
||
|
Test := func(L *State) int {
|
||
|
L.CheckString(-1)
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
L.Register("test", Test)
|
||
|
err := L.DoString("test();")
|
||
|
if err == nil {
|
||
|
t.Fatal("DoString did not return an error\n")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPCallHidden(t *testing.T) {
|
||
|
L := NewState()
|
||
|
L.OpenLibs()
|
||
|
defer L.Close()
|
||
|
|
||
|
err := L.DoString("pcall(print, \"ciao\")")
|
||
|
if err == nil {
|
||
|
t.Fatal("Can use pcall\n")
|
||
|
}
|
||
|
|
||
|
err = L.DoString("unsafe_pcall(print, \"ciao\")")
|
||
|
if err != nil {
|
||
|
t.Fatal("Can not use unsafe_pcall\n")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCall(t *testing.T) {
|
||
|
L := NewState()
|
||
|
L.OpenLibs()
|
||
|
defer L.Close()
|
||
|
|
||
|
test := func(L *State) int {
|
||
|
arg1 := L.ToString(1)
|
||
|
arg2 := L.ToString(2)
|
||
|
arg3 := L.ToString(3)
|
||
|
|
||
|
if arg1 != "Argument1" {
|
||
|
t.Fatal("Got wrong argument (1)")
|
||
|
}
|
||
|
|
||
|
if arg2 != "Argument2" {
|
||
|
t.Fatal("Got wrong argument (2)")
|
||
|
}
|
||
|
|
||
|
if arg3 != "Argument3" {
|
||
|
t.Fatal("Got wrong argument (3)")
|
||
|
}
|
||
|
|
||
|
L.PushString("Return1")
|
||
|
L.PushString("Return2")
|
||
|
|
||
|
return 2
|
||
|
}
|
||
|
|
||
|
L.Register("test", test)
|
||
|
|
||
|
L.PushString("Dummy")
|
||
|
L.GetGlobal("test")
|
||
|
L.PushString("Argument1")
|
||
|
L.PushString("Argument2")
|
||
|
L.PushString("Argument3")
|
||
|
err := L.Call(3, 2)
|
||
|
|
||
|
if err != nil {
|
||
|
t.Fatalf("Error executing call: %v\n", err)
|
||
|
}
|
||
|
|
||
|
dummy := L.ToString(1)
|
||
|
ret1 := L.ToString(2)
|
||
|
ret2 := L.ToString(3)
|
||
|
|
||
|
if dummy != "Dummy" {
|
||
|
t.Fatal("The stack was disturbed")
|
||
|
}
|
||
|
|
||
|
if ret1 != "Return1" {
|
||
|
t.Fatalf("Wrong return value (1) got: <%s>", ret1)
|
||
|
}
|
||
|
|
||
|
if ret2 != "Return2" {
|
||
|
t.Fatalf("Wrong return value (2) got: <%s>", ret2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// equivalent to basic.go
|
||
|
func TestLikeBasic(t *testing.T) {
|
||
|
L := NewState()
|
||
|
defer L.Close()
|
||
|
L.OpenLibs()
|
||
|
|
||
|
testCalled := 0
|
||
|
|
||
|
test := func(L *State) int {
|
||
|
testCalled++
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
test2Arg := -1
|
||
|
test2Argfrombottom := -1
|
||
|
test2 := func(L *State) int {
|
||
|
test2Arg = L.CheckInteger(-1)
|
||
|
test2Argfrombottom = L.CheckInteger(1)
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
L.GetField(LUA_GLOBALSINDEX, "print")
|
||
|
L.PushString("Hello World!")
|
||
|
if err := L.Call(1, 0); err != nil {
|
||
|
t.Fatalf("Call to print returned error")
|
||
|
}
|
||
|
|
||
|
L.PushGoFunction(test)
|
||
|
L.PushGoFunction(test)
|
||
|
L.PushGoFunction(test)
|
||
|
L.PushGoFunction(test2)
|
||
|
L.PushInteger(42)
|
||
|
if err := L.Call(1, 0); err != nil {
|
||
|
t.Fatalf("Call to print returned error")
|
||
|
}
|
||
|
if (test2Arg != 42) || (test2Argfrombottom != 42) {
|
||
|
t.Fatalf("Call to test2 didn't work")
|
||
|
}
|
||
|
|
||
|
if err := L.Call(0, 0); err != nil {
|
||
|
t.Fatalf("Call to print returned error")
|
||
|
}
|
||
|
if err := L.Call(0, 0); err != nil {
|
||
|
t.Fatalf("Call to print returned error")
|
||
|
}
|
||
|
if err := L.Call(0, 0); err != nil {
|
||
|
t.Fatalf("Call to print returned error")
|
||
|
}
|
||
|
if testCalled != 3 {
|
||
|
t.Fatalf("Test function not called the correct number of times: %d\n", testCalled)
|
||
|
}
|
||
|
|
||
|
// this will fail as we didn't register test2 function
|
||
|
if err := L.DoString("test2(42)"); err == nil {
|
||
|
t.Fatal("No error when calling unregistered function")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// equivalent to quickstart.go
|
||
|
func TestLikeQuickstart(t *testing.T) {
|
||
|
adder := func(L *State) int {
|
||
|
a := L.ToInteger(1)
|
||
|
b := L.ToInteger(2)
|
||
|
L.PushInteger(int64(a + b))
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
L := NewState()
|
||
|
defer L.Close()
|
||
|
L.OpenLibs()
|
||
|
|
||
|
L.Register("adder", adder)
|
||
|
|
||
|
if err := L.DoString("return adder(2, 2)"); err != nil {
|
||
|
t.Fatalf("Error during call to adder: %v\n", err)
|
||
|
}
|
||
|
if r := L.ToInteger(1); r != 4 {
|
||
|
t.Fatalf("Wrong return value from adder (was: %d)\n", r)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// equivalent to userdata.go
|
||
|
func TestLikeUserdata(t *testing.T) {
|
||
|
type Userdata struct {
|
||
|
a, b int
|
||
|
}
|
||
|
|
||
|
userDataProper := func(L *State) {
|
||
|
rawptr := L.NewUserdata(uintptr(unsafe.Sizeof(Userdata{})))
|
||
|
var ptr *Userdata
|
||
|
ptr = (*Userdata)(rawptr)
|
||
|
ptr.a = 2
|
||
|
ptr.b = 3
|
||
|
|
||
|
rawptr2 := L.ToUserdata(-1)
|
||
|
ptr2 := (*Userdata)(rawptr2)
|
||
|
|
||
|
if ptr != ptr2 {
|
||
|
t.Fatalf("Failed to create userdata\n")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
testCalled := 0
|
||
|
test := func(L *State) int {
|
||
|
testCalled++
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
goDefinedFunctions := func(L *State) {
|
||
|
// example_function is registered inside Lua VM
|
||
|
L.Register("test", test)
|
||
|
|
||
|
// This code demonstrates checking that a value on the stack is a go function
|
||
|
L.CheckStack(1)
|
||
|
L.GetGlobal("test")
|
||
|
if !L.IsGoFunction(-1) {
|
||
|
t.Fatalf("IsGoFunction failed to recognize a Go function object")
|
||
|
}
|
||
|
L.Pop(1)
|
||
|
|
||
|
// We call example_function from inside Lua VM
|
||
|
testCalled = 0
|
||
|
if err := L.DoString("test()"); err != nil {
|
||
|
t.Fatalf("Error executing test function: %v\n", err)
|
||
|
}
|
||
|
if testCalled != 1 {
|
||
|
t.Fatalf("It appears the test function wasn't actually called\n")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type TestObject struct {
|
||
|
AField int
|
||
|
}
|
||
|
|
||
|
goDefinedObjects := func(L *State) {
|
||
|
z := &TestObject{42}
|
||
|
|
||
|
L.PushGoStruct(z)
|
||
|
L.SetGlobal("z")
|
||
|
|
||
|
// This code demonstrates checking that a value on the stack is a go object
|
||
|
L.CheckStack(1)
|
||
|
L.GetGlobal("z")
|
||
|
if !L.IsGoStruct(-1) {
|
||
|
t.Fatal("IsGoStruct failed to recognize a Go struct\n")
|
||
|
}
|
||
|
L.Pop(1)
|
||
|
|
||
|
// This code demonstrates access and assignment to a field of a go object
|
||
|
if err := L.DoString("return z.AField"); err != nil {
|
||
|
t.Fatal("Couldn't execute code")
|
||
|
}
|
||
|
before := L.ToInteger(-1)
|
||
|
L.Pop(1)
|
||
|
if before != 42 {
|
||
|
t.Fatalf("Wrong value of z.AField before change (%d)\n", before)
|
||
|
}
|
||
|
if err := L.DoString("z.AField = 10;"); err != nil {
|
||
|
t.Fatal("Couldn't execute code")
|
||
|
}
|
||
|
if err := L.DoString("return z.AField"); err != nil {
|
||
|
t.Fatal("Couldn't execute code")
|
||
|
}
|
||
|
after := L.ToInteger(-1)
|
||
|
L.Pop(1)
|
||
|
if after != 10 {
|
||
|
t.Fatalf("Wrong value of z.AField after change (%d)\n", after)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
L := NewState()
|
||
|
defer L.Close()
|
||
|
L.OpenLibs()
|
||
|
|
||
|
userDataProper(L)
|
||
|
goDefinedFunctions(L)
|
||
|
goDefinedObjects(L)
|
||
|
}
|
||
|
|
||
|
func TestStackTrace(t *testing.T) {
|
||
|
L := NewState()
|
||
|
defer L.Close()
|
||
|
L.OpenLibs()
|
||
|
|
||
|
err := L.DoFile("../example/calls.lua")
|
||
|
if err == nil {
|
||
|
t.Fatal("No error returned from the execution of calls.lua")
|
||
|
}
|
||
|
|
||
|
le := err.(*LuaError)
|
||
|
|
||
|
if le.Code() != LUA_ERRERR {
|
||
|
t.Fatalf("Wrong kind of error encountered running calls.lua: %v (%d %d)\n", le, le.Code(), LUA_ERRERR)
|
||
|
}
|
||
|
|
||
|
if len(le.StackTrace()) != 6 {
|
||
|
t.Fatalf("Wrong size of stack trace (%v)\n", le.StackTrace())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestConv(t *testing.T) {
|
||
|
L := NewState()
|
||
|
defer L.Close()
|
||
|
L.OpenLibs()
|
||
|
|
||
|
L.PushString("10")
|
||
|
n := L.ToNumber(-1)
|
||
|
if n != 10 {
|
||
|
t.Fatalf("Wrong conversion (str -> int)")
|
||
|
}
|
||
|
if L.Type(-1) != LUA_TSTRING {
|
||
|
t.Fatalf("Wrong type (str)")
|
||
|
}
|
||
|
|
||
|
L.Pop(1)
|
||
|
|
||
|
L.PushInteger(10)
|
||
|
s := L.ToString(-1)
|
||
|
if s != "10" {
|
||
|
t.Fatalf("Wrong conversion (int -> str)")
|
||
|
}
|
||
|
|
||
|
L.Pop(1)
|
||
|
|
||
|
L.PushString("a\000test")
|
||
|
s = L.ToString(-1)
|
||
|
if s != "a\000test" {
|
||
|
t.Fatalf("Wrong conversion (str -> str): <%s>", s)
|
||
|
}
|
||
|
}
|