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