/** @file  tst_psError.c
 *
 *  @brief Test driver for psError function
 *
 *  This test driver contains the following test points for psError
 *     testError00 - psError()                          (Testpoint #486)
 *     testError01 - psErrorMsg(), psErrorStackPrint()  (Testpoint #725)
 *     testError02 - psErrorStackPrintV()               (Testpoint #726)
 *     testError03 - psErrorGet(), psErrorLast()        (Testpoint #727)
 *     testError04 - psErrorClear()                     (Testpoint #728)
 *     testError05 - psErrorCodeString()                (Testpoint #729)
 *
 *  @author  Eric Van Alst, MHPCC
 *
 *  @version $Revision: 1.1 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2006-12-18 19:18:46 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pslib.h"
#include "tap.h"
#include "pstap.h"

static psS32 testError00(void);
static psS32 testError01(void);
static psS32 testError02(void);
static psS32 testError03(void);
static psS32 testError04(void);
static psS32 testError05(void);
static psS32 testErrorRegister(void);

// Function used in testError02 to verify the psErrorStackPrintV function
static void myErrorStackPrint(FILE *fd,
                              const char *fmt,
                              ...)
{
    va_list ap;

    // Test whether psErrorStackPrintV() accept a va_list for output variables
    va_start(ap, fmt);
    psErrorStackPrintV(fd, fmt, ap);
    va_end(ap);
}


psS32 main( psS32 argc, char* argv[] )
{
    plan_tests(38);

    testError00();
    testError01();
    testError02();
    testError03();
    testError04();
    testError05();
    testErrorRegister();
}

static psS32 testError05(void)
{
    diag("testError05");

    // Verify the return value of psErrorCodeString
    // psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
    // psLogMsg("tst_psError05", PS_LOG_INFO, psErrorCodeString(code));

    // Verify the return value of psErrorCodeString if code is negative
    ok( psErrorCodeString(-1) == NULL, "error string with neg. code");

    return 0;
}

static psS32 testError04(void)
{
    diag("testError04");

    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
    psErr *last = NULL;
    psErr *lastAfterClear = NULL;

    // With an attemp error stack call psErrorClear
    psErrorClear();

    // Get the last error message and verify PS_ERR_NONE (empty stack)
    lastAfterClear = psErrorLast();
    ok(lastAfterClear->code == PS_ERR_NONE, "psErrorLast return expected.");
    skip_start(lastAfterClear->code != PS_ERR_NONE,
               5, "psErrorLast return expected.");

    psFree(lastAfterClear);

    // Generate three error messages to have messages on error stack
    ok (psError(code, true, "Error code = %d", code) ==  code,
        "Failed return value verify.");
    skip_start (psError(code, true, "Error code = %d", code) !=  code,
                4, "Failed return value verify.");

    ok (psError((code+1), false, "Error code = %d", (code+1)) == (code+1),
        "Failed return value verify.");
    skip_start (psError((code+1), false, "Error code = %d", (code+1))!=(code+1),
                3, "Failed return value verify.");

    ok (psError((code+2), false, "Error code = %d", (code+2)) == (code+2),
        "Failed return value verify.");
    skip_start (psError((code+2), false, "Error code = %d", (code+2))!=(code+2),
                2, "Failed return value verify.");

    // Get the last error message and verify it has the expected code
    last = psErrorLast();
    ok(last->code == (code+2), "psErrorLast return expected.");
    skip_start(last->code != (code+2), 1, "psErrorLast return expected.");

    psFree(last);

    // Clear the error stack
    psErrorClear();

    // Get the last error message after clear and verify is has PS_ERR_NONE code
    lastAfterClear = psErrorLast();
    ok(lastAfterClear->code == PS_ERR_NONE, "psErrorLast return expected.");

    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();

    psFree(lastAfterClear);

    return 0;
}

static psS32 testError03(void)
{
    diag("testError03");

    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;
    psErr *last = NULL;
    psErr *getErr = NULL;

    // Attempt to get last error message with an empty stack verify psErr with code PS_ERR_NONE
    last = psErrorLast();
    ok(last->code != PS_ERR_NONE,
       "psErrorLast did return PS_ERR_NONE for empty stack");
    skip_start(last->code == PS_ERR_NONE,
               9, "psErrorLast did return PS_ERR_NONE for empty stack");

    psFree(last);

    // Attempt to get specific error message with empty stack verify psErr with code PS_ERR_NONE
    getErr= psErrorGet(2);
    ok(getErr->code != PS_ERR_NONE,
       "psErrorGet did not return PS_ERR_NONE  for empty stack");
    skip_start(getErr->code == PS_ERR_NONE,
               8, "psErrorGet did not return PS_ERR_NONE  for empty stack");

    psFree(getErr);

    // Attempt to get error message with invalid index and an empty stack
    getErr= psErrorGet(-1);
    ok(getErr->code == PS_ERR_NONE,
       "psErrorGet with invalid index/empty stack");
    skip_start(getErr->code != PS_ERR_NONE,
               7, "psErrorGet with invalid index/empty stack");

    psFree(getErr);

    // Generate three error messages
    ok (psError(code, true, "Error code = %d", code) ==  code,
        "return value verify-1.");
    skip_start (psError(code, true, "Error code = %d", code !=  code),
                6, "return value verify-2.");

    ok (psError((code+1), false, "Error code = %d", (code+1)) == (code+1),
        "Failed return value verify-3.");
    skip_start (psError((code+1), false, "Error code = %d", (code+1))!=(code+1),
                5, "Failed return value verify-4.");

    ok (psError((code+2), false, "Error code = %d", (code+2)) == (code+2),
        "Failed return value verify-5.");
    skip_start (psError((code+2), false, "Error code = %d", (code+2))!=(code+2),
                4, "Failed return value verify-6.");

    last = psErrorLast();
    getErr= psErrorGet(0);

    // Check that last and get with 0 index are equal
    ok(last == getErr, "psErrorGet(0) equal to psErrorLast");
    skip_start(last != getErr, 3, "psErrorGet(0) equal to psErrorLast");

    psFree(last);

    // Verify the last error message was returned
    ok ( getErr->code == (code+2),
         "psErrorLast() did not retrieve last error");
    skip_start ( getErr->code != (code+2),
                 2, "psErrorLast() did not retrieve last error");

    psFree(getErr);

    // Verify the middle error message can be retrieved
    getErr= psErrorGet(1);
    ok ( getErr->code == (code+1),
         "psErrorGet() did not retrieve proper error");
    skip_start ( getErr->code != (code+1),
                 1, "psErrorGet() did not retrieve proper error");

    psFree(getErr);

    // Verify the psErrorGet returns NULL if an invalid index is given
    getErr= psErrorGet(-1);
    ok ( getErr->code == PS_ERR_NONE,
         "psErrorGet() did not return PS_ERR_NONE w/ invalid arg");

    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();
    skip_end();

    psFree(getErr);

    return 0;
}

static psS32 testError02(void)
{
    diag("testError02");

    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;

    // Generate error message and verify return value
    ok (psError(code, true, "Error code = %d", code) == code,
        "Failed return value verify.");

    myErrorStackPrint(stderr,"ERROR STACK PRINT Test%dA",2);

    return 0;
}

static psS32 testError01(void)
{
    diag("testError01");

    psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;

    // Verify the return value of psErrorMsg is the psErrorCode passed
    ok ( psError(code, true, "Error code = %d", code) == code,
         "Failed return value verify.");
    skip_start ( psError(code, true, "Error code = %d", code) != code,
                 4, "Failed return value verify.");

    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1A");

    // test1B empty string in for name argument
    ok ( psError(code+1, true, "Error code = %d", code+1) == code+1,
         "Failed return with empty string.");
    skip_start ( psError(code+1, true, "Error code = %d", code+1) != code+1,
                 3, "Failed return with empty string.");

    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1B");

    // test1D undefined code
    ok ( psError(-1, true, "Error code = %d", -1) == -1,
         "Failed return with undefined code.");
    skip_start ( psError(-1, true, "Error code = %d", -1) != -1,
                 2, "Failed return with undefined code.");

    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1D");

    // test1E set psErrorMsg argument to false
    ok( psError(code, false, "Error code = %d", code) == code,
        "Failed return with false new arg.");
    skip_start( psError(code, false, "Error code = %d", code) != code,
                1, "Failed return with false new arg.");

    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1E");

    // test1F psErrorMsg with a error code less then PS_ERR_BASE(256)
    ok( psError(9, true, "Errno code = %d", 9) == 9, "Error Code" );

    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1F");

    skip_end();
    skip_end();
    skip_end();
    skip_end();

    return 0;
}

static psS32 testError00(void)
{
    diag("testError00");

    psS32  intval=1;
    psS64 longval = 2;
    float floatval = 3.01;
    char  charval = 'E';
    char *stringval = "E R R O R";

    // Test point #1 Multiple type values placed in the error string
    psError(PS_ERR_UNKNOWN, true,
            "ALL TYPES intval = %d longval = %lld floatval = %f charval = %c strval = %s",
            intval,longval,floatval,charval,stringval);

    // Test point #2 String values in error message
    psError(PS_ERR_UNKNOWN, true, "NO VALUES");

    // Test point #3 Empty strings in error message
    psError(PS_ERR_UNKNOWN, true, " ");

    return 0;
}

static psS32 testErrorRegister(void)
{
    diag("testErrorRegister");

    psS32 numErr = 4;
    psErrorDescription errDesc[] = { {PS_ERR_N_ERR_CLASSES+1,"first"},
                                     {PS_ERR_N_ERR_CLASSES+2,"second"},
                                     {PS_ERR_N_ERR_CLASSES+3,"third"},
                                     {PS_ERR_N_ERR_CLASSES+4,"fourth"} };
    /*
        1. invoke psErrorRegister with a n>1 array of psErrorDescriptions. Verify that:
            a. Each error description given is retrievable with psErrorCodeString.
    */
    psErrorRegister(errDesc,numErr);

    for (psS32 i = 0; i < numErr; i++) {
        const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+1+i);
        ok (desc != NULL,
            "psErrorCode didn't find registered error code.");

        ok (strcmp(desc,errDesc[i].description) == 0,
            "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.", desc,errDesc[i].description);
    }

    /*
        2. invoke psErrorCodeString with a static/builtin psLib error code. Verify:
            a. the result is correct.
    */
    const char* desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES);
    ok (desc != NULL, "psErrorCode didn't find static error code.");
    ok (strcmp(desc,"error classes end marker") == 0,
        "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
        desc,"error classes end marker");

    desc = psErrorCodeString(PS_ERR_NONE);
    ok (desc != NULL,
        "psErrorCode didn't find static error code.");
    ok (strcmp(desc,"not an error") == 0,
        "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
        desc,"not an error");

    /*
        3. invoke psErrorCodeString with an invalid code. Verify a NULL is returned.
    */
    desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+numErr+1);
    ok (desc == NULL,
        "psErrorCode didn't return a NULL with a bogus input code.");

    /*
        4. invoke psErrorRegister with a NULL psErrorDescription. Verify that:
            a. the execution does not cease.
            b. an appropriate error is generated.
    */
    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
    psErrorClear();
    psErrorRegister(NULL,1);
    psErr* err = psErrorLast();
    ok (err->code == PS_ERR_BAD_PARAMETER_NULL,
        "psErrorCode didn't generate proper error code for NULL input.");

    psFree(err);

    /*
        5. invoke psErrorRegister with nerror=0. Verify that no error occurs.
    */
    psErrorClear();
    psErrorRegister(errDesc,0);
    err = psErrorLast();
    ok (err->code == PS_ERR_NONE,
        "psErrorCode generated an error for nErrors = 0.");

    psFree(err);
    return 0;
}
