/*************************************************************************************************
 * Test cases of Cabin
 *                                                      Copyright (C) 2000-2003 Mikio Hirabayashi
 * This file is part of QDBM, Quick Database Manager.
 * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License or any later version.  QDBM is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 * details.
 * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307 USA.
 *************************************************************************************************/


#include <cabin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#undef TRUE
#define TRUE           1                 /* boolean true */
#undef FALSE
#define FALSE          0                 /* boolean false */

#define RECBUFSIZ      32                /* buffer for records */


/* for RISC OS */
#if defined(__riscos__) || defined(__riscos)
#include <unixlib/local.h>
int __riscosify_control = __RISCOSIFY_NO_PROCESS;
#endif


/* global variables */
const char *progname;                    /* program name */


/* function prototypes */
int main(int argc, char **argv);
void usage(void);
int runsort(int argc, char **argv);
int runlist(int argc, char **argv);
int runmap(int argc, char **argv);
int runwicked(int argc, char **argv);
int runmisc(int argc, char **argv);
int printfflush(const char *format, ...);
int strpcmp(const void *ap, const void *bp);
int myrand(void);
int dosort(int rnum, int disp);
int dolist(int rnum, int disp);
int domap(int rnum, int disp);
int dowicked(int rnum);
int domisc(void);


/* main routine */
int main(int argc, char **argv){
  int rv;
  progname = argv[0];
  if(argc < 2) usage();
  rv = 0;
  if(!strcmp(argv[1], "sort")){
    rv = runsort(argc, argv);
  } else if(!strcmp(argv[1], "list")){
    rv = runlist(argc, argv);
  } else if(!strcmp(argv[1], "map")){
    rv = runmap(argc, argv);
  } else if(!strcmp(argv[1], "wicked")){
    rv = runwicked(argc, argv);
  } else if(!strcmp(argv[1], "misc")){
    rv = runmisc(argc, argv);
  } else {
    usage();
  }
  return rv;
}


/* print the usage and exit */
void usage(void){
  fprintf(stderr, "%s: test cases for Cabin\n", progname);
  fprintf(stderr, "\n");
  fprintf(stderr, "usage:\n");
  fprintf(stderr, "  %s sort [-d] rnum\n", progname);
  fprintf(stderr, "  %s list [-d] rnum\n", progname);
  fprintf(stderr, "  %s map [-d] rnum\n", progname);
  fprintf(stderr, "  %s wicked rnum\n", progname);
  fprintf(stderr, "  %s misc\n", progname);
  exit(1);
}


/* parse arguments of sort command */
int runsort(int argc, char **argv){
  int i, rnum, disp, rv;
  char *rstr;
  rstr = NULL;
  rnum = 0;
  disp = FALSE;
  for(i = 2; i < argc; i++){
    if(argv[i][0] == '-'){
      if(!strcmp(argv[i], "-d")){
        disp = TRUE;
      } else {
        usage();
      }
    } else if(!rstr){
      rstr = argv[i];
    } else {
      usage();
    }
  }
  if(!rstr) usage();
  rnum = atoi(rstr);
  if(rnum < 1) usage();
  rv = dosort(rnum, disp);
  return rv;
}


/* parse arguments of list command */
int runlist(int argc, char **argv){
  int i, rnum, disp, rv;
  char *rstr;
  rstr = NULL;
  rnum = 0;
  disp = FALSE;
  for(i = 2; i < argc; i++){
    if(argv[i][0] == '-'){
      if(!strcmp(argv[i], "-d")){
        disp = TRUE;
      } else {
        usage();
      }
    } else if(!rstr){
      rstr = argv[i];
    } else {
      usage();
    }
  }
  if(!rstr) usage();
  rnum = atoi(rstr);
  if(rnum < 1) usage();
  rv = dolist(rnum, disp);
  return rv;
}


/* parse arguments of map command */
int runmap(int argc, char **argv){
  int i, rnum, disp, rv;
  char *rstr;
  rstr = NULL;
  rnum = 0;
  disp = FALSE;
  for(i = 2; i < argc; i++){
    if(argv[i][0] == '-'){
      if(!strcmp(argv[i], "-d")){
        disp = TRUE;
      } else {
        usage();
      }
    } else if(!rstr){
      rstr = argv[i];
    } else {
      usage();
    }
  }
  if(!rstr) usage();
  rnum = atoi(rstr);
  if(rnum < 1) usage();
  rv = domap(rnum, disp);
  return rv;
}


