From 1c54f860fa0a1624b3f55f6f76d9dc7679d5756a Mon Sep 17 00:00:00 2001 From: mat ess Date: Sun, 13 Aug 2023 16:46:47 -0400 Subject: [PATCH] Use hypothesis for testing --- roll/roll.py | 9 ++++----- roll/throw.py | 4 ++-- tests/roll_test.py | 45 +++++++++++++++++++++++++++++++++++---------- tests/throw_test.py | 17 +++++++++++------ 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/roll/roll.py b/roll/roll.py index d6ac444..6b1cf84 100644 --- a/roll/roll.py +++ b/roll/roll.py @@ -16,7 +16,7 @@ class Roll: dice_count: int = 1 sides: int = 20 - modifier: int | None = None + modifier: int = 0 def __post_init__(self) -> None: if self.dice_count < 1: @@ -34,14 +34,13 @@ class Roll: msg = f"expected {value!r} to match pattern {ROLL_PATTERN.pattern!r}" raise ValueError(msg) dice_count, sides, modifier = match.groups() - return cls(int(dice_count), int(sides), int(modifier) if modifier else None) + return cls(int(dice_count), int(sides), int(modifier) if modifier else 0) def modifier_str(self) -> str: """return the modifier as a string""" - if self.modifier is None: + if self.modifier == 0: return "" - sign = "+" if self.modifier > 0 else "" - return f"{sign}{self.modifier}" + return f"{self.modifier:+}" def to_str(self) -> str: """return the short representation of a roll, e.g. 3d4 or 2d20+3""" diff --git a/roll/throw.py b/roll/throw.py index ce38119..4eecf2c 100644 --- a/roll/throw.py +++ b/roll/throw.py @@ -7,12 +7,12 @@ D20_MAX = 20 class Throw: results: list[int] sides: int - modifier: int | None = None + modifier: int = 0 @property def total(self) -> int: """calculate the total of the throw, accounting for the modifier""" - return sum(self.results) + (self.modifier or 0) + return sum(self.results) + self.modifier @property def is_critical_hit(self) -> bool: diff --git a/tests/roll_test.py b/tests/roll_test.py index eaec67c..a13d8ee 100644 --- a/tests/roll_test.py +++ b/tests/roll_test.py @@ -1,13 +1,33 @@ import pytest +from hypothesis import assume, given +from hypothesis import strategies as st from roll.roll import Roll -def test_roll_validation(): +@given(st.integers(max_value=0)) +def test_bad_dice_count(n: int): with pytest.raises(ValueError): - Roll(0, 20) + Roll(n, 20) + + +@given(st.integers(max_value=1)) +def test_bad_sides(sides: int): with pytest.raises(ValueError): - Roll(1, 1) + Roll(1, sides) + + +@given( + st.integers(min_value=1), + st.integers(min_value=2), + st.integers(), +) +def test_valid_dice(n: int, sides: int, modifier: int): + string = f"{n}d{sides}{modifier:+}" if modifier else f"{n}d{sides}" + roll = Roll(n, sides, modifier) + roll_from_str = Roll.from_str(string) + assert roll == roll_from_str + assert string == roll.to_str() and string == roll_from_str.to_str() @pytest.mark.parametrize( @@ -25,16 +45,21 @@ def test_from_bad_str(roll: str): Roll.from_str(roll) -def test_modify(): - roll = Roll(2, 20) - modified_roll = roll.modify(3) - assert modified_roll == Roll(2, 20, 3) - assert roll == Roll(2, 20) +@given( + st.integers(min_value=1), + st.integers(min_value=2), + st.integers(), + st.integers(), +) +def test_modify(n: int, sides: int, modifier: int, new_modifier: int): + assume(modifier != new_modifier) + roll = Roll(n, sides, modifier) + modified_roll = roll.modify(new_modifier) + assert modified_roll == Roll(n, sides, new_modifier) assert modified_roll is not roll and modified_roll != roll -@pytest.mark.parametrize("n", list(range(1, 5))) -@pytest.mark.parametrize("sides", list(range(2, 100))) +@given(st.integers(min_value=1, max_value=50_000), st.integers(min_value=2)) def test_throw(n: int, sides: int): roll = Roll(n, sides) throw = roll.throw() diff --git a/tests/throw_test.py b/tests/throw_test.py index a94d7de..4de5f5f 100644 --- a/tests/throw_test.py +++ b/tests/throw_test.py @@ -1,16 +1,21 @@ +from hypothesis import given +from hypothesis import strategies as st + from roll.throw import Throw -def test_throw(): - throw = Throw([1, 2, 3], sides=4) - assert throw.total == 6 +@given(st.lists(st.integers(min_value=1), min_size=1), st.integers(min_value=2)) +def test_throw(results: list[int], sides: int): + throw = Throw(results, sides=sides) + assert throw.total == sum(results) assert not throw.is_critical_hit assert not throw.is_critical_miss -def test_throw_with_modifier(): - throw = Throw([1, 2, 3], sides=4, modifier=5) - assert throw.total == 11 +@given(st.lists(st.integers(min_value=1), min_size=1), st.integers(min_value=2), st.integers()) +def test_throw_with_modifier(results: list[int], sides: int, modifier: int): + throw = Throw(results, sides=sides, modifier=modifier) + assert throw.total == (sum(results) + modifier) assert not throw.is_critical_hit assert not throw.is_critical_miss