1 /*
2  * Copyright (C) 1996-2021 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 "DiskIO/DiskIOModule.h"
11 #include "fs/ufs/UFSSwapDir.h"
12 #include "globals.h"
13 #include "HttpHeader.h"
14 #include "HttpReply.h"
15 #include "MemObject.h"
16 #include "RequestFlags.h"
17 #include "SquidConfig.h"
18 #include "Store.h"
19 #include "store/Disks.h"
20 #include "testStoreSupport.h"
21 #include "testUfs.h"
22 #include "unitTestMain.h"
23 
24 #include <stdexcept>
25 
26 #define TESTDIR "testUfs_Store"
27 
28 CPPUNIT_TEST_SUITE_REGISTRATION( testUfs );
29 
30 typedef RefCount<Fs::Ufs::UFSSwapDir> MySwapDirPointer;
31 extern REMOVALPOLICYCREATE createRemovalPolicy_lru; /* XXX fails with --enable-removal-policies=heap */
32 
33 static void
addSwapDir(MySwapDirPointer aStore)34 addSwapDir(MySwapDirPointer aStore)
35 {
36     allocate_new_swapdir(&Config.cacheSwap);
37     Config.cacheSwap.swapDirs[Config.cacheSwap.n_configured] = aStore.getRaw();
38     ++Config.cacheSwap.n_configured;
39 }
40 
41 /* TODO make this a cbdata class */
42 
43 static bool cbcalled;
44 
45 static void
searchCallback(void * cbdata)46 searchCallback(void *cbdata)
47 {
48     cbcalled = true;
49 }
50 
51 void
commonInit()52 testUfs::commonInit()
53 {
54     static bool inited = false;
55 
56     if (inited)
57         return;
58 
59     Config.Store.avgObjectSize = 1024;
60     Config.Store.objectsPerBucket = 20;
61     Config.Store.maxObjectSize = 2048;
62 
63     Config.store_dir_select_algorithm = xstrdup("round-robin");
64 
65     Config.replPolicy = new RemovalPolicySettings;
66     Config.replPolicy->type = xstrdup("lru");
67 
68     Config.memShared.defaultTo(false);
69 
70     /* garh garh */
71     storeReplAdd("lru", createRemovalPolicy_lru);
72 
73     Mem::Init();
74 
75     comm_init();
76 
77     httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
78 
79     inited = true;
80 }
81 
82 void
testUfsSearch()83 testUfs::testUfsSearch()
84 {
85     /* test sequence
86      * make a valid working ufs swapdir
87      * put two entries in it and sync logs
88      * search the ufs dir
89      * check the entries we find are what we want
90      */
91 
92     if (0 > system ("rm -rf " TESTDIR))
93         throw std::runtime_error("Failed to clean test work directory");
94 
95     Store::Init();
96 
97     MySwapDirPointer aStore (new Fs::Ufs::UFSSwapDir("ufs", "Blocking"));
98 
99     aStore->IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find("Blocking")->createStrategy());
100 
101     addSwapDir(aStore);
102 
103     commonInit();
104     mem_policy = createRemovalPolicy(Config.replPolicy);
105 
106     char *path=xstrdup(TESTDIR);
107 
108     char *config_line=xstrdup("100 1 1");
109 
110     visible_appname_string = xstrdup(PACKAGE "/" VERSION);
111 
112     ConfigParser::SetCfgLine(config_line);
113 
114     aStore->parse(0, path);
115     store_maxobjsize = 1024*1024*2;
116 
117     safe_free(path);
118 
119     safe_free(config_line);
120 
121     /* ok, ready to create */
122     aStore->create();
123 
124     /* ok, ready to use - inits store & hash too */
125     Store::Root().init();
126 
127     /* our swapdir must be scheduled to rebuild */
128     CPPUNIT_ASSERT_EQUAL(2, StoreController::store_dirs_rebuilding);
129 
130     /* rebuild is a scheduled event */
131     StockEventLoop loop;
132 
133     while (StoreController::store_dirs_rebuilding)
134         loop.runOnce();
135 
136     /* cannot use loop.run(); as the loop will never idle: the store-dir
137      * clean() scheduled event prevents it
138      */
139 
140     /* nothing left to rebuild */
141     CPPUNIT_ASSERT_EQUAL(0, StoreController::store_dirs_rebuilding);
142 
143     /* add an entry */
144     {
145         /* Create "vary" base object */
146         RequestFlags flags;
147         flags.cachable = true;
148         StoreEntry *pe = storeCreateEntry("dummy url", "dummy log url", flags, Http::METHOD_GET);
149         HttpReply *rep = (HttpReply *) pe->getReply();  // bypass const
150         rep->setHeaders(Http::scOkay, "dummy test object", "x-squid-internal/test", 0, -1, squid_curtime + 100000);
151 
152         pe->setPublicKey();
153 
154         pe->buffer();
155         pe->getReply()->packHeadersUsingSlowPacker(*pe);
156         pe->flush();
157         pe->timestampsSet();
158         pe->complete();
159         pe->swapOut();
160         CPPUNIT_ASSERT_EQUAL(0, pe->swap_dirn);
161         CPPUNIT_ASSERT_EQUAL(0, pe->swap_filen);
162         pe->unlock("testUfs::testUfsSearch vary");
163     }
164 
165     storeDirWriteCleanLogs(0);
166 
167     /* here we cheat: we know that UFSSwapDirs search off disk. If we did an init call to a new
168      * swapdir instance, we'd not be testing a clean build.
169      */
170     StoreSearchPointer search = Store::Root().search(); /* search for everything in the store */
171 
172     /* nothing should be immediately available */
173 #if 0
174 
175     CPPUNIT_ASSERT_EQUAL(false, search->next());
176 #endif
177 
178     CPPUNIT_ASSERT_EQUAL(false, search->error());
179     CPPUNIT_ASSERT_EQUAL(false, search->isDone());
180     CPPUNIT_ASSERT_EQUAL(static_cast<StoreEntry *>(NULL), search->currentItem());
181 
182     /* trigger a callback */
183     cbcalled = false;
184     search->next(searchCallback, NULL);
185     CPPUNIT_ASSERT_EQUAL(true, cbcalled);
186 
187     /* we should have access to a entry now, that matches the entry we had before */
188     //CPPUNIT_ASSERT_EQUAL(false, search->next());
189     CPPUNIT_ASSERT_EQUAL(false, search->error());
190     CPPUNIT_ASSERT_EQUAL(false, search->isDone());
191     CPPUNIT_ASSERT(search->currentItem() != NULL);
192 
193     /* trigger another callback */
194     cbcalled = false;
195     search->next(searchCallback, NULL);
196     CPPUNIT_ASSERT_EQUAL(true, cbcalled);
197 
198     /* now we should have no error, we should have finished and have no current item */
199     //CPPUNIT_ASSERT_EQUAL(false, search->next());
200     CPPUNIT_ASSERT_EQUAL(false, search->error());
201     CPPUNIT_ASSERT_EQUAL(true, search->isDone());
202     CPPUNIT_ASSERT_EQUAL(static_cast<StoreEntry *>(NULL), search->currentItem());
203 
204     Store::FreeMemory();
205 
206     free_cachedir(&Config.cacheSwap);
207 
208     /* todo: here we should test a dirty rebuild */
209 
210     safe_free(Config.replPolicy->type);
211     delete Config.replPolicy;
212 
213     if (0 > system ("rm -rf " TESTDIR))
214         throw std::runtime_error("Failed to clean test work directory");
215 }
216 
217 /* The UFS store should always configure an IO engine even if none is
218  * supplied on the configuration line.
219  */
220 void
testUfsDefaultEngine()221 testUfs::testUfsDefaultEngine()
222 {
223     /* boring common test boilerplate */
224     if (0 > system ("rm -rf " TESTDIR))
225         throw std::runtime_error("Failed to clean test work directory");
226 
227     // This assertion may fail if previous test cases fail.
228     // Apparently, CPPUNIT_ASSERT* failure may prevent destructors of local
229     // objects such as "StorePointer aRoot" from being called.
230     CPPUNIT_ASSERT(!store_table); // or StoreHashIndex ctor will abort below
231 
232     Store::Init();
233     MySwapDirPointer aStore (new Fs::Ufs::UFSSwapDir("ufs", "Blocking"));
234     addSwapDir(aStore);
235     commonInit();
236     Config.replPolicy = new RemovalPolicySettings;
237     Config.replPolicy->type = xstrdup("lru");
238     mem_policy = createRemovalPolicy(Config.replPolicy);
239 
240     char *path=xstrdup(TESTDIR);
241     char *config_line=xstrdup("100 1 1");
242     ConfigParser::SetCfgLine(config_line);
243     aStore->parse(0, path);
244     safe_free(path);
245     safe_free(config_line);
246     CPPUNIT_ASSERT(aStore->IO->io != NULL);
247 
248     Store::FreeMemory();
249     free_cachedir(&Config.cacheSwap);
250     safe_free(Config.replPolicy->type);
251     delete Config.replPolicy;
252 
253     if (0 > system ("rm -rf " TESTDIR))
254         throw std::runtime_error("Failed to clean test work directory");
255 }
256 
257