/* parse arguments of wicked command */
int runwicked(int argc, char **argv){
  int i, rnum, rv;
  char *rstr;
  rstr = NULL;
  rnum = 0;
  for(i = 2; i < argc; i++){
    if(argv[i][0] == '-'){
      usage();
    } else if(!rstr){
      rstr = argv[i];
    } else {
      usage();
    }
  }
  if(!rstr) usage();
  rnum = atoi(rstr);
  if(rnum < 1) usage();
  rv = dowicked(rnum);
  return rv;
}


/* parse arguments of misc command */
int runmisc(int argc, char **argv){
  int rv;
  rv = domisc();
  return rv;
}


/* print formatted string and flush the buffer */
int prinfflush(const char *format, ...){
  va_list ap;
  int rv;
  va_start(ap, format);
  rv = vprintf(format, ap);
  if(fflush(stdout) == EOF) rv = -1;
  va_end(ap);
  return rv;
}


/* comparing function */
int strpcmp(const void *ap, const void *bp){
  char *a, *b;
  a = *(char **)ap;
  b = *(char **)bp;
  return strcmp(a, b);
}


/* pseudo random number generator */
int myrand(void){
  static int cnt = 0;
  if(cnt == 0) srand(time(NULL));
  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & 0x7FFFFFFF;
}


/* perform sort command */
int dosort(int rnum, int disp){
  char **ivector1, **ivector2, **ivector3, **ivector4, **ivector5;
  char buf[RECBUFSIZ];
  int i, len, err;
  if(!disp) prinfflush("<Sorting Test>\n  rnum=%d  disp=%d\n\n", rnum, disp);
  ivector1 = cbmalloc(rnum * sizeof(ivector1[0]));
  ivector2 = cbmalloc(rnum * sizeof(ivector2[0]));
  ivector3 = cbmalloc(rnum * sizeof(ivector3[0]));
  ivector4 = cbmalloc(rnum * sizeof(ivector4[0]));
  ivector5 = cbmalloc(rnum * sizeof(ivector5[0]));
  err = FALSE;
  for(i = 0; i < rnum; i++){
    len = sprintf(buf, "%08d", myrand() % rnum + 1);
    ivector1[i] = cbmemdup(buf, len);
    ivector2[i] = cbmemdup(buf, len);
    ivector3[i] = cbmemdup(buf, len);
    ivector4[i] = cbmemdup(buf, len);
    ivector5[i] = cbmemdup(buf, len);
  }
  if(!disp) prinfflush("Sorting with insert sort ... ");
  cbisort(ivector1, rnum, sizeof(ivector1[0]), strpcmp);
  if(!disp) prinfflush("ok\n");
  if(!disp) prinfflush("Sorting with shell sort ... ");
  cbssort(ivector2, rnum, sizeof(ivector2[0]), strpcmp);
  if(!disp) prinfflush("ok\n");
  if(!disp) prinfflush("Sorting with heap sort ... ");
  cbhsort(ivector3, rnum, sizeof(ivector3[0]), strpcmp);
  if(!disp) prinfflush("ok\n");
  if(!disp) prinfflush("Sorting with quick sort ... ");
  cbqsort(ivector4, rnum, sizeof(ivector4[0]), strpcmp);
  if(!disp) prinfflush("ok\n");
  for(i = 0; i < rnum; i++){
    if(disp) prinfflush("%s\t%s\t%s\t%s\t[%s]\n",
                        ivector1[i], ivector2[i], ivector3[i], ivector4[i], ivector5[i]);
    if(strcmp(ivector1[i], ivector2[i])) err = TRUE;
    if(strcmp(ivector1[i], ivector3[i])) err = TRUE;
    if(strcmp(ivector1[i], ivector4[i])) err = TRUE;
    free(ivector1[i]);
    free(ivector2[i]);
    free(ivector3[i]);
    free(ivector4[i]);
    free(ivector5[i]);
  }
  free(ivector1);
  free(ivector2);
  free(ivector3);
  free(ivector4);
  free(ivector5);
  if(err) fprintf(stderr, "%s: sorting failed\n", progname);
  if(!disp && !err) prinfflush("all ok\n\n");
  return err ? 1 : 0;
}


/* perform list command */
int dolist(int rnum, int disp){
  CBLIST *list;
  int i, err, len;
  char buf[RECBUFSIZ];
  const char *val;
  if(!disp) prinfflush("<List Writing Test>\n  rnum=%d  disp=%d\n\n", rnum, disp);
  list = cblistopen();
  err = FALSE;
  for(i = 1; i <= rnum; i++){
    len = sprintf(buf, "%08d", i);
    cblistpush(list, buf, len);
    if(!disp && rnum > 250 && i % (rnum / 250) == 0){
      putchar('.');
      fflush(stdout);
      if(i == rnum || i % (rnum / 10) == 0){
        prinfflush(" (%08d)\n", i);
      }
    }
  }
  if(disp){
    for(i = 0; i < cblistnum(list); i++){
      if((val = cblistval(list, i, NULL)) != NULL){
        prinfflush("%s\n", val);
      } else {
        fprintf(stderr, "%s: val error\n", progname);
        err = TRUE;
        break;
      }
    }
  }
  cblistclose(list);
  if(!disp && !err) prinfflush("ok\n\n");
  return err ? 1 : 0;
}


