testClpMap.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9#include "squid.h"
10#include "base/ClpMap.h"
11#include "compat/cppunit.h"
12#include "SquidConfig.h"
13#include "unitTestMain.h"
14
15#include <ctime>
16
17class TestClpMap: public CPPUNIT_NS::TestFixture
18{
34
35protected:
37
38 void testMemoryCounter();
39 void testConstructor();
40 void testEntryCounter();
41 void testPutGetDelete();
42 void testMisses();
43 void testMemoryLimit();
44 void testTtlExpiration();
46 void testZeroTtl();
47 void testNegativeTtl();
48 void testPurgeIsLru();
51
55 void addSequenceOfEntriesToMap(Map &, size_t count, Map::mapped_type startWith, Map::Ttl);
56
58 void fillMapWithEntries(Map &);
59
63
67};
68
70
72
73void
74TestClpMap::addSequenceOfEntriesToMap(Map &m, size_t count, const Map::mapped_type startWith, const Map::Ttl ttl)
75{
76 for (auto j = startWith; count; ++j, --count)
77 CPPUNIT_ASSERT(m.add(std::to_string(j), j, ttl));
78}
79
80void
82{
84}
85
86void
88{
89 const auto key = std::to_string(value);
90 CPPUNIT_ASSERT(m.add(key, value));
91 CPPUNIT_ASSERT(m.get(key));
92 CPPUNIT_ASSERT_EQUAL(value, *m.get(key));
93}
94
95void
97{
98 const auto key = std::to_string(value);
99 CPPUNIT_ASSERT(m.add(key, value, ttl));
100 CPPUNIT_ASSERT(m.get(key));
101 CPPUNIT_ASSERT_EQUAL(value, *m.get(key));
102}
103
104void
106{
107 Map m(1024);
108 addSequenceOfEntriesToMap(m, 10, 0, 10);
109 CPPUNIT_ASSERT(m.get("1")); // we get something
110 CPPUNIT_ASSERT_EQUAL(1, *(m.get("1"))); // we get what we put in
111 CPPUNIT_ASSERT(m.get("9"));
112 CPPUNIT_ASSERT_EQUAL(9, *(m.get("9")));
113 m.add("1", 99);
114 CPPUNIT_ASSERT(m.get("1"));
115 CPPUNIT_ASSERT_EQUAL(99, *(m.get("1")));
116 m.del("1");
117 CPPUNIT_ASSERT(!m.get("1")); // entry has been cleared
118}
119
120void
122{
123 Map m(1024);
125 const auto entriesBefore = m.entries();
126 CPPUNIT_ASSERT(!m.get("not-there"));
127 m.del("not-there");
128 CPPUNIT_ASSERT_EQUAL(entriesBefore, m.entries());
129}
130
131void
133{
134 {
135 Map m(10*1024*1024, 10);
136 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), m.entries());
137 addSequenceOfEntriesToMap(m, 10, 10, 10);
138 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(10), m.entries());
139 m.add("new-key", 0);
140 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(11), m.entries());
141 }
142 {
143 Map m(1024, 5);
144 addSequenceOfEntriesToMap(m, 1000, 0, 10);
145 CPPUNIT_ASSERT(m.entries() < 1000);
146 }
147}
148
149void
151{
152 CPPUNIT_ASSERT_EQUAL(sizeof(int), static_cast<size_t>(DefaultMemoryUsage(int{})));
153 CPPUNIT_ASSERT_EQUAL(sizeof(int32_t), static_cast<size_t>(DefaultMemoryUsage(int32_t{})));
154 CPPUNIT_ASSERT_EQUAL(sizeof(int64_t), static_cast<size_t>(DefaultMemoryUsage(int64_t{})));
155 CPPUNIT_ASSERT_EQUAL(sizeof(char), static_cast<size_t>(DefaultMemoryUsage(char{})));
156 using Str = char[10];
157 CPPUNIT_ASSERT_EQUAL(sizeof(Str), static_cast<size_t>(DefaultMemoryUsage(Str{})));
158}
159
160void
162{
163 const Map nilA(0);
164 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilA.memLimit());
165 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilA.freeMem());
166 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilA.memoryUsed());
167 CPPUNIT_ASSERT_EQUAL(size_t(0), nilA.entries());
168
169 const Map nilB(0, 0);
170 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilB.memLimit());
171 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilB.freeMem());
172 CPPUNIT_ASSERT_EQUAL(uint64_t(0), nilB.memoryUsed());
173 CPPUNIT_ASSERT_EQUAL(size_t(0), nilB.entries());
174
175 const Map emptyC(1);
176 CPPUNIT_ASSERT_EQUAL(uint64_t(1), emptyC.memLimit());
177 CPPUNIT_ASSERT_EQUAL(uint64_t(1), emptyC.freeMem());
178 CPPUNIT_ASSERT_EQUAL(uint64_t(0), emptyC.memoryUsed());
179 CPPUNIT_ASSERT_EQUAL(size_t(0), emptyC.entries());
180
181 const Map emptyD(1024);
182 CPPUNIT_ASSERT_EQUAL(uint64_t(1024), emptyD.memLimit());
183 CPPUNIT_ASSERT_EQUAL(uint64_t(1024), emptyD.freeMem());
184 CPPUNIT_ASSERT_EQUAL(uint64_t(0), emptyD.memoryUsed());
185 CPPUNIT_ASSERT_EQUAL(size_t(0), emptyD.entries());
186}
187
188void
190{
191 const size_t initialCapacity = 1024; // bytes
192 Map m(initialCapacity);
194 const auto entriesAtInitialCapacity = m.entries();
195
196 // check that all entries are removed if we prohibit storage of any entries
197 m.setMemLimit(0);
198 CPPUNIT_ASSERT_EQUAL(size_t(0), m.entries());
199
200 // test whether the map can grow after the all-at-once purging above
201 const auto increasedCapacity = initialCapacity * 2;
202 m.setMemLimit(increasedCapacity);
204 CPPUNIT_ASSERT(m.entries() > entriesAtInitialCapacity);
205
206 // test that memory usage and entry count decrease when the map is shrinking
207 // but prevent endless loops no matter how broken ClpMap implementation is
208 auto iterationsLeft = m.entries();
209 CPPUNIT_ASSERT(0 < iterationsLeft && iterationsLeft <= increasedCapacity);
210 while (m.entries()) {
211 // TODO: Check that we can still add a (smaller) entry here.
212
213 const auto memoryUsedBefore = m.memoryUsed();
214 const auto entriesBefore = m.entries();
215
216 const auto newMemoryLimit = memoryUsedBefore/2; // may become zero
217 m.setMemLimit(newMemoryLimit);
218
219 CPPUNIT_ASSERT(m.memoryUsed() <= newMemoryLimit);
220 CPPUNIT_ASSERT(m.entries() < entriesBefore);
221
222 // the assertion below may fail if ClpMap::entries() returns bogus numbers
223 CPPUNIT_ASSERT(iterationsLeft > 0);
224 --iterationsLeft;
225 }
226
227 // test whether the map can grow after all that gradual purging above
228 m.setMemLimit(increasedCapacity);
230 CPPUNIT_ASSERT(m.entries() > entriesAtInitialCapacity);
231}
232
233void
235{
236 {
237 Map m(2048);
238 addOneEntry(m, 0, 100);
239 squid_curtime += 20;
240 CPPUNIT_ASSERT(m.get("0")); // still fresh
241 squid_curtime += 100;
242 CPPUNIT_ASSERT(!m.get("0")); // has expired
243 }
244
245 {
246 // same test, but using a map-specific TTL instead of entry-specific one
247 Map m(2048, 100);
248 addOneEntry(m, 0);
249 squid_curtime += 20;
250 CPPUNIT_ASSERT(m.get("0")); // still fresh
251 squid_curtime += 100;
252 CPPUNIT_ASSERT(!m.get("0")); // has expired
253 }
254
255 {
256 // same test, but using both map-specific and entry-specific TTLs
257 Map m(2048, 1);
258 addOneEntry(m, 0, 100);
259 squid_curtime += 20;
260 CPPUNIT_ASSERT(m.get("0")); // still fresh
261 squid_curtime += 100;
262 CPPUNIT_ASSERT(!m.get("0")); // has expired
263 }
264}
265
266void
268{
269 Map m(2048);
270 addOneEntry(m, 0, 100);
271 addOneEntry(m, 0, 10); // same (key, value) entry but with shorter TTL
272 squid_curtime += 20;
273 CPPUNIT_ASSERT(!m.get("0")); // has expired
274
275 // now the same sequence but with a time change between additions
276 addOneEntry(m, 0, 100);
277 squid_curtime += 200;
278 addOneEntry(m, 0, 10);
279 CPPUNIT_ASSERT(m.get("0")); // still fresh due to new TTL
280 squid_curtime += 20;
281 CPPUNIT_ASSERT(!m.get("0")); // has expired
282}
283
284void
286{
287 {
288 Map m(2048);
289 addOneEntry(m, 0, 0);
290 squid_curtime += 1;
291 CPPUNIT_ASSERT(!m.get("0")); // expired, we get nothing
292 }
293
294 {
295 // same test, but using a map-specific TTL instead of entry-specific one
296 Map m(2048, 0);
297 addOneEntry(m, 0);
298 squid_curtime += 1;
299 CPPUNIT_ASSERT(!m.get("0")); // expired, we get nothing
300 }
301
302 {
303 // same test, but using both map-specific and entry-specific TTLs
304 Map m(2048, 10);
305 addOneEntry(m, 0, 0);
306 squid_curtime += 1;
307 CPPUNIT_ASSERT(!m.get("0")); // expired, we get nothing
308 }
309}
310
311void
313{
314 Map m(2048);
315
316 // we start with an ordinary-TTL entry to check that it will be purged below
317 addOneEntry(m, 0, 10);
318
319 // check that negative-TTL entries are rejected
320 CPPUNIT_ASSERT(!m.add("0", 0, -1));
321
322 // check that an attempt to add a negative-TTL entry purges the previously
323 // added ordinary-TTL entry
324 CPPUNIT_ASSERT(!m.get("0"));
325
326 // check that the same entry can be re-added with a non-negative TTL
327 addOneEntry(m, 0);
328}
329
330void
332{
333 Map m(2048);
334 for (int j = 0; j < 10; ++j)
335 addOneEntry(m, j);
336 // now overflow the map while keeping "0" the Least Recently Used
337 for (int j = 100; j < 1000; ++j) {
338 addOneEntry(m, j);
339 CPPUNIT_ASSERT(m.get("0"));
340 }
341 // these should have been aged out
342 CPPUNIT_ASSERT(!m.get("1"));
343 CPPUNIT_ASSERT(!m.get("2"));
344 CPPUNIT_ASSERT(!m.get("3"));
345 CPPUNIT_ASSERT(!m.get("4"));
346
348 CPPUNIT_ASSERT(!m.get("0")); // removable when not recently used
349}
350
351void
353{
354 Map m(2048);
355 const size_t expectedEntryCount = 10;
356 addSequenceOfEntriesToMap(m, expectedEntryCount, 0, 50);
357 size_t iterations = 0;
358 for (auto i = m.cbegin(); i != m.cend(); ++i) {
359 ++iterations;
360 const auto expectedValue = static_cast<Map::mapped_type>(expectedEntryCount - iterations);
361 CPPUNIT_ASSERT_EQUAL(expectedValue, i->value);
362 }
363 CPPUNIT_ASSERT_EQUAL(expectedEntryCount, iterations);
364}
365
366void
368{
369 Map m(2048);
370 const size_t expectedEntryCount = 10;
371 addSequenceOfEntriesToMap(m, expectedEntryCount, 0, 50);
372 size_t iterations = 0;
373 for (const auto &entry: m) {
374 ++iterations;
375 const auto expectedValue = static_cast<Map::mapped_type>(expectedEntryCount - iterations);
376 CPPUNIT_ASSERT_EQUAL(expectedValue, entry.value);
377 }
378 CPPUNIT_ASSERT_EQUAL(expectedEntryCount, iterations);
379}
380
382class MyTestProgram: public TestProgram
383{
384public:
385 /* TestProgram API */
386 void startup() override { squid_curtime = time(nullptr); }
387};
388
389int
390main(int argc, char *argv[])
391{
392 return MyTestProgram().run(argc, argv);
393}
394
uint64_t DefaultMemoryUsage(const Value &e)
Definition: ClpMap.h:24
time_t squid_curtime
Definition: stub_libtime.cc:20
Definition: ClpMap.h:41
ConstEntriesIterator cbegin() const
Definition: ClpMap.h:114
const Value * get(const Key &)
Definition: ClpMap.h:188
void del(const Key &)
Remove the corresponding entry (if any)
Definition: ClpMap.h:268
uint64_t memLimit() const
The memory capacity for the map.
Definition: ClpMap.h:100
uint64_t freeMem() const
The free space of the map.
Definition: ClpMap.h:103
int Ttl
maximum desired entry caching duration (a.k.a. TTL), in seconds
Definition: ClpMap.h:47
void setMemLimit(uint64_t newLimit)
Reset the memory capacity for this map, purging if needed.
Definition: ClpMap.h:158
size_t entries() const
The number of currently stored entries, including expired ones.
Definition: ClpMap.h:109
bool add(const Key &, const Value &, Ttl)
Definition: ClpMap.h:220
Value mapped_type
Definition: ClpMap.h:44
ConstEntriesIterator cend() const
Definition: ClpMap.h:115
uint64_t memoryUsed() const
The current (approximate) memory usage of the map.
Definition: ClpMap.h:106
customizes our test setup
void startup() override
Definition: testClpMap.cc:386
the representation of the configuration. POD.
Definition: SquidConfig.h:79
void testClassicLoopTraversal()
Definition: testClpMap.cc:352
CPPUNIT_TEST_SUITE(TestClpMap)
void testRangeLoopTraversal()
Definition: testClpMap.cc:367
void testConstructor()
Definition: testClpMap.cc:161
void fillMapWithEntries(Map &)
add (more than) enough entries to make the map full
Definition: testClpMap.cc:81
void testZeroTtl()
Definition: testClpMap.cc:285
void testTtlExpiration()
Definition: testClpMap.cc:234
CPPUNIT_TEST(testPurgeIsLru)
CPPUNIT_TEST(testClassicLoopTraversal)
void testPutGetDelete()
Definition: testClpMap.cc:105
void testPurgeIsLru()
Definition: testClpMap.cc:331
void testMisses()
Definition: testClpMap.cc:121
void addOneEntry(Map &, Map::mapped_type)
Definition: testClpMap.cc:87
void testReplaceEntryWithShorterTtl()
Definition: testClpMap.cc:267
void addSequenceOfEntriesToMap(Map &, size_t count, Map::mapped_type startWith, Map::Ttl)
Definition: testClpMap.cc:74
CPPUNIT_TEST(testNegativeTtl)
CPPUNIT_TEST_SUITE_END()
void testNegativeTtl()
Definition: testClpMap.cc:312
CPPUNIT_TEST(testMisses)
CPPUNIT_TEST(testEntryCounter)
CPPUNIT_TEST(testPutGetDelete)
CPPUNIT_TEST(testMemoryLimit)
void testMemoryCounter()
Definition: testClpMap.cc:150
CPPUNIT_TEST(testConstructor)
void testMemoryLimit()
Definition: testClpMap.cc:189
CPPUNIT_TEST(testTtlExpiration)
CPPUNIT_TEST(testMemoryCounter)
void testEntryCounter()
Definition: testClpMap.cc:132
CPPUNIT_TEST(testReplaceEntryWithShorterTtl)
CPPUNIT_TEST(testZeroTtl)
CPPUNIT_TEST(testRangeLoopTraversal)
implements test program's main() function while enabling customization
Definition: unitTestMain.h:26
int run(int argc, char *argv[])
Definition: unitTestMain.h:44
int main(int argc, char *argv[])
Definition: testClpMap.cc:390
class SquidConfig Config
Definition: testClpMap.cc:71
CPPUNIT_TEST_SUITE_REGISTRATION(TestClpMap)

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors