Files

116 lines
3.6 KiB
Python

"""Tests for the RNG protocol, SeededRng, and ScriptedRng."""
import pytest
from pf1e_simulator.rng import RngExhaustedError, ScriptedRng, SeededRng
class TestSeededRngRollDie:
"""Given: a SeededRng with a fixed seed
When: roll_die is called
Then: returns values in 1..=sides deterministically."""
def test_roll_die_returns_in_range(self) -> None:
rng = SeededRng(42)
for _ in range(100):
result = rng.roll_die(6)
assert 1 <= result <= 6
def test_roll_die_d20_in_range(self) -> None:
rng = SeededRng(42)
for _ in range(100):
result = rng.d20()
assert 1 <= result <= 20
def test_d20_is_roll_die_20(self) -> None:
rng1 = SeededRng(123)
rng2 = SeededRng(123)
assert rng1.d20() == rng2.roll_die(20)
class TestSeededRngChoiceIndex:
"""Given: a SeededRng
When: choice_index(n) is called
Then: returns values in 0..n-1."""
def test_choice_index_in_range(self) -> None:
rng = SeededRng(42)
for _ in range(100):
result = rng.choice_index(5)
assert 0 <= result <= 4
class TestSeededRngReproducibility:
"""Given: two SeededRng instances with the same seed
When: each produces 1000 rolls
Then: the streams are identical."""
def test_identical_d20_streams(self) -> None:
rng1 = SeededRng(42)
rng2 = SeededRng(42)
rolls1 = [rng1.d20() for _ in range(1000)]
rolls2 = [rng2.d20() for _ in range(1000)]
assert rolls1 == rolls2
def test_identical_choice_index_streams(self) -> None:
rng1 = SeededRng(42)
rng2 = SeededRng(42)
idx1 = [rng1.choice_index(8) for _ in range(1000)]
idx2 = [rng2.choice_index(8) for _ in range(1000)]
assert idx1 == idx2
def test_different_seeds_produce_different_streams(self) -> None:
rng1 = SeededRng(1)
rng2 = SeededRng(2)
rolls1 = [rng1.d20() for _ in range(100)]
rolls2 = [rng2.d20() for _ in range(100)]
assert rolls1 != rolls2
class TestScriptedRng:
"""Given: a ScriptedRng with a queue of ints
When: roll_die / choice_index is called
Then: returns queued values in order."""
def test_roll_die_returns_queued_values(self) -> None:
rng = ScriptedRng([3, 1, 4, 1, 5])
assert rng.roll_die(6) == 3
assert rng.roll_die(6) == 1
assert rng.roll_die(6) == 4
def test_d20_returns_queued_values(self) -> None:
rng = ScriptedRng([20, 1, 13])
assert rng.d20() == 20
assert rng.d20() == 1
assert rng.d20() == 13
def test_choice_index_returns_queued_values(self) -> None:
rng = ScriptedRng([0, 2, 1])
assert rng.choice_index(5) == 0
assert rng.choice_index(5) == 2
assert rng.choice_index(5) == 1
def test_raises_on_exhaustion_roll_die(self) -> None:
rng = ScriptedRng([5])
rng.roll_die(6)
with pytest.raises(RngExhaustedError):
rng.roll_die(6)
def test_raises_on_exhaustion_d20(self) -> None:
rng = ScriptedRng([10])
rng.d20()
with pytest.raises(RngExhaustedError):
rng.d20()
def test_raises_on_exhaustion_choice_index(self) -> None:
rng = ScriptedRng([0])
rng.choice_index(3)
with pytest.raises(RngExhaustedError):
rng.choice_index(3)
def test_exhaustion_error_carries_message(self) -> None:
rng = ScriptedRng([1])
rng.roll_die(6)
with pytest.raises(RngExhaustedError, match="exhausted"):
rng.roll_die(6)