2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
10 #include "compat/cppunit.h"
11 #include "ConfigParser.h"
12 #include "DiskIO/DiskIOModule.h"
14 #include "fs/rock/RockSwapDir.h"
16 #include "HttpHeader.h"
17 #include "HttpReply.h"
18 #include "MemObject.h"
19 #include "RequestFlags.h"
20 #include "SquidConfig.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"
40 * test the store framework
43 class TestRock
: public CPPUNIT_NS::TestFixture
45 CPPUNIT_TEST_SUITE(TestRock
);
46 CPPUNIT_TEST(testRockCreate
);
47 CPPUNIT_TEST(testRockSwapOut
);
48 CPPUNIT_TEST_SUITE_END();
51 TestRock() : rr(nullptr) {}
52 void setUp() override
;
53 void tearDown() override
;
55 typedef RefCount
<Rock::SwapDir
> SwapDirPointer
;
60 StoreEntry
*createEntry(const int i
);
61 StoreEntry
*addEntry(const int i
);
62 StoreEntry
*getEntry(const int i
);
63 void testRockCreate();
64 void testRockSwapOut();
70 CPPUNIT_TEST_SUITE_REGISTRATION(TestRock
);
73 addSwapDir(TestRock::SwapDirPointer aStore
)
75 allocate_new_swapdir(Config
.cacheSwap
);
76 Config
.cacheSwap
.swapDirs
[Config
.cacheSwap
.n_configured
] = aStore
.getRaw();
77 ++Config
.cacheSwap
.n_configured
;
83 CPPUNIT_NS::TestFixture::setUp();
85 if (0 > system ("rm -rf " TESTDIR
))
86 throw std::runtime_error("Failed to clean test work directory");
88 store
= new Rock::SwapDir();
92 char *path
=xstrdup(TESTDIR
);
94 char *config_line
=xstrdup("10 max-size=16384");
96 ConfigParser::SetCfgLine(config_line
);
98 store
->parse(0, path
);
99 store_maxobjsize
= 1024*1024*2;
103 safe_free(config_line
);
105 /* ok, ready to create */
108 rr
= new Rock::SwapDirRr
;
115 CPPUNIT_NS::TestFixture::tearDown();
119 free_cachedir(&Config
.cacheSwap
);
121 rr
->finishShutdown(); // deletes rr
124 // TODO: do this once, or each time.
125 // safe_free(Config.replPolicy->type);
126 // delete Config.replPolicy;
128 if (0 > system ("rm -rf " TESTDIR
))
129 throw std::runtime_error("Failed to clean test work directory");
133 TestRock::storeInit()
135 /* ok, ready to use */
136 Store::Root().init();
138 /* rebuild is a scheduled event */
141 /* our swapdir must be scheduled to rebuild */
142 CPPUNIT_ASSERT_EQUAL(2, StoreController::store_dirs_rebuilding
);
146 /* cannot use loop.run(); as the loop will never idle: the store-dir
147 * clean() scheduled event prevents it
150 /* nothing left to rebuild */
151 CPPUNIT_ASSERT_EQUAL(0, StoreController::store_dirs_rebuilding
);
158 snprintf(buf
, sizeof(buf
), "dummy url %i", i
);
159 buf
[sizeof(buf
) - 1] = '\0';
164 TestRock::createEntry(const int i
)
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);
179 TestRock::addEntry(const int i
)
181 StoreEntry
*const pe
= createEntry(i
);
184 pe
->mem().freshestReply().packHeadersUsingSlowPacker(*pe
);
194 TestRock::getEntry(const int i
)
196 return storeGetPublic(storeId(i
), Http::METHOD_GET
);
200 TestRock::testRockCreate()
204 CPPUNIT_ASSERT_EQUAL(0, ::stat(TESTDIR
, &sb
));
206 /* TODO: check the size */
208 /* TODO: test rebuild */
212 TestRock::testRockSwapOut()
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());
220 StoreEntry
*const pe
= addEntry(i
);
222 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe
->swap_status
);
223 CPPUNIT_ASSERT_EQUAL(0, pe
->swap_dirn
);
224 CPPUNIT_ASSERT(pe
->swap_filen
>= 0);
226 // Rock::IoState::finishedWriting() schedules an AsyncCall
227 // storeSwapOutFileClosed(). Let it fire.
231 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe
->swap_status
);
233 pe
->unlock("TestRock::testRockSwapOut priming");
236 CPPUNIT_ASSERT_EQUAL((uint64_t)5, store
->currentCount());
238 // try to swap out entry to a used unlocked slot
240 // without marking the old entry as deleted
241 StoreEntry
*const pe
= addEntry(3);
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");
248 // after marking the old entry as deleted
249 StoreEntry
*const pe2
= getEntry(4);
250 CPPUNIT_ASSERT(pe2
!= nullptr);
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);
261 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe3
->swap_status
);
263 pe
->unlock("TestRock::testRockSwapOut e#4");
266 // try to swap out entry to a used locked slot
268 StoreEntry
*const pe
= addEntry(5);
270 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe
->swap_status
);
271 CPPUNIT_ASSERT_EQUAL(0, pe
->swap_dirn
);
272 CPPUNIT_ASSERT(pe
->swap_filen
>= 0);
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
);
284 pe
->unlock("TestRock::testRockSwapOut e#5.1");
285 pe2
->unlock("TestRock::testRockSwapOut e#5.2");
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);
294 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe3
->swap_status
);
295 pe3
->unlock("TestRock::testRockSwapOut e#5.3");
298 CPPUNIT_ASSERT_EQUAL((uint64_t)6, store
->currentCount());
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);
305 pe
->release(); // destroys pe
307 StoreEntry
*const pe2
= getEntry(i
);
308 CPPUNIT_ASSERT_EQUAL(static_cast<StoreEntry
*>(nullptr), pe2
);
312 /// customizes our test setup
313 class MyTestProgram
: public TestProgram
316 /* TestProgram API */
317 void startup() override
;
321 MyTestProgram::startup()
323 Config
.memShared
.defaultTo(false);
324 Config
.shmLocking
.defaultTo(false);
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
= ".";
332 Config
.Store
.avgObjectSize
= 1024;
333 Config
.Store
.objectsPerBucket
= 20;
334 Config
.Store
.maxObjectSize
= 2048;
336 Config
.store_dir_select_algorithm
= xstrdup("round-robin");
338 Config
.replPolicy
= new RemovalPolicySettings
;
339 Config
.replPolicy
->type
= xstrdup("lru");
340 Config
.replPolicy
->args
= nullptr;
343 extern REMOVALPOLICYCREATE createRemovalPolicy_lru
;
344 storeReplAdd("lru", createRemovalPolicy_lru
);
346 visible_appname_string
= xstrdup(APP_FULLNAME
);
351 httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
353 mem_policy
= createRemovalPolicy(Config
.replPolicy
);
357 main(int argc
, char *argv
[])
359 return MyTestProgram().run(argc
, argv
);