Files
018-turtle/tests/fakes.py
Tirsvad e2249de46e Add the Pen protocol, window helpers and a recording fake turtle
The drawing functions take a Pen, the part of turtle.Turtle they use, so a test can
pass a fake that records every call. window.py is the only module that imports
turtle, and it does so inside the functions so that importing the package needs no Tk.

Task: MIL-002#1
Task: MIL-002#2
2026-10-07 23:58:50 +08:00

69 lines
2.0 KiB
Python

"""Test doubles that stand in for the turtle and its window."""
from turtle_challenges.pen import Color
class FakePen:
"""A pen that records every call instead of drawing.
The heading follows `setheading` and `right`, like a real turtle that
starts facing east, so code that reads `heading()` behaves as in a window.
"""
def __init__(self) -> None:
self.calls: list[tuple[str, tuple[object, ...]]] = []
self._heading = 0.0
def _record(self, name: str, *args: object) -> None:
self.calls.append((name, args))
def args_of(self, name: str) -> list[tuple[object, ...]]:
"""Return the arguments of every call to `name`, in order."""
return [args for call, args in self.calls if call == name]
def names(self) -> list[str]:
"""Return the name of every call, in order."""
return [call for call, _ in self.calls]
def forward(self, distance: float, /) -> None:
self._record("forward", distance)
def right(self, angle: float, /) -> None:
self._record("right", angle)
self._heading = (self._heading - angle) % 360
def penup(self) -> None:
self._record("penup")
def pendown(self) -> None:
self._record("pendown")
def color(self, color: Color, /) -> None:
self._record("color", color)
def circle(self, radius: float, /) -> None:
self._record("circle", radius)
def heading(self) -> float:
return self._heading
def setheading(self, to_angle: float, /) -> None:
self._record("setheading", to_angle)
self._heading = to_angle % 360
def pensize(self, width: int, /) -> None:
self._record("pensize", width)
def speed(self, speed: int, /) -> None:
self._record("speed", speed)
class FakeWindow:
"""A window that records whether it was told to wait for a click."""
def __init__(self) -> None:
self.waited_for_click = False
def exitonclick(self) -> None:
self.waited_for_click = True