Add console interface: calculator loop, ASCII title and tests
MIL-003 Console interface. - Add calculator() with recursive restart: y continues with the result, n starts a new calculation, q quits - Show one ASCII title at start: a calculator with the lettering to its right - Re-prompt on invalid numbers, operations and answers; restart on division by zero - Add __main__.py for python -m calc and the calc console script - Add interface tests with input() fed through monkeypatch Closes #22 Closes #23 Closes #24 Closes #25 Task: MIL-003#1 Task: MIL-003#2 Task: MIL-003#3 Task: MIL-003#4
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""!
|
||||
@file test_calculator.py
|
||||
@brief Tests for the interactive calculator loop.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from calc.calculator import calculator, format_number, main
|
||||
|
||||
|
||||
def feed(monkeypatch: pytest.MonkeyPatch, answers: list[str]) -> None:
|
||||
"""!
|
||||
@brief Make input() return the given answers in order.
|
||||
@param monkeypatch The pytest monkeypatch fixture.
|
||||
@param answers The lines the "user" types.
|
||||
"""
|
||||
lines = iter(answers)
|
||||
monkeypatch.setattr("builtins.input", lambda _prompt="": next(lines))
|
||||
|
||||
|
||||
def test_format_number_drops_trailing_zero() -> None:
|
||||
assert format_number(5.0) == "5"
|
||||
assert format_number(2.5) == "2.5"
|
||||
|
||||
|
||||
def test_calculator_adds_then_quits(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["2", "+", "3", "q"])
|
||||
calculator()
|
||||
assert "2 + 3 = 5" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_calculator_continues_with_previous_result(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["2", "+", "3", "y", "*", "4", "q"])
|
||||
calculator()
|
||||
assert "5 * 4 = 20" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_calculator_starts_new_calculation(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["2", "+", "3", "n", "10", "/", "4", "q"])
|
||||
calculator()
|
||||
assert "10 / 4 = 2.5" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_calculator_reprompts_on_invalid_input(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["abc", "1", "^", "+", "x", "1", "?", "q"])
|
||||
calculator()
|
||||
out = capsys.readouterr().out
|
||||
assert "not a number" in out
|
||||
assert "not a valid operation" in out
|
||||
assert "Please answer" in out
|
||||
assert "1 + 1 = 2" in out
|
||||
|
||||
|
||||
def test_calculator_restarts_after_division_by_zero(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["1", "/", "0", "6", "-", "1", "q"])
|
||||
calculator()
|
||||
out = capsys.readouterr().out
|
||||
assert "cannot divide by zero" in out
|
||||
assert "6 - 1 = 5" in out
|
||||
|
||||
|
||||
def test_main_shows_ascii_art(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
feed(monkeypatch, ["1", "+", "1", "q"])
|
||||
main()
|
||||
out = capsys.readouterr().out
|
||||
assert "|_____________________|" in out
|
||||
assert "Goodbye!" in out
|
||||
Reference in New Issue
Block a user