]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/testRock.cc
Fix Controller.cc TheRoot assertion during shutdown (#1707)
[thirdparty/squid.git] / src / tests / testRock.cc
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 "compat/cppunit.h"
11 #include "ConfigParser.h"
12 #include "DiskIO/DiskIOModule.h"
13 #include "fde.h"
14 #include "fs/rock/RockSwapDir.h"
15 #include "globals.h"
16 #include "HttpHeader.h"
17 #include "HttpReply.h"
18 #include "MemObject.h"
19 #include "RequestFlags.h"
20 #include "SquidConfig.h"
21 #include "Store.h"
22 #include "store/Disk.h"
23 #include "store/Disks.h"
24 #include "StoreFileSystem.h"
25 #include "StoreSearch.h"
26 #include "testStoreSupport.h"
27 #include "unitTestMain.h"
28
29 #include <stdexcept>
30 #if HAVE_SYS_STAT_H
31 #include <sys/stat.h>
32 #endif
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #define TESTDIR "tr"
38
39 /*
40 * test the store framework
41 */
42
43 class TestRock : public CPPUNIT_NS::TestFixture
44 {
45 CPPUNIT_TEST_SUITE(TestRock);
46 CPPUNIT_TEST(testRockCreate);
47 CPPUNIT_TEST(testRockSwapOut);
48 CPPUNIT_TEST_SUITE_END();
49
50 public:
51 TestRock() : rr(nullptr) {}
52 void setUp() override;
53 void tearDown() override;
54
55 typedef RefCount<Rock::SwapDir> SwapDirPointer;
56
57 protected:
58 void commonInit();
59 void storeInit();
60 StoreEntry *createEntry(const int i);
61 StoreEntry *addEntry(const int i);
62 StoreEntry *getEntry(const int i);
63 void testRockCreate();
64 void testRockSwapOut();
65
66 private:
67 SwapDirPointer store;
68 Rock::SwapDirRr *rr;
69 };
70 CPPUNIT_TEST_SUITE_REGISTRATION(TestRock);
71
72 static void
73 addSwapDir(TestRock::SwapDirPointer aStore)
74 {
75 allocate_new_swapdir(Config.cacheSwap);
76 Config.cacheSwap.swapDirs[Config.cacheSwap.n_configured] = aStore.getRaw();
77 ++Config.cacheSwap.n_configured;
78 }
79
80 void
81 TestRock::setUp()
82 {
83 CPPUNIT_NS::TestFixture::setUp();
84
85 if (0 > system ("rm -rf " TESTDIR))
86 throw std::runtime_error("Failed to clean test work directory");
87
88 store = new Rock::SwapDir();
89
90 addSwapDir(store);
91
92 char *path=xstrdup(TESTDIR);
93
94 char *config_line=xstrdup("10 max-size=16384");
95
96 ConfigParser::SetCfgLine(config_line);
97
98 store->parse(0, path);
99 store_maxobjsize = 1024*1024*2;
100
101 safe_free(path);
102
103 safe_free(config_line);
104
105 /* ok, ready to create */
106 store->create();
107
108 rr = new Rock::SwapDirRr;
109 rr->useConfig();
110 }
111
112 void
113 TestRock::tearDown()
114 {
115 CPPUNIT_NS::TestFixture::tearDown();
116
117 store = nullptr;
118
119 free_cachedir(&Config.cacheSwap);
120
121 rr->finishShutdown(); // deletes rr
122 rr = nullptr;
123
124 // TODO: do this once, or each time.
125 // safe_free(Config.replPolicy->type);
126 // delete Config.replPolicy;
127
128 if (0 > system ("rm -rf " TESTDIR))
129 throw std::runtime_error("Failed to clean test work directory");
130 }
131
132 void
133 TestRock::storeInit()
134 {
135 /* ok, ready to use */
136 Store::Root().init();
137
138 /* rebuild is a scheduled event */
139 StockEventLoop loop;
140
141 /* our swapdir must be scheduled to rebuild */
142 CPPUNIT_ASSERT_EQUAL(2, StoreController::store_dirs_rebuilding);
143
144 loop.run();
145
146 /* cannot use loop.run(); as the loop will never idle: the store-dir
147 * clean() scheduled event prevents it
148 */
149
150 /* nothing left to rebuild */
151 CPPUNIT_ASSERT_EQUAL(0, StoreController::store_dirs_rebuilding);
152 }
153
154 static const char *
155 storeId(const int i)
156 {
157 static char buf[64];
158 snprintf(buf, sizeof(buf), "dummy url %i", i);
159 buf[sizeof(buf) - 1] = '\0';
160 return buf;
161 }
162
163 StoreEntry *
164 TestRock::createEntry(const int i)
165 {
166 RequestFlags flags;
167 flags.cachable.support();
168 StoreEntry *const pe =
169 storeCreateEntry(storeId(i), "dummy log url", flags, Http::METHOD_GET);
170 auto &rep = pe->mem().adjustableBaseReply();
171 rep.setHeaders(Http::scOkay, "dummy test object", "x-squid-internal/test", 0, -1, squid_curtime + 100000);
172
173 pe->setPublicKey();
174
175 return pe;
176 }
177
178 StoreEntry *
179 TestRock::addEntry(const int i)
180 {
181 StoreEntry *const pe = createEntry(i);
182
183 pe->buffer();
184 pe->mem().freshestReply().packHeadersUsingSlowPacker(*pe);
185 pe->flush();
186 pe->timestampsSet();
187 pe->complete();
188 pe->swapOut();
189
190 return pe;
191 }
192
193 StoreEntry *
194 TestRock::getEntry(const int i)
195 {
196 return storeGetPublic(storeId(i), Http::METHOD_GET);
197 }
198
199 void
200 TestRock::testRockCreate()
201 {
202 struct stat sb;
203
204 CPPUNIT_ASSERT_EQUAL(0, ::stat(TESTDIR, &sb));
205
206 /* TODO: check the size */
207
208 /* TODO: test rebuild */
209 }
210
211 void
212 TestRock::testRockSwapOut()
213 {
214 storeInit();
215
216 // add few entries to prime the database
217 for (int i = 0; i < 5; ++i) {
218 CPPUNIT_ASSERT_EQUAL((uint64_t)i, store->currentCount());
219
220 StoreEntry *const pe = addEntry(i);
221
222 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING, pe->swap_status);
223 CPPUNIT_ASSERT_EQUAL(0, pe->swap_dirn);
224 CPPUNIT_ASSERT(pe->swap_filen >= 0);
225
226 // Rock::IoState::finishedWriting() schedules an AsyncCall
227 // storeSwapOutFileClosed(). Let it fire.
228 StockEventLoop loop;
229 loop.run();
230
231 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE, pe->swap_status);
232
233 pe->unlock("TestRock::testRockSwapOut priming");
234 }
235
236 CPPUNIT_ASSERT_EQUAL((uint64_t)5, store->currentCount());
237
238 // try to swap out entry to a used unlocked slot
239 {
240 // without marking the old entry as deleted
241 StoreEntry *const pe = addEntry(3);
242
243 CPPUNIT_ASSERT_EQUAL(SWAPOUT_NONE, pe->swap_status);
244 CPPUNIT_ASSERT_EQUAL(-1, pe->swap_dirn);
245 CPPUNIT_ASSERT_EQUAL(-1, pe->swap_filen);
246 pe->unlock("TestRock::testRockSwapOut e#3");
247
248 // after marking the old entry as deleted
249 StoreEntry *const pe2 = getEntry(4);
250 CPPUNIT_ASSERT(pe2 != nullptr);
251 pe2->release();
252
253 StoreEntry *const pe3 = addEntry(4);
254 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING, pe3->swap_status);
255 CPPUNIT_ASSERT_EQUAL(0, pe3->swap_dirn);
256 CPPUNIT_ASSERT(pe3->swap_filen >= 0);
257
258 StockEventLoop loop;
259 loop.run();
260
261 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE, pe3->swap_status);
262
263 pe->unlock("TestRock::testRockSwapOut e#4");
264 }
265
266 // try to swap out entry to a used locked slot
267 {
268 StoreEntry *const pe = addEntry(5);
269
270 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING, pe->swap_status);
271 CPPUNIT_ASSERT_EQUAL(0, pe->swap_dirn);
272 CPPUNIT_ASSERT(pe->swap_filen >= 0);
273
274 // the slot is locked here because the async calls have not run yet
275 StoreEntry *const pe2 = addEntry(5);
276 CPPUNIT_ASSERT_EQUAL(SWAPOUT_NONE, pe2->swap_status);
277 CPPUNIT_ASSERT_EQUAL(MemObject::SwapOut::swImpossible, pe2->mem_obj->swapout.decision);
278 CPPUNIT_ASSERT_EQUAL(-1, pe2->swap_dirn);
279 CPPUNIT_ASSERT_EQUAL(-1, pe2->swap_filen);
280
281 StockEventLoop loop;
282 loop.run();
283
284 pe->unlock("TestRock::testRockSwapOut e#5.1");
285 pe2->unlock("TestRock::testRockSwapOut e#5.2");
286
287 // pe2 has the same public key as pe so it marks old pe for release
288 // here, we add another entry #5 into the now-available slot
289 StoreEntry *const pe3 = addEntry(5);
290 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING, pe3->swap_status);
291 CPPUNIT_ASSERT_EQUAL(0, pe3->swap_dirn);
292 CPPUNIT_ASSERT(pe3->swap_filen >= 0);
293 loop.run();
294 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE, pe3->swap_status);
295 pe3->unlock("TestRock::testRockSwapOut e#5.3");
296 }
297
298 CPPUNIT_ASSERT_EQUAL((uint64_t)6, store->currentCount());
299
300 // try to get and release all entries
301 for (int i = 0; i < 6; ++i) {
302 StoreEntry *const pe = getEntry(i);
303 CPPUNIT_ASSERT(pe != nullptr);
304
305 pe->release(); // destroys pe
306
307 StoreEntry *const pe2 = getEntry(i);
308 CPPUNIT_ASSERT_EQUAL(static_cast<StoreEntry *>(nullptr), pe2);
309 }
310 }
311
312 /// customizes our test setup
313 class MyTestProgram: public TestProgram
314 {
315 public:
316 /* TestProgram API */
317 void startup() override;
318 };
319
320 void
321 MyTestProgram::startup()
322 {
323 Config.memShared.defaultTo(false);
324 Config.shmLocking.defaultTo(false);
325
326 // use current directory for shared segments (on path-based OSes)
327 static char cwd[MAXPATHLEN];
328 Ipc::Mem::Segment::BasePath = getcwd(cwd, MAXPATHLEN);
329 if (!Ipc::Mem::Segment::BasePath)
330 Ipc::Mem::Segment::BasePath = ".";
331
332 Config.Store.avgObjectSize = 1024;
333 Config.Store.objectsPerBucket = 20;
334 Config.Store.maxObjectSize = 2048;
335
336 Config.store_dir_select_algorithm = xstrdup("round-robin");
337
338 Config.replPolicy = new RemovalPolicySettings;
339 Config.replPolicy->type = xstrdup("lru");
340 Config.replPolicy->args = nullptr;
341
342 /* garh garh */
343 extern REMOVALPOLICYCREATE createRemovalPolicy_lru;
344 storeReplAdd("lru", createRemovalPolicy_lru);
345
346 visible_appname_string = xstrdup(APP_FULLNAME);
347
348 Mem::Init();
349 fde::Init();
350 comm_init();
351 httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
352
353 mem_policy = createRemovalPolicy(Config.replPolicy);
354 }
355
356 int
357 main(int argc, char *argv[])
358 {
359 return MyTestProgram().run(argc, argv);
360 }
361