Add comprehensive test suite for game mechanics and level handling

- Introduced `test_final_level_flow.py` to validate final level transitions and game end scenarios.
- Created `test_game_over_flow.py` to ensure game over conditions trigger correctly based on rat counts.
- Implemented `test_keybindings.py` to verify keybinding configurations and their context-specific actions.
- Developed `test_level_editor.py` to assess level editor functionalities and layout computations.
- Added `test_level_io.py` for testing level data serialization and deserialization.
- Established `test_loop_logic_parity.py` to ensure consistent game state across multiple simulation runs.
- Created `test_non_regression.py` to simulate game behavior and capture states for future verification.
- Implemented `test_verify.py` to compare current game states against a golden master for regression detection.
This commit is contained in:
2026-05-19 22:18:43 +02:00
parent 486cd6b7c5
commit c7ed24483d
53 changed files with 10169 additions and 3886 deletions
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
import importlib.util
import unittest
from pathlib import Path
from engine import maze
MODULE_PATH = Path(__file__).resolve().parent / "tools" / "level_editor.py"
SPEC = importlib.util.spec_from_file_location("level_editor_module", MODULE_PATH)
level_editor = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(level_editor)
class LevelEditorLogicTests(unittest.TestCase):
def test_compute_canvas_layout_scales_to_viewport(self):
layout = level_editor.compute_canvas_layout(
maze.LEVEL_WIDTH,
maze.LEVEL_HEIGHT,
1200,
900,
level_editor.DEFAULT_CELL_SIZE,
True,
)
self.assertGreater(layout["cell_size"], level_editor.DEFAULT_CELL_SIZE)
self.assertAlmostEqual(layout["cell_size"], (900 - level_editor.VIEWPORT_PADDING * 2) / maze.LEVEL_HEIGHT)
self.assertAlmostEqual(layout["origin_y"], level_editor.VIEWPORT_PADDING)
self.assertGreater(layout["origin_x"], 0)
def test_compute_canvas_layout_centers_manual_zoom(self):
layout = level_editor.compute_canvas_layout(
maze.LEVEL_WIDTH,
maze.LEVEL_HEIGHT,
1000,
800,
16,
False,
)
self.assertEqual(layout["cell_size"], 16)
self.assertGreater(layout["origin_x"], 0)
self.assertGreater(layout["origin_y"], 0)
def test_fit_level_to_dat_size_centers_small_map(self):
source = [
[maze.MAP_WALL, maze.MAP_EMPTY],
[maze.MAP_TUNNEL, maze.MAP_EMPTY],
]
fitted = level_editor.fit_level_to_dat_size(source)
self.assertEqual(len(fitted), maze.LEVEL_HEIGHT)
self.assertEqual(len(fitted[0]), maze.LEVEL_WIDTH)
start_x = (maze.LEVEL_WIDTH - len(source[0])) // 2
start_y = (maze.LEVEL_HEIGHT - len(source)) // 2
self.assertEqual(fitted[start_y][start_x], maze.MAP_WALL)
self.assertEqual(fitted[start_y][start_x + 1], maze.MAP_EMPTY)
self.assertEqual(fitted[start_y + 1][start_x], maze.MAP_TUNNEL)
self.assertEqual(fitted[start_y + 1][start_x + 1], maze.MAP_EMPTY)
def test_compute_level_stats_reports_spawnable_default_level(self):
level = maze.create_level()
stats = level_editor.compute_level_stats(level)
self.assertEqual(stats["width"], maze.LEVEL_WIDTH)
self.assertEqual(stats["height"], maze.LEVEL_HEIGHT)
self.assertEqual(stats["border_openings"], 0)
self.assertEqual(stats["component_count"], 1)
self.assertGreater(stats["spawnable_count"], 0)
self.assertEqual(stats["warnings"], [])
def test_compute_level_stats_flags_unusable_map(self):
level = [[maze.MAP_WALL for _ in range(maze.LEVEL_WIDTH)] for _ in range(maze.LEVEL_HEIGHT)]
stats = level_editor.compute_level_stats(level)
self.assertEqual(stats["traversable_count"], 0)
self.assertEqual(stats["spawnable_count"], 0)
self.assertIn("nessuna cella attraversabile", stats["warnings"])
self.assertIn("nessuna cella EMPTY: niente spawn e niente armi", stats["warnings"])
def test_missing_tkinter_is_reported_cleanly(self):
if level_editor.TKINTER_IMPORT_ERROR is None:
level_editor.ensure_tkinter_available()
return
with self.assertRaises(RuntimeError):
level_editor.ensure_tkinter_available()
if __name__ == "__main__":
unittest.main()