From 75eff6c5ad01fe851080e823fa15eaf975ef64f6 Mon Sep 17 00:00:00 2001 From: bdnugget Date: Wed, 22 Jan 2025 00:40:31 +0100 Subject: [PATCH] Stash bunch of broken generated test with shitty broken mocking --- game/chat.go | 18 ++-- game/chat_test.go | 106 +++++++++++++++++++++ game/game.go | 8 +- game/game_test.go | 106 +++++++++++++++++++++ game/input.go | 85 ++++++++++++++++- game/input_test.go | 187 ++++++++++++++++++++++++++++++++++++++ game/login_test.go | 80 ++++++++++++++++ game/mock/raylib.go | 15 +++ game/test_helpers.go | 141 ++++++++++++++++++++++++++++ game/test_setup.go | 26 ++++++ game/testutils/helpers.go | 153 +++++++++++++++++++++++++++++++ go.mod | 7 ++ go.sum | 9 ++ 13 files changed, 927 insertions(+), 14 deletions(-) create mode 100644 game/chat_test.go create mode 100644 game/game_test.go create mode 100644 game/input_test.go create mode 100644 game/login_test.go create mode 100644 game/mock/raylib.go create mode 100644 game/test_helpers.go create mode 100644 game/test_setup.go create mode 100644 game/testutils/helpers.go diff --git a/game/chat.go b/game/chat.go index 9aced0d..ec598ad 100644 --- a/game/chat.go +++ b/game/chat.go @@ -25,12 +25,14 @@ type Chat struct { cursorPos int scrollOffset int userData interface{} + input InputHandler } func NewChat() *Chat { return &Chat{ messages: make([]types.ChatMessage, 0, maxMessages), inputBuffer: make([]rune, 0, runeLimit), + input: &RaylibInput{}, } } @@ -153,23 +155,23 @@ func (c *Chat) Update() (string, bool) { c.scrollOffset = clamp(c.scrollOffset-int(wheelMove), 0, maxScroll) } - if rl.IsKeyPressed(rl.KeyT) { + if c.input.IsKeyPressed(rl.KeyT) { c.isTyping = true return "", false } return "", false } - key := rl.GetCharPressed() + key := c.input.GetCharPressed() for key > 0 { if len(c.inputBuffer) < runeLimit { c.inputBuffer = append(c.inputBuffer[:c.cursorPos], append([]rune{key}, c.inputBuffer[c.cursorPos:]...)...) c.cursorPos++ } - key = rl.GetCharPressed() + key = c.input.GetCharPressed() } - if rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter) { + if c.input.IsKeyPressed(rl.KeyEnter) || c.input.IsKeyPressed(rl.KeyKpEnter) { if len(c.inputBuffer) > 0 { message := string(c.inputBuffer) c.inputBuffer = c.inputBuffer[:0] @@ -180,21 +182,21 @@ func (c *Chat) Update() (string, bool) { c.isTyping = false } - if rl.IsKeyPressed(rl.KeyEscape) && c.isTyping { + if c.input.IsKeyPressed(rl.KeyEscape) && c.isTyping { c.inputBuffer = c.inputBuffer[:0] c.cursorPos = 0 c.isTyping = false } - if rl.IsKeyPressed(rl.KeyBackspace) && c.cursorPos > 0 { + if c.input.IsKeyPressed(rl.KeyBackspace) && c.cursorPos > 0 { c.inputBuffer = append(c.inputBuffer[:c.cursorPos-1], c.inputBuffer[c.cursorPos:]...) c.cursorPos-- } - if rl.IsKeyPressed(rl.KeyLeft) && c.cursorPos > 0 { + if c.input.IsKeyPressed(rl.KeyLeft) && c.cursorPos > 0 { c.cursorPos-- } - if rl.IsKeyPressed(rl.KeyRight) && c.cursorPos < len(c.inputBuffer) { + if c.input.IsKeyPressed(rl.KeyRight) && c.cursorPos < len(c.inputBuffer) { c.cursorPos++ } diff --git a/game/chat_test.go b/game/chat_test.go new file mode 100644 index 0000000..470d68e --- /dev/null +++ b/game/chat_test.go @@ -0,0 +1,106 @@ +package game + +import ( + "testing" + "time" + + "gitea.boner.be/bdnugget/goonscape/game/testutils" + "gitea.boner.be/bdnugget/goonscape/types" + pb "gitea.boner.be/bdnugget/goonserver/actions" + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/stretchr/testify/assert" +) + +func TestChat_AddMessage(t *testing.T) { + chat := NewChat() + + // Test adding single message + chat.AddMessage(1, "Hello") + assert.Equal(t, 1, len(chat.messages)) + assert.Equal(t, int32(1), chat.messages[0].PlayerID) + assert.Equal(t, "Hello", chat.messages[0].Content) + + // Test message limit + for i := 0; i < maxMessages+10; i++ { + chat.AddMessage(1, "spam") + } + assert.Equal(t, maxMessages, len(chat.messages)) + assert.Equal(t, "spam", chat.messages[len(chat.messages)-1].Content) +} + +func TestChat_HandleServerMessages(t *testing.T) { + chat := NewChat() + mockGame := &Game{ + Player: &types.Player{ID: 1}, + OtherPlayers: map[int32]*types.Player{ + 2: {ID: 2}, + }, + } + chat.userData = mockGame + + messages := []*pb.ChatMessage{ + { + PlayerId: 1, + Username: "player1", + Content: "test1", + Timestamp: time.Now().UnixNano(), + }, + { + PlayerId: 2, + Username: "player2", + Content: "test2", + Timestamp: time.Now().UnixNano(), + }, + } + + chat.HandleServerMessages(messages) + assert.Equal(t, 2, len(chat.messages)) + assert.Equal(t, "test1", chat.messages[0].Content) + assert.Equal(t, "test2", chat.messages[1].Content) + + // Test duplicate message prevention + chat.HandleServerMessages(messages) + assert.Equal(t, 2, len(chat.messages)) +} + +func TestChat_Update(t *testing.T) { + t.Parallel() + done := make(chan bool) + go func() { + game, cleanup := setupTestEnvironment(t) + defer cleanup() + + chat := game.Chat + + // Test starting chat + testutils.SimulateKeyPress(rl.KeyT) + msg, sent := chat.Update() + assert.True(t, chat.isTyping) + assert.False(t, sent) + assert.Empty(t, msg) + + // Test typing message + testutils.SimulateCharInput('h') + msg, sent = chat.Update() + testutils.SimulateCharInput('i') + msg, sent = chat.Update() + assert.Equal(t, 2, len(chat.inputBuffer)) + assert.False(t, sent) + assert.Empty(t, msg) + + // Test sending message + testutils.SimulateKeyPress(rl.KeyEnter) + msg, sent = chat.Update() + assert.True(t, sent) + assert.Equal(t, "hi", msg) + assert.False(t, chat.isTyping) + done <- true + }() + + select { + case <-done: + // Test completed successfully + case <-time.After(5 * time.Second): + t.Fatal("Test timed out") + } +} diff --git a/game/game.go b/game/game.go index 2a26f9a..bac78bf 100644 --- a/game/game.go +++ b/game/game.go @@ -22,6 +22,7 @@ type Game struct { QuitChan chan struct{} // Channel to signal shutdown loginScreen *LoginScreen isLoggedIn bool + input InputHandler } func New() *Game { @@ -38,6 +39,7 @@ func New() *Game { Chat: NewChat(), QuitChan: make(chan struct{}), loginScreen: NewLoginScreen(), + input: &RaylibInput{}, } game.Chat.userData = game return game @@ -86,7 +88,7 @@ func (g *Game) Update(deltaTime float32) { } // Handle ESC for menu - if rl.IsKeyPressed(rl.KeyEscape) { + if g.input.IsKeyPressed(rl.KeyEscape) { g.MenuOpen = !g.MenuOpen return } @@ -325,13 +327,13 @@ func (g *Game) DrawMenu() { } // Check mouse hover - mousePoint := rl.GetMousePosition() + mousePoint := g.input.GetMousePosition() mouseHover := rl.CheckCollisionPointRec(mousePoint, buttonRect) // Draw button if mouseHover { rl.DrawRectangleRec(buttonRect, rl.ColorAlpha(rl.White, 0.3)) - if rl.IsMouseButtonPressed(rl.MouseLeftButton) { + if g.input.IsMouseButtonPressed(toInt32(rl.MouseLeftButton)) { switch item { case "Resume": g.MenuOpen = false diff --git a/game/game_test.go b/game/game_test.go new file mode 100644 index 0000000..6637ea4 --- /dev/null +++ b/game/game_test.go @@ -0,0 +1,106 @@ +package game + +import ( + "testing" + + "gitea.boner.be/bdnugget/goonscape/game/testutils" + "gitea.boner.be/bdnugget/goonscape/types" + pb "gitea.boner.be/bdnugget/goonserver/actions" + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/stretchr/testify/assert" +) + +func TestGame_HandleInput(t *testing.T) { + game := New() + game.Player = &types.Player{ + ID: 1, + Speed: 50.0, + } + + // Test valid click + simulateMouseRay(rl.Ray{ + Position: rl.Vector3{X: 0, Y: 10, Z: 0}, + Direction: rl.Vector3{X: 0, Y: -1, Z: 0}, + }) + simulateMouseButton(toInt32(rl.MouseLeftButton), true) + game.HandleInput() + assert.NotEmpty(t, game.Player.TargetPath) + + // Test invalid click (outside map) + simulateMouseRay(rl.Ray{ + Position: rl.Vector3{X: 1000, Y: 10, Z: 1000}, + Direction: rl.Vector3{X: 0, Y: -1, Z: 0}, + }) + simulateMouseButton(toInt32(rl.MouseLeftButton), true) + game.HandleInput() + assert.Empty(t, game.Player.TargetPath) +} + +func TestGame_UpdateCamera(t *testing.T) { + game := New() + + // Test zoom limits + testutils.SimulateMouseWheel(1.0) // Zoom in + testutils.SimulateMouseWheel(1.0) + assert.GreaterOrEqual(t, cameraDistance, float32(10.0)) + + testutils.SimulateMouseWheel(-1.0) // Zoom out + testutils.SimulateMouseWheel(-1.0) + assert.LessOrEqual(t, cameraDistance, float32(250.0)) + + // Test camera rotation + originalYaw := cameraYaw + testutils.SimulateKeyDown(rl.KeyRight, true) + game.Update(0.1) + assert.Greater(t, cameraYaw, originalYaw) + + // Test pitch limits + simulateKeyDown(rl.KeyUp, true) + for i := 0; i < 100; i++ { + game.Update(0.1) + } + assert.GreaterOrEqual(t, cameraPitch, float32(20.0)) + assert.LessOrEqual(t, cameraPitch, float32(85.0)) +} + +func TestGame_ChatIntegration(t *testing.T) { + game := New() + game.Player = &types.Player{ID: 1} + + // Test chat message to action queue + testutils.SimulateKeyPress(rl.KeyT) + game.Update(0.1) + assert.True(t, game.Chat.isTyping) + + testutils.SimulateCharInput('h') + testutils.SimulateCharInput('i') + testutils.SimulateKeyPress(rl.KeyEnter) + game.Update(0.1) + + assert.Equal(t, 1, len(game.Player.ActionQueue)) + assert.Equal(t, pb.Action_CHAT, game.Player.ActionQueue[0].Type) + assert.Equal(t, "hi", game.Player.ActionQueue[0].ChatMessage) +} + +func TestGame_MenuHandling(t *testing.T) { + game := New() + + // Test menu toggle + assert.False(t, game.MenuOpen) + testutils.SimulateKeyPress(rl.KeyEscape) + game.Update(0.1) + assert.True(t, game.MenuOpen) + + // Test input blocking when menu is open + game.Player = &types.Player{ID: 1} + testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true) + game.Update(0.1) + assert.Empty(t, game.Player.TargetPath) + + // Test menu close + testutils.SimulateKeyPress(rl.KeyEscape) + game.Update(0.1) + assert.False(t, game.MenuOpen) +} + +// Add more test helpers as needed... diff --git a/game/input.go b/game/input.go index 9957871..67cb3b3 100644 --- a/game/input.go +++ b/game/input.go @@ -3,16 +3,95 @@ package game import ( "fmt" + "gitea.boner.be/bdnugget/goonscape/game/mock" "gitea.boner.be/bdnugget/goonscape/types" rl "github.com/gen2brain/raylib-go/raylib" ) +// InputHandler abstracts raylib input functions for testing +type InputHandler interface { + IsKeyPressed(key int32) bool + IsKeyDown(key int32) bool + IsMouseButtonPressed(button int32) bool + GetMousePosition() rl.Vector2 + GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray + GetMouseWheelMove() float32 + GetCharPressed() rune +} + +// RaylibInput implements InputHandler using actual raylib functions +type RaylibInput struct{} + +func (r *RaylibInput) IsKeyPressed(key int32) bool { return rl.IsKeyPressed(key) } +func (r *RaylibInput) IsKeyDown(key int32) bool { return rl.IsKeyDown(key) } +func (r *RaylibInput) IsMouseButtonPressed(button int32) bool { + return rl.IsMouseButtonPressed(rl.MouseButton(button)) +} +func (r *RaylibInput) GetMousePosition() rl.Vector2 { return rl.GetMousePosition() } +func (r *RaylibInput) GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray { + return rl.GetMouseRay(mousePos, camera) +} +func (r *RaylibInput) GetMouseWheelMove() float32 { return rl.GetMouseWheelMove() } +func (r *RaylibInput) GetCharPressed() rune { return rl.GetCharPressed() } + +// MockInput implements InputHandler using our mock functions +type MockInput struct{} + +func (m *MockInput) IsKeyPressed(key int32) bool { + if mock.IsKeyPressed == nil { + return false + } + return mock.IsKeyPressed(key) +} + +func (m *MockInput) IsKeyDown(key int32) bool { + if mock.IsKeyDown == nil { + return false + } + return mock.IsKeyDown(key) +} + +func (m *MockInput) IsMouseButtonPressed(button int32) bool { + if mock.IsMouseButtonPressed == nil { + return false + } + return mock.IsMouseButtonPressed(button) +} + +func (m *MockInput) GetMousePosition() rl.Vector2 { + if mock.GetMousePosition == nil { + return rl.Vector2{} + } + return mock.GetMousePosition() +} + +func (m *MockInput) GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray { + if mock.GetMouseRay == nil { + return rl.Ray{} + } + return mock.GetMouseRay(mousePos, camera) +} + +func (m *MockInput) GetMouseWheelMove() float32 { + if mock.GetMouseWheelMove == nil { + return 0 + } + return mock.GetMouseWheelMove() +} + +func (m *MockInput) GetCharPressed() rune { + if mock.GetCharPressed == nil { + return 0 + } + return mock.GetCharPressed() +} + func (g *Game) GetTileAtMouse() (types.Tile, bool) { - if !rl.IsMouseButtonPressed(rl.MouseLeftButton) { + if !g.input.IsMouseButtonPressed(toInt32(rl.MouseLeftButton)) { return types.Tile{}, false } - mouse := rl.GetMousePosition() - ray := rl.GetMouseRay(mouse, g.Camera) + mouse := g.input.GetMousePosition() + ray := g.input.GetMouseRay(mouse, g.Camera) for x := 0; x < types.MapWidth; x++ { for y := 0; y < types.MapHeight; y++ { diff --git a/game/input_test.go b/game/input_test.go new file mode 100644 index 0000000..bc2efc3 --- /dev/null +++ b/game/input_test.go @@ -0,0 +1,187 @@ +package game + +import ( + "testing" + + "gitea.boner.be/bdnugget/goonscape/game/testutils" + "gitea.boner.be/bdnugget/goonscape/types" + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/stretchr/testify/assert" +) + +func TestMouseInput_EdgeCases(t *testing.T) { + game, cleanup := setupTestEnvironment(t) + defer cleanup() + + game.Player = &types.Player{ID: 1} + + tests := []struct { + name string + ray rl.Ray + expected bool + }{ + { + name: "Click outside map bounds", + ray: rl.Ray{ + Position: rl.Vector3{X: 1000, Y: 10, Z: 1000}, + Direction: rl.Vector3{X: 0, Y: -1, Z: 0}, + }, + expected: false, + }, + { + name: "Click at map edge", + ray: rl.Ray{ + Position: rl.Vector3{X: float32(types.MapWidth * types.TileSize), Y: 10, Z: 0}, + Direction: rl.Vector3{X: 0, Y: -1, Z: 0}, + }, + expected: false, + }, + { + name: "Click on valid tile", + ray: rl.Ray{ + Position: rl.Vector3{X: 32, Y: 10, Z: 32}, + Direction: rl.Vector3{X: 0, Y: -1, Z: 0}, + }, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testutils.ResetMockInput() + testutils.SimulateMouseRay(tt.ray) + testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true) + tile, clicked := game.GetTileAtMouse() + assert.Equal(t, tt.expected, clicked) + if tt.expected { + assert.NotEmpty(t, tile) + } + }) + } +} + +func TestChat_InputValidation(t *testing.T) { + game, cleanup := setupTestEnvironment(t) + defer cleanup() + + game.Player = &types.Player{ID: 1} + + tests := []struct { + name string + input []rune + expected string + }{ + { + name: "Empty message", + input: []rune{}, + expected: "", + }, + { + name: "Message with only spaces", + input: []rune(" "), + expected: "", + }, + { + name: "Very long message", + input: []rune(string(make([]rune, runeLimit))), + expected: string(make([]rune, runeLimit)), + }, + { + name: "Unicode characters", + input: []rune("Hello 世界"), + expected: "Hello 世界", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testutils.ResetMockInput() + testutils.SimulateKeyPress(rl.KeyT) + game.Update(0.1) + + for _, r := range tt.input { + testutils.SimulateCharInput(r) + game.Update(0.1) + } + + testutils.SimulateKeyPress(rl.KeyEnter) + game.Update(0.1) + + if tt.expected != "" { + assert.Equal(t, 1, len(game.Player.ActionQueue)) + assert.Equal(t, tt.expected, game.Player.ActionQueue[0].ChatMessage) + } else { + assert.Empty(t, game.Player.ActionQueue) + } + }) + } +} + +func TestLogin_InputValidation(t *testing.T) { + _, cleanup := setupTestEnvironment(t) + defer cleanup() + + tests := []struct { + name string + username string + password string + expectSuccess bool + }{ + { + name: "Valid credentials", + username: "validuser", + password: "validpass", + expectSuccess: true, + }, + { + name: "Empty username", + username: "", + password: "password", + expectSuccess: false, + }, + { + name: "Empty password", + username: "username", + password: "", + expectSuccess: false, + }, + { + name: "Username too long", + username: "verylongusername123", + password: "password", + expectSuccess: false, + }, + { + name: "Special characters in username", + username: "user@name", + password: "password", + expectSuccess: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + login := NewLoginScreen() + testutils.ResetMockInput() + + // Simulate typing username + login.focusedField = 0 + for _, r := range tt.username { + testutils.SimulateCharInput(r) + login.Update() + } + + // Simulate typing password + login.focusedField = 1 + for _, r := range tt.password { + testutils.SimulateCharInput(r) + login.Update() + } + + // Simulate clicking login button + testutils.SimulateMouseClick(400, 365) + _, _, _, submitted := login.Update() + assert.Equal(t, tt.expectSuccess, submitted) + }) + } +} diff --git a/game/login_test.go b/game/login_test.go new file mode 100644 index 0000000..89fe56e --- /dev/null +++ b/game/login_test.go @@ -0,0 +1,80 @@ +package game + +import ( + "testing" + + "gitea.boner.be/bdnugget/goonscape/game/testutils" + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/stretchr/testify/assert" +) + +func TestLoginScreen_Update(t *testing.T) { + login := NewLoginScreen() + + // Test field focus switching + simulateMouseClick(400, 215) // Click username field + assert.Equal(t, 0, login.focusedField) + + simulateMouseClick(400, 265) // Click password field + assert.Equal(t, 1, login.focusedField) + + // Test input length limits + login.focusedField = 0 + for i := 0; i < 20; i++ { + simulateCharInput('x') + } + assert.LessOrEqual(t, len(login.username), 12) + + login.focusedField = 1 + for i := 0; i < 30; i++ { + simulateCharInput('x') + } + assert.LessOrEqual(t, len(login.password), 20) + + // Test mode switching + simulateMouseClick(600, 365) // Click switch mode button + assert.True(t, login.isRegistering) + simulateMouseClick(600, 365) // Click again + assert.False(t, login.isRegistering) + + // Test submission + login.username = "test" + login.password = "password" + testutils.SimulateMousePosition(400, 365) + testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true) + username, password, isRegistering, submitted := login.Update() + assert.True(t, submitted) + assert.Equal(t, "test", username) + assert.Equal(t, "password", password) + assert.False(t, isRegistering) +} + +func TestLoginScreen_ErrorHandling(t *testing.T) { + login := NewLoginScreen() + + // Test empty fields + login.username = "" + login.password = "test" + testutils.SimulateMousePosition(400, 365) + testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true) + _, _, _, submitted := login.Update() + assert.False(t, submitted) + assert.Contains(t, login.errorMessage, "username") + + // Test special characters + login.username = "test!@#" + login.password = "password" + testutils.SimulateMousePosition(400, 365) + testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true) + _, _, _, submitted = login.Update() + assert.False(t, submitted) + assert.Contains(t, login.errorMessage, "invalid characters") + + // Test error message display + login.SetError("Test error") + assert.Equal(t, "Test error", login.errorMessage) +} + +func simulateMouseClick(x, y float32) { + // Implementation would depend on how raylib is mocked +} diff --git a/game/mock/raylib.go b/game/mock/raylib.go new file mode 100644 index 0000000..335e6e6 --- /dev/null +++ b/game/mock/raylib.go @@ -0,0 +1,15 @@ +package mock + +import ( + rl "github.com/gen2brain/raylib-go/raylib" +) + +var ( + IsKeyPressed func(key int32) bool + IsKeyDown func(key int32) bool + IsMouseButtonPressed func(button int32) bool + GetMousePosition func() rl.Vector2 + GetMouseRay func(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray + GetMouseWheelMove func() float32 + GetCharPressed func() rune +) diff --git a/game/test_helpers.go b/game/test_helpers.go new file mode 100644 index 0000000..9608c9c --- /dev/null +++ b/game/test_helpers.go @@ -0,0 +1,141 @@ +package game + +import ( + "sync" + + "gitea.boner.be/bdnugget/goonscape/game/mock" + rl "github.com/gen2brain/raylib-go/raylib" +) + +var ( + mockInput struct { + sync.Mutex + keyPressed map[int32]bool + keyDown map[int32]bool + mousePressed map[int32]bool + mousePosition rl.Vector2 + mouseRay rl.Ray + mouseWheel float32 + charPressed rune + } +) + +func init() { + resetMockInput() + setupMockFunctions() +} + +func setupMockFunctions() { + mock.IsKeyPressed = mockIsKeyPressed + mock.IsKeyDown = mockIsKeyDown + mock.IsMouseButtonPressed = mockIsMouseButtonPressed + mock.GetMousePosition = mockGetMousePosition + mock.GetMouseRay = mockGetMouseRay + mock.GetMouseWheelMove = mockGetMouseWheelMove + mock.GetCharPressed = mockGetCharPressed +} + +func resetMockInput() { + mockInput.Lock() + defer mockInput.Unlock() + mockInput.keyPressed = make(map[int32]bool) + mockInput.keyDown = make(map[int32]bool) + mockInput.mousePressed = make(map[int32]bool) + mockInput.mousePosition = rl.Vector2{} + mockInput.mouseRay = rl.Ray{} + mockInput.mouseWheel = 0 + mockInput.charPressed = 0 +} + +// Mock input simulation functions +func simulateKeyPress(key int32) { + mockInput.Lock() + mockInput.keyPressed[key] = true + mockInput.Unlock() +} + +func simulateKeyDown(key int32, isDown bool) { + mockInput.Lock() + mockInput.keyDown[key] = isDown + mockInput.Unlock() +} + +func simulateMouseButton(button int32, isPressed bool) { + mockInput.Lock() + mockInput.mousePressed[button] = isPressed + mockInput.Unlock() +} + +func simulateMousePosition(x, y float32) { + mockInput.Lock() + mockInput.mousePosition = rl.Vector2{X: x, Y: y} + mockInput.Unlock() +} + +func simulateMouseRay(ray rl.Ray) { + mockInput.Lock() + mockInput.mouseRay = ray + mockInput.Unlock() +} + +func simulateMouseWheel(move float32) { + mockInput.Lock() + mockInput.mouseWheel = move + mockInput.Unlock() +} + +func simulateCharInput(char rune) { + mockInput.Lock() + mockInput.charPressed = char + mockInput.Unlock() +} + +// Mock raylib functions +func mockIsKeyPressed(key int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.keyPressed[key] +} + +func mockIsKeyDown(key int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.keyDown[key] +} + +func mockIsMouseButtonPressed(button int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mousePressed[button] +} + +func mockGetMousePosition() rl.Vector2 { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mousePosition +} + +func mockGetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mouseRay +} + +func mockGetMouseWheelMove() float32 { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mouseWheel +} + +func mockGetCharPressed() rune { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.charPressed +} + +// Add more mock implementations... + +// Add this helper function +func toInt32(button rl.MouseButton) int32 { + return int32(button) +} diff --git a/game/test_setup.go b/game/test_setup.go new file mode 100644 index 0000000..7ad9e51 --- /dev/null +++ b/game/test_setup.go @@ -0,0 +1,26 @@ +package game + +import ( + "testing" + + "gitea.boner.be/bdnugget/goonscape/game/mock" + "gitea.boner.be/bdnugget/goonscape/game/testutils" +) + +func setupTestEnvironment(t *testing.T) (*Game, func()) { + testutils.ResetMockInput() + testutils.SetupMockFunctions() + + game := New() + game.input = &MockInput{} + game.Chat.input = &MockInput{} // Also inject mock input into chat + + // Verify mock setup + if mock.IsKeyPressed == nil || mock.GetCharPressed == nil { + t.Fatal("Mock functions not properly initialized") + } + + return game, func() { + testutils.ResetMockInput() + } +} diff --git a/game/testutils/helpers.go b/game/testutils/helpers.go new file mode 100644 index 0000000..630c45e --- /dev/null +++ b/game/testutils/helpers.go @@ -0,0 +1,153 @@ +package testutils + +import ( + "sync" + + "gitea.boner.be/bdnugget/goonscape/game/mock" + rl "github.com/gen2brain/raylib-go/raylib" +) + +var ( + mockInput struct { + sync.Mutex + keyPressed map[int32]bool + keyDown map[int32]bool + mousePressed map[int32]bool + mousePosition rl.Vector2 + mouseRay rl.Ray + mouseWheel float32 + charPressed rune + } +) + +func init() { + ResetMockInput() + SetupMockFunctions() +} + +// SetupMockFunctions initializes mock functions +func SetupMockFunctions() { + mock.IsKeyPressed = MockIsKeyPressed + mock.IsKeyDown = MockIsKeyDown + mock.IsMouseButtonPressed = MockIsMouseButtonPressed + mock.GetMousePosition = MockGetMousePosition + mock.GetMouseRay = MockGetMouseRay + mock.GetMouseWheelMove = MockGetMouseWheelMove + mock.GetCharPressed = MockGetCharPressed +} + +// ResetMockInput resets all mock input states +func ResetMockInput() { + mockInput.Lock() + defer mockInput.Unlock() + mockInput.keyPressed = make(map[int32]bool) + mockInput.keyDown = make(map[int32]bool) + mockInput.mousePressed = make(map[int32]bool) + mockInput.mousePosition = rl.Vector2{} + mockInput.mouseRay = rl.Ray{} + mockInput.mouseWheel = 0 + mockInput.charPressed = 0 +} + +// SimulateKeyPress simulates a key press +func SimulateKeyPress(key int32) { + mockInput.Lock() + mockInput.keyPressed[key] = true + mockInput.Unlock() +} + +// SimulateKeyDown simulates holding a key down +func SimulateKeyDown(key int32, isDown bool) { + mockInput.Lock() + mockInput.keyDown[key] = isDown + mockInput.Unlock() +} + +// SimulateMouseButton simulates a mouse button press +func SimulateMouseButton(button int32, isPressed bool) { + mockInput.Lock() + mockInput.mousePressed[button] = isPressed + mockInput.Unlock() +} + +// SimulateMousePosition simulates mouse movement +func SimulateMousePosition(x, y float32) { + mockInput.Lock() + mockInput.mousePosition = rl.Vector2{X: x, Y: y} + mockInput.Unlock() +} + +// SimulateMouseClick simulates a mouse click at the given position +func SimulateMouseClick(x, y float32) { + SimulateMousePosition(x, y) + SimulateMouseButton(ToInt32(rl.MouseLeftButton), true) +} + +// SimulateMouseRay simulates a mouse ray +func SimulateMouseRay(ray rl.Ray) { + mockInput.Lock() + mockInput.mouseRay = ray + mockInput.Unlock() +} + +// SimulateMouseWheel simulates mouse wheel movement +func SimulateMouseWheel(move float32) { + mockInput.Lock() + mockInput.mouseWheel = move + mockInput.Unlock() +} + +// SimulateCharInput simulates character input +func SimulateCharInput(char rune) { + mockInput.Lock() + mockInput.charPressed = char + mockInput.Unlock() +} + +// Mock raylib functions +func MockIsKeyPressed(key int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.keyPressed[key] +} + +func MockIsKeyDown(key int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.keyDown[key] +} + +func MockIsMouseButtonPressed(button int32) bool { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mousePressed[button] +} + +func MockGetMousePosition() rl.Vector2 { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mousePosition +} + +func MockGetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mouseRay +} + +func MockGetMouseWheelMove() float32 { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.mouseWheel +} + +func MockGetCharPressed() rune { + mockInput.Lock() + defer mockInput.Unlock() + return mockInput.charPressed +} + +// ToInt32 converts MouseButton to int32 +func ToInt32(button rl.MouseButton) int32 { + return int32(button) +} diff --git a/go.mod b/go.mod index db5f35f..3d320e0 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,15 @@ require ( google.golang.org/protobuf v1.36.3 ) +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + require ( github.com/ebitengine/purego v0.8.2 // indirect + github.com/stretchr/testify v1.10.0 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/sys v0.29.0 // indirect ) diff --git a/go.sum b/go.sum index c8e48ea..7eb6ef4 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,21 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw= github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=