/* * re_tests.c * * A programmers editor * * Tests (ensure correct functionality of modules) * * Author: Dario Rodriguez antartica@whereismybit.com * This program is licensed under the terms of GNU GPL v2.1+ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <limits.h> #include <fcntl.h> #include <errno.h> #include "re_data.h" #define PREFIX "retest_" #define TEST_OK "OK" typedef struct test_t { char *name; char *(*fn)(redata_t *,char *,char *, int, int); char *param1; char *param2; int int1; int int2; } test_t; static char *malloc_data(int size, int seed); static int write_file(char *filename, char *buf, int buflen); char *test_newfile(redata_t *redata, char *filename, char *dummy, int filesize, int dummy2); char *test_edit(redata_t *redata, char *filename, char *edits, int filesize, int seed); int main(int argc, char *argv[]) { static int sizes[]={-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,30,31,32,33,63,64,65,127,128,129,1023,1024,1025,16384}; test_t tests[]={ {"newfile_16",test_newfile,PREFIX "FILE", NULL, 16, 0}, {"newfile_1024",test_newfile,PREFIX "FILE", NULL, 1024, 0}, {"newfile_32767",test_newfile,PREFIX "FILE", NULL, 32767, 0}, {"newfile_32768",test_newfile,PREFIX "FILE", NULL, 32768, 0}, {"newfile_32769",test_newfile,PREFIX "FILE", NULL, 32769, 0}, {"newfile_131072",test_newfile,PREFIX "FILE", NULL, 131072, 0}, {"testedit_add",test_edit,PREFIX "EDIT", "A0$Testing add$",0,0}, {"testedit_del",test_edit,PREFIX "EDIT", "A0$Testing add/del$D8$add/$",0,0}, {"testedit_move",test_edit,PREFIX "EDIT", "A0$This is a text to move.$M17,7$ move$",0,0}, }; int flag_exit=0,flag_all=0; redata_t *redata; int i,s; int nerrors,total; char *res; for(i=1;i<argc;i++) { if(strcmp(argv[i],"--help")==0) { fprintf(stderr,"Syntax: %s [--all] [--exit] [--help]\nExplanation:\n\t--all: do even the slow tests\n\t--exit: exit program at first unsuccessful test\n\t--help: this text\n",argv[0]); return(1); } else if(strcmp(argv[i],"--all")==0) { flag_all=1; } else if(strcmp(argv[i],"--exit")==0) { flag_exit=1; } } nerrors=0; total=0; for(s=0;s<(sizeof(sizes)/sizeof(sizes[0]));s++) { if(sizes[s]!=-1) fprintf(stderr,"SIZE: %i\n",sizes[s]); for(i=0;i<(sizeof(tests)/sizeof(tests[0]));i++,total++) { if(!flag_all && tests[i].int1>=1024) continue; /* too slow: skip unless testing --all */ if((redata=redata_init(NULL))==NULL) { fprintf(stderr,"ERROR: problem initializing redata module\n"); return(1); } if(sizes[s]!=-1) redata_config_chunkdatasize(redata,sizes[s]); fprintf(stderr,"%s...",tests[i].name); res=tests[i].fn(redata,tests[i].param1,tests[i].param2,tests[i].int1,tests[i].int2); if(strcmp(res,TEST_OK)==0) { fprintf(stderr," ok.\n"); } else { fprintf(stderr," ERROR: %s\n",res); nerrors++; if(flag_exit) { /* exit on first error */ s=sizeof(sizes)/sizeof(sizes[0]); break; } } redata_free(redata),redata=NULL; } fprintf(stderr,"\n"); } if(nerrors==0) fprintf(stderr,"All %i tests passed OK\n",total); else fprintf(stderr,"%i test(s) failed of %i tests run.\n",nerrors,total); redata_free(redata),redata=NULL; return((nerrors==0)?0:1); } static char * malloc_data(int size, int seed) { char *mem; int i; if(size<=0 || (mem=malloc(size))==NULL) return(NULL); memset(mem,0,size); if(seed==0) { for(i=0;i<size;i++) ((unsigned char *)mem)[i]=(i%256); } else { srandom(*((unsigned int *)(&seed))); for(i=0;i<size;i++) ((unsigned char *)mem)[i]=(random()&0xff); } return(mem); } static int write_file(char *filename, char *buf, int buflen) { int fd; if((fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644))==-1) { free(buf),buf=NULL; return(-1); } write(fd,buf,buflen); close(fd),fd=-1; return(0); } char * test_newfile(redata_t *redata, char *filename, char *dummy, int filesize, int dummy2) { char *mem; char hash129_pre[129]; char hash129_post[129]; fprintf(stderr,"\ntest_newfile(%s%s%s,%s%s%s,%i,%i);\nResult: ",(filename!=NULL)?"\"":"",(filename!=NULL)?filename:"NULL",(filename!=NULL)?"\"":"",(dummy!=NULL)?"\"":"",(dummy!=NULL)?dummy:"NULL",(dummy!=NULL)?"\"":"",filesize,dummy2); /* prepare file for loading */ if((mem=malloc_data(filesize,0))==NULL) return("insuf. mem. for temp. buffer"); if(write_file(filename,mem,filesize)==-1) return("couldn't create temporary file"); redata_filehash(redata,filename,hash129_pre); free(mem),mem=NULL; /* load file */ if(redata_load(redata,filename,NULL)!=0) { unlink(filename); return("couldn't load file"); } unlink(filename); redata_hash(redata,hash129_post); if(strcmp(hash129_pre,hash129_post)!=0) return("loaded file is corrupted"); /* save file */ redata_save(redata,filename,NULL); hash129_post[0]='\0'; redata_filehash(redata,filename,hash129_post); if(strcmp(hash129_pre,hash129_post)!=0) return("saved file is corrupted"); unlink(filename); return(TEST_OK); } char * test_edit(redata_t *redata, char *filename, char *edits, int filesize, int seed) { static char errorbuf[256]; char progress[256]; int k; int cursize,maxsize; char *ptr; int l,o; char *mem; char endcode; char *ptrend; int size; char hash129_memold[129]; char hash129_mem[129]; char hash129_redata[129]; mem=NULL; fprintf(stderr,"\ntest_edit(%s%s%s,%s%s%s,%i,%i);\nResult: ",(filename!=NULL)?"\"":"",(filename!=NULL)?filename:"NULL",(filename!=NULL)?"\"":"",(edits!=NULL)?"\"":"",(edits!=NULL)?edits:"NULL",(edits!=NULL)?"\"":"",filesize,seed); /* two passes: k==0 count needed memory, k==1 do edits */ for(k=0,maxsize=cursize=filesize,progress[0]='\0';k<2;k++,cursize=0,progress[0]='\0') { for(ptr=edits;*ptr!='\0';) { if(k!=0) { redata_memhash(redata,mem,cursize,hash129_memold); } if((l=strlen(progress))<(sizeof(progress)-1)) { progress[l]=(l<(sizeof(progress)-2))?*ptr:'+'; progress[l+1]='\0'; } if(k!=0) unlink("test.pre"),unlink("test.post"),unlink("test.undo"),unlink("test.redo"); if(k!=0) redata_save(redata,"test.pre",NULL); if(*ptr=='A') { /* A<insertpos><endchar><text><endchar> */ ptr++; errno=0; l=(int)strtol(ptr,&ptr,10); if(errno!=0 || l<0 || l>cursize) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): error parsing position"); } endcode=*ptr; ptr+=(*ptr!='\0')?1:0; ptrend=strchr(ptr,endcode); ptrend=(ptrend==NULL)?ptr+strlen(ptr):ptrend; size=ptrend-ptr; if(k!=0) { /* editing */ memmove(mem+l+size,mem+l,cursize-l); memcpy(mem+l,ptr,size); redata_op_add(redata,l,ptr,size,NULL); } cursize+=size; ptr+=size; ptr+=(*ptr!='\0')?1:0; } else if(*ptr=='D') { /* D<delpos><endchar><text><endchar> */ ptr++; errno=0; l=(int)strtol(ptr,&ptr,10); if(errno!=0) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): error parsing position"); } endcode=*ptr; ptr+=(*ptr!='\0')?1:0; ptrend=strchr(ptr,endcode); ptrend=(ptrend==NULL)?ptr+strlen(ptr):ptrend; size=ptrend-ptr; if(l<0 || (l+size)>cursize) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): internal error: invalid pasition or size"); } if(k!=0) { if(memcmp(mem+l,ptr,size)!=0 || redata_data_compare(redata,l,ptr,size)!=0) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): internal error: deletion data doesn't match"); } /* editing */ memmove(mem+l,mem+l+size,cursize-l-size); redata_op_del(redata,l,size,NULL); } cursize-=size; ptr+=size; ptr+=(*ptr!='\0')?1:0; } else if(*ptr=='M') { /* M<origpos>,<destpos><endchar><text><endchar> */ ptr++; errno=0; l=(int)strtol(ptr,&ptr,10); if(errno!=0) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): error parsing position"); } ptr+=(*ptr==',')?1:0; errno=0; o=(int)strtol(ptr,&ptr,10); if(errno!=0) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): error parsing position"); } endcode=*ptr; ptr+=(*ptr!='\0')?1:0; ptrend=strchr(ptr,endcode); ptrend=(ptrend==NULL)?ptr+strlen(ptr):ptrend; size=ptrend-ptr; if(l<0 || (l+size)>cursize || o<0 || o>cursize || (o>=l && o<(l+size))) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): internal error: invalid pasition or size"); } if(k!=0) { if(memcmp(mem+l,ptr,size)!=0 || redata_data_compare(redata,l,ptr,size)!=0) { if(mem!=NULL) free(mem),mem=NULL; return("test_edit(): internal error: move data doesn't match"); } /* editing */ if(l>o) { memmove(mem+o+size,mem+o,l-o); memcpy(mem+o,ptr,size); } else { memmove(mem+l,mem+l+size,o-l-size); memcpy(mem+o-size,ptr,size); } redata_op_move(redata,l,size,o,NULL); } ptr+=size; ptr+=(*ptr!='\0')?1:0; } else { if(mem!=NULL) free(mem),mem=NULL; snprintf(errorbuf,sizeof(errorbuf), "test_edit(): unknown edit action at pos %i: '%c' (progress: %s)",(int) (ptr-edits),*ptr,progress); errorbuf[sizeof(errorbuf)-1]='\0'; return(errorbuf); } if(cursize>maxsize) maxsize=cursize; if(k!=0) { if(k!=0) redata_save(redata,"test.post",NULL); redata_hash(redata,hash129_redata); redata_memhash(redata,mem,cursize,hash129_mem); if(strcmp(hash129_redata,hash129_mem)!=0) { if(mem!=NULL) free(mem),mem=NULL; snprintf(errorbuf,sizeof(errorbuf), "corrupted edit before pos %i (progress: %s)",(int) (ptr-edits),progress); errorbuf[sizeof(errorbuf)-1]='\0'; return(errorbuf); } redata_op_undo(redata,NULL); redata_save(redata,"test.undo",NULL); redata_hash(redata,hash129_redata); if(strcmp(hash129_redata,hash129_memold)!=0) { if(mem!=NULL) free(mem),mem=NULL; snprintf(errorbuf,sizeof(errorbuf), "corrupted undo before pos %i (progress: %s)",(int) (ptr-edits),progress); errorbuf[sizeof(errorbuf)-1]='\0'; return(errorbuf); } redata_op_redo(redata,NULL); redata_save(redata,"test.redo",NULL); redata_hash(redata,hash129_redata); if(strcmp(hash129_redata,hash129_mem)!=0) { if(mem!=NULL) free(mem),mem=NULL; snprintf(errorbuf,sizeof(errorbuf), "corrupted redo before pos %i (progress: %s)",(int) (ptr-edits),progress); errorbuf[sizeof(errorbuf)-1]='\0'; return(errorbuf); } } } if(k==0) { /* reserve memory, do init */ if((mem=malloc_data(maxsize,seed))==NULL) return("insuf. mem. for temp. buffer"); if(write_file(filename,mem,filesize)==-1) return("couldn't create temporary file"); if(redata_load(redata,filename,NULL)!=0) { unlink(filename); return("couldn't load file"); } unlink(filename); } } if(mem!=NULL) free(mem),mem=NULL; unlink("test.pre"),unlink("test.post"),unlink("test.undo"),unlink("test.redo"); return(TEST_OK); }