/** @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.16 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2004-11-30 23:44:17 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pslib.h"
#include "psTest.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);
}

testDescription tests[] = {
                              {testError00, 486, "psError()", 0, false},
                              {testError01, 725, "psErrorMsg(),psErrorStackPrint()", 0, false},
                              {testError02, 726, "psErrorStackPrintV()", 0, false},
                              {testError03, 727, "psErrorGet(),psErrorLast()", 0, false},
                              {testError04, 728, "psErrorClear()", 0, false},
                              {testError05, 729, "psErrorCodeString()", 0, false},
                              {testErrorRegister, 751, "psErrorRegister()", 0, false},
                              {NULL}
                          };

psS32 main( psS32 argc, char* argv[] )
{
    psLogSetLevel( PS_LOG_INFO );

    return ( !runTestSuite(stderr, "psError", tests, argc, argv) );
}

static psS32 testError05(void)
{
    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;

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

    // Verify the return value of psErrorCodeString if code is negative
    if( psErrorCodeString(-1) != NULL) {
        psLogMsg("tst_psError05", PS_LOG_INFO, "Failed error string with neg. code");
        return 40;
    }

    return 0;
}

static psS32 testError04(void)
{
    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();
    if(lastAfterClear->code != PS_ERR_NONE) {
        psLogMsg("tst_psError05", PS_LOG_ERROR, "psErrorLast did not return expected.");
        return 30;
    }
    psFree(lastAfterClear);

    // Generate three error messages to have messages on error stack
    if (psError(code, true, "Error code = %d", code) !=  code) {
        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
        return 31;
    }
    if (psError((code+1), false, "Error code = %d", (code+1)) != (code+1)) {
        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
        return 32;
    }
    if (psError((code+2), false, "Error code = %d", (code+2)) != (code+2)) {
        psLogMsg("tst_psError04", PS_LOG_ERROR, "Failed return value verify.");
        return 33;
    }

    // Get the last error message and verify it has the expected code
    last = psErrorLast();
    if(last->code != (code+2)) {
        psLogMsg("tst_psError04", PS_LOG_ERROR, "psErrorLast did not return expected.");
        return 34;
    }
    psFree(last);

    // Clear the error stack
    psErrorClear();

    // Get the last error message after clear and verify is has PS_ERR_NONE code
    lastAfterClear = psErrorLast();
    if(lastAfterClear->code != PS_ERR_NONE) {
        psLogMsg("tst_psError05", PS_LOG_ERROR, "psErrorLast did not return expected.");
        return 35;
    }
    psFree(lastAfterClear);

    return 0;
}

static psS32 testError03(void)
{
    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();
    if(last->code != PS_ERR_NONE) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorLast did return PS_ERR_NONE for empty stack");
        return 20;
    }
    psFree(last);

    // Attempt to get specific error message with empty stack verify psErr with code PS_ERR_NONE
    getErr= psErrorGet(2);
    if(getErr->code != PS_ERR_NONE) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet did not return PS_ERR_NONE  for empty stack");
        return 21;
    }
    psFree(getErr);

    // Attempt to get error message with invalid index and an empty stack
    getErr= psErrorGet(-1);
    if(getErr->code != PS_ERR_NONE) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet with invalid index/empty stack");
        return 22;
    }
    psFree(getErr);

    // Generate three error messages
    if (psError(code, true, "Error code = %d", code) !=  code) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
        return 23;
    }
    if (psError((code+1), false, "Error code = %d", (code+1)) != (code+1)) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
        return 24;
    }
    if (psError((code+2), false, "Error code = %d", (code+2)) != (code+2)) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "Failed return value verify.");
        return 25;
    }

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

    // Check that last and get with 0 index are equal
    if(last != getErr) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet(0) not equal to psErrorLast");
        return 26;
    }
    psFree(last);

    // Verify the last error message was returned
    if ( getErr->code != (code+2) ) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorLast() did not retrieve last error");
        return 27;
    }
    psFree(getErr);

    // Verify the middle error message can be retrieved
    getErr= psErrorGet(1);
    if ( getErr->code != (code+1)) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet() did not retrieve proper error");
        return 28;
    }
    psFree(getErr);

    // Verify the psErrorGet returns NULL if an invalid index is given
    getErr= psErrorGet(-1);
    if ( getErr->code != PS_ERR_NONE ) {
        psLogMsg("tst_psError03", PS_LOG_ERROR, "psErrorGet() did not return PS_ERR_NONE w/ invalid arg");
        return 29;
    }
    psFree(getErr);

    return 0;
}

static psS32 testError02(void)
{
    psErrorCode code = PS_ERR_BAD_PARAMETER_VALUE;

    // Generate error message and verify return value
    if (psError(code, true, "Error code = %d", code) != code ) {
        psLogMsg("tst_psError02", PS_LOG_ERROR, "Failed return value verify.");
        return 10;
    }
    myErrorStackPrint(stderr,"ERROR STACK PRINT Test%dA",2);

    return 0;
}

static psS32 testError01(void)
{
    psErrorCode code=PS_ERR_BAD_PARAMETER_VALUE;

    // Verify the return value of psErrorMsg is the psErrorCode passed
    if ( psError(code, true, "Error code = %d", code) != code) {
        psLogMsg("tst_psError01", PS_LOG_ERROR, "Failed return value verify.");
        return 1;
    }
    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1A");

    // test1B empty string in for name argument
    if ( psError(code+1, true, "Error code = %d", code+1) != code+1) {
        psLogMsg("tst_psError01", PS_LOG_ERROR, "Failed return with empty string.");
        return 2;
    }
    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1B");

    // test1D undefined code
    if ( psError(-1, true, "Error code = %d", -1) != -1) {
        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with undefined code.");
        return 4;
    }
    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1D");

    // test1E set psErrorMsg argument to false
    if( psError(code, false, "Error code = %d", code) != code) {
        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with false new arg.");
        return 5;
    }
    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1E");

    // test1F psErrorMsg with a error code less then PS_ERR_BASE(256)
    if( psError(9, true, "Errno code = %d", 9) != 9) {
        psLogMsg("test_psError01", PS_LOG_ERROR, "Failed return with errno code.");
        return 6;
    }
    psErrorStackPrint(stderr,"ERROR STACK PRINT Test1F");

    return 0;
}

static psS32 testError00(void)
{

    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)
{

    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);
        if (desc == NULL) {
            psLogMsg(__func__,PS_LOG_ERROR,
                     "psErrorCode didn't find registered error code.");
            return 1+i*10;
        }
        if (strcmp(desc,errDesc[i].description) != 0) {
            psLogMsg(__func__,PS_LOG_ERROR,
                     "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
                     desc,errDesc[i].description);
            return 2+i*10;
        }
    }

    /*
        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);
    if (desc == NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't find static error code.");
        return 40;
    }
    if (strcmp(desc,"error classes end marker") != 0) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
                 desc,"error classes end marker");
        return 41;
    }

    desc = psErrorCodeString(PS_ERR_NONE);
    if (desc == NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't find static error code.");
        return 42;
    }
    if (strcmp(desc,"not an error") != 0) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't return the proper description.  Got '%s', expected '%s'.",
                 desc,"not an error");
        return 43;
    }

    /*
        3. invoke psErrorCodeString with an invalid code. Verify a NULL is returned.
    */
    desc = psErrorCodeString(PS_ERR_N_ERR_CLASSES+numErr+1);
    if (desc != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't return a NULL with a bogus input code.");
        return 44;
    }

    /*
        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();
    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode didn't generate proper error code for NULL input.");
        return 45;
    }
    psFree(err);

    /*
        5. invoke psErrorRegister with nerror=0. Verify that no error occurs.
    */
    psErrorClear();
    psErrorRegister(errDesc,0);
    err = psErrorLast();
    if (err->code != PS_ERR_NONE) {
        psLogMsg(__func__,PS_LOG_ERROR,
                 "psErrorCode generated an error for nErrors = 0.");
        return 46;
    }
    psFree(err);
    return 0;
}
