1 /*************************************************************************************************
2 * Test cases of Villa for C++
3 * Copyright (C) 2000-2005 Mikio Hirabayashi
4 * This file is part of QDBM, Quick Database Manager.
5 * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * Lesser General Public License as published by the Free Software Foundation; either version
7 * 2.1 of the License or any later version. QDBM is distributed in the hope that it will be
8 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
10 * details.
11 * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
12 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
13 * 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include <xvilla.h>
18 #include <cstdlib>
19 #include <cstdio>
20 #include <cstring>
21 #include <ctime>
22 #include <exception>
23 #include <iostream>
24 #include <iomanip>
25
26 extern "C" {
27 #include <pthread.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 }
31
32 using namespace std;
33 using namespace qdbm;
34
35
36 /* global constants */
37 const int RECBUFSIZ = 32;
38 const int LRECMAX = 25;
39 const int NIDXMAX = 64;
40 const int LCNUM = 32;
41 const int NCNUM = 32;
42
43
44 /* struct for closure per thread */
45 struct Mission {
46 Villa* villa;
47 int rnum;
48 int id;
49 bool printer;
50 };
51
52
53 /* global variables */
54 const char* progname;
55
56
57 /* function prototypes */
58 int main(int argc, char** argv);
59 void myterminate();
60 void myunexpected();
61 void usage();
62 int runwrite(int argc, char** argv);
63 int runread(int argc, char** argv);
64 int runmulti(int argc, char** argv);
65 int runmisc(int argc, char** argv);
66 void pxdperror(const char* name, DBM_error &e);
67 int myrand(void);
68 int dowrite(const char* name, int rnum);
69 int doread(const char* name);
70 int domulti(const char* name, int rnum, int tnum);
71 void* mtwriter(void* arg);
72 int domisc(const char* name);
73
74
75 /* main routine */
main(int argc,char ** argv)76 int main(int argc, char** argv){
77 int rv;
78 set_terminate(myterminate);
79 set_unexpected(myunexpected);
80 progname = argv[0];
81 if(argc < 2) usage();
82 rv = 0;
83 if(!strcmp(argv[1], "write")){
84 rv = runwrite(argc, argv);
85 } else if(!strcmp(argv[1], "read")){
86 rv = runread(argc, argv);
87 } else if(!strcmp(argv[1], "multi")){
88 rv = runmulti(argc, argv);
89 } else if(!strcmp(argv[1], "misc")){
90 rv = runmisc(argc, argv);
91 } else {
92 usage();
93 }
94 return rv;
95 }
96
97
98 /* for debug */
myterminate()99 void myterminate(){
100 cout << progname << ": terminate" << endl;
101 exit(1);
102 }
103
104
105 /* for debug */
myunexpected()106 void myunexpected(){
107 cout << progname << ": unexpected" << endl;
108 exit(1);
109 }
110
111
112 /* print the usage and exit */
usage()113 void usage(){
114 cerr << progname << ": test cases for Villa for C++" << endl;
115 cerr << endl;
116 cerr << "usage:" << endl;
117 cerr << " " << progname << " write name rnum" << endl;
118 cerr << " " << progname << " read name" << endl;
119 cerr << " " << progname << " multi name rnum tnum" << endl;
120 cerr << " " << progname << " misc name" << endl;
121 cerr << endl;
122 exit(1);
123 }
124
125
126 /* parse arguments of write command */
runwrite(int argc,char ** argv)127 int runwrite(int argc, char** argv){
128 char *name, *rstr;
129 int i, rnum, rv;
130 name = 0;
131 rstr = 0;
132 for(i = 2; i < argc; i++){
133 if(argv[i][0] == '-'){
134 usage();
135 } else if(!name){
136 name = argv[i];
137 } else if(!rstr){
138 rstr = argv[i];
139 } else {
140 usage();
141 }
142 }
143 if(!name || !rstr) usage();
144 rnum = atoi(rstr);
145 if(rnum < 1) usage();
146 rv = dowrite(name, rnum);
147 return rv;
148 }
149
150
151 /* parse arguments of read command */
runread(int argc,char ** argv)152 int runread(int argc, char** argv){
153 char* name;
154 int i, rv;
155 name = 0;
156 for(i = 2; i < argc; i++){
157 if(argv[i][0] == '-'){
158 usage();
159 } else if(!name){
160 name = argv[i];
161 } else {
162 usage();
163 }
164 }
165 if(!name) usage();
166 rv = doread(name);
167 return rv;
168 }
169
170
171 /* parse arguments of multi command */
runmulti(int argc,char ** argv)172 int runmulti(int argc, char** argv){
173 char *name, *rstr, *tstr;
174 int i, rnum, tnum, rv;
175 name = 0;
176 rstr = 0;
177 tstr = 0;
178 for(i = 2; i < argc; i++){
179 if(argv[i][0] == '-'){
180 usage();
181 } else if(!name){
182 name = argv[i];
183 } else if(!rstr){
184 rstr = argv[i];
185 } else if(!tstr){
186 tstr = argv[i];
187 } else {
188 usage();
189 }
190 }
191 if(!name || !rstr || !tstr) usage();
192 rnum = atoi(rstr);
193 tnum = atoi(tstr);
194 if(rnum < 1 || tnum < 1) usage();
195 rv = domulti(name, rnum, tnum);
196 return rv;
197 }
198
199
200 /* parse arguments of misc command */
runmisc(int argc,char ** argv)201 int runmisc(int argc, char** argv){
202 char* name;
203 int i, rv;
204 name = 0;
205 for(i = 2; i < argc; i++){
206 if(argv[i][0] == '-'){
207 usage();
208 } else if(!name){
209 name = argv[i];
210 } else {
211 usage();
212 }
213 }
214 if(!name) usage();
215 rv = domisc(name);
216 return rv;
217 }
218
219
220 /* print an error message */
pxdperror(const char * name,DBM_error & e)221 void pxdperror(const char* name, DBM_error &e){
222 cerr << progname << ": " << name << ": " << e << endl;
223 }
224
225
226 /* pseudo random number generator */
myrand(void)227 int myrand(void){
228 static int cnt = 0;
229 if(cnt == 0) std::srand(std::time(NULL));
230 return (std::rand() * std::rand() + (std::rand() >> (sizeof(int) * 4)) + (cnt++)) & 0x7FFFFFFF;
231 }
232
233
234 /* do writing test */
dowrite(const char * name,int rnum)235 int dowrite(const char* name, int rnum){
236 int i, len;
237 char buf[RECBUFSIZ];
238 cout << "<Writing Test>" << endl;
239 cout << " name=" << name << " rnum=" << rnum << endl;
240 cout << endl;
241 Villa* villa = 0;
242 bool err = false;
243 try {
244 // open the database
245 villa = new Villa(name, Villa::OWRITER | Villa::OCREAT | Villa::OTRUNC);
246 // roop for storing
247 for(i = 1; i <= rnum; i++){
248 // set a buffer for a key and a value
249 len = sprintf(buf, "%08d", i);
250 // store a record into the database
251 villa->put(buf, len, buf, len);
252 // print progression
253 if(rnum > 250 && i % (rnum / 250) == 0){
254 cout << '.';
255 cout.flush();
256 if(i == rnum || i % (rnum / 10) == 0){
257 cout << " (" << setw(8) << setfill('0') << i << ")" << endl;
258 cout.flush();
259 }
260 }
261 }
262 // close the database
263 villa->close();
264 cout << "ok" << endl << endl;
265 } catch(Villa_error& e){
266 err = true;
267 // report an error
268 pxdperror(name, e);
269 }
270 // destroy instance
271 if(villa) delete villa;
272 return err ? 1 : 0;
273 }
274
275
276 /* do reading test */
doread(const char * name)277 int doread(const char* name){
278 int i, rnum, len;
279 char buf[RECBUFSIZ], *val;
280 cout << "<Reading Test>" << endl;
281 cout << " name=" << name << endl;
282 cout << endl;
283 Villa* villa = 0;
284 bool err = false;
285 try {
286 // open the database
287 villa = new Villa(name);
288 // get the number of records
289 rnum = villa->rnum();
290 // roop for retrieving
291 for(i = 1; i <= rnum; i++){
292 // set a buffer for a key
293 len = sprintf(buf, "%08d", i);
294 // retrieve a record from the database
295 val = villa->get(buf, len);
296 free(val);
297 // print progression
298 if(rnum > 250 && i % (rnum / 250) == 0){
299 cout << '.';
300 cout.flush();
301 if(i == rnum || i % (rnum / 10) == 0){
302 cout << " (" << setw(8) << setfill('0') << i << ")" << endl;
303 cout.flush();
304 }
305 }
306 }
307 // close the database
308 villa->close();
309 cout << "ok" << endl << endl;
310 } catch(Villa_error& e){
311 err = true;
312 // report an error
313 pxdperror(name, e);
314 }
315 // destroy instance
316 if(villa) delete villa;
317 return err ? 1 : 0;
318 }
319
320
321 /* do multi thread test */
domulti(const char * name,int rnum,int tnum)322 int domulti(const char* name, int rnum, int tnum){
323 Villa* villa;
324 pthread_t* tids;
325 bool* tsts;
326 Mission* mss;
327 Villa_error* ep;
328 int i, len, size, vnum;
329 bool err;
330 char buf[RECBUFSIZ], *val;
331 cout << "<Multi-thread Test>" << endl;
332 cout << " name=" << name << " rnum=" << rnum << " tnum=" << tnum << endl;
333 cout << endl;
334 // open the database
335 villa = 0;
336 try {
337 cout << "Creating a database ... ";
338 villa = new Villa(name, Villa::OWRITER | Villa::OCREAT | Villa::OTRUNC);
339 villa->settuning(LRECMAX, NIDXMAX, LCNUM, NCNUM);
340 cout << "ok" << endl;
341 } catch(Villa_error& e){
342 if(villa) delete villa;
343 pxdperror(name, e);
344 return 1;
345 }
346 // prepare threads
347 tids = new pthread_t[tnum];
348 tsts = new bool[tnum];
349 mss = new Mission[tnum];
350 err = false;
351 // roop for each threads
352 cout << "Writing" << endl;
353 for(i = 0; i < tnum; i++){
354 mss[i].villa = villa;
355 mss[i].rnum = rnum;
356 mss[i].id = i;
357 mss[i].printer = (i == tnum / 2);
358 if(pthread_create(tids + i, 0, mtwriter, (void*)(mss + i)) == 0){
359 tsts[i] = true;
360 } else {
361 tsts[i] = false;
362 err = true;
363 cerr << progname << ": " << name << ": " << "pthread error" << endl;
364 }
365 }
366 // join every threads
367 for(i = 0; i < tnum; i++){
368 if(tsts[i]){
369 if(pthread_join(tids[i], (void**)&ep) != 0){
370 err = true;
371 cerr << progname << ": " << name << ": " << "pthread error" << endl;
372 }
373 }
374 if(ep){
375 err = true;
376 pxdperror(name, *ep);
377 delete ep;
378 }
379 }
380 delete[] mss;
381 delete[] tsts;
382 delete[] tids;
383 if(!err) cout << "ok" << endl;
384 cout.flush();
385 // check every record
386 cout << "Validation checking ... ";
387 try {
388 for(i = 1; i <= rnum; i++){
389 len = sprintf(buf, "%08d", i);
390 val = villa->get(buf, len, &size);
391 free(val);
392 if(size != 1 || villa->vsiz(buf, len) != 1){
393 cerr << progname << ": " << name << ": " << "size error: " << size << endl;
394 err = true;
395 }
396 vnum = villa->vnum(buf, len);
397 if(vnum != tnum){
398 cerr << progname << ": " << name << ": " << "vnum error: " << vnum << endl;
399 err = true;
400 }
401 }
402 } catch(Villa_error& e){
403 pxdperror(name, e);
404 err = true;
405 }
406 if(!err) cout << "ok" << endl;
407 // close the database
408 cout << "Closing the database ... ";
409 try {
410 villa->close();
411 } catch(Villa_error& e){
412 pxdperror(name, e);
413 delete villa;
414 return 1;
415 }
416 cout << "ok" << endl;
417 if(!err) cout << "all ok" << endl << endl;
418 delete villa;
419 return err ? 1 : 0;
420 }
421
422
423 /* writer with each thread */
mtwriter(void * arg)424 void* mtwriter(void* arg){
425 Mission* mp = (Mission*)arg;
426 Villa* villa = mp->villa;
427 int rnum = mp->rnum;
428 int id = mp->id;
429 bool printer = mp->printer;
430 char buf[RECBUFSIZ];
431 void* mc;
432 int i, len;
433 // roop for storing
434 for(i = 1; i <= rnum; i++){
435 len = sprintf(buf, "%08d", i);
436 // store a record
437 bool tran = false;
438 try {
439 if(myrand() % (mp->rnum / 7 + 1) == 0){
440 villa->tranbegin();
441 tran = true;
442 }
443 villa->put(buf, len, id % 2 ? "*" : "=", 1, Villa::DDUP);
444 } catch(Villa_error& e){
445 if(tran) villa->tranabort();
446 return new Villa_error(e);
447 }
448 if(tran) villa->trancommit();
449 // print progression
450 if(printer && rnum > 250 && i % (rnum / 250) == 0){
451 cout << '.';
452 cout.flush();
453 if(i == rnum || i % (rnum / 10) == 0){
454 cout << " (" << setw(8) << setfill('0') << i << ")" << endl;
455 cout.flush();
456 }
457 }
458 // malloc reentrant check
459 if((mc = (char*)malloc(1)) != 0){
460 free(mc);
461 } else {
462 return new Villa_error(Villa::EALLOC);
463 }
464 }
465 return 0;
466 }
467
468
469 /* do combination test */
domisc(const char * name)470 int domisc(const char* name){
471 bool err;
472 int i, len;
473 char buf[RECBUFSIZ], *tmp;
474 cout << "<Miscellaneous Test>" << endl;
475 cout << " name=" << name << endl;
476 cout << endl;
477 err = false;
478 ADBM* adbm = 0;
479 try {
480 // open the database
481 cout << "Creating a database ... ";
482 adbm = new Villa(name, Villa::OWRITER | Villa::OCREAT | Villa::OTRUNC);
483 cout << "ok" << endl;
484 // write with datadase abstraction
485 cout << "Writing with database abstraction ... ";
486 for(i = 1; i <= 100; i++){
487 len = sprintf(buf, "%08d", i);
488 Datum key(buf, len);
489 Datum val(buf, len);
490 adbm->storerec(key, val, true);
491 }
492 cout << "ok" << endl;
493 // read with datadase abstraction
494 cout << "Reading with database abstraction ... ";
495 for(i = 1; i <= 100; i++){
496 len = sprintf(buf, "%08d", i);
497 Datum key(buf, len);
498 const Datum& val = adbm->fetchrec(key);
499 len = val.size();
500 if(len != 8) throw DBM_error("size error");
501 }
502 cout << "ok" << endl;
503 // traversal access with database abstraction
504 cout << "Traversal access with database abstraction ... ";
505 i = 0;
506 try {
507 for(adbm->firstkey(); true; adbm->nextkey()){
508 i++;
509 }
510 } catch(DBM_error& e){
511 if(adbm->error()) throw;
512 }
513 if(i != 100) throw DBM_error("iteration error");
514 cout << "ok" << endl;
515 // close the database
516 cout << "Closing the database ... ";
517 adbm->close();
518 cout << "ok" << endl;
519 } catch(DBM_error& e){
520 err = true;
521 pxdperror(name, e);
522 }
523 if(adbm) delete adbm;
524 if(err) return 1;
525 Villa* villa = 0;
526 try {
527 // open the database
528 cout << "Opening the database ... ";
529 villa = new Villa(name);
530 cout << "ok" << endl;
531 // traversal access
532 cout << "Traversal access ... ";
533 try {
534 villa->curfirst();
535 for(i = 0; true; i++){
536 villa->curnext();
537 }
538 if(i != 100) throw Villa_error();
539 } catch(Villa_error& e){
540 if(e != Villa::ENOITEM) throw;
541 }
542 cout << "ok" << endl;
543 // checking information
544 cout << "Checking information" << endl;
545 cout << " - fsiz ... " << villa->fsiz() << endl;
546 cout << " - lnum ... " << villa->lnum() << endl;
547 cout << " - nnum ... " << villa->nnum() << endl;
548 cout << " - rnum ... " << villa->rnum() << endl;
549 cout << " - writable ... " << villa->writable() << endl;
550 cout << " - fatalerror ... " << villa->fatalerror() << endl;
551 cout << " - inode ... " << villa->inode() << endl;
552 cout << " - mtime ... " << villa->mtime() << endl;
553 cout << "ok" << endl;
554 // close the database
555 cout << "Closing the database ... ";
556 villa->close();
557 cout << "ok" << endl;
558 } catch(Villa_error& e){
559 err = true;
560 pxdperror(name, e);
561 }
562 if(villa) delete villa;
563 if(err) return 1;
564 villa = 0;
565 try {
566 // open the database
567 cout << "Opening the database ... ";
568 villa = new Villa(name, Villa::OWRITER);
569 villa->silent = true;
570 cout << "ok" << endl;
571 // sync the database
572 cout << "Syncing the database ... ";
573 villa->sync();
574 cout << "ok" << endl;
575 // optimize the database
576 cout << "Optimizing the database ... ";
577 villa->optimize();
578 cout << "ok" << endl;
579 // write in silent mode
580 cout << "Writing in silent mode ... ";
581 for(i = 1; i <= 100; i++){
582 len = sprintf(buf, "%d", rand() % 100);
583 villa->put(buf, len, buf, len, Villa::DKEEP);
584 }
585 cout << "ok" << endl;
586 // read in silent mode
587 cout << "Reading in silent mode ... ";
588 for(i = 1; i <= 100; i++){
589 len = sprintf(buf, "%d", rand() % 100);
590 if((tmp = villa->get(buf, len)) != 0) free(tmp);
591 }
592 cout << "ok" << endl;
593 // traversal access
594 cout << "Traversal access in silent mode ... ";
595 villa->curfirst();
596 while((tmp = villa->curkey()) != 0){
597 free(tmp);
598 villa->curnext();
599 }
600 cout << "ok" << endl;
601 // close the database
602 cout << "Closing the database ... ";
603 villa->close();
604 cout << "ok" << endl;
605 } catch(Villa_error& e){
606 err = true;
607 pxdperror(name, e);
608 }
609 if(villa) delete villa;
610 if(err) return 1;
611 villa = 0;
612 try {
613 // open the database
614 cout << "Opening the database with leaves compressed ... ";
615 Villa zvilla(name, Villa::OWRITER | Villa::OCREAT | Villa::OTRUNC | Villa::OZCOMP);
616 cout << "ok" << endl;
617 // write records
618 cout << "Writing records ... ";
619 for(i = 1; i <= 100; i++){
620 len = sprintf(buf, "%08d", i);
621 zvilla.put(buf, len, buf,len, Villa::DCAT);
622 zvilla.put(buf, len, buf,len, Villa::DDUP);
623 zvilla.put(buf, len, buf,len, Villa::DCAT);
624 }
625 cout << "ok" << endl;
626 // read records
627 cout << "Checking records ... ";
628 for(i = 1; i <= 100; i++){
629 len = sprintf(buf, "%08d", i);
630 if(zvilla.vnum(buf, len) != 2) throw Villa_error();
631 }
632 cout << "ok" << endl;
633 // cursor operations
634 cout << "Cursor operations ... ";
635 zvilla.curjump("00000010", -1, Villa::JFORWARD);
636 zvilla.curput("CURRENT", -1, Villa::CPCURRENT);
637 zvilla.curput("BEFORE", -1, Villa::CPBEFORE);
638 zvilla.curput("AFTER", -1, Villa::CPAFTER);
639 zvilla.curnext();
640 zvilla.curout();
641 zvilla.curput("OMIT", -1);
642 cout << "ok" << endl;
643 // close the database
644 cout << "Closing the database ... ";
645 zvilla.close();
646 cout << "ok" << endl;
647 } catch(Villa_error& e){
648 err = true;
649 pxdperror(name, e);
650 }
651 if(err) return 1;
652 cout << "all ok" << endl << endl;
653 return 0;
654 }
655
656
657
658 /* END OF FILE */
659