/**
 *  C Implementation: tap_psMetadataConfig_output
 *
 * Description:  Tests for psMetadataConfigWrite, psMetadataConfigPrint,
 *               & psMetadataConfigFormat
 *
 * Author: dRob <David.Robbins@mhpcc.hpc.mil>, (C) 2006
 *
 * Copyright: See COPYING file that comes with this distribution
 *
 */

#include <pslib.h>
#include <string.h>

#include "tap.h"
#include "pstap.h"

static void testMetaConfigWrite(void);
static void testMetaConfigPrint(void);
static void testMetaConfigFormat(void);

static psMetadata *setupMeta(void);

int main(void)
{
    plan_tests(25);

    diag("Tests for psMetadataConfig Output Functions");

    testMetaConfigWrite();
    testMetaConfigPrint();
    testMetaConfigFormat();

    done();
}

static psMetadata *setupMeta(void)
{
    psMetadata *md = NULL;
    psVector *vec1 = NULL;
    psVector *vec2 = NULL;
    psVector *vec3 = NULL;
    psVector *vec4 = NULL;
    psVector *vec5 = NULL;
    psVector *vec6 = NULL;
    psVector *vec7 = NULL;
    psVector *vec8 = NULL;
    psVector *vec9 = NULL;
    psVector *vec10 = NULL;
    psMetadata *newMD = NULL;
    psTime *time1;
    psTime *time2;
    psTime *time3;
    psTime *time4;
    int i = 0;
    md = psMetadataAlloc();
    newMD = psMetadataAlloc();
    vec1 = psVectorAlloc(60, PS_DATA_S8);
    vec2 = psVectorAlloc(60, PS_DATA_S16);
    vec3 = psVectorAlloc(60, PS_DATA_S32);
    vec4 = psVectorAlloc(60, PS_DATA_S64);
    vec5 = psVectorAlloc(60, PS_DATA_U8);
    vec6 = psVectorAlloc(60, PS_DATA_U16);
    vec7 = psVectorAlloc(60, PS_DATA_U32);
    vec8 = psVectorAlloc(60, PS_DATA_U64);
    vec9 = psVectorAlloc(60, PS_DATA_F32);
    vec10 = psVectorAlloc(60, PS_DATA_F64);
    time1 = psTimeAlloc(PS_TIME_TAI);
    time2 = psTimeAlloc(PS_TIME_TT);
    time3 = psTimeAlloc(PS_TIME_UTC);
    time4 = psTimeAlloc(PS_TIME_UT1);
    for (i = 0; i < 5; i++) {
        vec1->data.S8[i] = i+1;
        vec2->data.S16[i] = i+1;
        vec3->data.S32[i] = i+1;
        vec4->data.S64[i] = i+1;
        vec5->data.U8[i] = i+1;
        vec6->data.U16[i] = i+1;
        vec7->data.U32[i] = i+1;
        vec8->data.U64[i] = i+1;
        vec9->data.F32[i] = i+1.0;
        vec10->data.F64[i] = i+1.0;
    }
    vec1->n = 5;
    vec2->n = 5;
    vec3->n = 5;
    vec4->n = 5;
    vec5->n = 5;
    vec6->n = 5;
    vec7->n = 5;
    vec8->n = 5;
    vec9->n = 5;
    vec10->n = 5;
    time1->sec = 1000;
    time1->nsec = 25;
    time1->leapsecond = true;
    psMetadataAddBool(md, PS_LIST_TAIL, "item1-1", 0, "I am a boolean", true);
    psMetadataAddBool(md, PS_LIST_TAIL, "item1-2", 0, "I am a boolean2", false);
    psMetadataAddS8(md, PS_LIST_TAIL, "item2-8", 0, "", 8);
    psMetadataAddS16(md, PS_LIST_TAIL, "item2-16", 0, "", 16);
    psMetadataAddS32(md, PS_LIST_TAIL, "item2-32", 0, "", 32);
    psMetadataAddS64(md, PS_LIST_TAIL, "item2-64", 0, "", 64);
    psMetadataAddU8(md, PS_LIST_TAIL, "item3-8", 0, "", 8);
    psMetadataAddU16(md, PS_LIST_TAIL, "item3-16", 0, "", 16);
    psMetadataAddU32(md, PS_LIST_TAIL, "item3-32", 0, "", 32);
    psMetadataAddU64(md, PS_LIST_TAIL, "item3-64", 0, "", 64);
    psMetadataAddF32(md, PS_LIST_TAIL, "item4", 0, NULL, 3.14);
    psMetadataAddF64(md, PS_LIST_TAIL, "item5", 0, "", 6.28);
    psMetadataAddStr(md, PS_LIST_TAIL, "item6", 0, "I am a string", "GNIRTS");
    psMetadataAddStr(md, PS_LIST_TAIL, "item6-2", 0, "", "");
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorS8", 0, "I am a vector", vec1);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorS16", 0, "", vec2);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorS32", 0, "", vec3);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorS64", 0, "", vec4);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorU8", 0, "", vec5);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorU16", 0, "I am a vector", vec6);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorU32", 0, "", vec7);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorU64", 0, "", vec8);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorF32", 0, "", vec9);
    psMetadataAddVector(md, PS_LIST_TAIL, "vectorF64", 0, "", vec10);
    psMetadataAddTime(md, PS_LIST_TAIL, "time01", 0, "I am time1", time1);
    psMetadataAddTime(md, PS_LIST_TAIL, "time02", 0, "I am time2", time2);
    psMetadataAddTime(md, PS_LIST_TAIL, "time03", 0, "I am time3", time3);
    psMetadataAddTime(md, PS_LIST_TAIL, "time04", 0, "I am time4", time4);

    psMetadataAddS32(newMD, PS_LIST_TAIL, "ITEM01", 0, NULL, 666);
    psMetadata *newestMD = NULL;
    newestMD = psMetadataAlloc();
    psMetadataAddVector(newestMD, PS_LIST_TAIL, "VECTORNEW", 0, "Newest VECTOR", vec1);
    psMetadataAddStr(newestMD, PS_LIST_TAIL, "cell", 0, "I am a p-Star", "pStArRs");
    psMetadataAddMetadata(newMD, PS_LIST_TAIL, "META_NEW", 0, "", newestMD);
    psMetadataAddF32(newMD, PS_LIST_TAIL, "ITEM02", 0, "I AM FLOAT", 666.6);
    psMetadataAddF64(newMD, PS_LIST_TAIL, "ITEM03", 0, "I AM DOUBLE", 666.666);

    psMetadataAddMetadata(md, PS_LIST_TAIL, "metadata7", 0, "I am a metadata", newMD);
    psFree(time1);
    psFree(time2);
    psFree(time3);
    psFree(time4);
    psFree(newestMD);
    psFree(vec1);
    psFree(vec2);
    psFree(vec3);
    psFree(vec4);
    psFree(vec5);
    psFree(vec6);
    psFree(vec7);
    psFree(vec8);
    psFree(vec9);
    psFree(vec10);
    psFree(newMD);
    return md;
}

