/*
* re_tests.c
*
* A programmers editor
*
* Tests (ensure correct functionality of modules)
*
* Author: Dario Rodriguez dario@softhome.net
* 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,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);
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");
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");
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);
redata_save(redata,"test.undo");
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);
redata_save(redata,"test.redo");
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,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);
}