//
// Copyright (C) 1991 Texas Instruments Incorporated.
//
// Permission is granted to any individual or institution to use, copy, modify,
// and distribute this software, provided that this complete copyright and
// permission notice is maintained, intact, in all copies and supporting
// documentation.
//
// Texas Instruments Incorporated provides this software "as is" without
// express or implied warranty.
//

#if defined(DOS)
#include <strstrm.hxx>				// for ostrstream class
extern "C" {
#include <string.h>				// for strcmp()
#include <errno.h>				// For errno ENOENT

}
#else
#include <strstream.h>				// for ostrstream class
#include <string.h>				// for strcmp()
#include <errno.h>				// For errno ENOENT
#endif

#include <cool/Generic.h>
#include <cool/Ignore_Errs.h>
#include <test.h>

// **************
// ** My_Error **
// **************

class My_Error : public Error {
public:
  int slot1;
  char* slot2;
  virtual void report(ostream&) CONST;
  My_Error();
  ~My_Error();
};

My_Error::My_Error() {
  format_msg = "There are two slots: %d and %s";
  slot1 = 999;
  slot2 = "nothing";
}

My_Error::~My_Error() {}

void My_Error::report(ostream& os) CONST {
  message_prefix();
  os << form(format_msg, slot1, slot2);
}

// ******************
// ** My_Exception **
// ******************

class My_Exception : public Exception {
public:
  int my_handler_called;
  My_Exception();
  ~My_Exception();
};

My_Exception::My_Exception() {
  my_handler_called = 0;
}

My_Exception::~My_Exception() {}

// ****************
// ** My_Handler **
// ****************

void my_handler(Exception* excp) {
  ((My_Exception*)excp)->my_handler_called = 1;
}

// *********
// get_arg()
// *********
char* get_arg(int index) {
  switch (index) {
    case 0:  return "zero";
    case 1:  return "one";
    case 2:  return "two";
    default: return  "number";
  }
}

// ********************
// number_of_handlers() 
// ********************

int number_of_handlers() {
  int i = 0;
  Excp_Handler* eh = find_handler();
  for(;eh != NULL; i++, eh = find_handler(NULL, eh));
  return i;
}


// **********************
// Error Handling testing
// **********************