void testMetaConfigWrite(void)
{
    diag("  >>>Test 1:  psMetadataConfigWrite");

    psMetadata *md = NULL;

    //Return false for NULL metadata input
    {
        ok( !psMetadataConfigWrite(md, "mdcfg.wrt"),
            "psMetadataConfigWrite:           return false for NULL metadata input.");
    }
    md = psMetadataAlloc();
    //Return false for NULL filename input
    {
        ok( !psMetadataConfigWrite(md, NULL),
            "psMetadataConfigWrite:           return false for NULL filename input.");
    }
    //Return false for invalid filename input
    {
        ok( !psMetadataConfigWrite(md, "."),
            "psMetadataConfigWrite:           return false for invalid filename input.");
    }
    //Return false for empty metadata input
    {
        ok( !psMetadataConfigWrite(md, "mdcfg.wrt"),
            "psMetadataConfigWrite:           return false for empty metadata input.");
    }
    //Return true for valid inputs
    {
        psFree(md);
        md = setupMeta();
        ok( psMetadataConfigWrite(md, "mdcfg.wrt"),
            "psMetadataConfigWrite:           return true for valid inputs.");
        char configTest[61];
        char fileStr[62];
        FILE *mdcfg = fopen("mdcfg.wrt", "r");
        fgets(fileStr, 61, mdcfg);
        strncpy(configTest,
                "item1-1          BOOL      T                # I am a boolean", 60);
        ok_strn(configTest, fileStr, 60,
                "psMetadataConfigWrite:           return correct output.");
        fclose(mdcfg);
    }

    //Check for Memory leaks
    {
        remove
            ("mdcfg.wrt");
        psFree(md);
        checkMem();
    }
}

