/* Evil tests */


#include "cstring.h"
#include "cvector.h"
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <string.h>

int blow_stack() {
  int x[10000];
  int i;

  for (i = 0; i < 10000; i++) x[i] = 0;
}

void test_new_String() {
  String *s;
  printf("Testing new_String:\n");
  printf("    Creating a string : ");
  s = new_String("Hello World");
  printf("%s\n", s ? "OK" : "FAILED!");

  printf("    Checking for heap allocation : ");
  if (s > (String *) sbrk(0)) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking NULL pointer : ");
  s = new_String(0);
  /* Shouldn't crash */
  if (!s) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
}


void test_init() {
  String *s, *t;
  printf("Testing init_String():\n");
  
  s = new_String("Hello World\n");
  printf("    Checking init (smaller) : ");
  init_String(s,"Foo");
  if (strcmp(String_cstr(s), "Foo") != 0) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking init (larger) : ");
  init_String(s,"This is a test with a somewhat longer string");
  if (strcmp(String_cstr(s),"This is a test with a somewhat longer string") != 0) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
}

void test_copy() {
  String *s, *t;

  printf("Testing copy_String():\n");
  
  s = new_String("Hello");
  t = copy_String(s);

  printf("    Checking that returned pointer is difference : ");
  if (s != t) printf("OK\n");
  else printf("FAILED!\n");

  printf("    Checking that strings compare the same : ");
  if (String_strcmp(s,t) == 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  printf("    Checking that String_cstr() returns different values : ");
  if (String_cstr(s) != String_cstr(t)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  
  printf("    Checking length : ");
  if (String_len(s) == String_len(t)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  printf("    Checking that data was actually copied : ");
  {
    char *c = (char *) String_cstr(s);
    *c = 'x';
    if (String_strcmp(s,t) != 0) {
      printf("OK\n");
    } else {
      printf("FAILED!\n");
    }
  }
}

void test_integrity() {
  String *s, *t;
  char *c;
  const char *cs;
  const char *ss;

  printf("Testing integrity of string data\n");
  
  c = "Hello";
  s = new_String(c);

  printf("    Checking length : ");
  if (String_len(s) != 5) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  cs = String_cstr(s);

  printf("    Checking heap allocation of cstr : ");
  if (cs > (const char *) sbrk(0)) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking that cstr returns same pointer each time : ");
  ss = String_cstr(s);
  if (cs != ss) {
    printf("FAILED\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking that string makes a copy of initial value : ");
  if (cs != c) {
    printf("OK\n");
  } else {
    printf("FAILED\n");
  }
}

void test_cmp() {
  String *s,*t,*w;

  printf("Testing String_strcmp()\n");
  s = new_String("Hello");
  t = new_String("World");
  w = new_String("Hello");
  printf("    Checking equal : ");
  if (String_strcmp(s,w) == 0) {
    printf("OK\n");
  } else {
    printf("FAILED\n");
  }
  printf("    Checking < : ");
  if (String_strcmp(s,t) < 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  printf("    Checking > : ");
  if (String_strcmp(t,s) > 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  delete_String(s);
  delete_String(t);
  delete_String(w);
}

void test_strstr() {
  String *s, *t, *w;

  printf("Testing String_strstr()\n");
  s = new_String("This is a test\n");
  t = new_String("is");

  printf("    Checking for substring (found) : ");
  if (String_strstr(s,t) != 2) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking for substring (not found) : ");
  init_String(t,"whatever");
  if (String_strstr(s,t) != -1) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
}


void test_strchr() {
  String *s;

  printf("Testing String_strchr()\n");
  s = new_String("This is a test\n");

  printf("    Checking for character (found) : ");
  if (String_strchr(s,'a') != 8) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking for character (not found) : ");
  if (String_strchr(s,'z') != -1) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
}

void test_slice() {
  String *s, *t, *w;

  printf("Testing String_getslice():\n");

  s = new_String("I love my attitude problem");
  t = new_String("attitude");

  printf("    Checking slice middle : ");
  w = String_getslice(s,10,18);
  if ((String_strcmp(t,w) != 0) || (String_len(w) != 8)) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking slice start : ");
  w = String_getslice(s,0,3);
  init_String(t,"I l");
  if ((String_strcmp(w,t) != 0) || (String_len(w) != 3)) {
    printf("FAILED\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking slice end : ");
  w = String_getslice(s, String_len(s) - 4, String_len(s));
  init_String(t,"blem");
  if ((String_strcmp(w,t) != 0) || (String_len(w) != 4)) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  delete_String(w);
  delete_String(t);
  delete_String(s);
}


void test_strcat() {
  String *s, *t, *w;

  printf("Testing String_strcat()\n");
  s = new_String("Hello");
  t = new_String("World");
  w = new_String("HelloWorld");

  String_strcat(s,t);
  printf("    Checking value : ");
  if (String_strcmp(s,w) == 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  printf("    Checking length : ");
  if (String_len(s) == 10) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  printf("    Checking repeated concatenation : ");
  fflush(stdout);
  {
    int i;
    for (i = 0; i < 100000; i++) {
      String_strcat(s,t);
    }
    if (String_len(s) == 500010) {
      printf("OK\n");
    } else {
      printf("FAILED!\n");
    }
  }
}

void test_readline() {
  FILE *f;
  String *s;

  printf("Testing String_readline():\n");
  
  f = tmpfile();
  fprintf(f,"Hello\n"
	  "World\n"
	  "This is a test\n"
	  "\n"
	  "Last line");
  fseek(f,0,SEEK_SET);
  s = new_String("");

  printf("    Checking a simple line : ");
  String_readline(s,f,10);
  if ((strcmp(String_cstr(s),"Hello\n") == 0) && (String_len(s) == 6)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  
  printf("    Checking with any length : ");
  String_readline(s,f,-1);  
  if ((strcmp(String_cstr(s),"World\n") == 0) && (String_len(s) == 6)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
    
  printf("    Checking small chunks : ");
  String_readline(s,f,8);  
  if ((strcmp(String_cstr(s),"This is ") == 0) && (String_len(s) == 8)) {
    String_readline(s,f,50);
    if ((strcmp(String_cstr(s),"a test\n") == 0) && (String_len(s) == 7)) {    
      printf("OK\n");
    } else {
      printf("FAILED!\n");
    }
  } else {
    printf("FAILED! %s %d\n", String_cstr(s), String_len(s));
  }
  printf("    Checking blank line : ");
  String_readline(s,f,10);  
  if ((strcmp(String_cstr(s),"\n") == 0) && (String_len(s) == 1)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  printf("    Checking line before EOF : ");
  String_readline(s,f,10);  
  if ((strcmp(String_cstr(s),"Last line") == 0) && (String_len(s) == 9)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  
  printf("    Checking line before EOF : ");
  if (String_readline(s,f,10) < 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  fclose(f);
}

void test_memory() {
  String *s,*t;
  int     x,y;
  int     i;

  printf("Testing for memory leaks:\n");
  x = (int) sbrk(0);
  for (i = 0; i < 1000000; i++) {
    s = new_String("HelloWorld\n");
    delete_String(s);
  }
  s = new_String("");
  for (i = 0; i < 1000000; i++) {
    init_String(s,"HelloWorld\n");
  }
  for (i = 0; i < 1000000; i++) {
    String_cstr(s);
  }
  for (i = 0; i < 1000000; i++) {
    t = copy_String(s);
    delete_String(t);
  }
  delete_String(s);
  y = (int) sbrk(0);
  if (y > (x + 8192)) {
    printf("    Your library is leaking memory.  Maybe you forgot to\n"
	   "    call free() in your delete_String() function.\n");
  } else {
    printf("    Passed.\n");
  }
}

/* This code is used to catch assertions.  It's not particularly "safe"
   or portable though --- one wouldn't normally write code to do this.
*/

static jmp_buf _env;
static int    tryabort = 0;

void abort_handler(int signo) {
  if (!tryabort) raise(SIGABRT);
  signal(SIGABRT, abort_handler);
  longjmp(_env, 1);
  tryabort = 0;
}

#define TRYABORT   tryabort = 1; signal(SIGABRT, abort_handler); if (setjmp(_env) == 0)

void test_assert() {
  String *s, *t;
  int fail = 0;

  s = new_String("hello");
  t = new_String("world");

  printf("\nChecking assertions.  This will generate a sequence of\n"
	 "error messages.  However, the test should *not* crash. You\n"
	 "will get a success message at the end if everything is working.\n\n");

  TRYABORT {
    printf("Testing init_String(0,s):\n");
    init_String(0,"hello");
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing init_String(s,0):\n");
    init_String(s,0);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }
    
  TRYABORT {
    String *x;
    printf("Testing copy_String(0):\n");
    x = copy_String(0);
    printf("FAILED!\n");
    fail = 1;
  } else { 
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_len(0):\n");
    String_len(0);
    printf("FAILED!\n");
    fail = 1;
  } else { 
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_cstr(0):\n");
    String_cstr(0);
    printf("FAILED!\n");
    fail = 1;
  } else { 
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_strcmp(0,s):\n");
    String_strcmp(0,s);
    printf("FAILED!\n");
    fail = 1;
  } else { 
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_strcmp(s,0):\n");
    String_strcmp(s,0);
    printf("FAILED!\n");
    fail = 1;
  } else { 
    printf("OK\n");
  }
   
  TRYABORT {
    printf("Testing String_strstr(0,s):\n");
    String_strstr(0,t);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_strstr(s,0):\n");
    String_strstr(s,0);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_strchr(0,c):\n");
    String_strchr(0,'x');
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_getslice(0,0,1):\n");
    String_getslice(0,0,1);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_getslice(s,1,0):\n");
    String_getslice(s,1,0);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }


  TRYABORT {
    printf("Testing String_getslice(s,-1,1):\n");
    String_getslice(s,-1,1);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_getslice(s,0,10000):\n");
    String_getslice(s,0,10000);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_readline(0,f,10):\n");
    String_readline(0,stdin,10);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  TRYABORT {
    printf("Testing String_readline(s,0,10):\n");
    String_readline(s,0,10);
    printf("FAILED!\n");
    fail = 1;
  } else {
    printf("OK\n");
  }

  if (!fail) {
    printf("\nYour library seems to be passing all of the assertion checks.\n\n");
  } else {
    printf("\nASSERTION CHECK FAILED!\n"
	   "Your library didn't crash, but it also failed an assertion check.\n"
	   "This could be caused by a function that checks for an error, but\n"
	   "simply returns instead of using the assert() statement.  Look at the\n"
	   "above messages to determine what might be broken.\n\n");
  }
  tryabort = 0;
}


/* Tests of vectors */

void test_new_Vector() {
  Vector *v;

  printf("Testing new_Vector():\n");

  printf("    Creating a new vector : ");
  v = new_Vector(10);
  if (!v) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking initial size : ");
  if (Vector_len(v) != 0) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  
  printf("    Deleting it : ");
  delete_Vector(v);
  printf("OK\n");
  
  printf("    Creating vector of any size : ");
  v = new_Vector(-1);
  if (!v) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  } 
  printf("   Checking initial size : ");
  if (Vector_len(v) != 0) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  delete_Vector(v);
}

void test_vector_append() {
  Vector *v;
  int i;
  int fail = 0;

  printf("Testing Vector_append():\n");

  printf("    Checking append : ");
  v = new_Vector(7);

  for (i = 0; i < 6; i++) {
    if (Vector_append(v,new_String("Hello")) != (i+1)) fail = 1;
  }
  if (!fail) printf("OK\n");
  else printf("FAILED! - Return value is bad\n");
  
  printf("    Checking length after append : ");
  if (Vector_len(v) != 6) {
    printf("FAILED\n");
  } else {
    printf("OK\n");
  }

  printf("    Checking for maximum size : ");
  Vector_append(v,new_String("Hello"));
  if (Vector_append(v,new_String("End")) != -1) {
    printf("FAILED\n");
  } else {
    printf("OK\n");
  }
}

void test_vector_items() {
  Vector *v;
  char  temp[100];
  int i;

  printf("Testing Vector_{set,get,del}item():\n");
  v = new_Vector(-1);
  for (i = 0; i < 1000; i++) {
    sprintf(temp,"item%d", i);
    Vector_append(v,new_String(temp));
  }
  
  printf("    Checking getitem : ");
  if (strcmp(String_cstr(Vector_getitem(v,151)),"item151") == 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  printf("    Checking setitem : ");
  Vector_setitem(v,151,new_String("new151"));
  if (strcmp(String_cstr(Vector_getitem(v,151)),"new151") == 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  printf("    Checking delitem : ");
  Vector_delitem(v,151);
  if ((strcmp(String_cstr(Vector_getitem(v,151)),"item152") == 0) && (Vector_len(v) == 999)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  
  printf("    Checking delitem(0) : ");
  Vector_delitem(v,0);
  if ((strcmp(String_cstr(Vector_getitem(v,0)),"item1") == 0) && (Vector_len(v) == 998)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }

  printf("    Checking delitem(last) : ");
  Vector_delitem(v,997);
  if ((strcmp(String_cstr(Vector_getitem(v,996)),"item998") == 0) && (Vector_len(v) == 997)) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
  printf("    Deleting all items : ");
  for (i = 0; i < 997; i++) {
    delete_String(Vector_delitem(v,0));
  }
  if (Vector_len(v) == 0) {
    printf("OK\n");
  } else {
    printf("FAILED!\n");
  }
}

void test_vector_slice() {
  Vector *v,*w;
  char  temp[100];
  int i;
  int fail = 0;

  printf("Testing Vector_getslice():\n");
  v = new_Vector(-1);
  for (i = 0; i < 1000; i++) {
    sprintf(temp,"item%d", i);
    Vector_append(v,new_String(temp));
  }
  printf("    Checking slice at beginning : ");
  w = Vector_getslice(v,0,10);
  if (Vector_len(w) != 10) fail = 1;
  for (i = 0; i < 10; i++) {
    String *s;
    sprintf(temp,"item%d",i);
    s = (String *) Vector_getitem(w,i);
    if (strcmp(String_cstr(s),temp) != 0) {
      fail = 1;
      break;
    }
  }
  if (fail) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  fail = 0;

  printf("    Checking slice in middle : ");
  w = Vector_getslice(v,100,110);
  if (Vector_len(w) != 10) fail = 1;
  for (i = 100; i < 110; i++) {
    String *s;
    sprintf(temp,"item%d",i);
    s = (String *) Vector_getitem(w,i-100);
    if (strcmp(String_cstr(s),temp) != 0) {
      fail = 1;
      break;
    }
  }
  if (fail) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  fail = 0;

  printf("    Checking slice at end : ");
  w = Vector_getslice(v,990,1000);
  if (Vector_len(w) != 10) fail = 1;
  for (i = 990; i < 1000; i++) {
    String *s;
    sprintf(temp,"item%d",i);
    s = (String *) Vector_getitem(w,i-990);
    if (strcmp(String_cstr(s),temp) != 0) {
      fail = 1;
      break;
    }
  }
  if (fail) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  fail = 0;
  for (i = 0; i < 1000; i++) {
    delete_String(Vector_delitem(v,0));
  }
  delete_Vector(v);
}  

void test_vector_search() {
  Vector *v;
  char  temp[100];
  int i;
  int   n;
  String *s;

  printf("Testing Vector_search():\n");
  v = new_Vector(-1);
  for (i = 0; i < 1000; i++) {
    sprintf(temp,"item%d", i);
    Vector_append(v,new_String(temp));
  }
  printf("    Checking for item (found) : ");
  
  s = new_String("item723");
  n = Vector_search(v,s,(int (*)(void*,void*)) String_strcmp);
  if (n != 723) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  printf("    Checking for item (not found) : ");

  init_String(s,"WMD");
  n = Vector_search(v,s,(int (*)(void*,void*)) String_strcmp);
  if (n != -1) {
    printf("FAILED!\n");
  } else {
    printf("OK\n");
  }
  for (i = 0; i < 1000; i++) {
    delete_String(Vector_delitem(v,0));
  }
  delete_Vector(v);
  delete_String(s);
}  

void test_vector_sort() {
  Vector *v;
  char  temp[100];
  int i;
  int   n;
  int   fail = 0;
  String *s;

  printf("Testing Vector_sort():\n");
  v = new_Vector(-1);
  for (i = 0; i < 1000; i++) {
    sprintf(temp,"item%04d", 999-i);
    Vector_append(v,new_String(temp));
  }
  printf("    Checking sort : ");
  Vector_qsort(v,(int (*)(void *,void*)) String_strcmp);

  for (i = 0; i < 1000; i++) {
    s = (String *) Vector_getitem(v,i);
    sprintf(temp,"item%04d",i);
    if (strcmp(String_cstr(s),temp) != 0) fail = 1;
  }
  if (fail) printf("FAILED!\n");
  else printf("OK\n");

  for (i = 0; i < 1000; i++) {
    delete_String(Vector_delitem(v,0));
  }
  delete_Vector(v);
}  


int main() {
  test_new_String();
  test_init();
  test_copy();
  test_integrity();
  test_cmp();
  test_strstr();
  test_strchr();
  test_strcat();
  test_slice();
  test_readline();
  test_memory();
  test_assert();

  test_new_Vector();
  test_vector_append();
  test_vector_items();
  test_vector_slice();
  test_vector_search();
  test_vector_sort();
}
