Controller-Fix: Joystick-Objekte wurden nach dem Namens-Auslesen freigegeben, dadurch lieferte SDL keine Events mehr. Jetzt offen halten + Hotplug. Neu: tools/controller_check.py zur Diagnose, Tests fuer Tastenbelegung (62).

This commit is contained in:
Project Bridge committed 2026-11-01 13:10:43 +00:00
1 parent a088c23a09
commit 5237b0a39c
7 files changed
+343 -17

No files matched your search

+94
View File
@@ -14,10 +14,13 @@ import unittest
os.environ.setdefault("SDL_VIDEODRIVER", "dummy")
os.environ.setdefault("SDL_AUDIODRIVER", "dummy")
import pygame # noqa: E402 (muss nach den SDL-Variablen kommen)
from loader_game import config
from loader_game.case import Case
from loader_game.difficulty import DEFAULT_DIFFICULTY, DIFFICULTIES, by_key
from loader_game.game import Game, HandleResult, State
from loader_game import input_map
from loader_game.input_map import Action
from loader_game.intro import StartSequence, Tutorial
from loader_game.menu import DifficultyMenu
@@ -493,6 +496,97 @@ class SequenceChoiceTests(unittest.TestCase):
self.assertEqual(game.sequence_index, 0)
class ControllerMappingTests(unittest.TestCase):
"""Controller-Events → Aktionen (ohne echtes Gerät)."""
@staticmethod
def event(**kwargs):
import types
return types.SimpleNamespace(**kwargs)
def test_keyboard(self) -> None:
self.assertEqual(
input_map.actions_from_event(self.event(type=pygame.KEYDOWN, key=pygame.K_SPACE)),
[Action.PRIMARY],
)
self.assertEqual(
input_map.actions_from_event(self.event(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)),
[Action.BACK],
)
self.assertEqual(
input_map.actions_from_event(self.event(type=pygame.KEYDOWN, key=pygame.K_a)), []
)
def test_xbox_buttons(self) -> None:
for button, expected in (
(0, Action.PRIMARY),
(1, Action.BACK),
(2, Action.LEFT),
(3, Action.UP),
):
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYBUTTONDOWN, button=button, joy=0)
),
[expected],
)
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYBUTTONDOWN, button=7, joy=0)
),
[],
)
def test_dpad_as_hat(self) -> None:
for value, expected in (
((-1, 0), Action.LEFT),
((1, 0), Action.RIGHT),
((0, -1), Action.UP),
((0, 1), Action.DOWN),
):
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYHATMOTION, hat=0, value=value, joy=0)
),
[expected],
)
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYHATMOTION, hat=0, value=(0, 0), joy=0)
),
[],
)
def test_left_stick_with_deadzone(self) -> None:
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYAXISMOTION, axis=0, value=-0.9, joy=0)
),
[Action.LEFT],
)
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYAXISMOTION, axis=1, value=0.9, joy=0)
),
[Action.DOWN],
)
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYAXISMOTION, axis=0, value=0.2, joy=0)
),
[],
)
def test_open_controllers_without_devices_does_not_crash(self) -> None:
# Im Testrechner ist kein Controller angeschlossen – das muss ruhig
# eine leere Liste ergeben (und darf keine Ausnahme werfen).
names = input_map.open_controllers()
self.assertIsInstance(names, list)
self.assertEqual(names, input_map.controller_names())
class RenderTests(unittest.TestCase):
"""Rendert je ein Bild von Anleitung, Startsequenz, Menü und Spiel (ohne Fenster).