/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test of ConfBin class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define TESTING 1
#include "test.hh"
#include "base/module.hh"
#include "base/conf_bin.hh"
using namespace base;

INTERN const char FNAME[]       = "/tmp/base_config_file_test.cfg";
INTERN const char PROGNAME[]    = "test";

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test1( void )
{
    // ------------------------------------------------------------------------
    // First series of tests are for when no configuration file exists yet.
    // ------------------------------------------------------------------------

    unlink( FNAME ); // ensure configuration file doesn't exist

    ConfBin config( PROGNAME, FNAME );

    int         right_highscore = 536000;
    string      right_game      = "pacman";
    double      right_offset    = 1.2345;
    int         highscore;
    string      game;
    double      offset;

    // Test writing.
    if ( ! config.Write( "highscore", right_highscore ) )
    {
        CDEBUG << "FAIL: Write(highscore) \n";
        return Fail();
    }

    if ( ! config.Write( "game", right_game ) )
    {
        CDEBUG << "FAIL: Write(game) \n";
        return Fail();
    }

    if ( ! config.Write( "offset", right_offset ) )
    {
        CDEBUG << "FAIL: Write(highscore) \n";
        return Fail();
    }

    // Test reading.
    if ( config.Read( "highscore", highscore ) && highscore == right_highscore )
        { CDEBUG << "(ok) highscore = " << highscore << std::endl; }
    else
        { CDEBUG << "FAIL: Read(highscore)\n"; return Fail(); }

    if ( config.Read( "game", game ) && game == right_game )
        { CDEBUG << "(ok) game      = " << game << std::endl; }
    else
        { CDEBUG << "FAIL: Read(game)\n"; return Fail(); }

    if ( config.Read( "offset", offset ) && offset == right_offset )
        { CDEBUG << "(ok) offset    = " << offset << std::endl; }
    else
        { CDEBUG << "FAIL: Read(offset)\n"; return Fail(); }

    return true;
}

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test2( void )
{
    // ------------------------------------------------------------------------
    // Second series of tests are for when a configuration file DOES exist.
    // ------------------------------------------------------------------------

    ConfBin config( PROGNAME, FNAME );

    int         right_highscore = 536000;
    string      right_game      = "pacman";
    double      right_offset    = 1.2345;
    int         highscore;
    string      game;
    double      offset;

    // Test reading.
    if ( config.Read( "highscore", highscore ) && highscore == right_highscore )
        { CDEBUG << "(ok) highscore = " << highscore << std::endl; }
    else
        { CDEBUG << "FAIL: Read(highscore)\n"; return Fail(); }

    if ( config.Read( "game", game ) && game == right_game )
        { CDEBUG << "(ok) game[]    = " << game << std::endl; }
    else
        { CDEBUG << "FAIL: Read(game)\n"; return Fail(); }

    if ( config.Read( "offset", offset ) && offset == right_offset )
        { CDEBUG << "(ok) offset    = " << offset << std::endl; }
    else
        { CDEBUG << "FAIL: Read(offset)\n"; return Fail(); }

    return true;
}

/*****************************************************************************
 * 
 *****************************************************************************/