int main (void) {

  START("Exception");

  // ** Test of empty pre_defined Exception types **

  Exception* ep1 = new Exception;
  TEST ("Exception ep1;", (ep1->format_msg==NULL && ep1->msg_prefix==NULL), TRUE);

  ep1 = new Error;
  TEST ("Error ep1;", (ep1->format_msg==NULL && ep1->msg_prefix==NULL), TRUE);
  delete ep1;

  ep1 = new Warning;
  TEST ("Warning w1;", (ep1->format_msg==NULL && ep1->msg_prefix==NULL), TRUE);
  delete ep1;

  ep1 = new Fatal;
  TEST ("Fatal f1;", (ep1->format_msg==NULL && ep1->msg_prefix==NULL), TRUE);
  delete ep1;

  ep1 = new System_Error;
  TEST ("System_Error s1;",
	(ep1->format_msg==NULL && ep1->msg_prefix==NULL
	 && ((System_Error*)ep1)->error_code==-1),
	TRUE);
  delete ep1;

  ep1 = new System_Signal;
  TEST ("System_Signal s1;",
	(ep1->format_msg==NULL && ep1->msg_prefix==NULL
	 && ((System_Signal*)ep1)->signo==-1),
	TRUE);
  delete ep1;

  ep1 = new Verify_Error;
  TEST ("Verify_Error v1;",
	(ep1->format_msg!=NULL && ep1->msg_prefix==NULL
	 && ((Verify_Error*)ep1)->test!=NULL),
	TRUE);

  // ** Test macro EXCEPTION **

  My_Error* my1 = EXCEPTION(My_Error);
  TEST ("EXCEPTION(My_Error);",
        (!strcmp(my1->format_msg, "There are two slots: %d and %s")
	 && my1->slot1 == 999 && !strcmp(my1->slot2,"nothing")),
        TRUE);
  delete my1;

  my1 = EXCEPTION(My_Error, "This is a new %s message: %d %s %s",
		  "error", slot1=44, "and", slot2=get_arg(2));
  TEST ("EXCEPTION(My_Error, \"An error message\", args);",
        (!strcmp(my1->format_msg, "This is a new error message: 44 and two")
	 && my1->slot1 == 44 && !strcmp(my1->slot2,"two")),
        TRUE);
  delete my1;

  ep1 = EXCEPTION(Error, "Another error");
  TEST ("EXCEPTION(Error, \"Another error\");",
        (!strcmp(ep1->format_msg,"Another error")),
        TRUE);
  delete ep1;

  int x = 2;
  ep1 = EXCEPTION(Error, "Argument %d is of wrong type", x);
  TEST ("EXCEPTION(Error, \"Argument %d is of wrong type\", x);",
        (!strcmp(ep1->format_msg, "Argument 2 is of wrong type")),
        TRUE);
  delete ep1;

  // ** Test macro IGNORE_ERRORS and function raise **

  IGNORE_ERRORS(ep1) {
    (EXCEPTION(Error, "Hi %s", "there"))->raise();
  }
  TEST ("(EXCEPTION(Error))->raise();",
        (ep1 != NULL && !strcmp(ep1->format_msg, "Hi there")), TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    (EXCEPTION(Error, "Hi %s", "there"))->stop();
  }
  TEST ("(EXCEPTION(Error))->stop();",
        (ep1 != NULL && !strcmp(ep1->format_msg, "Hi there")), TRUE);
  delete ep1;

  // ** Test macro RAISE **

  IGNORE_ERRORS(ep1) {
    RAISE(Exception, "Hi");
  }
  TEST ("RAISE(Exception);", (ep1 == NULL), TRUE);

  IGNORE_ERRORS(ep1) {
    RAISE(Error, "Hi");
  }
  TEST ("RAISE(Error, \"error message\");",
	(ep1 != NULL && !strcmp(ep1->format_msg, "Hi")), TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(Error, "Hi %s", "there");
  }
  TEST ("RAISE(Error, \"error message %s\", arg);",
	(ep1 != NULL && !strcmp(ep1->format_msg, "Hi there")), TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(My_Error);
  }
  my1 = (My_Error*) ep1;
  TEST ("RAISE(My_Error);",
        (!strcmp(my1->format_msg, "There are two slots: %d and %s")
	 && my1->slot1 == 999 && !strcmp(my1->slot2,"nothing")),
        TRUE);
   delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(My_Error, "This is a new %s message: %d %s %s",
	  "error", slot1=44, "and", slot2=get_arg(2));
  }
  my1 = (My_Error*) ep1;
  TEST ("RAISE(My_Error, \"An error message\", args, slots);",
        (!strcmp(my1->format_msg, "This is a new error message: 44 and two")
	 && my1->slot1 == 44 && !strcmp(my1->slot2,"two")),
        TRUE);
  delete ep1;
   
  // ** Test macro STOP **

  IGNORE_ERRORS(ep1) {
    STOP(Error, "Hi");
  }
  TEST ("STOP(Error, \"error message\");",
	(ep1 != NULL && !strcmp(ep1->format_msg, "Hi")), TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    STOP(Error, "Hi %s", "there");
  }
  TEST ("STOP(Error, \"error message %s\", arg);",
	(ep1 != NULL && !strcmp(ep1->format_msg, "Hi there")), TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    STOP(My_Error);
  }
  my1 = (My_Error*) ep1;
  TEST ("STOP(My_Error);",
        (!strcmp(my1->format_msg, "There are two slots: %d and %s")
	 && my1->slot1 == 999 && !strcmp(my1->slot2,"nothing")),
        TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    STOP(My_Error, "This is a new %s message: %d %s %s",
	 "error", slot1=44, "and", slot2=get_arg(2));
  }
  my1 = (My_Error*) ep1;
  TEST ("STOP(My_Error, \"An error message\", args, slots);",
        (!strcmp(my1->format_msg, "This is a new error message: 44 and two")
	 && my1->slot1 == 44 && !strcmp(my1->slot2,"two")),
        TRUE);
  delete ep1;

  // ** Test macro VERIFY **

  IGNORE_ERRORS(ep1) {
    VERIFY((1==3));
  }
  Verify_Error* v1 = (Verify_Error*) ep1;
  TEST ("VERIFY((1==3));",
	(!strcmp(v1->format_msg, "Test [ %s ] failed.")
	 && !strcmp(v1->test,"(1==3)")),
	TRUE);
  delete ep1;
  
  IGNORE_ERRORS(ep1) {
    VERIFY((1==3), SYM(Test_Error), "An error %s in test");
  }
  v1 = (Verify_Error*) ep1;
  TEST ("VERIFY((1==3), SYM(Test_Error), \"An error message\");",
	(!strcmp(v1->format_msg, "An error (1==3) in test")
	 && !strcmp(v1->test,"(1==3)")),
	TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    VERIFY((1==3), SYM(Test_Error), "A %d error in test %s",x);
  }
  v1 = (Verify_Error*) ep1;
  TEST ("VERIFY((1==3), SYM(Test_Error), \"Test %s\", arg);",
	(!strcmp(v1->format_msg, "A 2 error in test (1==3)")
	 && !strcmp(v1->test,"(1==3)")),
	TRUE);
  delete ep1;

  // Test report member function

  char* emsg = new char[MSG_MAX];

