From 62ce3a5387e209b2d589626fb79b5780e64cdf1e Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 21 May 2025 14:56:59 -0500 Subject: [PATCH] add basic benchmarking --- tests/basic_test.go | 314 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 tests/basic_test.go diff --git a/tests/basic_test.go b/tests/basic_test.go new file mode 100644 index 0000000..8effca6 --- /dev/null +++ b/tests/basic_test.go @@ -0,0 +1,314 @@ +package tests + +import ( + "context" + "io" + "log" + "os" + "path/filepath" + "testing" + + "Moonshark/routers" + "Moonshark/runner" + "Moonshark/utils/logger" +) + +// setupTestEnv initializes test components and returns cleanup function +func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) { + // Completely silence logging during benchmarks + logger.InitGlobalLogger(logger.LevelFatal, false, false) + + // Redirect standard logger output to discard + log.SetOutput(io.Discard) + + // Store original stderr to restore later + originalStderr := os.Stderr + devNull, _ := os.Open(os.DevNull) + os.Stderr = devNull + + // Create temp directories + tempDir, err := os.MkdirTemp("", "moonshark-bench") + if err != nil { + b.Fatalf("Failed to create temp dir: %v", err) + } + + // Rest of the function remains the same... + routesDir := filepath.Join(tempDir, "routes") + staticDir := filepath.Join(tempDir, "static") + libsDir := filepath.Join(tempDir, "libs") + dataDir := filepath.Join(tempDir, "data") + fsDir := filepath.Join(tempDir, "fs") + + os.MkdirAll(routesDir, 0755) + os.MkdirAll(staticDir, 0755) + os.MkdirAll(libsDir, 0755) + os.MkdirAll(dataDir, 0755) + os.MkdirAll(fsDir, 0755) + + // Create test routes + createTestRoutes(routesDir) + + // Initialize router + luaRouter, err := routers.NewLuaRouter(routesDir) + if err != nil { + b.Fatalf("Failed to create router: %v", err) + } + + // Initialize runner + luaRunner, err := runner.NewRunner( + runner.WithPoolSize(4), + runner.WithLibDirs(libsDir), + runner.WithDataDir(dataDir), + runner.WithFsDir(fsDir), + ) + if err != nil { + b.Fatalf("Failed to create runner: %v", err) + } + + // Return cleanup function that restores stderr + cleanup := func() { + luaRunner.Close() + os.RemoveAll(tempDir) + os.Stderr = originalStderr + devNull.Close() + } + + return luaRouter, luaRunner, cleanup +} + +// createTestRoutes creates test Lua scripts for benchmarking +func createTestRoutes(routesDir string) { + // Simple GET endpoint + getCode := []byte(`return "Hello, World!"`) + os.WriteFile(filepath.Join(routesDir, "GET_hello.lua"), getCode, 0644) + + // POST endpoint with form handling + postCode := []byte(` + local data = ctx.form or {} + return "Received: " .. (data.message or "no message") + `) + os.WriteFile(filepath.Join(routesDir, "POST_hello.lua"), postCode, 0644) + + // Computationally intensive endpoint + complexCode := []byte(` + local result = {} + for i = 1, 1000 do + table.insert(result, i * i) + end + return "Calculated " .. #result .. " squared numbers" + `) + os.WriteFile(filepath.Join(routesDir, "GET_complex.lua"), complexCode, 0644) +} + +// BenchmarkRouterLookup tests route lookup performance +func BenchmarkRouterLookup(b *testing.B) { + luaRouter, _, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/hello" + params := &routers.Params{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, _, _ = luaRouter.GetRouteInfo(method, path, params) + } +} + +// BenchmarkSimpleLuaExecution tests execution of a simple Lua script +func BenchmarkSimpleLuaExecution(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/hello" + params := &routers.Params{} + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + ctx := runner.NewContext() + defer ctx.Release() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + } +} + +// BenchmarkComplexLuaExecution tests execution of a computation-heavy script +func BenchmarkComplexLuaExecution(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/complex" + params := &routers.Params{} + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + ctx := runner.NewContext() + defer ctx.Release() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + } +} + +// BenchmarkGetEndpoint tests end-to-end processing for GET endpoint +func BenchmarkGetEndpoint(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/hello" + params := &routers.Params{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Route lookup + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + // Context setup + ctx := runner.NewContext() + + // Script execution + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + + // Cleanup + ctx.Release() + } +} + +// BenchmarkPostEndpoint tests end-to-end processing for POST endpoint +func BenchmarkPostEndpoint(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "POST" + path := "/hello" + params := &routers.Params{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Route lookup + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + // Context setup with form data + ctx := runner.NewContext() + ctx.Set("form", map[string]any{ + "message": "Hello from benchmark test", + }) + + // Script execution + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + + // Cleanup + ctx.Release() + } +} + +// BenchmarkConcurrentExecution tests parallel execution performance +func BenchmarkConcurrentExecution(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/hello" + params := &routers.Params{} + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := runner.NewContext() + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + ctx.Release() + } + }) +} + +// BenchmarkConcurrentComplexExecution tests parallel execution of intensive scripts +func BenchmarkConcurrentComplexExecution(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/complex" + params := &routers.Params{} + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := runner.NewContext() + _, _ = luaRunner.Run(bytecode, ctx, scriptPath) + ctx.Release() + } + }) +} + +// BenchmarkRouteCompilation tests the performance of route compilation +func BenchmarkRouteCompilation(b *testing.B) { + tempDir, err := os.MkdirTemp("", "moonshark-compile") + if err != nil { + b.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + routesDir := filepath.Join(tempDir, "routes") + os.MkdirAll(routesDir, 0755) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + os.RemoveAll(routesDir) + os.MkdirAll(routesDir, 0755) + createTestRoutes(routesDir) + b.StartTimer() + + // Creating router triggers compilation + _, _ = routers.NewLuaRouter(routesDir) + } +} + +// BenchmarkContextCreation measures the cost of creating execution contexts +func BenchmarkContextCreation(b *testing.B) { + for i := 0; i < b.N; i++ { + ctx := runner.NewContext() + ctx.Release() + } +} + +// BenchmarkContextWithData measures context creation with realistic data +func BenchmarkContextWithData(b *testing.B) { + for i := 0; i < b.N; i++ { + ctx := runner.NewContext() + ctx.Set("method", "POST") + ctx.Set("path", "/api/users") + ctx.Set("host", "example.com") + ctx.Set("params", map[string]any{"id": "123"}) + ctx.Set("form", map[string]any{ + "username": "testuser", + "email": "user@example.com", + "active": true, + }) + ctx.Release() + } +} + +// BenchmarkRunnerExecute tests the runner's Execute method with timeout +func BenchmarkRunnerExecute(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/hello" + params := &routers.Params{} + bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ctx := runner.NewContext() + _, _ = luaRunner.Execute(context.Background(), bytecode, ctx, scriptPath) + ctx.Release() + } +}