bool
Test3( void )
{
    // ------------------------------------------------------------------------
    // Test giving different values using the same key/type.
    // ------------------------------------------------------------------------

    ConfBin config( PROGNAME, FNAME );

    CDEBUG << "Testing changing value of same key..." << std::endl;

    const char key_highscore[] = "highscore_changing";
    int right_highscore = 1234;
    int highscore;

    //
    right_highscore = 0xabcd;
    if ( ! config.Write( key_highscore, right_highscore ) )
    {
        CDEBUG << "FAIL: Write(highscore) \n";
        return Fail();
    }
    if ( ! config.Read( key_highscore, highscore ) || highscore != right_highscore )
        { CDEBUG << "FAIL: Read(highscore)\n"; return Fail(); }

    //
    right_highscore = 0;
    if ( ! config.Write( key_highscore, right_highscore ) )
    {
        CDEBUG << "FAIL: Write(highscore) \n";
        return Fail();
    }
    if ( ! config.Read( key_highscore, highscore ) || highscore != right_highscore )
        { CDEBUG << "FAIL: Read(highscore)"; return Fail(); }

    //
    right_highscore = 123467;
    if ( ! config.Write( key_highscore, right_highscore ) )
    {
        CDEBUG << "FAIL: Write(highscore) \n";
        return Fail();
    }
    if ( ! config.Read( key_highscore, highscore ) || highscore != right_highscore )
        { CDEBUG << "FAIL: Read(highscore)\n"; return Fail(); }

    CDEBUG << "ok." << std::endl;

    return true;
}

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test4( void )
{
    // ------------------------------------------------------------------------
    // Delete test.
    // ------------------------------------------------------------------------

    ConfBin config( PROGNAME, FNAME );

//  int         right_highscore = 536000;
    string      right_game      = "pacman";
    double      right_offset    = 1.2345;
    int         highscore;
    string      game;
    double      offset;

    // Test deleting
    if ( config.Remove( "highscore" ) )
        { CDEBUG << "(ok) deleted highscore \n"; }
    else
        { CDEBUG << "FAIL: Remove(highscore)\n"; return Fail(); }
    

    // Test reading.
    if ( config.Read( "highscore", highscore ) )        // deleted
        { CDEBUG << "FAIL: highscore should be deleted\n"; return Fail(); }

    if ( config.Read( "game", game ) && game == right_game )
        { CDEBUG << "(ok) game[]    = " << game << std::endl; }
    else
        { CDEBUG << "FAIL: Read(game)\n"; return Fail(); }

    if ( config.Read( "offset", offset ) && offset == right_offset )
        { CDEBUG << "(ok) offset    = " << offset << std::endl; }
    else
        { CDEBUG << "FAIL: Read(offset)\n"; return Fail(); }

    return true;
}

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test5( void )
{
    // ------------------------------------------------------------------------
    // Corner cases, bad args.
    // ------------------------------------------------------------------------

    ConfBin config( PROGNAME, FNAME );

    string s;

    // Test a string containing whitespace.
    const char ws_key[] = "_key_whitespace";
    string ws_val = "\tthis string has whitespace\n";
    config.Write( ws_key, ws_val );
    if ( config.Read( ws_key, s ) && s == ws_val )
    { CDEBUG << "(ok) reading string with whitespace" << std::endl; }
    else
    { CDEBUG << "FAIL: reading string with whitespace" << std::endl; return Fail(); }

    // Test a empty string value.
    const char empty_string_key[] = "_key_empty_string";
    config.Write( empty_string_key, "" );
    s = "dummy";
    if ( config.Read( empty_string_key, s ) && s == "" )
    { CDEBUG << "(ok) reading empty string" << std::endl; }
    else
    { CDEBUG << "FAIL: reading empty string" << std::endl; return Fail(); }

    const char key_big[] =
    "AAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCC"
    "DDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
    "GGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIII"
    "KKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMM"
    "NNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP";
    string val_string_big =
    "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"
    "SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT"
    "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
    "WWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYY";

    // NULL args aren't tested -- let 'em die.
    // This won't succeed but it shouldn't cause a crash.
    string str;
    config.Write( key_big, val_string_big );
    if ( config.Read( key_big, str ) )
    { CDEBUG << "(ok) too big " << str << std::endl; }

    return true;
}

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test6( void )
{
    // ------------------------------------------------------------------------
    // Test Access() and more interesting keys/values.
    // ------------------------------------------------------------------------

    ConfBin config( PROGNAME, FNAME );

    int         right_int1      = -1;
    int         right_int2      = 0x88889999;
    int         right_int3      = 0;
    string      right_str       = "ABCDzxcv123409~!@#$%&*\"*(){}[];',.//<>?";
    double      right_float     = 123456.430000;

    int         val_int1        = 0xdead;
    int         val_int2        = 0xdead;
    int         val_int3        = 0xdead;
    string      val_str         = "";
    double      val_float       = 0;

    PERSISTENT const char key_int1[]    = "___int1____";
    PERSISTENT const char key_int2[]    = "___int 2____";
    PERSISTENT const char key_int3[]    = "___int3____";
    PERSISTENT const char key_str[]     = "___String____";
    PERSISTENT const char key_float[]   = "___Float#1____";

    int res;

    CDEBUG << "Testing Access()..." << std::endl;

    // Test writing by Access().
    if ( ! config.Access( ConfBin::eAccess_WRITE, key_int1, right_int1 ) )
        { CDEBUG << "FAIL: Access(WRITE,int1)\n"; return Fail(); }

    if ( ! config.Access( ConfBin::eAccess_WRITE, key_int2, right_int2 ) )
        { CDEBUG << "FAIL: Access(WRITE,int2)\n"; return Fail(); }

    if ( ! config.Access( ConfBin::eAccess_WRITE, key_int3, right_int3 ) )
        { CDEBUG << "FAIL: Access(WRITE,int3)\n"; return Fail(); }

    if ( ! config.Access( ConfBin::eAccess_WRITE, key_str, right_str ) )
        { CDEBUG << "FAIL: Access(WRITE,str)\n"; return Fail(); }

    if ( ! config.Access( ConfBin::eAccess_WRITE, key_float, right_float ) )
        { CDEBUG << "FAIL: Access(WRITE,float)\n"; return Fail(); }

    // Test reading by Access().
    res = config.Access( ConfBin::eAccess_READ, key_int1, val_int1 );
    if ( ! res || val_int1 != right_int1 )
        { CDEBUG << "FAIL: Access(WRITE,int1)\n"; return Fail(); }

    res = config.Access( ConfBin::eAccess_READ, key_int2, val_int2 );
    if ( ! res || val_int2 != right_int2 )
        { CDEBUG << "FAIL: Access(WRITE,int2)\n"; return Fail(); }

    res = config.Access( ConfBin::eAccess_READ, key_int3, val_int3 );
    if ( ! res || val_int3 != right_int3 )
        { CDEBUG << "FAIL: Access(WRITE,int3)\n"; return Fail(); }

    res = config.Access( ConfBin::eAccess_READ, key_str, val_str );
    if ( ! res || val_str != right_str )
        { CDEBUG << "FAIL: Access(WRITE,str)\n"; return Fail(); }

    res = config.Access( ConfBin::eAccess_READ, key_float, val_float );
    if ( ! res || val_float < right_float - 0.2 || val_float > right_float + 0.2 )
        { CDEBUG << "FAIL: Access(WRITE,float)\n"; return Fail(); }

    CDEBUG << "ok." << std::endl;

    return true;
}