/* perform list command */
int domap(int rnum, int disp){
  CBMAP *map;
  int i, err, len;
  char buf[RECBUFSIZ];
  const char *val;
  if(!disp) prinfflush("<Map Writing Test>\n  rnum=%d  disp=%d\n\n", rnum, disp);
  map = cbmapopen();
  err = FALSE;
  for(i = 1; i <= rnum; i++){
    len = sprintf(buf, "%08d", i);
    if(!cbmapput(map, buf, len, buf, len, FALSE)){
      fprintf(stderr, "%s: put error\n", progname);
      err = TRUE;
      break;
    }
    if(!disp && rnum > 250 && i % (rnum / 250) == 0){
      putchar('.');
      fflush(stdout);
      if(i == rnum || i % (rnum / 10) == 0){
        prinfflush(" (%08d)\n", i);
      }
    }
  }
  if(disp){
    for(i = 1; i <= rnum; i++){
      len = sprintf(buf, "%08d", i);
      if((val = cbmapget(map, buf, len, NULL)) != NULL){
        prinfflush("%s\n", val);
      } else {
        fprintf(stderr, "%s: get error\n", progname);
      }
    }
  }
  cbmapclose(map);
  if(!disp && !err) prinfflush("ok\n\n");
  return err ? 1 : 0;
}


/* perform wicked command */
int dowicked(int rnum){
  CBLIST *list;
  CBMAP *map;
  int i, len;
  char buf[RECBUFSIZ];
  prinfflush("<Wicked Writing Test>\n  rnum=%d\n\n", rnum);
  list = cblistopen();
  for(i = 1; i <= rnum; i++){
    len = sprintf(buf, "%08d", myrand() % rnum + 1);
    switch(myrand() % 16){
    case 0:
      free(cblistpop(list, NULL));
      putchar('O');
      break;
    case 1:
      cblistunshift(list, buf, len);
      putchar('U');
      break;
    case 2:
      free(cblistshift(list, NULL));
      putchar('S');
      break;
    case 3:
      cblistinsert(list, myrand() % (cblistnum(list) + 1), buf, len);
      putchar('I');
      break;
    case 4:
      free(cblistremove(list, myrand() % (cblistnum(list) + 1), NULL));
      putchar('R');
      break;
    default:
      cblistpush(list, buf, len);
      putchar('P');
      break;
    }
    if(i % 50 == 0) prinfflush(" (%08d)\n", i);
  }
  cblistclose(list);
  map = cbmapopen();
  for(i = 1; i <= rnum; i++){
    len = sprintf(buf, "%08d", myrand() % rnum + 1);
    switch(myrand() % 16){
    case 0:
      cbmapput(map, buf, len, buf, len, FALSE);
      putchar('I');
      break;
    case 1:
      cbmapget(map, buf, len, NULL);
      putchar('V');
      break;
    case 2:
      cbmapout(map, buf, len);
      putchar('D');
      break;
    case 3:
      cbmapmove(map, buf, len, myrand() % 2);
      putchar('M');
      break;
    default:
      cbmapput(map, buf, len, buf, len, TRUE);
      putchar('O');
      break;
    }
    if(i % 50 == 0) prinfflush(" (%08d)\n", i);
  }
  cbmapclose(map);
  prinfflush("ok\n\n");
  return 0;
}


