Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for Root folder #328

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
git submodule update --init --recursive

- name: Run Unit Tests
run: pytest --cov=rubato tests
run: make test

lint:
name: Lint Code
Expand Down
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"Tomer",
"Yamm"
],
"python.analysis.diagnosticMode": "workspace",
"python.formatting.provider": "yapf",
"python.linting.pylintCategorySeverity.refactor": "Information",
"githubPullRequests.ignoredPrBranches": ["main"],
Expand Down Expand Up @@ -105,7 +104,6 @@
"todo-tree.tree.showBadges": true,
"todo-tree.filtering.excludeGlobs": ["**/node_modules", "**/static"],
"python.analysis.completeFunctionParens": true,
"python.analysis.importFormat": "relative",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"autoDocstring.customTemplatePath": "autodocstringtemplate.mustache"
Expand Down
16 changes: 10 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,31 @@ pre-commit:
@make lint
@echo "Building Rubato..."
@make build-test >/dev/null
@pytest --cov=rubato --cov-report term-missing --log-format="%(asctime)s %(levelname)s %(thread)d %(message)s" tests
@pytest --cov=rubato tests
@cd demo && ./_run_all.sh

build-test: delete-build
@export CFLAGS=-DCYTHON_TRACE=1
@export TEST_MODE=1 && python setup.py build_ext --force --inplace --define CYTHON_TRACE

quick-test:
@export TEST_MODE=1 && python setup.py build_ext --inplace --define CYTHON_TRACE
@pytest --cov=rubato tests

test: build-test
@pytest --cov=rubato --cov-report term-missing --log-format="%(asctime)s %(levelname)s %(thread)d %(message)s" tests
@pytest --cov=rubato tests

test-rub: build-test
@pytest -m "rub" --cov=rubato --cov-report term-missing tests -s
@pytest -m "rub" --cov=rubato tests -s

test-sdl: build-test
@pytest -m "sdl or rub" --cov=rubato --cov-report term-missing tests -s
@pytest -m "sdl or rub" --cov=rubato tests -s

test-no-rub: build-test
@pytest -m "not rub" --cov=rubato --cov-report term-missing tests
@pytest -m "not rub" --cov=rubato tests

test-no-sdl: build-test
@pytest -m "not sdl and not rub" --cov=rubato --cov-report term-missing tests -s
@pytest -m "not sdl and not rub" --cov=rubato tests -s

test-indiv: build-test
@pytest tests -k "$(test)"
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ JOIN_MULTIPLE_LINES = true
plugins = ["Cython.Coverage"]