/*****************************************************************************
 * Test.
 *****************************************************************************/
bool
Test7( void )
{
    // ------------------------------------------------------------------------
    // Corner cases, bad args.
    // ------------------------------------------------------------------------

    try
    {
        int i;
        CDEBUG << "--- THIS SHOULD PRINT ERRORS due to bad filename ---" << std::endl;
        ConfBin config( PROGNAME, "/a/b/c/doesnt_exist" );
        if ( config.IfValid() )                 { CDEBUG << "IfValid() is wrong \n"; return Fail(); }
        if ( config.Write( "abc", (int)123 ) )  { CDEBUG << "Write() is wrong \n"; return Fail(); }
        if ( config.Read( "abc", i ) )          { CDEBUG << "Read() is wrong \n"; return Fail(); }
        if ( config.Remove( "abc" ) )           { CDEBUG << "Remove() is wrong \n"; return Fail(); }
        CDEBUG << "---  ---\n" << std::endl;
    }
    catch (...)
    {
        return true;
    }
    return false;
}

/*****************************************************************************
 * Called if all other tests passed else no cleanup.
 *****************************************************************************/
bool
Cleanup( void )
{
    unlink( FNAME );
    return true;
}

/*****************************************************************************
 * main.
 *****************************************************************************/
int
main( int argc, char** argv )
{
    RUN_TEST( "ConfBin", TEST_ERROR_MODE_CATCH,  // this test expects an exception
              Test1()
          and Test2()
          and Test3()
          and Test4()
          and Test5()
          and Test6()
          and Test7()
          and Cleanup() )
}
