diff --git a/ppb/testutils.py b/ppb/testutils.py index 983090cf..000dbbb5 100644 --- a/ppb/testutils.py +++ b/ppb/testutils.py @@ -1,11 +1,29 @@ import time from typing import Callable +from hypothesis import strategies as st + +from ppb import Vector from ppb.engine import GameEngine from ppb.events import Quit from ppb.systems import System +def integer_vectors(min_value=None, max_value=None): + return st.builds( + Vector, + st.integers(min_value=min_value, max_value=max_value), + st.integers(min_value=min_value, max_value=max_value), + ) + +def vectors(max_magnitude=1e75): + return st.builds( + Vector, + st.floats(min_value=-max_magnitude, max_value=max_magnitude), + st.floats(min_value=-max_magnitude, max_value=max_magnitude), + ) + + class Failer(System): def __init__(self, *, fail: Callable[[GameEngine], bool], message: str, diff --git a/requirements-tests.txt b/requirements-tests.txt index e079f8a6..9a54149c 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1 +1,2 @@ +hypothesis pytest diff --git a/tests/test_camera.py b/tests/test_camera.py index 6b29781e..cdcd1a12 100644 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -1,21 +1,30 @@ +from hypothesis import given, note, strategies as st + from ppb import BaseSprite from ppb import Vector from ppb.camera import Camera +from ppb.testutils import integer_vectors, vectors + + +ONE_K = 1024 +ONE_M = ONE_K * ONE_K -def test_camera_move(): - cam = Camera() - cam.position = Vector(500, 500) - assert cam.position == Vector(500, 500) - cam.position += Vector(100, 100) - assert cam.position == Vector(600, 600) +def cameras(): + return st.builds( + lambda offset, diagonal: Camera(viewport=(*offset, *(offset+diagonal))), + integer_vectors(min_value=-ONE_M, max_value=ONE_M), + integer_vectors(min_value=2, max_value=ONE_M), + ) -def test_camera_viewport(): - cam = Camera(viewport=(0, 0, 800, 600)) - assert cam.point_in_viewport(Vector(400, 400)) - assert not cam.point_in_viewport(Vector(900, 600)) - assert cam.viewport_offset == Vector(400, 300) +@given(diagonal=integer_vectors(min_value=2, max_value=ONE_M)) +def test_camera_viewport(diagonal: Vector): + x, y = diagonal + cam = Camera(viewport=(0, 0, x, y)) + assert cam.point_in_viewport(0.5 * diagonal) + assert not cam.point_in_viewport(diagonal + (100, 100)) + assert cam.viewport_offset == 0.5 * diagonal def test_camera_point_in_viewport_not_at_origin(): @@ -26,6 +35,13 @@ def test_camera_point_in_viewport_not_at_origin(): assert not cam.point_in_viewport(Vector(901, 600)) +@given(cam=cameras(), v=vectors()) +def test_camera_roundtrip_frame_viewport(cam: Camera, v: Vector): + """Check that Camera.translate_to_{frame,viewport} are inverse of one another.""" + assert cam.translate_to_frame(cam.translate_to_viewport(v)).isclose(v) + assert cam.translate_to_viewport(cam.translate_to_frame(v)).isclose(v) + + def test_camera_translate_to_frame(): cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80) assert cam.position == Vector(0, 0) @@ -70,4 +86,4 @@ def test_viewport_change_affects_frame_height(): cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80) assert cam.frame_left == -5 cam.viewport_width = 400 - assert cam.frame_left == -2.5 \ No newline at end of file + assert cam.frame_left == -2.5