File size: 2,104 Bytes
1c66cf8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""Regression test for feature_cache content-based keying.

Ensures cache doesn't collide when CPython reuses id() after GC.
"""
from __future__ import annotations

from werewolftown.features.card_value import card_cluster_gain


def test_cache_by_content_not_id(make_state):
    """Cache must key on state content, not id(). Different states must get different results."""
    state_a = make_state(
        plots=frozenset({4, 5, 6}),
        built_on={4: "Happy", 5: "Happy"},
        shops=(),
    )
    state_b = make_state(
        plots=frozenset({4, 5, 7}),
        built_on={4: "Happy", 5: "Happy"},
        shops=("Happy",),
    )

    gain_a = card_cluster_gain(state_a, "Happy")
    gain_b = card_cluster_gain(state_b, "Happy")

    # state_a: empty 6 adjacent to Happy 2-cluster, 0 cards → gain=20000
    # state_b: empty 7 not adjacent, 1 card already → gain=0
    assert gain_a != gain_b, "Cache polluted: different states returned same result"
    assert gain_a == 20000
    assert gain_b == 0


def test_cache_same_state_reuses(make_state):
    """Same state instance called twice must return cached result (no recompute)."""
    state = make_state(
        plots=frozenset({4, 5, 6}),
        built_on={4: "Happy", 5: "Happy"},
        shops=(),
    )

    # Clear cache first
    card_cluster_gain._cache.clear()

    gain1 = card_cluster_gain(state, "Happy")
    assert len(card_cluster_gain._cache) == 1

    gain2 = card_cluster_gain(state, "Happy")
    assert gain1 == gain2
    assert len(card_cluster_gain._cache) == 1  # No new entry


def test_cache_survives_gc(make_state):
    """Cache must not break when intermediate states are GC'd."""
    import gc

    state_main = make_state(
        plots=frozenset({4, 5, 6}),
        built_on={4: "Happy", 5: "Happy"},
        shops=(),
    )

    # Create and discard many states to trigger id reuse
    for i in range(100):
        _ = make_state(plots=frozenset({i}), round=i)
    gc.collect()

    # Original state should still return correct result
    gain = card_cluster_gain(state_main, "Happy")
    assert gain == 20000