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 
17 class TestClpMap: public CPPUNIT_NS::TestFixture
18 {
34 
35 protected:
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 
73 void
74 TestClpMap::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 
80 void
82 {
83  addSequenceOfEntriesToMap(m, m.memLimit() / sizeof(Map::mapped_type), 0, 10);
84 }
85 
86 void
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 
95 void
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 
104 void
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 
120 void
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 
131 void
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 
149 void
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 
160 void
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 
188 void
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 
233 void
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 
266 void
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 
284 void
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 
311 void
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 
330 void
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 
351 void
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 
366 void
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 
382 class MyTestProgram: public TestProgram
383 {
384 public:
385  /* TestProgram API */
386  void startup() override { squid_curtime = time(nullptr); }
387 };
388 
389 int
390 main(int argc, char *argv[])
391 {
392  return MyTestProgram().run(argc, argv);
393 }
394 
ConstEntriesIterator cend() const
Definition: ClpMap.h:115
uint64_t freeMem() const
The free space of the map.
Definition: ClpMap.h:103
implements test program's main() function while enabling customization
Definition: unitTestMain.h:25
CPPUNIT_TEST(testMemoryCounter)
void testMisses()
Definition: testClpMap.cc:121
void testEntryCounter()
Definition: testClpMap.cc:132
void testClassicLoopTraversal()
Definition: testClpMap.cc:352
uint64_t memLimit() const
The memory capacity for the map.
Definition: ClpMap.h:100
void testConstructor()
Definition: testClpMap.cc:161
void testMemoryLimit()
Definition: testClpMap.cc:189
const Value * get(const Key &)
Definition: ClpMap.h:188
int run(int argc, char *argv[])
Definition: unitTestMain.h:44
void addOneEntry(Map &, Map::mapped_type)
Definition: testClpMap.cc:87
ConstEntriesIterator cbegin() const
Definition: ClpMap.h:114
CPPUNIT_TEST_SUITE_END()
CPPUNIT_TEST_SUITE_REGISTRATION(TestClpMap)
Definition: ClpMap.h:40
void testNegativeTtl()
Definition: testClpMap.cc:312
int main(int argc, char *argv[])
Definition: testClpMap.cc:390
int Ttl
maximum desired entry caching duration (a.k.a. TTL), in seconds
Definition: ClpMap.h:47
void testPutGetDelete()
Definition: testClpMap.cc:105
void del(const Key &)
Remove the corresponding entry (if any)
Definition: ClpMap.h:268
void fillMapWithEntries(Map &)
add (more than) enough entries to make the map full
Definition: testClpMap.cc:81
void addSequenceOfEntriesToMap(Map &, size_t count, Map::mapped_type startWith, Map::Ttl)
Definition: testClpMap.cc:74
void testPurgeIsLru()
Definition: testClpMap.cc:331
size_t entries() const
The number of currently stored entries, including expired ones.
Definition: ClpMap.h:109
time_t squid_curtime
Definition: stub_libtime.cc:20
the representation of the configuration. POD.
Definition: SquidConfig.h:78
bool add(const Key &, const Value &, Ttl)
Definition: ClpMap.h:220
CPPUNIT_TEST_SUITE(TestClpMap)
void setMemLimit(uint64_t newLimit)
Reset the memory capacity for this map, purging if needed.
Definition: ClpMap.h:158
class SquidConfig Config
Definition: testClpMap.cc:71
void testReplaceEntryWithShorterTtl()
Definition: testClpMap.cc:267
void testZeroTtl()
Definition: testClpMap.cc:285
Value mapped_type
Definition: ClpMap.h:44
void testMemoryCounter()
Definition: testClpMap.cc:150
void testRangeLoopTraversal()
Definition: testClpMap.cc:367
uint64_t memoryUsed() const
The current (approximate) memory usage of the map.
Definition: ClpMap.h:106
void testTtlExpiration()
Definition: testClpMap.cc:234
customizes our test setup
uint64_t DefaultMemoryUsage(const Value &e)
Definition: ClpMap.h:24
void startup() override
Definition: testClpMap.cc:386

 

Introduction

Documentation

Support

Miscellaneous