[tool.coverage.report]
fail_under = 47
skip_covered = true
skip_empty = true
show_missing = true
exclude_lines=[
"if TYPE_CHECKING:",
"if sys.platform",
"# test: skip",
"import",
]
omit=[
"rubato/tests/*",
Expand Down
10 changes: 5 additions & 5 deletions rubato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@

# Sets the sdl path to the proper rubato sdl directory, from now on all sdl imports will be relative to this directory.
if sys.platform.startswith("darwin"):
if os.uname().machine == "arm64": # type: ignore
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/mac/silicon")
if os.uname().machine == "arm64": # type: ignore # test: skip
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/mac/silicon") # test: skip
else:
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/mac/intel")
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/mac/intel") # test: skip
if sys.platform.startswith("win32"):
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/windows")
os.environ["PYSDL2_DLL_PATH"] = str(Path(__file__).parent / "static/dll/windows") # test: skip

simplefilter("ignore", UserWarning)

Expand All @@ -45,7 +45,7 @@ def init(
fullscreen: Literal["off", "desktop", "exclusive"] = "off",
target_fps: int = 0,
physics_fps: int = Time.physics_fps,
hidden: bool = False,
hidden: bool = False # test: skip
):
"""
Initializes rubato.
Expand Down
11 changes: 5 additions & 6 deletions rubato/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def camera(cls) -> Camera | None: # test: skip
return cls.current.camera if cls.current else None # pylint: disable=using-constant-test

@classmethod
def quit(cls): # test: skip
def quit(cls):
"""Quit the game and close the python process."""
Radio.broadcast(Events.EXIT)
cls.state = cls.STOPPED
Expand All @@ -118,7 +118,7 @@ def quit(cls): # test: skip
sys.exit(0)

@classmethod
def start(cls): # test: skip
def start(cls) -> None:
"""
Starts the main game loop. Called automatically by :meth:`rubato.begin`.
"""
Expand All @@ -140,7 +140,7 @@ def start(cls): # test: skip
sys.stdout.flush()

@classmethod
def loop(cls): # test: skip
def loop(cls):
"""
Rubato's main game loop. Called automatically by :meth:`rubato.Game.start`.
"""
Expand Down Expand Up @@ -179,8 +179,7 @@ def loop(cls): # test: skip
Time.physics_counter += Time.delta_time

while Time.physics_counter >= Time.fixed_delta:
if cls.state != Game.PAUSED:
curr.private_fixed_update()
curr.private_fixed_update()
Time.physics_counter -= Time.fixed_delta

curr.private_draw()
Expand Down Expand Up @@ -209,7 +208,7 @@ def loop(cls): # test: skip
sdl2.SDL_Delay(1)

# clock the time the update call took
Time.delta_time = (Time.now() - Time.frame_start) / 1000 # pylint: disable= comparison-with-callable
Time.delta_time = (Time.now() - Time.frame_start) / 1000 # pylint: disable=comparison-with-callable

@staticmethod
def update(): # test: skip
Expand Down
2 changes: 1 addition & 1 deletion rubato/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def wrap(
comp: Component | list[Component],
name: str = "",
pos: Vector | tuple[float, float] = (0, 0),
rotation: float = 0,
rotation: float = 0, # test: skip
z_index: int = 0,
debug: bool = False
):
Expand Down
2 changes: 0 additions & 2 deletions rubato/struct/gameobject/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import cython

from ... import Vector, Camera

if TYPE_CHECKING:
from .. import GameObject


@cython.cclass
class Component:
"""
A component adds functionality to the game object it is attached to. Note that this is a template class and should
Expand Down
1 change: 0 additions & 1 deletion rubato/struct/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ def private_draw(self):
self.draw()
self.root.draw(self.camera)
self.ui.draw(self._ui_cam)
Draw.dump()

def private_update(self):
if not self.started:
Expand Down
3 changes: 2 additions & 1 deletion rubato/utils/rb_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class Time:
_schedule_queue: list[ScheduledTask] = []

delta_time: float = 0.001
fixed_delta: float = 0
"""The number of seconds between the last frame and the current frame."""
fixed_delta: float = 0.1
"""The number of seconds since the last fixed update."""
normal_delta: float = 0
fps = 60
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def package_files(directory):
"embedsignature": True,
"language_level": 3,
"linetrace": linetrace,
"emit_code_comments": True,
},
),
]
Expand Down
148 changes: 145 additions & 3 deletions tests/game_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
"""Test the Game class"""
import pytest

from unittest.mock import Mock
import rubato
from rubato.game import Game
from rubato.struct.scene import Scene
# pylint: disable=redefined-outer-name
from rubato.utils.debug import Debug
from rubato.utils.display import Display
from rubato.utils.draw import Draw
from rubato.utils.error import Error, InitError, IdError, PrintError
from rubato.utils.radio import Radio
from rubato.utils.rb_input import Input
from rubato.utils.rb_time import Time
# pylint: disable=redefined-outer-name, unused-argument, protected-access


@pytest.fixture
Expand All @@ -21,4 +29,138 @@ def test_state():

def test_camera():
Scene()
assert Game.camera == Game.current.camera # type: ignore
assert Game.camera == Game.current.camera # type: ignore


def test_init():
with pytest.raises(InitError):
Game()


def test__add():
with pytest.raises(IdError):
Game._add(Scene(name="scene"))
Game._add(Scene(name="scene"))


@pytest.mark.rub
def test_quit(rub):
with pytest.raises(SystemExit):
Game.quit()
assert Game.state == Game.STOPPED


def test_start(monkeypatch: pytest.MonkeyPatch):
loop = Mock()
monkeypatch.setattr(Game, "loop", loop)
Game.start()
loop.assert_called_once()

loop = Mock(side_effect=KeyboardInterrupt)
quit_mock = Mock()
monkeypatch.setattr(Game, "quit", quit_mock)
monkeypatch.setattr(Game, "loop", loop)
Game.start()
quit_mock.assert_called_once()

loop = Mock(side_effect=PrintError)
monkeypatch.setattr(Game, "loop", loop)
with pytest.raises(PrintError):
Game.start()
loop.assert_called_once()

loop = Mock(side_effect=Error)
monkeypatch.setattr(Game, "loop", loop)
with pytest.raises(Error):
Game.start()
loop.assert_called_once()

assert Game.state == Game.RUNNING
Game.state = Game.STOPPED


@pytest.mark.rub
def test_loop(monkeypatch: pytest.MonkeyPatch, rub):
time_now = Mock(side_effect=[0] + [x * 1000 for x in range(20)])
monkeypatch.setattr(Time, "now", time_now)
push = Mock(side_effect=Error)
monkeypatch.setattr(rubato.game.sdl2, "SDL_PushEvent", push)
pump = Mock()
monkeypatch.setattr(rubato.game.sdl2, "SDL_PumpEvents", pump)
handle = Mock(side_effect=[x == 0 for x in range(20)])
monkeypatch.setattr(Radio, "handle", handle)
quit_func = Mock()
monkeypatch.setattr(Game, "quit", quit_func)
update_controller = Mock()
monkeypatch.setattr(Input, "update_controllers", update_controller)
process = Mock()
monkeypatch.setattr(Time, "process_calls", process)

run_count = 0
scene_mock = Mock()

def update_override():
nonlocal run_count
if run_count == 0:
Game._current = ""
if run_count == 1:
monkeypatch.setattr(Game, "current", scene_mock)
Game.state = Game.PAUSED
elif run_count == 2:
Game.state = Game.STOPPED
run_count += 1

Game.update = update_override

Time.fixed_delta = 1

clear = Mock()
monkeypatch.setattr(Draw, "clear", clear)
dump = Mock()
monkeypatch.setattr(Draw, "dump", dump)
draw = Mock()
monkeypatch.setattr(Debug, "draw_fps", draw)

Game.show_fps = True

present = Mock()
monkeypatch.setattr(Display.renderer, "present", present)

Time.capped = True
Time.normal_delta = 50000

delay = Mock()
monkeypatch.setattr(rubato.game.sdl2, "SDL_Delay", delay)

with pytest.raises(Error):
Game.start()

time_now.assert_called()
assert time_now.call_count == 11
push.assert_called_once()
pump.assert_called()
assert pump.call_count == 3
handle.assert_called()
assert handle.call_count == 3
quit_func.assert_called_once()
update_controller.assert_called()
assert update_controller.call_count == 3
process.assert_called()
assert process.call_count == 3
assert run_count == 3
scene_mock.private_paused_update.assert_called_once()
scene_mock.private_update.assert_called_once()
scene_mock.private_fixed_update.assert_called()
assert scene_mock.private_fixed_update.call_count == 2
scene_mock.private_draw.assert_called()
assert scene_mock.private_draw.call_count == 2
clear.assert_called_once()
dump.assert_called()
assert dump.call_count == 3
draw.assert_called()
assert draw.call_count == 3
present.assert_called()
assert present.call_count == 3
delay.assert_called()
assert delay.call_count == 4
assert Game.state == Game.STOPPED
2 changes: 2 additions & 0 deletions tests/init_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def test_begin_init(monkeypatch, rub):
assert rubato.Game.state == rubato.Game.STOPPED
loop = Mock()
monkeypatch.setattr(rubato.Game, "start", loop)
rubato.Display.hidden = False
rubato.begin()
loop.assert_called_once() # This code is reachable because of the monkeypatch

Expand Down Expand Up @@ -67,5 +68,6 @@ def test_init(monkeypatch):
physics_fps=30,
icon=str(files("rubato.static.png").joinpath("logo_filled.ico")),
hidden=True,
fullscreen="desktop",
)
set_icon.assert_called_once_with(str(files("rubato.static.png").joinpath("logo_filled.ico")))
Loading