Files

685 lines
25 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests für die Spiellogik (ohne Fenster, ohne Ton).
Aufruf::
.venv/bin/python -m unittest discover -s tests -v
"""
from __future__ import annotations
import os
import random
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
from loader_game.sayings import saying_for
from loader_game.sequences import CaseSpec, load_sequences, parse_sequences
from loader_game.trailer import Trailer
class SequenceTests(unittest.TestCase):
def test_real_file_parses_into_six_sequences(self) -> None:
sequences, warnings = load_sequences()
self.assertEqual(warnings, [])
self.assertEqual(len(sequences), 6)
self.assertEqual(sequences[0][0], CaseSpec(3, 1))
self.assertEqual(sequences[0][3], CaseSpec(2, 1)) # aus "1,2,True"
def test_rotated_flag_swaps_dimensions(self) -> None:
sequences, _ = parse_sequences("1,3,True\n2,1,False")
self.assertEqual(sequences, [[CaseSpec(3, 1), CaseSpec(2, 1)]])
def test_blank_lines_separate_sequences(self) -> None:
sequences, _ = parse_sequences("1,1,False\n\n2,2,False\n\n")
self.assertEqual(len(sequences), 2)
def test_invalid_lines_warn_and_are_skipped(self) -> None:
sequences, warnings = parse_sequences("1,1\n1,1,False,extra\n0,1,False\n2,2,False")
self.assertEqual(sequences, [[CaseSpec(2, 2)]])
self.assertEqual(len(warnings), 3)
def test_garbage_falls_back_to_default(self) -> None:
sequences, warnings = parse_sequences("nur müll")
self.assertEqual(sequences, [])
# eine Meldung für die kaputte Zeile, eine für die fehlende Sequenz
self.assertEqual(len(warnings), 2)
def test_all_specs_fit_available_images(self) -> None:
sequences, _ = load_sequences()
for specs in sequences:
for spec in specs:
self.assertLessEqual(spec.units_w, config.MAX_UNITS_W, spec)
self.assertLessEqual(spec.units_h, config.MAX_UNITS_H, spec)
class LayoutTests(unittest.TestCase):
def test_bands_add_up_to_screen_height(self) -> None:
self.assertEqual(
config.TOP_BAR_HEIGHT + config.GAME_AREA_HEIGHT + config.BOTTOM_BAR_HEIGHT,
config.SCREEN_HEIGHT,
)
def test_trailer_fits_into_game_area(self) -> None:
self.assertGreaterEqual(config.TRAILER_X, 0)
self.assertLessEqual(config.TRAILER_RIGHT, config.SCREEN_WIDTH)
self.assertGreaterEqual(config.TRAILER_Y, config.TOP_BAR_HEIGHT)
self.assertLessEqual(
config.TRAILER_BOTTOM, config.TOP_BAR_HEIGHT + config.GAME_AREA_HEIGHT
)
def test_case_unit_is_square(self) -> None:
self.assertEqual(config.UNIT_LENGTH, config.UNIT_HEIGHT)
class DifficultyTests(unittest.TestCase):
def test_three_levels_in_order(self) -> None:
self.assertEqual([d.key for d in DIFFICULTIES], ["easy", "normal", "hard"])
self.assertEqual([d.label for d in DIFFICULTIES], ["Easy", "Normal", "Hard"])
def test_speeds_match_the_requested_levels(self) -> None:
self.assertEqual(
[(d.speed_fast, d.speed_slow) for d in DIFFICULTIES],
[(420.0, 140.0), (740.0, 250.0), (1080.0, 360.0)],
)
# Gegenüber der ersten Fassung (Normal 570, Hard 720):
# Normal ~30 % schneller, Hard ~50 % schneller
self.assertAlmostEqual(by_key("normal").speed_fast / 570.0, 1.30, delta=0.02)
self.assertAlmostEqual(by_key("hard").speed_fast / 720.0, 1.50, delta=0.02)
self.assertAlmostEqual(by_key("normal").speed_slow / 190.0, 1.30, delta=0.02)
self.assertAlmostEqual(by_key("hard").speed_slow / 240.0, 1.50, delta=0.02)
def test_speeds_increase_with_difficulty(self) -> None:
fast = [d.speed_fast for d in DIFFICULTIES]
slow = [d.speed_slow for d in DIFFICULTIES]
self.assertEqual(fast, sorted(fast))
self.assertEqual(slow, sorted(slow))
def test_unknown_key_falls_back_to_default(self) -> None:
self.assertIs(by_key("gibtsnicht"), DEFAULT_DIFFICULTY)
class CaseTests(unittest.TestCase):
def make_case(self, units_w: int = 2, units_h: int = 1, x: float | None = None) -> Case:
return Case(
units_w=units_w,
units_h=units_h,
x=float(config.SCREEN_WIDTH if x is None else x),
y=float(config.TRAILER_BOTTOM - units_h * config.UNIT_HEIGHT),
)
def test_moves_left_and_brakes_near_wall(self) -> None:
case = self.make_case()
start = case.x
case.move(1 / 60, wall_x=0)
fast_step = start - case.x
self.assertGreater(fast_step, 0)
case.x = float(config.FAST_TO_SLOW_DISTANCE - 20) # schon im Bremsbereich
before = case.x
case.move(1 / 60, wall_x=0)
slow_step = before - case.x
self.assertLess(slow_step, fast_step)
def test_difficulty_scales_speed(self) -> None:
easy = self.make_case()
hard = self.make_case()
easy.move(0.1, wall_x=0, difficulty=DIFFICULTIES[0])
hard.move(0.1, wall_x=0, difficulty=DIFFICULTIES[2])
self.assertGreater(easy.x, hard.x) # easy ist langsamer, bleibt weiter rechts
def test_allow_snap_only_near_wall(self) -> None:
case = self.make_case()
case.move(1 / 60, wall_x=0)
self.assertFalse(case.allow_snap)
case = self.make_case(x=config.SNAP_WINDOW + 2)
case.move(1 / 60, wall_x=0, difficulty=DIFFICULTIES[0])
self.assertTrue(case.allow_snap)
def test_placed_case_does_not_move(self) -> None:
case = self.make_case()
case.placed = True
start = case.x
case.move(1 / 60, wall_x=0)
self.assertEqual(case.x, start)
def test_case_keeps_its_texture_when_rotated(self) -> None:
case = self.make_case(units_w=1, units_h=2)
self.assertEqual(case.texture, (1, 2))
self.assertFalse(case.is_rotated)
case.rotate_footprint()
self.assertEqual((case.units_w, case.units_h), (2, 1))
self.assertEqual(case.texture, (1, 2)) # Textur bleibt
self.assertTrue(case.is_rotated)
def test_rotate_respects_trailer_height(self) -> None:
self.assertTrue(self.make_case(units_w=3, units_h=1).fits_trailer_height())
self.assertFalse(self.make_case(units_w=4, units_h=1).fits_trailer_height())
class TrailerTests(unittest.TestCase):
WALL = config.TRAILER_X + 500
def setUp(self) -> None:
self.trailer = Trailer()
def make_case(self, units_w: int, units_h: int) -> Case:
return Case(
units_w=units_w,
units_h=units_h,
x=float(config.TRAILER_RIGHT),
y=float(config.TRAILER_BOTTOM - units_h * config.UNIT_HEIGHT),
)
def add_base(self, units_w: int = 4, units_h: int = 1, y: float | None = None) -> Case:
"""Schwebender Koffer, rechtsbündig an der Barriere."""
base = Case(
units_w=units_w,
units_h=units_h,
x=0.0,
y=float(config.TRAILER_Y + 100 if y is None else y),
)
base.x = self.WALL - base.width
base.placed = True
base.snapped = True
self.trailer.stack.append(base)
self.trailer.wall_x = base.right
return base
# ------------------------------------------------------------------
def test_empty_trailer(self) -> None:
self.assertEqual(self.trailer.wall_x, self.trailer.x)
self.assertEqual(self.trailer.load_percent(), 0.0)
self.assertFalse(self.trailer.overflow())
self.assertIsNone(self.trailer.find_platform(self.make_case(1, 1)))
def test_snap_advances_wall_and_load(self) -> None:
case = self.make_case(4, 1)
self.trailer.stack_at_wall(case)
self.assertTrue(case.placed)
self.assertTrue(case.snapped)
self.assertEqual(self.trailer.wall_x, self.trailer.x + case.width)
expected = (
case.width * case.height / (self.trailer.width * self.trailer.height) * 100
)
self.assertAlmostEqual(self.trailer.load_percent(), expected)
def test_gap_placement_leaves_hole(self) -> None:
case = self.make_case(2, 1)
self.trailer.stack_with_gap(case)
self.assertEqual(case.x, self.trailer.x + config.FAIL_GAP)
self.assertFalse(case.snapped)
def test_platform_on_flat_ground_case_is_allowed(self) -> None:
# Ein flacher Koffer am Boden hat noch Platz darüber -> darf Plattform sein.
ground = self.make_case(4, 1)
self.trailer.stack_at_wall(ground)
self.assertIs(self.trailer.find_platform(self.make_case(1, 1)), ground)
def test_no_platform_when_case_reaches_top(self) -> None:
# 3 Einheiten hoch = Oberkante des Aufliegers -> kein Platz darüber.
ground = self.make_case(2, 3)
self.trailer.stack_at_wall(ground)
self.assertEqual(ground.y, config.TRAILER_Y)
self.assertIsNone(self.trailer.find_platform(self.make_case(1, 1)))
def test_platform_found_for_free_top(self) -> None:
base = self.add_base(units_w=4, units_h=1)
self.assertIs(self.trailer.find_platform(self.make_case(1, 1)), base)
def test_platform_rejects_too_wide_case(self) -> None:
self.add_base(units_w=2, units_h=1)
self.assertIsNone(self.trailer.find_platform(self.make_case(3, 1)))
def test_platform_rejects_case_not_aligned_with_wall(self) -> None:
base = self.add_base(units_w=4, units_h=1)
base.x -= 50 # nicht mehr bündig an der Barriere
self.assertIsNone(self.trailer.find_platform(self.make_case(1, 1)))
def test_platform_rejects_no_space_above(self) -> None:
self.add_base(units_w=4, units_h=1, y=config.TRAILER_Y + 50)
self.assertIsNone(self.trailer.find_platform(self.make_case(1, 1)))
def test_top_can_only_be_used_once(self) -> None:
base = self.add_base(units_w=4, units_h=1)
top = self.make_case(1, 1)
self.trailer.place_on_top(top, base)
self.assertEqual(top.bottom, base.y)
self.assertEqual(top.right, base.right)
self.assertIsNone(self.trailer.find_platform(self.make_case(1, 1)))
def test_overflow(self) -> None:
self.trailer.wall_x = self.trailer.right + 1
self.assertTrue(self.trailer.overflow())
class MenuTests(unittest.TestCase):
def test_navigation_wraps_around(self) -> None:
menu = DifficultyMenu(None, selected=DIFFICULTIES[1])
self.assertIs(menu.selected, DIFFICULTIES[1])
self.assertIsNone(menu.handle(Action.LEFT))
self.assertIs(menu.selected, DIFFICULTIES[0])
menu.handle(Action.LEFT) # Umlauf ans Ende
self.assertIs(menu.selected, DIFFICULTIES[-1])
menu.handle(Action.RIGHT) # Umlauf an den Anfang
self.assertIs(menu.selected, DIFFICULTIES[0])
def test_confirm_returns_selection(self) -> None:
menu = DifficultyMenu(None, selected=DIFFICULTIES[2])
self.assertIs(menu.handle(Action.PRIMARY), DIFFICULTIES[2])
def test_other_actions_do_not_select(self) -> None:
menu = DifficultyMenu(None)
self.assertIsNone(menu.handle(Action.UP))
self.assertIsNone(menu.handle(Action.DOWN))
self.assertIsNone(menu.handle(Action.BACK))
class GameTests(unittest.TestCase):
def make_game(self, sequence_length: int = 30, difficulty=None) -> Game:
sequences = [[CaseSpec(2, 1)] * sequence_length]
return Game(
None,
sequences,
difficulty=difficulty or DIFFICULTIES[2],
rng=random.Random(7),
)
def update_until(self, game: Game, predicate, steps: int = 5000) -> bool:
for _ in range(steps):
if predicate():
return True
game.update(1 / 60)
return predicate()
def test_starts_with_a_case(self) -> None:
game = self.make_game()
self.assertIsNotNone(game.current)
self.assertEqual(game.index, 1)
self.assertEqual(len(game.upcoming()), 3)
def test_snapping_places_case_and_advances_wall(self) -> None:
game = self.make_game()
self.assertTrue(
self.update_until(game, lambda: bool(game.current and game.current.allow_snap))
)
game.handle(Action.PRIMARY)
self.update_until(game, lambda: game.animation is None)
self.assertEqual(len(game.trailer.stack), 1)
self.assertAlmostEqual(
game.trailer.wall_x, config.TRAILER_X + 2 * config.UNIT_LENGTH
)
def test_rotation_keeps_texture(self) -> None:
game = Game(
None,
[[CaseSpec(1, 2)] * 5],
difficulty=DIFFICULTIES[2],
rng=random.Random(1),
)
case = game.current
case.x = float(game.trailer.wall_x + config.SNAP_WINDOW + 1)
case.allow_snap = True
game.handle(Action.LEFT)
self.update_until(game, lambda: game.animation is None)
self.assertEqual((case.units_w, case.units_h), (2, 1))
self.assertEqual(case.texture, (1, 2))
self.assertTrue(case.is_rotated)
self.assertFalse(case.can_rotate)
def test_rotation_ignored_for_wide_cases(self) -> None:
game = Game(
None,
[[CaseSpec(4, 1)] * 5],
difficulty=DIFFICULTIES[2],
rng=random.Random(1),
)
case = game.current
case.allow_snap = True
game.handle(Action.LEFT)
self.assertIsNone(game.animation)
self.assertEqual((case.units_w, case.units_h), (4, 1))
def test_missing_platform_stacks_with_gap(self) -> None:
game = self.make_game()
self.assertTrue(
self.update_until(game, lambda: bool(game.current and game.current.allow_snap))
)
game.handle(Action.UP) # es gibt nichts, worauf man stapeln könnte
self.assertEqual(len(game.trailer.stack), 1)
self.assertFalse(game.trailer.stack[0].snapped)
self.assertGreater(game.shake, 0)
def test_easy_is_slower_than_hard(self) -> None:
easy = self.make_game(difficulty=DIFFICULTIES[0])
hard = self.make_game(difficulty=DIFFICULTIES[2])
for _ in range(30):
easy.update(1 / 60)
hard.update(1 / 60)
self.assertGreater(easy.current.x, hard.current.x)
def test_game_ends_when_trailer_overflows(self) -> None:
game = self.make_game(sequence_length=40)
self.assertTrue(
self.update_until(game, lambda: game.state is State.FINISHED, steps=20000)
)
self.assertGreater(game.percent, 0)
self.assertLessEqual(game.percent, 100)
def test_game_ends_when_sequence_is_exhausted(self) -> None:
game = self.make_game(sequence_length=3)
for _ in range(20000):
if game.state is State.FINISHED:
break
if game.current is not None and game.current.allow_snap:
game.handle(Action.PRIMARY)
game.update(1 / 60)
self.assertIs(State.FINISHED, game.state)
self.assertEqual(len(game.trailer.stack), 3)
def test_back_quits(self) -> None:
game = self.make_game()
self.assertIs(game.handle(Action.BACK), HandleResult.QUIT)
def test_primary_after_finish_starts_new_round(self) -> None:
game = self.make_game(sequence_length=2)
for _ in range(20000):
if game.state is State.FINISHED:
break
if game.current is not None and game.current.allow_snap:
game.handle(Action.PRIMARY)
game.update(1 / 60)
self.assertIs(game.state, State.FINISHED)
# Cooldown: erst nach Ablauf darf die nächste Runde starten
self.assertIs(game.handle(Action.PRIMARY), HandleResult.CONTINUE)
for _ in range(int(config.RESULT_COOLDOWN * 60) + 5):
game.update(1 / 60)
self.assertIs(game.handle(Action.PRIMARY), HandleResult.NEW_ROUND)
def test_result_lines_show_difficulty_and_percent(self) -> None:
game = self.make_game(difficulty=DIFFICULTIES[2])
game.percent = 82
lines = game.result_lines()
self.assertIn("Hard", lines[0])
self.assertIn("82%", lines[0])
self.assertTrue(any("ESC" in line for line in lines))
class SayingTests(unittest.TestCase):
def test_full_load_gets_the_contract_line(self) -> None:
self.assertIn("Vertrag", saying_for(100))
self.assertIn("Vertrag", saying_for(100.0))
def test_every_percent_gets_a_saying(self) -> None:
rng = random.Random(5)
for percent in range(0, 101, 5):
self.assertTrue(saying_for(percent, rng).strip())
def test_high_and_low_use_different_pools(self) -> None:
rng = random.Random(1)
high = {saying_for(90, rng) for _ in range(40)}
low = {saying_for(10, rng) for _ in range(40)}
self.assertFalse(high & low)
def test_no_harsh_words(self) -> None:
# Zielgruppe Jugendliche: frech ja, beleidigend nein.
bad = ("dumm", "idiot", "blöd", "bloed", "versager", "looser", "hässlich")
rng = random.Random(4)
for percent in range(0, 101, 5):
text = saying_for(percent, rng).lower()
for word in bad:
self.assertNotIn(word, text)
class ResultCooldownTests(unittest.TestCase):
def finish_a_round(self) -> Game:
game = Game(
None,
[[CaseSpec(1, 1)]],
difficulty=DIFFICULTIES[1],
rng=random.Random(2),
)
for _ in range(4000):
if game.state is State.FINISHED:
break
game.update(1 / 60)
self.assertIs(game.state, State.FINISHED)
return game
def test_countdown_then_hint(self) -> None:
game = self.finish_a_round()
lines = game.result_lines()
self.assertEqual(len(lines), 3)
self.assertIn("Neue Runde in", lines[2])
self.assertIn("%", lines[0])
for _ in range(int(config.RESULT_COOLDOWN * 60) + 5):
game.update(1 / 60)
self.assertIn("ESC", game.result_lines()[2])
def test_primary_is_ignored_during_cooldown(self) -> None:
game = self.finish_a_round()
self.assertIs(game.handle(Action.PRIMARY), HandleResult.CONTINUE)
game.update(config.RESULT_COOLDOWN / 2)
self.assertIs(game.handle(Action.PRIMARY), HandleResult.CONTINUE)
class SequenceChoiceTests(unittest.TestCase):
def test_never_repeats_the_previous_sequence(self) -> None:
sequences = [[CaseSpec(1, 1)] for _ in range(4)]
rng = random.Random(11)
previous = None
for _ in range(40):
game = Game(None, sequences, rng=rng, previous_sequence=previous)
self.assertNotEqual(game.sequence_index, previous)
previous = game.sequence_index
def test_single_sequence_is_allowed(self) -> None:
game = Game(None, [[CaseSpec(1, 1)]], rng=random.Random(1), previous_sequence=0)
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_buttons(self) -> None:
# macOS meldet das D-Pad von Xbox-Controllern als Tasten 11-14.
for button, expected in (
(11, Action.UP),
(12, Action.DOWN),
(13, Action.LEFT),
(14, Action.RIGHT),
):
self.assertEqual(
input_map.actions_from_event(
self.event(type=pygame.JOYBUTTONDOWN, button=button, joy=0)
),
[expected],
)
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).
Fängt fehlende config-Konstanten und Zeichenfehler ab, die reine
Logiktests nicht bemerken.
"""
@classmethod
def setUpClass(cls) -> None:
import pygame
from loader_game.assets import Assets
pygame.mixer.pre_init(44100, -16, 2, 512)
pygame.init()
cls.surface = pygame.display.set_mode(
(config.SCREEN_WIDTH, config.SCREEN_HEIGHT)
)
cls.assets = Assets()
cls.assets.load()
@classmethod
def tearDownClass(cls) -> None:
import pygame
pygame.quit()
def make_game(self) -> Game:
return Game(
self.assets,
[[CaseSpec(3, 1), CaseSpec(1, 2), CaseSpec(2, 2)]],
difficulty=DIFFICULTIES[1],
rng=random.Random(3),
)
def test_tutorial_renders_and_waits(self) -> None:
tutorial = Tutorial(self.assets)
tutorial.draw(self.surface)
# Das Tutorial zeigt nur das Bild und wartet - keine Startsequenz.
self.assertFalse(tutorial.update(1 / 60))
self.assertFalse(tutorial.update(1 / 60))
tutorial.handle(Action.PRIMARY)
self.assertTrue(tutorial.update(1 / 60))
def test_start_sequence_renders_and_finishes(self) -> None:
start = StartSequence(self.assets)
start.draw(self.surface)
elapsed = 0.0
while not start.update(0.05) and elapsed < 10:
elapsed += 0.05
expected = (
len(config.INTRO_DOT_COLORS) * config.INTRO_DOT_DELAY
+ config.INTRO_ZIP_DURATION
)
self.assertAlmostEqual(elapsed, expected, delta=0.3)
start.draw(self.surface)
def test_menu_renders(self) -> None:
menu = DifficultyMenu(self.assets, selected=DIFFICULTIES[0])
menu.handle(Action.RIGHT)
menu.draw(self.surface)
def test_game_and_result_render(self) -> None:
game = self.make_game()
game.draw(self.surface)
game.percent = 61.5
game.state = State.FINISHED
game.draw(self.surface)
def test_rotated_case_draws_something(self) -> None:
from loader_game import scene
case = Case(units_w=1, units_h=2, x=200.0, y=400.0)
case.rotate_footprint()
self.assertTrue(case.is_rotated)
scene.draw_case(self.surface, self.assets, case)
if __name__ == "__main__":
unittest.main()