85 lines
2.1 KiB
Python
85 lines
2.1 KiB
Python
import random
|
|
from collections import OrderedDict
|
|
|
|
from lru_cache import LRUCache
|
|
|
|
|
|
def test_basic_get_put():
|
|
c = LRUCache(2)
|
|
c.put(1, 1)
|
|
c.put(2, 2)
|
|
assert c.get(1) == 1
|
|
c.put(3, 3) # evicts 2
|
|
assert c.get(2) == -1
|
|
assert c.get(1) == 1
|
|
assert c.get(3) == 3
|
|
|
|
|
|
def test_update_promotes_recency():
|
|
# subtle bug: updating existing key must move to MRU
|
|
c = LRUCache(2)
|
|
c.put(1, 1)
|
|
c.put(2, 2)
|
|
c.put(1, 10) # update -> 1 becomes MRU, 2 LRU
|
|
c.put(3, 3) # should evict 2, not 1
|
|
assert c.get(2) == -1
|
|
assert c.get(1) == 10
|
|
assert c.get(3) == 3
|
|
|
|
|
|
def test_eviction_removes_from_map():
|
|
c = LRUCache(1)
|
|
c.put(1, 1)
|
|
c.put(2, 2)
|
|
assert c.size() == 1
|
|
assert c.get(1) == -1
|
|
|
|
|
|
def test_capacity_validation():
|
|
for bad in (0, -1, True, "2", 2.0):
|
|
try:
|
|
LRUCache(bad)
|
|
raised = False
|
|
except (ValueError, TypeError):
|
|
raised = True
|
|
assert raised, bad
|
|
|
|
|
|
def test_order_introspection():
|
|
c = LRUCache(3)
|
|
for k in (1, 2, 3):
|
|
c.put(k, k)
|
|
assert c.order_mru_to_lru() == [3, 2, 1]
|
|
c.get(1)
|
|
assert c.order_mru_to_lru() == [1, 3, 2]
|
|
|
|
|
|
def test_randomized_against_ordereddict():
|
|
random.seed(42)
|
|
cap = 5
|
|
c = LRUCache(cap)
|
|
ref = OrderedDict()
|
|
ops = 2000
|
|
for _ in range(ops):
|
|
k = random.randrange(0, 12)
|
|
if random.random() < 0.5:
|
|
v = random.randrange(0, 100)
|
|
c.put(k, v)
|
|
ref[k] = v
|
|
# move to end (MRU) in reference
|
|
ref.move_to_end(k)
|
|
if len(ref) > cap:
|
|
ref.popitem(last=False)
|
|
else:
|
|
got = c.get(k)
|
|
if k in ref:
|
|
ref.move_to_end(k)
|
|
assert got == ref[k], (k, got, ref[k])
|
|
else:
|
|
assert got == -1
|
|
# size invariant
|
|
assert c.size() == len(ref)
|
|
# order invariant
|
|
expected_order = list(reversed(list(ref.keys()))) # MRU..LRU
|
|
assert c.order_mru_to_lru() == expected_order, (c.order_mru_to_lru(), expected_order)
|