MACRO STRING_CONVERT(thing) {
  ost.seekp(ios::beg);
  if (thing != NULL) ost << thing;
  ost << ends;
}
  ostrstream ost(emsg, MSG_MAX);

  IGNORE_ERRORS(ep1) {
    RAISE(System_Error, error_code=ENOENT);
  }		      
  STRING_CONVERT(ep1);
  TEST("System_Error::report()",
       strcmp(emsg,"System_Error: No such file or directory.\n"),
       0);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(System_Error);
  }
  STRING_CONVERT(ep1);
  TEST("System_Error::report() with invalid errno",
     strcmp(emsg,"System_Error: Unknown System Error (-1) was signalled.\n"),
     0);
  delete ep1;

  IGNORE_ERRORS(ep1, System_Signal) {
    RAISE(System_Signal, signo=3);
  }
  STRING_CONVERT(ep1);
  TEST("System_Signal::report()",
       strcmp(emsg,"System_Signal: Signum 3 was raised.\n"),
       0);
  delete ep1;

  IGNORE_ERRORS(ep1, System_Signal) {
    RAISE(System_Signal);
  }
  STRING_CONVERT(ep1);
  TEST("System_Signal::report() with invalid signum",
       strcmp(emsg,"System_Signal: Unknown System Signal (-1) was raised.\n"),
       0);
  delete ep1;

  IGNORE_ERRORS(ep1, Warning) {
    RAISE(Warning, "This is a warning.");
  }
  STRING_CONVERT(ep1);
  TEST("Warning::report()",
       strcmp(emsg,"Warning: This is a warning.\n"),
       0);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(Error, "This error should terminate program with exit.");
  }
  STRING_CONVERT(ep1);
  TEST("Error::report()",
      strcmp(emsg,"Error: This error should terminate program with exit.\n"),
      0);
  delete ep1;

  IGNORE_ERRORS(ep1, Fatal) {
    RAISE(Fatal, "This fatal error should abort with core image.");
  }
  STRING_CONVERT(ep1);
  TEST("Fatal::report()",
      strcmp(emsg,"Fatal: This fatal error should abort with core image.\n"),
      0);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(Verify_Error, "This error is raised in macro VERIFY.");
  }
  STRING_CONVERT(ep1);
  TEST("Verify_Error::report()",
      strcmp(emsg,"Verify_Error: This error is raised in macro VERIFY.\n"),
      0);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(Verify_Error);
  }
  STRING_CONVERT(ep1);
  TEST("Verify_Error::report() with default message",
      strcmp(emsg,"Verify_Error: Test [  ] failed.\n"),
      0);
  delete ep1;

  // ** Test of one or more group names in EXCEPTION and RAISE **

  ep1 = EXCEPTION(Error, SYM(Serious_Error), "Serious Error");
  TEST ("EXCEPTION(Error, SYM(Serious_Error), \"Serious Error\");",
        (ep1->match(SYM(Serious_Error)) && ep1->match(SYM(Error))),
        TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1) {
    RAISE(Error, SYM(Serious_Error), SYM(Very_Serious),
	  "Very Serious %s", "Problem");
  }
  TEST ("RAISE(Error, SYM(G1), SYM(G2), \"A %s message.\", arg);",
	(ep1 != NULL && ep1->match(SYM(Serious_Error))
	            && ep1->match(SYM(Very_Serious))
 	            && ep1->match(SYM(Error))),
	TRUE);
  delete ep1;

  // ** Test of one or more group names in IGNORE_ERRORS **

  IGNORE_ERRORS(ep1) {
    RAISE(Error, SYM(Serious_Error), SYM(Very_Serious),
	  "Very Serious %s", "Problem");
  }
  TEST ("IGNORE_ERRORS(ep1)",
	(ep1 != NULL && ep1->match(SYM(Serious_Error))
	            && ep1->match(SYM(Very_Serious))
 	            && ep1->match(SYM(Error))),
	TRUE);
  delete ep1;

  IGNORE_ERRORS(ep1, Error, SYM(Group1)) {
    RAISE(Error, SYM(Group2), SYM(Group1),
	  "Just a %s Test", "Ignore");
  }
  TEST ("IGNORE_ERRORS(ep1, Error, SYM(G1))",
	(ep1 != NULL && ep1->match(SYM(Group1))
	            && ep1->match(SYM(Group2))
	            && ep1->match(SYM(Error))),
	TRUE);  
  delete ep1;

  IGNORE_ERRORS(ep1, Exception, SYM(Not_Group1), SYM(Not_Group2)) {
    RAISE(Exception, SYM(Group_1), SYM(Group_2),
	  "Just a %s Test", "Ignore");
  }
  TEST ("IGNORE_ERRORS(ep1, Exception, SYM(NG1), SYM(NG2))",
	(ep1 == NULL),
	TRUE);  
  delete ep1;

  // ** Test of one or more group names in Exception Handler constructor **

  My_Exception* m1 = EXCEPTION(My_Exception, SYM(Test_Excp));

  Excp_Handler eh1(my_handler, SYM(My_Exception));
  m1->raise();
  TEST ("Excp_Handler eh1(my_handler, SYM(My_Exception))",
	m1->my_handler_called == 1 && number_of_handlers()==1,
	TRUE);  

  m1->my_handler_called = 0;
  Excp_Handler eh2(my_handler, SYM(Test_Excp));
  m1->raise();
  TEST ("Excp_Handler eh2(my_handler, SYM(Test_Excp))",
	m1->my_handler_called == 1 && number_of_handlers()==2,
	TRUE);  

  m1->my_handler_called = 0;
  Excp_Handler eh3(my_handler, 2, SYM(Not_Test_Excp), SYM(Exception));
  m1->raise();
  TEST ("Excp_Handler eh3(my_handler, 2, SYM(Not_G1), SYM(G2))",
	m1->my_handler_called == 1 && number_of_handlers()==3,
	TRUE);  
  delete m1;

  // ** Test of one or more group names in Exception constructor **
  
  Exception e1(SYM(Group1));
  TEST ("Exception e1(SYM(Group1))",
	e1.match(SYM(Group1)),
	TRUE);  

  Exception e2(3, SYM(Group1), SYM(Group2), SYM(Group3));
  TEST ("Exception e2(3, SYM(Group1), SYM(Group2), SYM(Group3))",
	(e2.match(SYM(Group1)) && e2.match(SYM(Group2))
	 && e2.match(SYM(Group3))),
	TRUE);  

  SUMMARY();
  return 0;
}