void testMetaConfigPrint(void)
{
    diag("  >>>Test 2:  psMetadataConfigPrint");
    FILE *openfile;
    openfile = fopen("mdcfg.prt", "w+");
    FILE *nofile = NULL;
    psMetadata *md = NULL;
    md = psMetadataAlloc();

    //Return false for NULL metadata input
    {
        ok( !psMetadataConfigPrint(openfile, NULL),
            "psMetadataConfigPrint:           return false for NULL metadata input.");
    }
    //Return false for empty metadata input
    {
        ok( !psMetadataConfigPrint(openfile, md),
            "psMetadataConfigPrint:           return false for empty metadata input.");
    }
    //Return false for NULL file input
    psMetadataAddS32(md, PS_LIST_HEAD, "new S32", 0, "", 666);
    {
        ok( !psMetadataConfigPrint(nofile, md),
            "psMetadataConfigPrint:           return false for NULL FILE* input.");
    }
    //Return false for read-only file input
    nofile = fopen("mdcfg.prt", "r");
    {
        ok( !psMetadataConfigPrint(nofile, md),
            "psMetadataConfigPrint:          return false for read-only FILE* input.");
    }
    fclose(nofile);
    //Return true for valid inputs
    {
        ok( psMetadataConfigPrint(openfile, md),
            "psMetadataConfigPrint:          return true for valid inputs.");
        fclose(openfile);
        char configTest[32];
        char fileStr2[62];
        FILE *mdcfg = fopen("mdcfg.prt", "r");
        fgets(fileStr2, 31, mdcfg);
        while (strlen(fileStr2) < 10)
        {
            fgets(fileStr2, 31, mdcfg);
        }
        strncpy(configTest,
                "new S32          S32       666 ", 31);
        ok_strn(configTest, fileStr2, 30,
                "psMetadataConfigWrite:          return correct output.");
        fclose(mdcfg);
    }

    //Check for Memory leaks
    {
        psFree(md);
        remove
            ("mdcfg.prt");
        checkMem();
    }
}

