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:
@@ -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()
|
||||
Reference in New Issue
Block a user