/*************************************************************************/
/* Package name  : Except.                                               */
/* Version       : 1.0                                                   */
/* Completed     : April 1993.                                           */
/* Released      : 29 September 1993.                                    */
/* First created : This package was first created in April 1993.         */
/* Summary       : This package provides Ada-like exceptions for C.      */
/* Components    : except.fw  - FunnelWeb source file.                   */
/*                 except.h   - Exported header file.                    */
/*                 except.c   - Implementation file.                     */
/*                 ex_test.c  - C test program.                          */
/* Requires      : style.h, as.h, as.c.                                  */
/* Author        : Ross N. Williams (ross@guest.adelaide.edu.au)         */
/*                 Rocksoft^tm Pty Ltd                                   */
/*                 16 Lerwick Avenue, Hazelwood Park 5066, Australia.    */
/* FTP Archive   : This file can be found in                             */
/*                 "ftp.adelaide.edu.au/pub/funnelweb/examples/"         */
/* Disclaimer    : This program is distributed WITHOUT ANY WARRANTY;     */
/*                 without even the implied warranty of MERCHANTABILITY  */
/*                 or FITNESS FOR A PARTICULAR PURPOSE.                  */
/* Copyright     : Copyright (C) Ross Williams 1993.                     */
/*                 However, permission is granted for anyone to copy,    */
/*                 modify, and distribute this work for any purpose,     */
/*                 commercial or non-commercial, so long as this notice  */
/*                 is included verbatim, and so long as all              */
/*                 modifications are recorded in the change log below.   */
/* Changes       : Please log any changes to this software either in the */
/*                 originating FunnelWeb source file, or, if you must,   */
/*                 in the C source files produced from the FunnelWeb     */
/*                 file.                                                 */
/* --<Start of Change Log>--                                             */
/* ??-Apr-93: RNW: Created this package.                                 */
/* 29-Sep-93: RNW: Released this package.                                */
/* --<End of Change Log>--                                               */
/*************************************************************************/


#include "style.h"
#include "except.h"
#include "as.h"

EX_LOCAL(sloth_ex,"Sloth exception");
EX_LOCAL(walrus_ex,"Walrus exception");

GLOVAR bool flag;


LOCAL void sc01 P_((void));
LOCAL void sc01 ()
{
 printf("Test SC01: Exception block with no exceptions raised.\n");

 /* The NULL exception block should be OK. */
 EX_BEGIN
 EX_FORGET
 EX_END

 /* Now put some code in the normal section. */
 flag=FALSE;
 EX_BEGIN
    flag=TRUE;
 EX_FORGET
 EX_END
 as_cold(flag,"sc01: First flag block test failed.");

 /* Make sure the EX_OTHERS branch only applies to exceptions. */
 flag=FALSE;
 EX_BEGIN
    flag=TRUE;
 EX_FORGET
    EX_OTHERS
       as_bomb("sc01: Raise failed.");
 EX_END
 as_cold(flag,"sc01: Second flag block test failed.");
}


LOCAL void sc02 P_((void));
LOCAL void sc02 ()
{
 printf("Test SC02: Exception caught within immediate block.\n");
 flag = FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc02: Raise failed.");
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
 EX_END
 as_cold(flag,"sc02: Exception was not caught by local block.");
}


LOCAL void sc03 P_((void));
LOCAL void sc03 ()
{
 printf("Test SC03: Exception is caught by OTHERS clause.\n");
 flag = FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc03: Raise failed.");
 EX_FORGET
    EX_OTHERS flag=TRUE;
 EX_END
 as_cold(flag,"sc03: Exception was not caught by EX_OTHERS clause.");
}


