Files
battery/tests/conftest.py

137 lines
3.4 KiB
Python
Raw Permalink Normal View History

2026-07-21 19:36:02 -04:00
"""Shared pytest fixtures + helpers for the Dying Phone engine tests.
Everything runs headless via SDL's dummy drivers, so no window is needed.
"""
import os
os.environ.setdefault("SDL_VIDEODRIVER", "dummy")
os.environ.setdefault("SDL_AUDIODRIVER", "dummy")
import pygame
import pytest
from game import settings as S
from game.level import Level
from game.game import Game
from game.player import InputState
DT = 1 / 60.0
@pytest.fixture(scope="session", autouse=True)
def _pygame():
pygame.init()
2026-08-01 12:19:46 -04:00
pygame.display.set_mode((64, 64)) # a display so convert_alpha() works
2026-07-21 19:36:02 -04:00
yield
pygame.quit()
# --- level/game factories ----------------------------------------------------
@pytest.fixture
def make_level(tmp_path):
n = [0]
def _make(yaml_text):
n[0] += 1
path = tmp_path / f"level_{n[0]}.yaml"
path.write_text(yaml_text)
return Level(str(path))
return _make
@pytest.fixture
def make_game(tmp_path):
n = [0]
def _make(yaml_text, **kw):
n[0] += 1
path = tmp_path / f"game_{n[0]}.yaml"
path.write_text(yaml_text)
# nonexistent assets dir -> placeholder rectangles (deterministic)
return Game([str(path)], str(tmp_path / "no_assets"), **kw)
return _make
# --- run-loop helpers ---------------------------------------------------------
def step(game, inp=None):
"""Advance one frame, mirroring Game.run()'s per-state dispatch."""
inp = inp or InputState()
if game.state == "playing":
game._update_play(DT, inp)
elif game.state == "dying":
game.death_timer -= DT
if game.death_timer <= 0:
game._respawn()
elif game.state == "charging":
game.charge_timer += DT
if game.charge_timer >= S.CHARGE_TIME:
game.state = "won_all" if game._final else "won_level"
elif game.state == "fading":
game._update_fade(DT)
def run(game, frames, inp_fn=None):
for i in range(frames):
step(game, inp_fn(i) if inp_fn else None)
def place(game, col, row):
"""Put the player's feet at the bottom of cell (col,row), centred."""
t = game.level.tile
game.player.fx = float(col * t + (t - game.player.w) / 2)
game.player.fy = float(row * t + (t - game.player.h))
game.player.vx = game.player.vy = 0.0
game.player._sync_rect()
def hold(**flags):
inp = InputState()
for k, v in flags.items():
setattr(inp, k, v)
return inp
# --- fakes for isolated trap unit tests --------------------------------------
class FakeLevel:
tile = 32
debug = False
width = 800
height = 480
def cell_rect(self, c, r):
return pygame.Rect(c * 32, r * 32, 32, 32)
class FakePlayer:
def __init__(self, rect):
self.rect = rect
self.w = rect.w
self.h = rect.h
self.fx = float(rect.x)
self.fy = float(rect.y)
self.vx = self.vy = 0.0
def _sync_rect(self):
self.rect.x = round(self.fx)
self.rect.y = round(self.fy)
class FakeGame:
def __init__(self, rect):
self.player = FakePlayer(rect)
def reversals(values):
"""Count direction reversals in a numeric sequence (for jitter tests)."""
rev, prev = 0, 0
for i in range(1, len(values)):
d = (values[i] > values[i - 1]) - (values[i] < values[i - 1])
if d and prev and d != prev:
rev += 1
if d:
prev = d
return rev