2 * Copyright (C) 1996-2021 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 "ConfigParser.h"
11 #include "DiskIO/DiskIOModule.h"
13 #include "fs/rock/RockSwapDir.h"
15 #include "HttpHeader.h"
16 #include "HttpReply.h"
17 #include "MemObject.h"
18 #include "RequestFlags.h"
19 #include "SquidConfig.h"
21 #include "store/Disk.h"
22 #include "store/Disks.h"
23 #include "StoreFileSystem.h"
24 #include "StoreSearch.h"
26 #include "testStoreSupport.h"
27 #include "unitTestMain.h"
39 CPPUNIT_TEST_SUITE_REGISTRATION( testRock
);
41 extern REMOVALPOLICYCREATE createRemovalPolicy_lru
;
43 static char cwd
[MAXPATHLEN
];
46 addSwapDir(testRock::SwapDirPointer aStore
)
48 allocate_new_swapdir(Config
.cacheSwap
);
49 Config
.cacheSwap
.swapDirs
[Config
.cacheSwap
.n_configured
] = aStore
.getRaw();
50 ++Config
.cacheSwap
.n_configured
;
56 CPPUNIT_NS::TestFixture::setUp();
58 if (0 > system ("rm -rf " TESTDIR
))
59 throw std::runtime_error("Failed to clean test work directory");
61 Config
.memShared
.defaultTo(false);
62 Config
.shmLocking
.defaultTo(false);
64 // use current directory for shared segments (on path-based OSes)
65 Ipc::Mem::Segment::BasePath
= getcwd(cwd
,MAXPATHLEN
);
66 if (Ipc::Mem::Segment::BasePath
== NULL
)
67 Ipc::Mem::Segment::BasePath
= ".";
71 store
= new Rock::SwapDir();
77 char *path
=xstrdup(TESTDIR
);
79 char *config_line
=xstrdup("10 max-size=16384");
81 ConfigParser::SetCfgLine(config_line
);
83 store
->parse(0, path
);
84 store_maxobjsize
= 1024*1024*2;
88 safe_free(config_line
);
90 /* ok, ready to create */
93 rr
= new Rock::SwapDirRr
;
100 CPPUNIT_NS::TestFixture::tearDown();
106 free_cachedir(&Config
.cacheSwap
);
108 rr
->finishShutdown(); // deletes rr
111 // TODO: do this once, or each time.
112 // safe_free(Config.replPolicy->type);
113 // delete Config.replPolicy;
115 if (0 > system ("rm -rf " TESTDIR
))
116 throw std::runtime_error("Failed to clean test work directory");
120 testRock::commonInit()
122 static bool inited
= false;
127 StoreFileSystem::SetupAllFs();
129 Config
.Store
.avgObjectSize
= 1024;
130 Config
.Store
.objectsPerBucket
= 20;
131 Config
.Store
.maxObjectSize
= 2048;
133 Config
.store_dir_select_algorithm
= xstrdup("round-robin");
135 Config
.replPolicy
= new RemovalPolicySettings
;
136 Config
.replPolicy
->type
= xstrdup("lru");
137 Config
.replPolicy
->args
= NULL
;
140 storeReplAdd("lru", createRemovalPolicy_lru
);
142 visible_appname_string
= xstrdup(APP_FULLNAME
);
150 httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
152 mem_policy
= createRemovalPolicy(Config
.replPolicy
);
158 testRock::storeInit()
160 /* ok, ready to use */
161 Store::Root().init();
163 /* rebuild is a scheduled event */
166 /* our swapdir must be scheduled to rebuild */
167 CPPUNIT_ASSERT_EQUAL(2, StoreController::store_dirs_rebuilding
);
171 /* cannot use loop.run(); as the loop will never idle: the store-dir
172 * clean() scheduled event prevents it
175 /* nothing left to rebuild */
176 CPPUNIT_ASSERT_EQUAL(0, StoreController::store_dirs_rebuilding
);
183 snprintf(buf
, sizeof(buf
), "dummy url %i", i
);
184 buf
[sizeof(buf
) - 1] = '\0';
189 testRock::createEntry(const int i
)
192 flags
.cachable
= true;
193 StoreEntry
*const pe
=
194 storeCreateEntry(storeId(i
), "dummy log url", flags
, Http::METHOD_GET
);
195 auto &rep
= pe
->mem().adjustableBaseReply();
196 rep
.setHeaders(Http::scOkay
, "dummy test object", "x-squid-internal/test", 0, -1, squid_curtime
+ 100000);
204 testRock::addEntry(const int i
)
206 StoreEntry
*const pe
= createEntry(i
);
209 pe
->mem().freshestReply().packHeadersUsingSlowPacker(*pe
);
219 testRock::getEntry(const int i
)
221 return storeGetPublic(storeId(i
), Http::METHOD_GET
);
225 testRock::testRockCreate()
229 CPPUNIT_ASSERT_EQUAL(0, ::stat(TESTDIR
, &sb
));
231 /* TODO: check the size */
233 /* TODO: test rebuild */
237 testRock::testRockSwapOut()
241 // add few entries to prime the database
242 for (int i
= 0; i
< 5; ++i
) {
243 CPPUNIT_ASSERT_EQUAL((uint64_t)i
, store
->currentCount());
245 StoreEntry
*const pe
= addEntry(i
);
247 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe
->swap_status
);
248 CPPUNIT_ASSERT_EQUAL(0, pe
->swap_dirn
);
249 CPPUNIT_ASSERT(pe
->swap_filen
>= 0);
251 // Rock::IoState::finishedWriting() schedules an AsyncCall
252 // storeSwapOutFileClosed(). Let it fire.
256 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe
->swap_status
);
258 pe
->unlock("testRock::testRockSwapOut priming");
261 CPPUNIT_ASSERT_EQUAL((uint64_t)5, store
->currentCount());
263 // try to swap out entry to a used unlocked slot
265 // without marking the old entry as deleted
266 StoreEntry
*const pe
= addEntry(3);
268 CPPUNIT_ASSERT_EQUAL(SWAPOUT_NONE
, pe
->swap_status
);
269 CPPUNIT_ASSERT_EQUAL(-1, pe
->swap_dirn
);
270 CPPUNIT_ASSERT_EQUAL(-1, pe
->swap_filen
);
271 pe
->unlock("testRock::testRockSwapOut e#3");
273 // after marking the old entry as deleted
274 StoreEntry
*const pe2
= getEntry(4);
275 CPPUNIT_ASSERT(pe2
!= nullptr);
278 StoreEntry
*const pe3
= addEntry(4);
279 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe3
->swap_status
);
280 CPPUNIT_ASSERT_EQUAL(0, pe3
->swap_dirn
);
281 CPPUNIT_ASSERT(pe3
->swap_filen
>= 0);
286 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe3
->swap_status
);
288 pe
->unlock("testRock::testRockSwapOut e#4");
291 // try to swap out entry to a used locked slot
293 StoreEntry
*const pe
= addEntry(5);
295 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe
->swap_status
);
296 CPPUNIT_ASSERT_EQUAL(0, pe
->swap_dirn
);
297 CPPUNIT_ASSERT(pe
->swap_filen
>= 0);
299 // the slot is locked here because the async calls have not run yet
300 StoreEntry
*const pe2
= addEntry(5);
301 CPPUNIT_ASSERT_EQUAL(SWAPOUT_NONE
, pe2
->swap_status
);
302 CPPUNIT_ASSERT_EQUAL(MemObject::SwapOut::swImpossible
, pe2
->mem_obj
->swapout
.decision
);
303 CPPUNIT_ASSERT_EQUAL(-1, pe2
->swap_dirn
);
304 CPPUNIT_ASSERT_EQUAL(-1, pe2
->swap_filen
);
309 pe
->unlock("testRock::testRockSwapOut e#5.1");
310 pe2
->unlock("testRock::testRockSwapOut e#5.2");
312 // pe2 has the same public key as pe so it marks old pe for release
313 // here, we add another entry #5 into the now-available slot
314 StoreEntry
*const pe3
= addEntry(5);
315 CPPUNIT_ASSERT_EQUAL(SWAPOUT_WRITING
, pe3
->swap_status
);
316 CPPUNIT_ASSERT_EQUAL(0, pe3
->swap_dirn
);
317 CPPUNIT_ASSERT(pe3
->swap_filen
>= 0);
319 CPPUNIT_ASSERT_EQUAL(SWAPOUT_DONE
, pe3
->swap_status
);
320 pe3
->unlock("testRock::testRockSwapOut e#5.3");
323 CPPUNIT_ASSERT_EQUAL((uint64_t)6, store
->currentCount());
325 // try to get and release all entries
326 for (int i
= 0; i
< 6; ++i
) {
327 StoreEntry
*const pe
= getEntry(i
);
328 CPPUNIT_ASSERT(pe
!= NULL
);
330 pe
->release(); // destroys pe
332 StoreEntry
*const pe2
= getEntry(i
);
333 CPPUNIT_ASSERT_EQUAL(static_cast<StoreEntry
*>(NULL
), pe2
);