4 #define DATA_SEQUENTIAL TEST_DATASOURCE_SEQUENCE
5 #define DATA_RANDOM TEST_DATASOURCE_RANDOM
7 typedef struct Datatest1 Datatest1
;
8 typedef struct Datatest2 Datatest2
;
11 ** An instance of the following structure contains parameters used to
12 ** customize the test function in this file. Test procedure:
14 ** 1. Create a data-source based on the "datasource definition" vars.
16 ** 2. Insert nRow key value pairs into the database.
18 ** 3. Delete all keys from the database. Deletes are done in the same
19 ** order as the inserts.
21 ** During steps 2 and 3 above, after each Datatest1.nVerify inserts or
22 ** deletes, the following:
24 ** a. Run Datasource.nTest key lookups and check the results are as expected.
26 ** b. If Datasource.bTestScan is true, run a handful (8) of range
27 ** queries (scanning forwards and backwards). Check that the results
30 ** c. Close and reopen the database. Then run (a) and (b) again.
33 /* Datasource definition */
36 /* Test procedure parameters */
37 int nRow
; /* Number of rows to insert then delete */
38 int nVerify
; /* How often to verify the db contents */
39 int nTest
; /* Number of keys to test (0==all) */
40 int bTestScan
; /* True to do scan tests */
44 ** An instance of the following data structure is used to describe the
45 ** second type of test case in this file. The chief difference between
46 ** these tests and those described by Datatest1 is that these tests also
47 ** experiment with range-delete operations. Tests proceed as follows:
49 ** 1. Open the datasource described by Datatest2.defn.
51 ** 2. Open a connection on an empty database.
53 ** 3. Do this Datatest2.nIter times:
55 ** a) Insert Datatest2.nWrite key-value pairs from the datasource.
57 ** b) Select two pseudo-random keys and use them as the start
58 ** and end points of a range-delete operation.
60 ** c) Verify that the contents of the database are as expected (see
61 ** below for details).
63 ** d) Close and then reopen the database handle.
65 ** e) Verify that the contents of the database are still as expected.
67 ** The inserts and range deletes are run twice - once on the database being
68 ** tested and once using a control system (sqlite3, kc etc. - something that
69 ** works). In order to verify that the contents of the db being tested are
70 ** correct, the test runs a bunch of scans and lookups on both the test and
71 ** control databases. If the results are the same, the test passes.
76 int nWrite
; /* Number of writes per iteration */
77 int nIter
; /* Total number of iterations to run */
81 ** Generate a unique name for the test case pTest with database system
84 static char *getName(const char *zSystem
, int bRecover
, Datatest1
*pTest
){
87 zData
= testDatasourceName(&pTest
->defn
);
88 zRet
= testMallocPrintf("data.%s.%s.rec=%d.%d.%d",
89 zSystem
, zData
, bRecover
, pTest
->nRow
, pTest
->nVerify
95 int testControlDb(TestDb
**ppDb
){
96 #ifdef HAVE_KYOTOCABINET
97 return tdb_open("kyotocabinet", "tmp.db", 1, ppDb
);
99 return tdb_open("sqlite3", "", 1, ppDb
);
103 void testDatasourceFetch(
104 TestDb
*pDb
, /* Database handle */
107 int *pRc
/* IN/OUT: Error code */
109 void *pKey
; int nKey
; /* Database key to query for */
110 void *pVal
; int nVal
; /* Expected result of query */
112 testDatasourceEntry(pData
, iKey
, &pKey
, &nKey
, &pVal
, &nVal
);
113 testFetch(pDb
, pKey
, nKey
, pVal
, nVal
, pRc
);
117 ** This function is called to test that the contents of database pDb
118 ** are as expected. In this case, expected is defined as containing
119 ** key-value pairs iFirst through iLast, inclusive, from data source
120 ** pData. In other words, a loop like the following could be used to
121 ** construct a database with identical contents from scratch.
123 ** for(i=iFirst; i<=iLast; i++){
124 ** testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
125 ** // insert (pKey, nKey) -> (pVal, nVal) into database
128 ** The key domain consists of keys 0 to (nRow-1), inclusive, from
129 ** data source pData. For both scan and lookup tests, keys are selected
130 ** pseudo-randomly from within this set.
132 ** This function runs nLookupTest lookup tests and nScanTest scan tests.
134 ** A lookup test consists of selecting a key from the domain and querying
135 ** pDb for it. The test fails if the presence of the key and, if present,
136 ** the associated value do not match the expectations defined above.
138 ** A scan test involves selecting a key from the domain and running
139 ** the following queries:
141 ** 1. Scan all keys equal to or greater than the key, in ascending order.
142 ** 2. Scan all keys equal to or smaller than the key, in descending order.
144 ** Additionally, if nLookupTest is greater than zero, the following are
147 ** 1. Scan all keys in the db, in ascending order.
148 ** 2. Scan all keys in the db, in descending order.
150 ** As you would assume, the test fails if the returned values do not match
154 TestDb
*pDb
, /* Database handle being tested */
155 Datasource
*pData
, /* pDb contains data from here */
156 int nRow
, /* Size of key domain */
157 int iFirst
, /* Index of first key from pData in pDb */
158 int iLast
, /* Index of last key from pData in pDb */
159 int nLookupTest
, /* Number of lookup tests to run */
160 int nScanTest
, /* Number of scan tests to run */
161 int *pRc
/* IN/OUT: Error code */
166 if( rc
==0 && nScanTest
){
169 /* Open a control db (i.e. one that we assume works) */
170 rc
= testControlDb(&pDb2
);
172 for(j
=iFirst
; rc
==0 && j
<=iLast
; j
++){
173 void *pKey
; int nKey
; /* Database key to insert */
174 void *pVal
; int nVal
; /* Database value to insert */
175 testDatasourceEntry(pData
, j
, &pKey
, &nKey
, &pVal
, &nVal
);
176 rc
= tdb_write(pDb2
, pKey
, nKey
, pVal
, nVal
);
182 void *pKey1
; int nKey1
; /* Start key */
183 void *pKey2
; int nKey2
; /* Final key */
185 iKey1
= testPrngValue((iFirst
<<8) + (iLast
<<16)) % nRow
;
186 iKey2
= testPrngValue((iLast
<<8) + (iFirst
<<16)) % nRow
;
187 testDatasourceEntry(pData
, iKey1
, &pKey2
, &nKey1
, 0, 0);
188 pKey1
= testMalloc(nKey1
+1);
189 memcpy(pKey1
, pKey2
, nKey1
+1);
190 testDatasourceEntry(pData
, iKey2
, &pKey2
, &nKey2
, 0, 0);
192 testScanCompare(pDb2
, pDb
, 0, 0, 0, 0, 0, &rc
);
193 testScanCompare(pDb2
, pDb
, 0, 0, 0, pKey2
, nKey2
, &rc
);
194 testScanCompare(pDb2
, pDb
, 0, pKey1
, nKey1
, 0, 0, &rc
);
195 testScanCompare(pDb2
, pDb
, 0, pKey1
, nKey1
, pKey2
, nKey2
, &rc
);
196 testScanCompare(pDb2
, pDb
, 1, 0, 0, 0, 0, &rc
);
197 testScanCompare(pDb2
, pDb
, 1, 0, 0, pKey2
, nKey2
, &rc
);
198 testScanCompare(pDb2
, pDb
, 1, pKey1
, nKey1
, 0, 0, &rc
);
199 testScanCompare(pDb2
, pDb
, 1, pKey1
, nKey1
, pKey2
, nKey2
, &rc
);
205 /* Test some lookups. */
206 for(j
=0; rc
==0 && j
<nLookupTest
; j
++){
207 int iKey
; /* Datasource key to test */
208 void *pKey
; int nKey
; /* Database key to query for */
209 void *pVal
; int nVal
; /* Expected result of query */
211 if( nLookupTest
>=nRow
){
214 iKey
= testPrngValue(j
+ (iFirst
<<8) + (iLast
<<16)) % nRow
;
217 testDatasourceEntry(pData
, iKey
, &pKey
, &nKey
, &pVal
, &nVal
);
218 if( iFirst
>iKey
|| iKey
>iLast
){
223 testFetch(pDb
, pKey
, nKey
, pVal
, nVal
, &rc
);
230 ** This function should be called during long running test cases to output
231 ** the progress dots (...) to stdout.
233 void testCaseProgress(int i
, int n
, int nDot
, int *piDot
){
235 while( iDot
< ( ((nDot
*2+1) * i
) / (n
*2) ) ){
243 int testCaseNDot(void){ return 20; }
246 static void printScanCb(
247 void *pCtx
, void *pKey
, int nKey
, void *pVal
, int nVal
249 printf("%s\n", (char *)pKey
);
254 void testReopenRecover(TestDb
**ppDb
, int *pRc
){
256 const char *zLib
= tdb_library_name(*ppDb
);
257 const char *zDflt
= tdb_default_db(zLib
);
258 testCopyLsmdb(zDflt
, "bak.db");
260 testCopyLsmdb("bak.db", zDflt
);
261 *pRc
= tdb_open(zLib
, 0, 0, ppDb
);
266 static void doDataTest1(
267 const char *zSystem
, /* Database system to test */
269 Datatest1
*p
, /* Structure containing test parameters */
270 int *pRc
/* OUT: Error code */
278 /* Start the test case, open a database and allocate the datasource. */
279 pDb
= testOpen(zSystem
, 1, &rc
);
280 pData
= testDatasourceNew(&p
->defn
);
284 while( rc
==LSM_OK
&& i
<p
->nRow
){
286 /* Insert some data */
287 testWriteDatasourceRange(pDb
, pData
, i
, p
->nVerify
, &rc
);
290 /* Check that the db content is correct. */
291 testDbContents(pDb
, pData
, p
->nRow
, 0, i
-1, p
->nTest
, p
->bTestScan
, &rc
);
294 testReopenRecover(&pDb
, &rc
);
296 testReopen(&pDb
, &rc
);
299 /* Check that the db content is still correct. */
300 testDbContents(pDb
, pData
, p
->nRow
, 0, i
-1, p
->nTest
, p
->bTestScan
, &rc
);
302 /* Update the progress dots... */
303 testCaseProgress(i
, p
->nRow
, testCaseNDot()/2, &iDot
);
308 while( rc
==LSM_OK
&& i
<p
->nRow
){
310 /* Delete some entries */
311 testDeleteDatasourceRange(pDb
, pData
, i
, p
->nVerify
, &rc
);
314 /* Check that the db content is correct. */
315 testDbContents(pDb
, pData
, p
->nRow
, i
, p
->nRow
-1,p
->nTest
,p
->bTestScan
,&rc
);
317 /* Close and reopen the database. */
319 testReopenRecover(&pDb
, &rc
);
321 testReopen(&pDb
, &rc
);
324 /* Check that the db content is still correct. */
325 testDbContents(pDb
, pData
, p
->nRow
, i
, p
->nRow
-1,p
->nTest
,p
->bTestScan
,&rc
);
327 /* Update the progress dots... */
328 testCaseProgress(i
, p
->nRow
, testCaseNDot()/2, &iDot
);
331 /* Free the datasource, close the database and finish the test case. */
332 testDatasourceFree(pData
);
340 const char *zSystem
, /* Database system name */
341 const char *zPattern
, /* Run test cases that match this pattern */
342 int *pRc
/* IN/OUT: Error code */
344 Datatest1 aTest
[] = {
345 { {DATA_RANDOM
, 500,600, 1000,2000}, 1000, 100, 10, 0},
346 { {DATA_RANDOM
, 20,25, 100,200}, 1000, 250, 1000, 1},
347 { {DATA_RANDOM
, 8,10, 100,200}, 1000, 250, 1000, 1},
348 { {DATA_RANDOM
, 8,10, 10,20}, 1000, 250, 1000, 1},
349 { {DATA_RANDOM
, 8,10, 1000,2000}, 1000, 250, 1000, 1},
350 { {DATA_RANDOM
, 8,100, 10000,20000}, 100, 25, 100, 1},
351 { {DATA_RANDOM
, 80,100, 10,20}, 1000, 250, 1000, 1},
352 { {DATA_RANDOM
, 5000,6000, 10,20}, 100, 25, 100, 1},
353 { {DATA_SEQUENTIAL
, 5,10, 10,20}, 1000, 250, 1000, 1},
354 { {DATA_SEQUENTIAL
, 5,10, 100,200}, 1000, 250, 1000, 1},
355 { {DATA_SEQUENTIAL
, 5,10, 1000,2000}, 1000, 250, 1000, 1},
356 { {DATA_SEQUENTIAL
, 5,100, 10000,20000}, 100, 25, 100, 1},
357 { {DATA_RANDOM
, 10,10, 100,100}, 100000, 1000, 100, 0},
358 { {DATA_SEQUENTIAL
, 10,10, 100,100}, 100000, 1000, 100, 0},
364 for(bRecover
=0; bRecover
<2; bRecover
++){
365 if( bRecover
==1 && memcmp(zSystem
, "lsm", 3) ) break;
366 for(i
=0; *pRc
==LSM_OK
&& i
<ArraySize(aTest
); i
++){
367 char *zName
= getName(zSystem
, bRecover
, &aTest
[i
]);
368 if( testCaseBegin(pRc
, zPattern
, "%s", zName
) ){
369 doDataTest1(zSystem
, bRecover
, &aTest
[i
], pRc
);
386 static int nCall
= 0;
389 testScanCompare(pControl
, pDb
, 0, 0, 0, 0, 0, pRc
);
390 testScanCompare(pControl
, pDb
, 1, 0, 0, 0, 0, pRc
);
395 void *pKey1
; int nKey1
; /* Start key */
396 void *pKey2
; int nKey2
; /* Final key */
398 iKey1
= testPrngValue(iSeed
) % nData
;
399 iKey2
= testPrngValue(iSeed
+1) % nData
;
400 testDatasourceEntry(pData
, iKey1
, &pKey2
, &nKey1
, 0, 0);
401 pKey1
= testMalloc(nKey1
+1);
402 memcpy(pKey1
, pKey2
, nKey1
+1);
403 testDatasourceEntry(pData
, iKey2
, &pKey2
, &nKey2
, 0, 0);
405 testScanCompare(pControl
, pDb
, 0, 0, 0, pKey2
, nKey2
, pRc
);
406 testScanCompare(pControl
, pDb
, 0, pKey1
, nKey1
, 0, 0, pRc
);
407 testScanCompare(pControl
, pDb
, 0, pKey1
, nKey1
, pKey2
, nKey2
, pRc
);
408 testScanCompare(pControl
, pDb
, 1, 0, 0, pKey2
, nKey2
, pRc
);
409 testScanCompare(pControl
, pDb
, 1, pKey1
, nKey1
, 0, 0, pRc
);
410 testScanCompare(pControl
, pDb
, 1, pKey1
, nKey1
, pKey2
, nKey2
, pRc
);
414 for(i
=0; i
<nData
&& *pRc
==0; i
++){
415 void *pKey
; int nKey
;
416 testDatasourceEntry(pData
, i
, &pKey
, &nKey
, 0, 0);
417 testFetchCompare(pControl
, pDb
, pKey
, nKey
, pRc
);
421 static void doDataTest2(
422 const char *zSystem
, /* Database system to test */
424 Datatest2
*p
, /* Structure containing test parameters */
425 int *pRc
/* OUT: Error code */
434 /* Start the test case, open a database and allocate the datasource. */
435 pDb
= testOpen(zSystem
, 1, &rc
);
436 pData
= testDatasourceNew(&p
->defn
);
437 rc
= testControlDb(&pControl
);
440 int nBuf
= 32 * 1024 * 1024;
441 lsm_config(tdb_lsm(pDb
), LSM_CONFIG_AUTOFLUSH
, &nBuf
);
444 for(i
=0; rc
==0 && i
<p
->nIter
; i
++){
445 void *pKey1
; int nKey1
;
446 void *pKey2
; int nKey2
;
448 int nRange
= MIN(p
->nIter
*p
->nWrite
, p
->nRange
);
450 for(ii
=0; rc
==0 && ii
<p
->nWrite
; ii
++){
451 int iKey
= (i
*p
->nWrite
+ ii
) % p
->nRange
;
452 testWriteDatasource(pControl
, pData
, iKey
, &rc
);
453 testWriteDatasource(pDb
, pData
, iKey
, &rc
);
456 testDatasourceEntry(pData
, i
+1000000, &pKey1
, &nKey1
, 0, 0);
457 pKey1
= testMallocCopy(pKey1
, nKey1
);
458 testDatasourceEntry(pData
, i
+2000000, &pKey2
, &nKey2
, 0, 0);
460 testDeleteRange(pDb
, pKey1
, nKey1
, pKey2
, nKey2
, &rc
);
461 testDeleteRange(pControl
, pKey1
, nKey1
, pKey2
, nKey2
, &rc
);
464 testCompareDb(pData
, nRange
, i
, pControl
, pDb
, &rc
);
466 testReopenRecover(&pDb
, &rc
);
468 testReopen(&pDb
, &rc
);
470 testCompareDb(pData
, nRange
, i
, pControl
, pDb
, &rc
);
472 /* Update the progress dots... */
473 testCaseProgress(i
, p
->nIter
, testCaseNDot(), &iDot
);
477 testClose(&pControl
);
478 testDatasourceFree(pData
);
483 static char *getName2(const char *zSystem
, int bRecover
, Datatest2
*pTest
){
486 zData
= testDatasourceName(&pTest
->defn
);
487 zRet
= testMallocPrintf("data2.%s.%s.rec=%d.%d.%d.%d",
488 zSystem
, zData
, bRecover
, pTest
->nRange
, pTest
->nWrite
, pTest
->nIter
495 const char *zSystem
, /* Database system name */
496 const char *zPattern
, /* Run test cases that match this pattern */
497 int *pRc
/* IN/OUT: Error code */
499 Datatest2 aTest
[] = {
500 /* defn, nRange, nWrite, nIter */
501 { {DATA_RANDOM
, 20,25, 100,200}, 10000, 10, 50 },
502 { {DATA_RANDOM
, 20,25, 100,200}, 10000, 200, 50 },
503 { {DATA_RANDOM
, 20,25, 100,200}, 100, 10, 1000 },
504 { {DATA_RANDOM
, 20,25, 100,200}, 100, 200, 50 },
510 for(bRecover
=0; bRecover
<2; bRecover
++){
511 if( bRecover
==1 && memcmp(zSystem
, "lsm", 3) ) break;
512 for(i
=0; *pRc
==LSM_OK
&& i
<ArraySize(aTest
); i
++){
513 char *zName
= getName2(zSystem
, bRecover
, &aTest
[i
]);
514 if( testCaseBegin(pRc
, zPattern
, "%s", zName
) ){
515 doDataTest2(zSystem
, bRecover
, &aTest
[i
], pRc
);
522 /*************************************************************************
526 typedef struct Datatest3 Datatest3
;
528 int nRange
; /* Keys are between 1 and this value, incl. */
529 int nIter
; /* Number of iterations */
530 int nWrite
; /* Number of writes per iteration */
531 int nDelete
; /* Number of deletes per iteration */
533 int nValMin
; /* Minimum value size for writes */
534 int nValMax
; /* Maximum value size for writes */
537 void testPutU32(u8
*aBuf
, u32 iVal
){
538 aBuf
[0] = (iVal
>> 24) & 0xFF;
539 aBuf
[1] = (iVal
>> 16) & 0xFF;
540 aBuf
[2] = (iVal
>> 8) & 0xFF;
541 aBuf
[3] = (iVal
>> 0) & 0xFF;
544 void dt3PutKey(u8
*aBuf
, int iKey
){
545 assert( iKey
<100000 && iKey
>=0 );
546 sprintf((char *)aBuf
, "%.5d", iKey
);
549 static void doDataTest3(
550 const char *zSystem
, /* Database system to test */
551 Datatest3
*p
, /* Structure containing test parameters */
552 int *pRc
/* OUT: Error code */
557 u8
*abPresent
; /* Array of boolean */
558 char *aVal
; /* Buffer to hold values */
560 u32 iSeq
= 10; /* prng counter */
562 abPresent
= (u8
*)testMalloc(p
->nRange
+1);
563 aVal
= (char *)testMalloc(p
->nValMax
+1);
564 pDb
= testOpen(zSystem
, 1, &rc
);
566 for(i
=0; i
<p
->nIter
&& rc
==0; i
++){
569 testCaseProgress(i
, p
->nIter
, testCaseNDot(), &iDot
);
571 /* Perform nWrite inserts */
572 for(ii
=0; ii
<p
->nWrite
; ii
++){
577 iKey
= (testPrngValue(iSeq
++) % p
->nRange
) + 1;
578 nVal
= (testPrngValue(iSeq
++) % (p
->nValMax
- p
->nValMin
)) + p
->nValMin
;
579 testPrngString(testPrngValue(iSeq
++), aVal
, nVal
);
580 dt3PutKey(aKey
, iKey
);
582 testWrite(pDb
, aKey
, sizeof(aKey
)-1, aVal
, nVal
, &rc
);
586 /* Perform nDelete deletes */
587 for(ii
=0; ii
<p
->nDelete
; ii
++){
592 iKey
= (testPrngValue(iSeq
++) % p
->nRange
) + 1;
593 dt3PutKey(aKey1
, iKey
-1);
594 dt3PutKey(aKey2
, iKey
+1);
596 testDeleteRange(pDb
, aKey1
, sizeof(aKey1
)-1, aKey2
, sizeof(aKey2
)-1, &rc
);
600 testReopen(&pDb
, &rc
);
602 for(ii
=1; rc
==0 && ii
<=p
->nRange
; ii
++){
609 dbrc
= tdb_fetch(pDb
, aKey
, sizeof(aKey
)-1, &pDbVal
, &nDbVal
);
610 testCompareInt(0, dbrc
, &rc
);
613 testCompareInt(1, (nDbVal
>0), &rc
);
615 testCompareInt(1, (nDbVal
<0), &rc
);
625 static char *getName3(const char *zSystem
, Datatest3
*p
){
626 return testMallocPrintf("data3.%s.%d.%d.%d.%d.(%d..%d)",
627 zSystem
, p
->nRange
, p
->nIter
, p
->nWrite
, p
->nDelete
,
628 p
->nValMin
, p
->nValMax
633 const char *zSystem
, /* Database system name */
634 const char *zPattern
, /* Run test cases that match this pattern */
635 int *pRc
/* IN/OUT: Error code */
637 Datatest3 aTest
[] = {
638 /* nRange, nIter, nWrite, nDelete, nValMin, nValMax */
639 { 100, 1000, 5, 5, 50, 100 },
640 { 100, 1000, 2, 2, 5, 10 },
645 for(i
=0; *pRc
==LSM_OK
&& i
<ArraySize(aTest
); i
++){
646 char *zName
= getName3(zSystem
, &aTest
[i
]);
647 if( testCaseBegin(pRc
, zPattern
, "%s", zName
) ){
648 doDataTest3(zSystem
, &aTest
[i
], pRc
);