LOCAL void sc04 P_((void));
LOCAL void sc04 ()
{
 printf("Test SC04: Exception is caught by nested exception block.\n");

 /* Try a simple nested handler. */
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc04: Raise failed (1).");
    EX_FORGET
       EX_WHEN(walrus_ex)
          as_bomb("sc04: Walrus exception caught (1).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
    EX_WHEN(walrus_ex) as_bomb("sc04: Walrus exception caught (x).");
 EX_END
 as_cold(flag,"sc04: First nested test failed.");

 /* Test a re-raise. */
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc04: Raise failed (2).");
    EX_FORGET
       EX_WHEN(sloth_ex)
          EX_RAISE(walrus_ex)
          as_bomb("sc04: Raise failed (3).");
       EX_WHEN(walrus_ex)
          as_bomb("sc04: Walrus exception caught (2).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex)
       as_bomb("sc04: Sloth exception escaped.");
    EX_WHEN(walrus_ex)
       flag=TRUE;
 EX_END
 as_cold(flag,"sc04: Second nested test failed.");
}


LOCAL void sc05 P_((void));
LOCAL void sc05 ()
{
 STAVAR bool flag2;
 printf("Test SC05: Single OTHERS handler reraises current exception.\n");
 flag=FALSE;
 flag2=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_RAISE(sloth_ex);
       as_bomb("sc05: Raise failed (2).");
    EX_FORGET
       EX_OTHERS
          flag=TRUE;
          EX_RAISE(EX_ID);
          as_bomb("sc05: Raise failed (3).");
    EX_END
 EX_FORGET
    EX_WHEN(sloth_ex)
       flag2=TRUE;
    EX_OTHERS
       as_bomb("sc05: Exception escaped.");
 EX_END
 as_cold(flag ,"sc05: OTHERS test failed (1).");
 as_cold(flag2,"sc05: OTHERS test failed (2).");
}


LOCAL void sc06 P_((void));
LOCAL void sc06 ()
{
 printf("Test SC06: Handlers all GOTO same code.\n");
 flag=FALSE;
 EX_BEGIN
    EX_RAISE(sloth_ex);
    as_bomb("sc06: Raise failed.");
 EX_FORGET
    EX_WHEN(sloth_ex) goto handle;
    EX_WHEN(walrus_ex)
       as_bomb("sc06: Walrus exception went off.");
       handle:
       flag=TRUE;
    EX_OTHERS
       as_bomb("sc06: Exception escaped to OTHERS.");
 EX_END
 as_cold(flag,"sc06: GOTO test failed.");
}


LOCAL void sc07 P_((void));
LOCAL void sc07 ()
{
 printf("Test SC07: Exercise EX_INFO.\n");
 EX_INFO = 3;
 as_cold(EX_INFO == 3,"sc07: EX_INFO test failed.");
}


LOCAL void sc08 P_((void));
LOCAL void sc08 ()
{
 printf("Test SC08: Exercise ex_str.\n");
 as_cold(ex_str(sloth_ex) == sloth_ex,"sc08: Failed.");
}


LOCAL void sc09 P_((void));
LOCAL void sc09 ()
{
 printf("Test SC09: Test EX_POP.\n");
 flag=FALSE;
 EX_BEGIN
    EX_BEGIN
       EX_POP; goto finish;
    EX_FORGET
       EX_WHEN(sloth_ex) as_bomb("sc09: Inner catcher caught the sloth.");
    EX_END
    finish: EX_RAISE(sloth_ex);
 EX_FORGET
    EX_WHEN(sloth_ex) flag=TRUE;
 EX_END
 as_cold(flag,"sc09: Sloth exception failed to work.");
 as_cold(_EX_CURR == NULL,"sc09: EX_POP failed to pop correctly.");
}



LOCAL void fa01 P_((void));
LOCAL void fa01 ()
{
 printf("Test FA01: Unhandled Exception\n");
 printf("------------------------------\n");
 printf("This test raises the sloth exception but doesn't handle it.\n");
 printf("The result should be an \"unhandled exception\" bomb specifying\n");
 printf("the sloth exception. Here we go...\n");
 printf("\n");
 EX_RAISE(sloth_ex);
 as_wl  ("FA01 FAILED: the exception package did not catch the\n");
 as_bomb("unhandled sloth exception.\n");
}


LOCAL void fa02 P_((void));
LOCAL void fa02 ()
{
 printf("Test FA02: Double EX_POP\n");
 printf("------------------------\n");
 printf("This test executes two calls to EX_POP within the normal code of\n");
 printf("an exception block. The second one should cause a\n");
 printf("\"context stack is empty\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_POP;
    EX_POP;
    as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (1).");
 EX_FORGET
 EX_END
 as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (2).");
}


LOCAL void fa03 P_((void));
LOCAL void fa03 ()
{
 printf("Test FA03: EX_POP Followed by Normal Termination\n");
 printf("------------------------------------------------\n");
 printf("This test executes a call to EX_POP then falls through to the\n");
 printf("end of the normal code of the exception block. The result should\n");
 printf("be a \"context stack is empty\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_POP;
 EX_FORGET
 EX_END
 as_bomb("FA03 FAILED: Fallthough after EX_POP did not cause bomb.");
}


LOCAL void fa04 P_((void));
LOCAL void fa04 ()
{
 printf("Test FA04: Exit a block without EX_POPing\n");
 printf("-----------------------------------------\n");
 printf("This test exits an exception block using a goto without first\n");
 printf("EX_POPing. The exception block is nested within another, and this\n");
 printf("test is to make sure that the outer block detects that it is\n");
 printf("popping the wrong context. The result should be a\n");
 printf("\"not current context\" bomb. Here we go...\n");
 printf("\n");
 EX_BEGIN
    EX_BEGIN
       goto there;
    EX_FORGET
    EX_END
    there:
 EX_FORGET
 EX_END
 as_bomb("FA04 FAILED: Outer block FAILED to detect stack misalignment.");
}


LOCAL void fa05_x P_((void));
LOCAL void fa05_x ()
{
 EX_BEGIN
    return;
 EX_FORGET
 EX_END
}

LOCAL void fa05 P_((void));
LOCAL void fa05 ()
{
 printf("Test FA05: Target Context Does Not Exist\n");
 printf("----------------------------------------\n");
 printf("This test exits an exception block from within a function using\n");
 printf("a return statement. Thus, the context remains stacked even though\n");
 printf("the enclosing C block has terminated. This test checks to see\n");
 printf("whether this problem is detected next time an exception is raised.\n");
 printf("The result should be an illegitimate context error. Here we go...\n");
 printf("\n");
 EX_BEGIN
    fa05_x();
    EX_RAISE(sloth_ex);
    as_bomb("FA05 FAILED: Failed to raise sloth.");
 EX_FORGET
    EX_WHEN(sloth_ex)
       as_bomb("FA05 FAILED: Sloth exception was raised.");
 EX_END
 as_bomb("FA05 FAILED: Costruct terminated normally - it shouldn't have.");
}


LOCAL void fa06 P_((void));
LOCAL void fa06 ()
{
 printf("Test FA06: Target Context Does Not Exist (Fallthrough)\n");
 printf("------------------------------------------------------\n");
 printf("This test is identical to test FA05 except that it does not\n");
 printf("raise an exception in the outer block. The result should be a\n");
 printf("\"not current context\" error. Here we go...\n");
 printf("\n");
 EX_BEGIN
    fa05_x();
 EX_FORGET
 EX_END
 as_bomb("FA06 FAILED: Costruct terminated normally - it shouldn't have.");
}


LOCAL void fa07 P_((void));
LOCAL void fa07 ()
{
 as_bomb("Test FA07: Test FA06 is the last test.");
}


LOCAL void fa08 P_((void));
LOCAL void fa08 ()
{
 as_bomb("Test FA08: Test FA06 is the last test.");
}


LOCAL void fa09 P_((void));
LOCAL void fa09 ()
{
 as_bomb("Test FA09: Test FA06 is the last test.");
}


main()
{
 char ch;

 printf("Test Program for the EXCEPT Package\n");
 printf("===================================\n");
 printf("This test program provides a number of different tests. Because\n");
 printf("some tests provoke the package to bomb on purpose, this test\n");
 printf("program must be run a number of times, once for each test.\n");
 printf("The 0 test performs a group of tests that are not supposed to\n");
 printf("fail. The 1..9 tests each perform a test that is supposed to\n");
 printf("bomb the program if the test succeeds. The 0 test should be run\n");
 printf("with the package compiled with both _EX_FAST==FALSE and\n");
 printf("_EX_FAST==TRUE. The 1..9 tests should use only _EX_FAST==FALSE.\n");
 printf("\n");
 if (_EX_FAST)
 printf("Current value of EX_FAST == TRUE.\n");
 else
 printf("Current value of EX_FAST == FALSE.\n");
 printf("\n");
 printf("Enter 0 for success tests, or 1..9 for one of nine fail tests>");
 ch=getchar(); printf("\n");
 if (ch == '0')
   {
    printf("Success Tests\n");
    printf("-------------\n");
    printf("The following tests (sc01..sc09) test the normal features\n");
    printf("the exceptions package. You should see nine success lines\n");
    printf("appear on the screen. If the package bombs during this test,\n");
    printf("then something is wrong and should be fixed.\n");
    printf("\n");
    sc01();
    sc02();
    sc03();
    sc04();
    sc05();
    sc06();
    sc07();
    sc08();
    sc09();
    printf("\n");
    printf("All of the success tests SUCCEEDED.\n");
   }
 else
   {
    if (_EX_FAST)
      {
       as_wl("Error in test configuration. An attempt was made to perform");
       as_wl("a 1..9 test with _EX_FAST==TRUE. This does not make sense as");
       as_wl("these tests test the error checking capability of the package");
       as_wl("and _EX_FAST=TRUE has all error checking turned off!");
       as_bomb("Please recompile with _EX_FAST==FALSE and try again.");
      }
    switch (ch)
     {
      case '1': fa01();
      case '2': fa02();
      case '3': fa03();
      case '4': fa04();
      case '5': fa05();
      case '6': fa06();
      case '7': fa07();
      case '8': fa08();
      case '9': fa09();
      default:
         printf("\nCharacter entered was not 0..9. Aborting...\n");
     }
    }
}