Skip to content

Commit 2639d40

Browse files
committed
Add tests for admission policies
1 parent ef33885 commit 2639d40

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed

tests/test_admission.py

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
"""
2+
Test cases for cache admission in libCacheSim Python bindings.
3+
4+
This module tests the PluginAdmissioner and existing admission policies
5+
"""
6+
7+
import pytest
8+
from libcachesim import (
9+
SizeAdmissioner,
10+
ProbAdmissioner,
11+
SizeProbabilisticAdmissioner,
12+
BloomFilterAdmissioner,
13+
PluginAdmissioner,
14+
LRU
15+
)
16+
from libcachesim.libcachesim_python import (
17+
Request,
18+
ReqOp
19+
)
20+
21+
22+
class TestSizeAdmissioner:
23+
"""test existing size admissioner policy"""
24+
25+
def test_default_configuration(self):
26+
int64_max = (2 ** 63) - 1
27+
cache = LRU(
28+
# Cache size must be large enough to fit the object
29+
cache_size=int64_max,
30+
admissioner=SizeAdmissioner()
31+
)
32+
33+
# We should be able to admit an item which lies underneath
34+
# the default threshold of INT64_MAX
35+
req = Request()
36+
req.obj_id = 0
37+
req.obj_size = int64_max - 1
38+
req.op = ReqOp.OP_GET
39+
assert cache.can_insert(req)
40+
41+
# Anything equating to the default threshold should fail
42+
req = Request()
43+
req.obj_id = 0
44+
req.obj_size = int64_max
45+
req.op = ReqOp.OP_GET
46+
assert not cache.can_insert(req)
47+
48+
@pytest.mark.parametrize("thresh", [0, 100, 250, 500, 750, 1000])
49+
def test_custom_configuration(self, thresh):
50+
cache = LRU(
51+
cache_size=1000,
52+
admissioner=SizeAdmissioner(size_threshold=thresh)
53+
)
54+
admits = 0
55+
56+
# Create 1000 requests of unique sizes and test to see and
57+
# use `cache_can_insert_default` to run the admissioner
58+
for i in range(1000):
59+
req = Request()
60+
req.obj_id = i
61+
req.obj_size = i
62+
req.op = ReqOp.OP_GET
63+
if cache.can_insert(req):
64+
admits += 1
65+
66+
# All items admitted should lie within the size threshold
67+
assert admits == thresh
68+
69+
70+
class TestProbAdmissioner:
71+
"""test existing probabilistic admissioner policy"""
72+
73+
# Note: The `ProbAdmissioner` does not accept zero as a valid
74+
# probability, hence we do not test a `admit_nothing` scenario
75+
def test_admit_all(self):
76+
cache = LRU(
77+
# Cache size must be large enough to fit the object
78+
cache_size=1000,
79+
admissioner=ProbAdmissioner(prob=1.0)
80+
)
81+
82+
# Probability threshold is one, so everything should be
83+
# admitted unconditionally
84+
for obj_id in range(1000):
85+
req = Request()
86+
req.obj_id = obj_id
87+
req.obj_size = 1
88+
req.op = ReqOp.OP_GET
89+
assert cache.can_insert(req)
90+
91+
@pytest.mark.parametrize("prob", [0.0001, 0.1, 0.5, 0.9, 0.9999])
92+
def test_admit_amount(self, prob):
93+
cache = LRU(
94+
# Cache size must be large enough to fit the object
95+
cache_size=1000,
96+
admissioner=ProbAdmissioner(prob=prob)
97+
)
98+
total_requests, admits = 1000, 0
99+
100+
# Probability threshold is one, so everything should be
101+
# admitted unconditionally
102+
for obj_id in range(total_requests):
103+
req = Request()
104+
req.obj_id = obj_id
105+
req.obj_size = 1
106+
req.op = ReqOp.OP_GET
107+
if cache.can_insert(req):
108+
admits += 1
109+
110+
# This value is not deterministic, hence just perform a
111+
# basic sanity check to make sure it lies between 0 and 1
112+
admit_rate = admits / total_requests
113+
assert 0 <= admit_rate and admit_rate <= 1
114+
115+
116+
class TestSizeProbabilisticAdmissioner:
117+
118+
@pytest.mark.parametrize("exponent", [0.0001, 0.1, 0.5, 0.9, 0.9999])
119+
def test_admit_amount(self, exponent):
120+
cache = LRU(
121+
# Cache size must be large enough to fit the object
122+
cache_size=1000,
123+
admissioner=SizeProbabilisticAdmissioner(exponent=exponent)
124+
)
125+
total_requests, admits = 1000, 0
126+
127+
# Probability threshold is one, so everything should be
128+
# admitted unconditionally
129+
for obj_id in range(total_requests):
130+
req = Request()
131+
req.obj_id = obj_id
132+
req.obj_size = 1
133+
req.op = ReqOp.OP_GET
134+
if cache.can_insert(req):
135+
admits += 1
136+
137+
# This value is not deterministic, hence just perform a
138+
# basic sanity check to make sure it lies between 0 and 1
139+
admit_rate = admits / total_requests
140+
assert 0 <= admit_rate and admit_rate <= 1
141+
142+
143+
class TestBloomFilter:
144+
"""test existing bloomfilter admissioner policy"""
145+
146+
@pytest.mark.parametrize("visits", [0, 1, 2, 3])
147+
def test_multi_pass(self, visits):
148+
cache = LRU(
149+
cache_size=1000,
150+
admissioner=BloomFilterAdmissioner()
151+
)
152+
admits = 0
153+
154+
# Here, we try to "see" each item a certain number of times
155+
# to increment it's "seen_times" count in the bloom filter
156+
# hash table.
157+
for _ in range(visits):
158+
for obj_id in range(1000):
159+
req = Request()
160+
req.obj_id = obj_id
161+
req.obj_size = 1
162+
req.op = ReqOp.OP_GET
163+
if cache.can_insert(req):
164+
cache.insert(req)
165+
166+
# Next, we check to see if the items were admitted to cache
167+
for obj_id in range(1000):
168+
req = Request()
169+
req.obj_id = obj_id
170+
req.obj_size = 1
171+
req.op = ReqOp.OP_GET
172+
if cache.get(req):
173+
admits += 1
174+
175+
# Only if each item is visited more than once should we see
176+
# that it was admitted to the cache
177+
expected = 1000 if visits > 1 else 0
178+
assert admits == expected
179+
180+
181+
# TODO: Tests crash if we do not explicitly delete the cache object
182+
class TestPluginAdmissioner:
183+
"""test PluginAdmissioner using custom simplistic policies"""
184+
185+
def test_admit_all(self):
186+
pa = PluginAdmissioner(
187+
"testAdmissioner",
188+
lambda: None,
189+
# Accept all items
190+
lambda data, req: True,
191+
lambda: None,
192+
lambda data, req: None,
193+
lambda data: None,
194+
)
195+
cache = LRU(cache_size=1000, admissioner=pa)
196+
197+
# Here, we test a basic custom admission policy which
198+
# should simply accept every single request
199+
for size in range(1000):
200+
req = Request()
201+
req.obj_id = 0
202+
req.obj_size = size
203+
req.op = ReqOp.OP_GET
204+
assert cache.can_insert(req)
205+
del cache
206+
207+
def test_admit_nothing(self):
208+
pa = PluginAdmissioner(
209+
"testAdmissioner",
210+
lambda: None,
211+
# Reject all items
212+
lambda data, req: False,
213+
lambda: None,
214+
lambda data, req: None,
215+
lambda data: None,
216+
)
217+
cache = LRU(cache_size=1000, admissioner=pa)
218+
219+
# Here, we test a basic custom admission policy which
220+
# should simply reject every single request
221+
for size in range(1000):
222+
req = Request()
223+
req.obj_id = 0
224+
req.obj_size = size
225+
req.op = ReqOp.OP_GET
226+
assert not cache.can_insert(req)
227+
del cache
228+
229+
@pytest.mark.parametrize("thresh", [0, 100, 250, 500, 750, 1000])
230+
def test_custom_size(self, thresh):
231+
pa = PluginAdmissioner(
232+
"testAdmissioner",
233+
lambda: None,
234+
# Equivalent to the size admissioner
235+
lambda data, req: req.obj_size < thresh,
236+
lambda: None,
237+
lambda data, req: None,
238+
lambda data: None,
239+
)
240+
cache, admits = LRU(cache_size=1000, admissioner=pa), 0
241+
242+
# Here, we test a custom implementation of the existing
243+
# size policy which admits everything under a static size
244+
# threshold
245+
for size in range(1000):
246+
req = Request()
247+
req.obj_id = 0
248+
req.obj_size = size
249+
req.op = ReqOp.OP_GET
250+
if cache.can_insert(req):
251+
admits += 1
252+
253+
# Same correctness criteria as `TestSizeAdmissioner`
254+
assert admits == thresh
255+
del cache

0 commit comments

Comments
 (0)