#include "xxl.h"

#include <stdlib.h>

#define FAIL(why)                                                           \
    do {                                                                    \
        fprintf(stderr, "%s\n", (why));                                     \
        exit(1);                                                            \
    } while (0)

typedef struct {
    int cleaned;
} test_asset_t;

static void
cleanup_test_asset(void *asset, void *arg)
{
    ((test_asset_t *)asset)->cleaned = 1;
}

static void
test_catch(void)
{
    int caught, pass;

    for (pass = 0;  pass < 3;  pass++)
    {
        XXL_TRY_BEGIN {
            caught = 0;
            switch (pass)
            {
                case 0: XXL_THROW_ERROR(0x42174217, NULL);
                case 1: XXL_THROW_ERROR(0x17421742, NULL);
                case 2: break;
            }
        }
        XXL_CATCH(0x42174217) { caught = 1; }
        XXL_EXCEPT            { caught = 2; }
        XXL_TRY_END;

        switch (pass)
        {
            case 0:
                if (caught == 0) FAIL("Did not catch exception 0x42174217\n");
                if (caught == 2) FAIL("Caught 0x42174217 as 0x17421742\n");
                if (caught != 1) FAIL("(catch pass 0) 'caught' is totally wrong!\n");
                break;
            case 1:
                if (caught == 0) FAIL("Did not catch exception 0x17421742\n");
                if (caught == 1) FAIL("Caught 0x17421742 as 0x42174217\n");
                if (caught != 2) FAIL("(catch pass 1) 'caught' is totally wrong!\n");

                break;
            default:
                if (caught != 0) FAIL("Exception caught when none thrown\n");
                break;
        }
    }
}

static void
test_finally(void)
{
    int caught, finally, pass;

    for (pass = 0;  pass < 3;  pass++)
    {
        XXL_TRY_BEGIN
        {
            caught = finally = 0;
            switch (pass)
            {
                case 0: break;
                case 1: XXL_THROW_ERROR(0x42174217, NULL);
                case 2: XXL_THROW_ERROR(0x17421742, NULL);
            }
        }
        XXL_CATCH(0x42174217)   { caught  = 1; }
        XXL_EXCEPT              { caught  = 2; }
        XXL_FINALLY             { finally = 1; }
        XXL_TRY_END;

        switch (pass)
        {
            case 0:
                if (caught == 1) FAIL("Caught 0x42174217 when none thrown with finally\n");
                if (caught == 2) FAIL("Caught 0x17421742 when none thrown with finally\n");
                if (caught != 0) FAIL("(finally pass 0) 'caught' is totally wrong!\n");
                if (!finally)    FAIL("Finally failed to run when no exception thrown\n");
                break;
            case 1:
                if (caught == 0) FAIL("Did not catch exception 0x42174217 with finally\n");
                if (caught == 2) FAIL("Caught 0x42174217 as 0x17421742 with finally\n");
                if (caught != 1) FAIL("(finally pass 1) 'caught' is totally wrong!\n");
                if (!finally)    FAIL("Finally failed to run when specific exception caught\n");
                break;
            case 2:
                if (caught == 0) FAIL("Did not catch exception 0x17421742 with finally\n");
                if (caught == 1) FAIL("Caught 0x17421742 as 0x42174217 with finally\n");
                if (caught != 2) FAIL("(finally pass 2) 'caught' is totally wrong!\n");
                if (!finally)    FAIL("Finally failed to run when non-specific exception caught\n");
                break;
        }
    }
}

static void
test_assets(void)
{
    int             pass;
    test_asset_t    asset;
    xxl_assettype_t type;

    for (pass = 0;  pass < 8;  pass++)
    {
        XXL_TRY_BEGIN
        {
            asset.cleaned = 0;
            switch (pass)
            {
                case 0: case 4: type = XXL_ASSET_PERMANENT;  break;
                case 1: case 5: type = XXL_ASSET_TEMPORARY;  break;
                case 2: case 6: type = XXL_ASSET_PROMOTE;    break;
                case 3: case 7: type = XXL_ASSET_DEMOTE;     break;
            }
            XXL_ASSET_SAVE(&asset, cleanup_test_asset, NULL, type);
            if (pass > 3) XXL_THROW_ERROR(0x42174217, NULL);
        }
        XXL_EXCEPT {}
        XXL_TRY_END;

        switch (pass)
        {
            case 0:
                if (asset.cleaned)
                    FAIL("PERMANENT asset cleaned; no exception thrown\n");
                break;
            case 1:
                if (!asset.cleaned)
                    FAIL("TEMPORARY asset NOT cleaned; no exception thrown\n");
                break;
            case 2:
                if (asset.cleaned)
                    FAIL("PROMOTE asset cleaned; no exception thrown\n");
                break;
            case 3:
                if (!asset.cleaned)
                    FAIL("DEMOTE asset NOT cleaned; no exception thrown\n");
                break;
            case 4:
                if (asset.cleaned)
                    FAIL("PERMANENT asset cleaned; exception thrown\n");
                break;
            case 5:
                if (!asset.cleaned)
                    FAIL("TEMPORARY asset NOT cleaned; exception thrown\n");
                break;
            case 6:
                if (!asset.cleaned)
                    FAIL("PROMOTE asset NOT cleaned; exception thrown\n");
                break;
            case 7:
                if (asset.cleaned)
                    FAIL("DEMOTE asset cleaned; exception thrown\n");
                break;
        }
    }
}

static void
test_nested(void)
{
    int caught, inner_finally, outer_finally, pass;

    for (pass = 0;  pass < 2;  pass++)
    {
        XXL_TRY_BEGIN
        {
            caught = inner_finally = outer_finally = 0;
            XXL_TRY_BEGIN
            {
                switch (pass)
                {
                    case 0: XXL_THROW_ERROR(0x42174217, NULL);
                    case 1: XXL_THROW_ERROR(0x17421742, NULL);
                }
            }
            XXL_CATCH(0x42174217)   { XXL_RETHROW_ERROR(); }
            XXL_FINALLY             { inner_finally = 1;   }
            XXL_TRY_END;
        }
        XXL_CATCH(0x42174217)       { caught = 1;          }
        XXL_CATCH(0x17421742)       { caught = 2;          }
        XXL_FINALLY                 { outer_finally = 1;   }
        XXL_TRY_END;

        switch (pass)
        {
            case 0:
                if (caught == 0)    FAIL("Outer try did not catch 0x42174217\n");
                if (caught == 2)    FAIL("Outer try caught 0x42174217 as 0x17421742\n");
                if (caught != 1)    FAIL("(nested pass 0) 'caught' is totally wrong!\n");
                if (!inner_finally) FAIL("(nested pass 0) Inner finally block did not run\n");
                if (!outer_finally) FAIL("(nested pass 0) Outer finally block did not run\n");
                break;
            case 1:
                if (caught == 0)    FAIL("Outer try did not catch 0x17421742\n");
                if (caught == 1)    FAIL("Outer try caught 0x17421742 as 0x42174217\n");
                if (caught != 2)    FAIL("(nested pass 1) 'caught' is totally wrong!\n");
                if (!inner_finally) FAIL("(nested pass 1) Inner finally block did not run\n");
                if (!outer_finally) FAIL("(nested pass 1) Outer finally block did not run\n");
                break;
        }
    }
}

int main(int argc, char *agrv[])
{
    printf("Testing simple exception catching ...\n");
    test_catch();

    printf("Testing simple finally block execution ...\n");
    test_finally();

    printf("Testing simple asset management ...\n");
    test_assets();

    printf("Testing nested exception handling ...\n");
    test_nested();

    exit(0);
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1