/** @file  tst_psMetadataIO.c
 *
 *  @brief Test driver for psMetadataIO functions
 *
 *  This test driver contains the following tests for psMetadata:
 *    Read config file with overwrite set true
 *    Read config file with overwrite set false
 *    Attempt to use null fileName argument
 *    Attempt to open nonexistant file
 *
 *  @author  Ross Harman, MHPCC
 *  @author  Robert DeSonia, MHPCC
 *  @author  Eric Van Alst, MHPCC
 *
 *  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2005-09-26 21:13:33 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <string.h>
#include "pslib_strict.h"
#include "psTest.h"

static psS32 testMetadataParseConfig(void);
static psS32 testMetadataParseConfig1(void);
static psS32 testMetadataParseConfig2(void);
static psS32 testMetadataParseConfig3(void);
static psS32 testMetadataParseConfig4(void);
static psS32 testMetadataPrint(void);

testDescription tests[] = {
                              {testMetadataParseConfig,000,"psMetadataConfigParse",0,false},
                              {testMetadataParseConfig1,000,"psMetadataConfigParse",0,false},
                              {testMetadataParseConfig2,000,"psMetadataConfigParse",0,false},
                              {testMetadataParseConfig3,000,"psMetadataConfigParse",0,false},
                              {testMetadataParseConfig4,000,"psMetadataConfigParse",0,false},
                              {testMetadataPrint,000,"psMetadataPrint",0,false},
                              {NULL}
                          };

static void writeMetadataItem(psMetadataItem* metadataIem, char* indentStr);
static void writeMetadata(psMetadata* metadata, char* indentStr);
static void writeMetadataList(psList *metadataItemList, char* indentStr);

static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile);
static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile);
static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile);
static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile);
static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile);
static psMetadata *setupMeta(void);

#define ERROR_TOL  0.0001

const char testConfig5[] = "test5.config";
const psS32 testConfig5Fails = 7;

const char testConfig4[] = "test4.config";
const psS32 testConfig4Fails = 0;

const char testConfig3[] = "test3.config";
const psS32 testConfig3Fails = 1;

const char testConfig2[] = "test2.config";
const psS32 testConfig2Fails = 17;

// SDR-14 test config file #1
const char  testConfig1[] = "test1.config";
const psS32 testConfig1FailsOverwrite = 0;
const psS32 testConfig1Fails = 1;
const psS32 testConfig1Items = 25;
const char* testConfig1KeyOverwrite[] =
    {
        "Double","String","boolean","primes","comment",
        "comment","comment","comment","comment","Float",
        "boolean1","negprimes","vector1","vector2","vector3",
        "vector4","vector5","vector6","vector7","vector8",
        "CELL.00", "CELL.01","MYCELL","MYCELL","cell"
    };
const char* testConfig1CommentOverwrite[] =
    {
        "This is a comment","comment","The value of 'boolean' is 'true'","These are prime numbers",
        "","","","","",
        "This generates a warning, and, if 'overwrite' is 'false', is ignored","The value of 'boolean' is 'false'",
        "","","","","",
        "","","","",
        "","","","A number",""
    };
const psDataType testConfig1TypeOverwrite[] =
    {
        PS_DATA_F64, PS_DATA_STRING, PS_DATA_BOOL, PS_DATA_VECTOR, PS_DATA_STRING,
        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING,  PS_DATA_F64,
        PS_DATA_BOOL, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR,
        PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR, PS_DATA_VECTOR,
        PS_DATA_METADATA, PS_DATA_METADATA, PS_DATA_METADATA, PS_DATA_S32, PS_DATA_METADATA
    };
const psS32 testConfig1ValueS32Overwrite[] =
    {
        0, 0, 0, 0, 0,
        0, 0, 0, 0, 0,
        0, 0, 0, 0, 0,
        0, 0, 0, 0, 0,
        0, 0, 0, 123, 0
    };
const psF32 testConfig1ValueF32Overwrite[] =
    {
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0
    };
const psF64 testConfig1ValueF64Overwrite[] =
    {
        1.23456789, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 1.23456,
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0, 0.0
    };
const psBool testConfig1ValueBoolOverwrite[] =
    {
        false, false, true, false, false,
        false, false, false, false, false,
        false, false, false, false, false,
        false, false, false, false, false,
        false, false, false, false, false
    };
const char* testConfig1ValueStrOverwrite[] =
    {
        "", "This is the string that forms the value","","","This",
        "is","a","non-unique","key","",
        "","","","","",
        "","","","","",
        "","","","",""
    };
const psS32 testConfig1ValueVecItemsU8 = 7;
const psElemType testConfig1ValueVecTypeU8 = PS_TYPE_U8;
const psU8 testConfig1ValueVecValueU8[] =
    {
        2, 3, 5, 7, 11, 13, 17
    };
const psS32 testConfig1ValueVecItemsS8 = 8;
const psElemType testConfig1ValueVecTypeS8 = PS_TYPE_S8;
const psS8 testConfig1ValueVecValueS8[] =
    {
        -2, -3, -5, -7, -11, -13, -17, -19
    };
const psS32 testConfig1ValueVecItemsU16 = 5;
const psElemType testConfig1ValueVecTypeU16 = PS_TYPE_U16;
const psU16 testConfig1ValueVecValueU16[] =
    {
        0, 1, 2, 4, 8
    };
const psS32 testConfig1ValueVecItemsU32 = 6;
const psElemType testConfig1ValueVecTypeU32 = PS_TYPE_U32;
const psU32 testConfig1ValueVecValueU32[] =
    {
        0, 8, 16, 32, 64, 128
    };
const psS32 testConfig1ValueVecItemsU64 = 3;
const psElemType testConfig1ValueVecTypeU64 = PS_TYPE_U64;
const psU64 testConfig1ValueVecValueU64[] =
    {
        0, 64, 256
    };
const psS32 testConfig1ValueVecItemsS16 = 5;
const psElemType testConfig1ValueVecTypeS16 = PS_TYPE_S16;
const psS16 testConfig1ValueVecValueS16[] =
    {
        -2, -1, 0, 1, 2
    };
const psS32 testConfig1ValueVecItemsS32 = 6;
const psElemType testConfig1ValueVecTypeS32 = PS_TYPE_S32;
const psS32 testConfig1ValueVecValueS32[] =
    {
        -4, -2, 0, 2, 4, 6
    };
const psS32 testConfig1ValueVecItemsS64 = 7;
const psElemType testConfig1ValueVecTypeS64 = PS_TYPE_S64;
const psS64 testConfig1ValueVecValueS64[] =
    {
        -16, -4, 0, 4, 16, 36, 64
    };
const psS32 testConfig1ValueVecItemsF32 = 4;
const psElemType testConfig1ValueVecTypeF32 = PS_TYPE_F32;
const psF32 testConfig1ValueVecValueF32[] =
    {
        -1.03, 1.04, -1.05, 1.06
    };
const psS32 testConfig1ValueVecItemsF64 = 5;
const psElemType testConfig1ValueVecTypeF64 = PS_TYPE_F64;
const psF64 testConfig1ValueVecValueF64[] =
    {
        -2.22, 2.21, -2.20, 2.19, -2.18
    };
const psS32 testConfig1ValueMetaItems1 = 3;
const char* testConfig1ValueMetaNames1[] =
    {
        "EXTNAME","BIASSEC","CHIP"
    };
const psDataType testConfig1ValueMetaTypes1[] =
    {
        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING
    };
const char* testConfig1ValueMetaValue1[] =
    {
        "CCD00","BSEC-00","CHIP.00"
    };
const psS32 testConfig1ValueMetaItems2 = 3;
const char* testConfig1ValueMetaNames2[] =
    {
        "EXTNAME","BIASSEC","CHIP"
    };
const psDataType testConfig1ValueMetaTypes2[] =
    {
        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING
    };
const char* testConfig1ValueMetaValue2[] =
    {
        "CCD01","BSEC-01","CHIP.00"
    };
const psS32 testConfig1ValueMetaItems3 = 4;
const char* testConfig1ValueMetaNames3[] =
    {
        "EXTNAME","BIASSEC","CHIP","NCELL"
    };
const psDataType testConfig1ValueMetaTypes3[] =
    {
        PS_DATA_STRING, PS_DATA_STRING, PS_DATA_STRING, PS_DATA_S32
    };
const char* testConfig1ValueMetaValueStr3[] =
    {
        "CCD00","BSEC-00","CHIP.00",""
    };
const psS32 testConfig1ValueMetaValueS323[] =
    {
        0, 0, 0, 24
    };

static psMetadata *setupMeta(void)
{
    psMetadata *md = NULL;
    md = psMetadataAlloc();

    psMetadataAddBool(md, PS_LIST_TAIL, "item1", 0, "I am a boolean", true);
    psMetadataAddS32(md, PS_LIST_TAIL, "item2", 0, "I am a integer", 55);
    psMetadataAddF32(md, PS_LIST_TAIL, "item3", 0, "I am a float", 3.14);
    psMetadataAddF64(md, PS_LIST_TAIL, "item4", 0, "I am a double", 6.28);
    psMetadataAddStr(md, PS_LIST_TAIL, "item5", 0, "I am a string", "GNIRTS");

    return md;
}

static void writeMetadata(psMetadata* metadata, char* indentStr)
{
    writeMetadataList(metadata->list, indentStr);
}

static void writeMetadataList(psList* metadataItemList, char* indentStr)
{
    psMetadataItem* entryChild        = NULL;
    psMetadataItem* searchChild       = NULL;
    psArray*        nonUniqueKeyArray = NULL;
    psBool          keyFound          = false;
    char*           tempKey           = NULL;

    // Allocate iterator for moving through metadata list
    psListIterator* iter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
    psListIterator* searchIter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);

    // Allocate array for nonUnique key names
    nonUniqueKeyArray = psArrayAlloc(10);
    nonUniqueKeyArray->n = 0;


    // Loop through all items in the list
    while ( (entryChild = psListGetAndIncrement(iter)) != NULL) {
        // Search list for another entry with same key name
        // Check if last entry
        if(iter->index != metadataItemList->n) {
            // Set search iterator to index
            if(!psListIteratorSet(searchIter,iter->index) ) {
                psError(PS_ERR_UNKNOWN,true,"Error searching list for multiple keys");
                break;
            }
            keyFound = false;
            while ( (searchChild = psListGetAndIncrement(searchIter)) != NULL) {
                if(strcmp(entryChild->name,searchChild->name) == 0) {
                    // Search nonUnique key array
                    for(psS32 i = 0; i < nonUniqueKeyArray->n; i++) {
                        if(strcmp(entryChild->name,nonUniqueKeyArray->data[i]) == 0) {
                            keyFound = true;
                        }
                    }
                    if(!keyFound) {
                        // Add key to non-unique array
                        tempKey = psStringCopy(entryChild->name);
                        nonUniqueKeyArray = psArrayAdd(nonUniqueKeyArray,1,tempKey);
                        psFree(tempKey);

                        // Print MULTI line
                        printf("%-25s MULTI\n",entryChild->name);

                        // Break out of loop
                        break;
                    }
                }
            }
        }
        writeMetadataItem(entryChild,indentStr);
    }

    psFree(iter);
    psFree(searchIter);
    psFree(nonUniqueKeyArray);
}

char* vectorToConfigString(psVector* vector)
{
    psS32  maxLength = 256;

    char* str = psAlloc(sizeof(char)*maxLength+1);

    if (vector == NULL) {
        snprintf(str,maxLength, "NULL");
        return str;
    }

    int size = vector->n;

    if (size == 0) {
        snprintf(str,maxLength, " ");
        return str;
    }

    char* tempStr = psAlloc(sizeof(char)*maxLength+1);
    *str = '\0';
    bool full = false;

    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
case PS_TYPE_##TYPE: \
    strcat(str,#TYPE); \
    for(psS32 i = 0; i < (strlen(str)-6); i++) {  \
        strcat(str," "); \
    } \
    for (lcv=0; lcv < size && ! full; lcv++) { \
        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
        strncat(str,tempStr,maxLength); \
        full = (strlen(str) > maxLength-2); \
        prefix = ","; \
    } \
    break;

    int lcv;
    char* prefix = " ";
    switch(vector->type.type) {
        APPEND_ELEMENTS_CASE(S8,char,"%hd")
        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
        APPEND_ELEMENTS_CASE(S32,int,"%d")
        APPEND_ELEMENTS_CASE(S64,long,"%ld")
        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
        APPEND_ELEMENTS_CASE(F32,double,"%g")
        APPEND_ELEMENTS_CASE(F64,double,"%g")
    default:
        snprintf(str,maxLength,"INVALID TYPE");
        break;
    }

    psFree(tempStr);

    return str;
}


static void writeMetadataItem(psMetadataItem *metadataItem, char* indentStr)
{
    char*  vecStr;

    switch(metadataItem->type) {
    case PS_DATA_BOOL:
        printf("%s%-25s BOOL  %40d",indentStr,metadataItem->name,metadataItem->data.B);
        break;
    case PS_DATA_S32:
        printf("%s%-25s S32   %40d",indentStr,metadataItem->name,metadataItem->data.S32);
        break;
    case PS_DATA_F32:
        printf("%s%-25s F32   %40f",indentStr,metadataItem->name,metadataItem->data.F32);
        break;
    case PS_DATA_F64:
        printf("%s%-25s F64   %40lf",indentStr,metadataItem->name,metadataItem->data.F64);
        break;
    case PS_DATA_VECTOR:
        vecStr = (char*)vectorToConfigString((psVector*)metadataItem->data.V);
        printf("%s@%-24s %s",indentStr,metadataItem->name,vecStr);
        psFree(vecStr);
        break;
    case PS_DATA_STRING:
        printf("%s%-25s STR   %40s",indentStr,metadataItem->name,(char*)metadataItem->data.V);
        break;
    case PS_DATA_METADATA:
        printf("%s%-25s METADATA\n",indentStr,metadataItem->name);
        char nestedStr[256] = "    ";
        strcat(nestedStr,indentStr);
        writeMetadata((psMetadata*)metadataItem->data.md,nestedStr);
        printf("%sEND",indentStr);
        break;
    default:
        printf("#%s%-24s BAD TYPE=%d",indentStr,metadataItem->name,metadataItem->type);
        break;
    }
    if(strlen(metadataItem->comment) > 0) {
        printf("  # %-20s\n",metadataItem->comment);
    } else {
        printf("\n");
    }
}

static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile)
{
    psBool     returnValue = true;

    // Verify the expected number of failed lines
    if(failedLines != expectedFailedLines) {
        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of failed lines = %d not as expected %d",
                configFile,failedLines,expectedFailedLines);
        returnValue = false;
    }
    return returnValue;
}

static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile)
{
    psBool     returnValue = true;

    // Verify the number of items in metadata
    if(md->list->n != expectedItems) {
        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of items = %d not as expected %d",
                configFile,md->list->n, expectedItems);
        returnValue = false;
    }
    return returnValue;
}

static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile)
{
    psBool    returnValue = true;

    // Compare key names
    if(strcmp(mdItem->name,expectedName) != 0) {
        psError(PS_ERR_UNKNOWN,true,"File: %s : Key name %s not as expected %s",
                configFile,mdItem->name,expectedName);
        returnValue = false;
    }

    return returnValue;
}

static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile)
{
    psBool     returnValue = true;

    // Compare types
    if(mdItem->type != expectedType) {
        psError(PS_ERR_UNKNOWN,true,"File: %s : Type %d not as expected %d",
                configFile,mdItem->type,expectedType);
        returnValue = false;
    }
    return returnValue;
}

static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile)
{
    psBool    returnValue = true;

    // Compare comments
    if(strcmp(mdItem->comment,expectedComment) != 0) {
        psError(PS_ERR_UNKNOWN,true,"File: %s : Comment %s not as expected %s",
                configFile,mdItem->comment,expectedComment);
        returnValue = false;
    }
    return returnValue;
}

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

    if( !runTestSuite(stderr,"psMetadataConfigParse",tests,argc,argv)) {
        return 1;
    }

    return 0;
}

psS32 testMetadataParseConfig(void)
{
    psMetadata*       metadata1      = NULL;
    psMetadataItem*   entryChild     = NULL;
    psU32             failedLines    = 0;
    psListIterator*   iter           = NULL;
    psListIterator*   mdIter         = NULL;
    psMetadataItem*   mdChild        = NULL;
    psS32             metaCounter    = 0;
    psS32             mdCounter      = 0;

    // Read config file test1.config with overwrite set true
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig1,true);
    // Verify the expected number of failed lines
    if(!checkFailedLines(failedLines,testConfig1FailsOverwrite,(char*)testConfig1)) {
        return 1;
    }
    // Verify the number of items in metadata
    if(!checkNumberOfItems(metadata1,testConfig1Items,(char*)testConfig1)) {
        return 2;
    }
    // Verify metadata item quads
    iter = psListIteratorAlloc(metadata1->list, PS_LIST_HEAD, true);
    for(psS32 i = 0; i < testConfig1Items; i++) {
        // Get list item
        entryChild = psListGetAndIncrement(iter);

        // Verify end of list not reached prematurely
        if(entryChild == NULL) {
            psError(PS_ERR_UNKNOWN,true,"End of list encountered at %d not as expected %d",
                    i,testConfig1Items);
            return i*10+1;
        }
        // Verify name
        if(!checkItemName(entryChild,(char*)testConfig1KeyOverwrite[i],(char*)testConfig1)) {
            return i*10+2;
        }
        // Verify type
        if(!checkItemType(entryChild,testConfig1TypeOverwrite[i],(char*)testConfig1)) {
            return i*10+3;
        }
        // Verify comment
        if(!checkItemComment(entryChild,(char*)testConfig1CommentOverwrite[i],(char*)testConfig1)) {
            return i*10+4;
        }
        // Compare values
        switch(entryChild->type) {
        case PS_DATA_S32:
            if(entryChild->data.S32 != testConfig1ValueS32Overwrite[i]) {
                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
                        testConfig1,entryChild->data.S32, testConfig1ValueS32Overwrite[i]);
                return i*10+5;
            }
            break;
        case PS_DATA_F32:
            if(fabs(entryChild->data.F32 - testConfig1ValueF32Overwrite[i]) > ERROR_TOL) {
                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %f not as expected %f",
                        testConfig1,entryChild->data.F32, testConfig1ValueF32Overwrite[i]);
                return i*10+5;
            }
            break;
        case PS_DATA_F64:
            if(fabs(entryChild->data.F64 - testConfig1ValueF64Overwrite[i]) > ERROR_TOL) {
                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %lf not as expected %lf",
                        testConfig1,entryChild->data.F64, testConfig1ValueF64Overwrite[i]);
                return i*10+5;
            }
            break;
        case PS_DATA_BOOL:
            if(entryChild->data.B != testConfig1ValueBoolOverwrite[i]) {
                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
                        testConfig1,entryChild->data.B,testConfig1ValueBoolOverwrite[i]);
                return i*10+5;
            }
            break;
        case PS_DATA_STRING:
            if(strcmp(entryChild->data.V,testConfig1ValueStrOverwrite[i]) != 0) {
                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %s not as expected %s",
                        testConfig1,entryChild->data.V,testConfig1ValueStrOverwrite[i]);
                return i*10+5;
            }
            break;
        case PS_DATA_VECTOR:
            // Verify the correct number of entries
            switch(((psVector*)(entryChild->data.V))->type.type) {
            case PS_TYPE_U8:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU8) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU8);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU8) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU8);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsU8; j++) {
                    if(((psVector*)entryChild->data.V)->data.U8[j] != testConfig1ValueVecValueU8[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.U8[j],
                                testConfig1ValueVecValueU8[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_U16:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU16) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU16);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU16) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU16);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsU16; j++) {
                    if(((psVector*)entryChild->data.V)->data.U16[j] != testConfig1ValueVecValueU16[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.U16[j],
                                testConfig1ValueVecValueU16[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_U32:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU32);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU32);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsU32; j++) {
                    if(((psVector*)entryChild->data.V)->data.U32[j] != testConfig1ValueVecValueU32[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.U32[j],
                                testConfig1ValueVecValueU32[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_U64:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU64);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU64);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsU64; j++) {
                    if(((psVector*)entryChild->data.V)->data.U64[j] != testConfig1ValueVecValueU64[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.U64[j],
                                testConfig1ValueVecValueU64[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_S8:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS8) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS8);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS8) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS8);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsS8; j++) {
                    if(((psVector*)entryChild->data.V)->data.S8[j] != testConfig1ValueVecValueS8[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.S8[j],
                                testConfig1ValueVecValueS8[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_S16:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS16) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS16);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS16) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS16);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsS16; j++) {
                    if(((psVector*)entryChild->data.V)->data.S16[j] != testConfig1ValueVecValueS16[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.S16[j],
                                testConfig1ValueVecValueS16[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_S32:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS32);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS32);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsS32; j++) {
                    if(((psVector*)entryChild->data.V)->data.S32[j] != testConfig1ValueVecValueS32[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.S32[j],
                                testConfig1ValueVecValueS32[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_S64:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS64);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS64);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsS64; j++) {
                    if(((psVector*)entryChild->data.V)->data.S64[j] != testConfig1ValueVecValueS64[j]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.S64[j],
                                testConfig1ValueVecValueS64[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_F32:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF32);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF32) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF32);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsF32; j++) {
                    if(fabs(((psVector*)entryChild->data.V)->data.F32[j]-testConfig1ValueVecValueF32[j])
                            > ERROR_TOL) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %f not as expected %f",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.F32[j],
                                testConfig1ValueVecValueF32[j]);
                        return i*10+5*j;
                    }
                }
                break;
            case PS_TYPE_F64:
                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF64);
                    return i*10+5;
                }
                // Verify the correct type
                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF64) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF64);
                    return i*10+5;
                }
                // Verify the values in vector
                for(psS32 j = 0; j < testConfig1ValueVecItemsF64; j++) {
                    if(fabs(((psVector*)entryChild->data.V)->data.F64[j]-testConfig1ValueVecValueF64[j])
                            > ERROR_TOL) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %lf not as expected %lf",
                                testConfig1,j,((psVector*)entryChild->data.V)->data.F64[j],
                                testConfig1ValueVecValueF64[j]);
                        return i*10+5*j;
                    }
                }
                break;
            default:
                break;
            }
            break;
        case PS_DATA_METADATA:
            if(metaCounter == 0) {
                // Check if number of items is as expected
                if(entryChild->data.md->list->n != testConfig1ValueMetaItems1) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 items %d not as expected %d",
                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems1);
                    return i*10+6;
                }
                // Loop through metadata items
                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
                mdCounter = 0;
                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
                    if(strcmp(mdChild->name,testConfig1ValueMetaNames1[mdCounter]) != 0) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 name[%d] %s not as expected %s",
                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames1[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    if(mdChild->type != testConfig1ValueMetaTypes1[mdCounter]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 type[%d] %d not as expected %d",
                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes1[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]) != 0) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 value[%d] %s not as expected %s",
                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    mdCounter++;
                }
                psFree(mdIter);
            } else if(metaCounter == 1) {
                // Check if number of items is as expected
                if(entryChild->data.md->list->n != testConfig1ValueMetaItems2) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 items %d not as expected %d",
                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems2);
                    return i*10+6;
                }
                // Loop through metadata items
                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
                mdCounter = 0;
                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
                    if(strcmp(mdChild->name,testConfig1ValueMetaNames2[mdCounter]) != 0) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 name[%d] %s not as expected %s",
                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames2[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    if(mdChild->type != testConfig1ValueMetaTypes2[mdCounter]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 type[%d] %d not as expected %d",
                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes2[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]) != 0) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 value[%d] %s not as expected %s",
                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    mdCounter++;
                }
                psFree(mdIter);
            } else if(metaCounter == 2) {
                // Check if number of items is as expected
                if(entryChild->data.md->list->n != testConfig1ValueMetaItems3) {
                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 items %d not as expected %d",
                            testConfig1,entryChild->data.md->list->n,testConfig1ValueMetaItems3);
                    return i*10+6;
                }
                // Loop through metadata items
                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
                mdCounter = 0;
                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
                    if(strcmp(mdChild->name,testConfig1ValueMetaNames3[mdCounter]) != 0) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 name[%d] %s not as expected %s",
                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames3[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    if(mdChild->type != testConfig1ValueMetaTypes3[mdCounter]) {
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 type[%d] %d not as expected %d",
                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes3[mdCounter]);
                        return i*10+6*mdCounter;
                    }
                    switch(mdChild->type) {
                    case PS_DATA_STRING:
                        if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValueStr3[mdCounter]) != 0) {
                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %s not as expected %s",
                                    testConfig1,mdCounter,(char*)mdChild->data.V,
                                    testConfig1ValueMetaValueStr3[mdCounter]);
                            return i*10+6*mdCounter;
                        }
                        break;
                    case PS_DATA_S32:
                        if(mdChild->data.S32 != testConfig1ValueMetaValueS323[mdCounter]) {
                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %d not as expected %d",
                                    testConfig1,mdCounter,mdChild->data.S32,
                                    testConfig1ValueMetaValueS323[mdCounter]);
                            return i*10+6*mdCounter;
                        }
                        break;
                    default:
                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] unknown type",
                                testConfig1,mdCounter);
                        return i*10+6*mdCounter;
                        break;
                    }
                    mdCounter++;
                }
                psFree(mdIter);
            }
            metaCounter++;
            break;
        default:
            psError(PS_ERR_UNKNOWN,true,"Unexpected type %d encountered",entryChild->type);
            return i*10+5;
            break;
        }
    }

    writeMetadata(metadata1,"");

    psFree(metadata1);

    return 0;
}

psS32 testMetadataParseConfig1(void)
{
    psMetadata*       metadata1      = NULL;
    psU32             failedLines    = 0;

    // Read config file test2.config with overwrite set true
    // This file contains parse errors
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate parse error message");
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig2,true);
    // Verify the expected number of failed lines
    if(!checkFailedLines(failedLines,testConfig2Fails,(char*)testConfig2)) {
        return 1;
    }
    // Verify return value is null
    if(metadata1 == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expected a NULL return for failed parse");
        return 2;
    }
    psFree(metadata1);

    // Attempt parse a non-existant file
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,"ab.config",true);
    if(metadata1 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for non-existant file");
        return 3;
    }

    // Attempt parse with NULL failed lines ptr
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL failed lines arg");
    metadata1 = psMetadataConfigParse(metadata1,NULL,testConfig2,true);
    if(metadata1 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for NULL failed lines argument");
        return 4;
    }

    // Attempt parse with NULL file name
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL file name arg");
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,NULL,true);
    if(metadata1 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for NULL filename argument");
        return 5;
    }

    psFree(metadata1);

    return 0;
}

psS32 testMetadataParseConfig2(void)
{
    psMetadata*       metadata1      = NULL;
    psU32             failedLines    = 0;

    // Read config file test3.config with overwrite set false
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error of duplicate key names");
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig3,false);
    // Verify the expected number of failed lines
    if(!checkFailedLines(failedLines,testConfig3Fails,(char*)testConfig3)) {
        return 1;
    }
    if(metadata1 == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expected a NULL return for failed due to overwrite false");
        return 2;
    }
    psFree(metadata1);

    return 0;
}

psS32 testMetadataParseConfig3(void)
{
    psMetadata*       metadata1      = NULL;
    psU32             failedLines    = 0;

    // Read config file test4.config with overwrite set false
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig4,false);
    // Verify the expected number of failed lines
    if(!checkFailedLines(failedLines,testConfig4Fails,(char*)testConfig4)) {
        return 1;
    }
    // Print out metadata items for comparison against STDERR verified file
    writeMetadata(metadata1,"");

    psFree(metadata1);

    return 0;
}

psS32 testMetadataParseConfig4(void)
{
    psMetadata*       metadata1      = NULL;
    psU32             failedLines    = 0;

    // Read config file test4.config with overwrite set false
    metadata1 = psMetadataConfigParse(metadata1,&failedLines,testConfig5,true);
    // Verify the expected number of failed lines
    if(!checkFailedLines(failedLines,testConfig5Fails,(char*)testConfig5)) {
        return 1;
    }
    psFree(metadata1);

    return 0;
}

psS32 testMetadataPrint(void)
{
    psMetadata *meta = NULL;
    meta = setupMeta();
    psMetadataPrint(meta, 0);
    psFree(meta);
    return 0;
}