void testMetaConfigFormat(void)
{
    diag("  >>>Test 3:  psMetadataConfigFormat");
    psMetadata *md = NULL;
    psString out = NULL;

    //psString psMetadataConfigFormat(psMetadata *md);

    //Return NULL for NULL metadata input
    {
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for NULL metadata input.");
    }
    //Return NULL for empty metadata input
    {
        md = psMetadataAlloc();
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for empty metadata input.");
    }
    //Return NULL for metadata with missing hash table
    {
        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", 0, "", 666);
        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", PS_META_DUPLICATE_OK, "", 665);
        psHash *temp = md->hash;
        md->hash = NULL;
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for metadata with missing "
            "hash table.");
        md->hash = temp;
    }
    //Return NULL for metadata containing a psList
    {
        psList *list = psListAlloc(NULL);
        psMetadataAddList(md, PS_LIST_HEAD, "new_list", 0, "", list);
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for metadata containing a psList.");
        psFree(list);
        psFree(md);
    }
    //Return NULL for attempting to format vector with type = complex
    {
        psVector *vec = psVectorAlloc(60, PS_TYPE_C64);
        for (int i = 0; i < 5; i++)
        {
            vec->data.C64[i] = i+1;
        }
        md = psMetadataAlloc();
        psMetadataAddVector(md, PS_LIST_TAIL, "vectorC64", 0, "", vec);
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for metadata containing a "
            "Complex vector.");
        psFree(vec);
    }
    //Return NULL for attempting to format psTime with type = -1
    {
        psFree(md);
        md = psMetadataAlloc();
        psTime *time = psTimeAlloc(PS_TIME_TT);
        time->type = -1;
        psMetadataAddTime(md, PS_LIST_TAIL, "time", 0, "", time);
        out = psMetadataConfigFormat(md);
        ok( out == NULL,
            "psMetadataConfigFormat:         return NULL for metadata containing a "
            "invalid time type.");
        psFree(time);
    }
    //Return valid metadata for attempting to format NULL psTime metadataItem
    {
        psFree(md);
        md = psMetadataAlloc();
        psTime *time = psTimeAlloc(PS_TIME_TT);
        psMetadataAddTime(md, PS_LIST_HEAD, "time", 0, "", time);
        psMetadataItem *item = psMetadataGet(md, PS_LIST_HEAD);
        psFree(item->data.V);
        item->data.V = NULL;
        out = psMetadataConfigFormat(md);
        ok( out != NULL,
            "psMetadataConfigFormat:         return valid metadata for metadata "
            "containing a NULL time.");
        char configTest[32];
        strncpy(configTest, "time             TAI       NULL", 31);
        ok_strn(configTest, out, 31,
                "psMetadataConfigFormat:         return correct output string.");
        psFree(time);
        psFree(out);
        out = NULL;
    }
    //Return valid metadata for attempting to format NULL psTime metadataItem
    {
        psFree(md);
        md = psMetadataAlloc();
        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", 0, NULL, 666);
        psMetadataAddS32(md, PS_LIST_HEAD, "new_S32", PS_META_DUPLICATE_OK, NULL, 665);
        out = psMetadataConfigFormat(md);
        ok( out != NULL,
            "psMetadataConfigFormat:         return valid metadata for metadata "
            "containing a NULL time.");
        char configTest[62];
        strncpy(configTest, "new_S32 MULTI\nnew_S32          S32       666", 61);
        ok_strn(configTest, out, strlen(configTest),
                "psMetadataConfigFormat:         return correct output string.");
        psFree(out);
        out = NULL;
    }
    //Return valid metadata for attempting to format NULL psTime metadataItem
    {
        psFree(md);
        md = psMetadataAlloc();
        psMetadataAddS32(md, PS_LIST_HEAD, "f32", 0, "f32_1", 666);
        psMetadataAddS32(md, PS_LIST_TAIL, "f32", PS_META_DUPLICATE_OK, "f32_3", 666);
        psMetadataAddS32(md, PS_LIST_TAIL, "f32", PS_META_DUPLICATE_OK, "f32_2", 665);
        out = psMetadataConfigFormat(md);
        ok( out != NULL,
            "psMetadataConfigFormat:         return valid metadata for metadata "
            "containing a NULL time.");
        char configTest[201];
        strncpy(configTest,
                "f32 MULTI"
                "\nf32              S32       666              # f32_1"
                "\nf32              S32       666              # f32_3"
                "\nf32              S32       665              # f32_2", 200);
        ok_strn(configTest, out, strlen(configTest),
                "psMetadataConfigFormat:         return correct output string.");
        psFree(out);
        out = NULL;
    }

    //Check for Memory leaks
    {
        psFree(md);
        checkMem();
    }
}