/* perform misc command */
int domisc(void){
  CBLIST *olist, *nlist, *elems;
  CBMAP *omap, *nmap, *pairs;
  int i, ssiz, osiz;
  char kbuf[RECBUFSIZ], vbuf[RECBUFSIZ], *sbuf, spbuf[1024], *tmp, *orig;
  const char *op, *np;
  prinfflush("<Miscellaneous Test>\n\n");
  prinfflush("Checking serialization of list ... ");
  olist = cblistopen();
  for(i = 1; i <= 1000; i++){
    sprintf(vbuf, "%d", i);
    cblistpush(olist, vbuf, -1);
  }
  sbuf = cblistdump(olist, &ssiz);
  nlist = cblistload(sbuf, ssiz);
  free(sbuf);
  for(i = 0; i < cblistnum(olist); i++){
    op = cblistval(olist, i, NULL);
    np = cblistval(nlist, i, NULL);
    if(!op || !np || strcmp(op, np)){
      cblistclose(nlist);
      cblistclose(olist);
      fprintf(stderr, "%s: validation failed\n", progname);
      return 1;
    }
  }
  cblistclose(nlist);
  cblistclose(olist);
  prinfflush("ok\n");
  prinfflush("Checking serialization of map ... ");
  omap = cbmapopen();
  for(i = 1; i <= 1000; i++){
    sprintf(kbuf, "%X", i);
    sprintf(vbuf, "[%d]", i);
  }
  sbuf = cbmapdump(omap, &ssiz);
  nmap = cbmapload(sbuf, ssiz);
  free(sbuf);
  cbmapiterinit(omap);
  while((op = cbmapiternext(omap, NULL)) != NULL){
    if(!(np = cbmapget(nmap, op, -1, NULL))){
      cbmapclose(nmap);
      cbmapclose(omap);
      fprintf(stderr, "%s: validation failed\n", progname);
      return 1;
    }
  }
  cbmapclose(nmap);
  cbmapclose(omap);
  prinfflush("ok\n");
  prinfflush("Checking string utilities ... ");
  sprintf(spbuf, "[%08d/%08o/%08u/%08x/%08X/%08.3e/%08.3E/%08.3f/%08.3g/%08.3G/%c/%s/%%]",
          123456, 123456, 123456, 123456, 123456,
          123456.789, 123456.789, 123456.789, 123456.789, 123456.789,
          'A', "hoge");
  tmp = cbsprintf("[%08d/%08o/%08u/%08x/%08X/%08.3e/%08.3E/%08.3f/%08.3g/%08.3G/%c/%s/%%]",
                  123456, 123456, 123456, 123456, 123456,
                  123456.789, 123456.789, 123456.789, 123456.789, 123456.789,
                  'A', "hoge");
  while(strcmp(spbuf, tmp)){
    free(tmp);
    fprintf(stderr, "%s: cbsprintf is invalid\n", progname);
    return 1;
  }
  free(tmp);
  pairs = cbmapopen();
  cbmapput(pairs, "aa", -1, "AAA", -1, TRUE);
  cbmapput(pairs, "bb", -1, "BBB", -1, TRUE);
  cbmapput(pairs, "cc", -1, "CCC", -1, TRUE);
  cbmapput(pairs, "ZZZ", -1, "z", -1, TRUE);
  tmp = cbreplace("[aaaaabbbbbcccccdddddZZZZ]", pairs);
  if(strcmp(tmp, "[AAAAAAaBBBBBBbCCCCCCcdddddzZ]")){
    free(tmp);
    cbmapclose(pairs);
    fprintf(stderr, "%s: cbreplace is invalid\n", progname);
    return 1;
  }
  free(tmp);
  cbmapclose(pairs);
  elems = cbsplit("aa bb,ccc-dd,", -1, " ,-");
  if(cblistnum(elems) != 5 || strcmp(cblistval(elems, 0, NULL), "aa") ||
     strcmp(cblistval(elems, 1, NULL), "bb") || strcmp(cblistval(elems, 2, NULL), "ccc") ||
     strcmp(cblistval(elems, 3, NULL), "dd") || strcmp(cblistval(elems, 4, NULL), "")){
    cblistclose(elems);
    fprintf(stderr, "%s: cbsplit is invalid\n", progname);
    return 1;
  }
  cblistclose(elems);
  prinfflush("ok\n");
  prinfflush("Checking encoding utilities ... ");
  strcpy(spbuf, "My name is \xca\xbf\xce\xd3\xb4\xb4\xcd\xba.\n\nGo ahead!\n");
  tmp = cburlencode(spbuf, -1);
  orig = cburldecode(tmp, &osiz);
  if(osiz != strlen(spbuf) || strcmp(orig, spbuf)){
    free(orig);
    free(tmp);
    fprintf(stderr, "%s: URL encoding is invalid\n", progname);
    return 1;
  }
  free(orig);
  free(tmp);
  tmp = cbbaseencode(spbuf, -1);
  orig = cbbasedecode(tmp, &osiz);
  if(osiz != strlen(spbuf) || strcmp(orig, spbuf)){
    free(orig);
    free(tmp);
    fprintf(stderr, "%s: Base64 encoding is invalid\n", progname);
    return 1;
  }
  free(orig);
  free(tmp);
  tmp = cbquoteencode(spbuf, -1);
  orig = cbquotedecode(tmp, &osiz);
  if(osiz != strlen(spbuf) || strcmp(orig, spbuf)){
    free(orig);
    free(tmp);
    fprintf(stderr, "%s: quoted-printable encoding is invalid\n", progname);
    return 1;
  }
  free(orig);
  free(tmp);
  prinfflush("ok\n");
  prinfflush("all ok\n\n");
  return 0;
}



/* END OF FILE */
