LuaJIT-to-Go/tests/functions_test.go
2025-02-26 07:00:01 -06:00

179 lines
4.1 KiB
Go

package luajit_test
import (
"testing"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
func TestPushGoFunction(t *testing.T) {
state := luajit.New()
if state == nil {
t.Fatal("Failed to create Lua state")
}
defer state.Close()
// Define a simple function that adds two numbers
add := func(s *luajit.State) int {
a := s.ToNumber(1)
b := s.ToNumber(2)
s.PushNumber(a + b)
return 1 // Return one result
}
// Push the function onto the stack
if err := state.PushGoFunction(add); err != nil {
t.Fatalf("PushGoFunction failed: %v", err)
}
// Verify that a function is on the stack
if !state.IsFunction(-1) {
t.Fatalf("Expected function at top of stack")
}
// Push arguments
state.PushNumber(3)
state.PushNumber(4)
// Call the function
if err := state.Call(2, 1); err != nil {
t.Fatalf("Failed to call function: %v", err)
}
// Check the result
if state.ToNumber(-1) != 7 {
t.Fatalf("Function returned %f, expected 7", state.ToNumber(-1))
}
state.Pop(1)
}
func TestRegisterGoFunction(t *testing.T) {
state := luajit.New()
if state == nil {
t.Fatal("Failed to create Lua state")
}
defer state.Close()
// Define a function that squares a number
square := func(s *luajit.State) int {
x := s.ToNumber(1)
s.PushNumber(x * x)
return 1
}
// Register the function
if err := state.RegisterGoFunction("square", square); err != nil {
t.Fatalf("RegisterGoFunction failed: %v", err)
}
// Call the function from Lua
if err := state.DoString("result = square(5)"); err != nil {
t.Fatalf("Failed to call registered function: %v", err)
}
// Check the result
state.GetGlobal("result")
if state.ToNumber(-1) != 25 {
t.Fatalf("Function returned %f, expected 25", state.ToNumber(-1))
}
state.Pop(1)
// Test UnregisterGoFunction
state.UnregisterGoFunction("square")
// Function should no longer exist
err := state.DoString("result = square(5)")
if err == nil {
t.Fatalf("Expected error after unregistering function, got nil")
}
}
func TestGoFunctionWithErrorHandling(t *testing.T) {
state := luajit.New()
if state == nil {
t.Fatal("Failed to create Lua state")
}
defer state.Close()
// Function that returns an error in Lua
errFunc := func(s *luajit.State) int {
s.PushString("error from Go function")
return -1 // Signal error
}
// Register the function
if err := state.RegisterGoFunction("errorFunc", errFunc); err != nil {
t.Fatalf("RegisterGoFunction failed: %v", err)
}
// Call the function expecting an error
err := state.DoString("result = errorFunc()")
if err == nil {
t.Fatalf("Expected error from function, got nil")
}
// Error message should contain our message
luaErr, ok := err.(*luajit.LuaError)
if !ok {
t.Fatalf("Expected LuaError, got %T: %v", err, err)
}
if luaErr.Message == "" {
t.Fatalf("Expected non-empty error message from Go function")
}
}
func TestCleanup(t *testing.T) {
state := luajit.New()
if state == nil {
t.Fatal("Failed to create Lua state")
}
// Register several functions
for i := 0; i < 5; i++ {
dummy := func(s *luajit.State) int { return 0 }
if err := state.RegisterGoFunction("dummy", dummy); err != nil {
t.Fatalf("RegisterGoFunction failed: %v", err)
}
}
// Call Cleanup explicitly
state.Cleanup()
// Make sure we can still close the state
state.Close()
// Also test that Close can be called after Cleanup
state = luajit.New()
if state == nil {
t.Fatal("Failed to create second Lua state")
}
state.Close() // Should call Cleanup internally
}
func TestGoFunctionErrorPointer(t *testing.T) {
state := luajit.New()
if state == nil {
t.Fatal("Failed to create Lua state")
}
defer state.Close()
// Create a Lua function that calls a non-existent Go function pointer
// This isn't a direct test of internal implementation, but tries to cover
// error cases in the goFunctionWrapper
code := `
function test()
-- This is a stub that doesn't actually call the wrapper,
-- but we're testing error handling in our State.DoString
return "test"
end
`
if err := state.DoString(code); err != nil {
t.Fatalf("Failed to define test function: %v", err)
}
// The real test is that Cleanup doesn't crash
state.Cleanup()
}