/*!***************************************************************************

  module      : vgg942.cpp
  responsible : UweH
  special area: ComponentTests
  created     : 2000-11-24
  last changed: 2000-11-30  9:36
  copyright   : (c) 2000-2004 SAP AG
  description : component tests
                DIAGNOSE CHECK INSTANCE <number>

    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*****************************************************************************/

/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include "gsp00.h"
#include "ggg00.h"
#include "Logging/Log_Types.hpp" // needed for vgg999 PTS 1124727 mb 2003-10-30

#if COMPILEMODE_MEO00 >= SLOW_MEO00
#include "hta99.h" /* nocheck */
#endif

#include "hgg942.h" // check of PASCAL header vgg942
#include "hgg999.h" // KernelAdministration_PascalInterface
#include "hbd03.h"  // bd03TestTree
#include "ggg92.h"  // in dependence of hgg06
#include "hgg06.h"  // gg06buildCompressedKey,gg06CalculateHash

#include "DataAccess/Data_Test.hpp"
#include "KernelCommon/Kernel_Test.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"
#include "KernelCommon/Kernel_OpMsg.hpp"
#include "Logging/Log_Test.hpp"
#include "Logging/Log_Transaction.hpp"
#include "Logging/Log_Volume.hpp"
#include "Logging/Log_Queue.hpp"
#include "Logging/Log_AfterImageSpace.hpp"
#include "RunTime/RTE_Test.hpp"
#include "Table/Table_Test.hpp"
#include "SAPDBCommon/Fields/SAPDBFields_FieldList.hpp"
#include "SAPDBCommon/Algorithms/SAPDBAlgo_Test.hpp"
#include "SQLManager/SQLMan_Context.hpp"
#include "SQLManager/SQLMan_Test.hpp"
#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"
#include "KernelCommon/Kernel_Trace.hpp"
#include "KernelCommon/Kernel_Version.hpp"
#include "RunTime/System/RTESys_MicroTime.h"
#include "RunTime/RTE_MySQLProxy.hpp"
#include "Restart/Rst_RedoFile.hpp"

#include "ggg91.h"
#include "ggg90.h"
#include "hkb560.h"
#include "hkb50.h"
#include "hkb51.h"
#include "hkb52.h"
#include "hkb53.h"
#include "hbd01.h"
#include "hgg01.h"

/*----------------------------------------------------------------------------*/
void ReserveRedoFile (Rst_RedoFile &file, SAPDB_UInt size, SAPDB_UInt minsize, SAPDB_UInt seq, SAPDB_UInt ioseq)
{
    Data_SplitSpaceWriter writer = file.ReserveSpace (size, minsize, seq, ioseq);
}

#define UNDO_CHANGES true

/*----------------------------------------------------------------------------*/
void testRedoFile(tgg00_TransContext &trans)
{
    Rst_RedoFile  file (trans, Data_PageNo(), Data_PageNo());

    trans.trWriteTransId_gg00.becomes(4711); // simulate write transaction

    if ( ! file.Create() )
    {
        trans.trWriteTransId_gg00.gg90SetNil();
        trans.trError_gg00 = e_system_error;
        return;
    }

    // reserve successfull in one page 

    ReserveRedoFile  (file, 8000, 10, 1, 10);
    file.ReleaseSpace( ! UNDO_CHANGES );
    file.Truncate    (0);

    // reserve UNsuccessfull in one page 

    ReserveRedoFile  (file, 8000, 10, 1, 10);
    file.ReleaseSpace( UNDO_CHANGES );

    // reserve successfull in one page and then reserve as much that it needs to write into a new page

    ReserveRedoFile  (file, 8000, 10, 2, 20);
    file.ReleaseSpace( ! UNDO_CHANGES );
    ReserveRedoFile  (file, 8000, 2000, 3, 30);
    file.ReleaseSpace( UNDO_CHANGES );

    // reserve successfull more then one page

    ReserveRedoFile (file, 8000*4, 40, 4, 40);
    file.ReleaseSpace( ! UNDO_CHANGES );

    // reserve UNsuccessfull more then one page

    ReserveRedoFile (file, 8000*4, 40, 5, 50);
    file.ReleaseSpace( UNDO_CHANGES );

    // drop the complete file

    file.Drop();

    trans.trWriteTransId_gg00.gg90SetNil();
}

/*----------------------------------------------------------------------------*/
void randomKeyAssign ( SAPDB_Byte                     *s,
                       SAPDB_UInt                      l,
                       RTE_MySQLPseudoRandomGenerator &rand,
                       bool                            onlyAscii )
{
    const SAPDB_UInt variableLength = l/10;
    const SAPDB_UInt firstOffset    = l/2 - variableLength/2;
    const SAPDB_UInt lastOffset     = firstOffset+variableLength;
    memset (s,0x20,l-1);
    for ( SAPDB_UInt i = firstOffset; i < lastOffset; i++ )
    {
        if ( onlyAscii )
        {
            SAPDB_Byte b = static_cast<SAPDB_Byte>(rand.RandomNumber() * 127);
            *(s+i) = b < 32 ? 32 : b;
        }
        else
            *(s+i) = static_cast<SAPDB_Byte>(rand.RandomNumber() * SAPDB_MAX_UINT1);
    }
    // Kernel_TraceBuffer (s,l,"randomKey", 80);
}


/*----------------------------------------------------------------------------*/
void testCompression(tgg00_TransContext& trans)
{
    const SAPDB_Byte key1[] = { 0xFF,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
                                0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
                                0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xFF,0xDD,0xDD,
                                0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,
                                0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,0xDD,
                                0xDD,0xDD,0xFF,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,
                                0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,
                                0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xFF
                              };
    const SAPDB_UInt   key1length = 80;
    const SAPDB_Byte key2[] = { 0xFF,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
                                0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
                                0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xFF,0xFF,0xBB,
                                0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,
                                0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,
                                0xBB,0xBB,0xBB,0xBB,0xBB,0xFF
                              };
    const SAPDB_UInt   key2length = 56;

	const tsp00_Uint4   COMPRESSEDKEYSIZE = 64;
          tsp00_Byte    compressedkey [COMPRESSEDKEYSIZE];
    
    (void)gg06buildCompressedKey (key1,key1length,1,key1length,COMPRESSEDKEYSIZE,compressedkey);

    if ( ! memcmp (key2,compressedkey,key2length) )
        Kernel_VTrace() << "Keycompression passed.";
	else
        Kernel_TraceBuffer (compressedkey,COMPRESSEDKEYSIZE,"BAD compressedkey", COMPRESSEDKEYSIZE);
}

/*----------------------------------------------------------------------------*/
void testKnownKeys(tgg00_TransContext& trans)
{
    const SAPDB_Byte key1[] = { 0x20,0x30,0x30,0x30,0x20,0x46,0x4f,0x52,0x4d,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x48,0x52,0x5f,0x49,0x44,0x5f,0x54,0x41,0x58,0x31,0x37,0x32,0x31,0x41,0x42,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x58,0x54,0x20,0x20,0x45 };
    const SAPDB_UInt   key1length = 93;
    const SAPDB_Byte key2[] = { 0x20,0x30,0x30,0x30,0x20,0x46,0x4f,0x52,0x4d,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x4f,0x44,0x5f,0x56,0x42,0x4c,0x5f,0x41,0x4e,0x4d,0x45,0x4c,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                                0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x58,0x54,0x20,0x20,0x45 };
    const SAPDB_UInt   key2length = 93;

    const tsp00_Uint4   MAXHASHTYPE       = 3;
	const tsp00_Uint4   COMPRESSEDKEYSIZE = 64;
          tsp00_Byte    compressedkey1 [COMPRESSEDKEYSIZE];
          tsp00_Byte    compressedkey2 [COMPRESSEDKEYSIZE];
          tsp00_Uint4   key1hashvalue;
          tsp00_Uint4   key2hashvalue;
    
    for ( tsp00_Byte hashtype = 0; hashtype < MAXHASHTYPE; hashtype++ )
    {
        (void)gg06buildCompressedKey (key1,key1length,1,key1length,COMPRESSEDKEYSIZE,compressedkey1);
        (void)gg06buildCompressedKey (key2,key2length,1,key2length,COMPRESSEDKEYSIZE,compressedkey2);

        Kernel_TraceBuffer (compressedkey1,COMPRESSEDKEYSIZE,"compressedkey1", COMPRESSEDKEYSIZE);
        Kernel_TraceBuffer (compressedkey2,COMPRESSEDKEYSIZE,"compressedkey2", COMPRESSEDKEYSIZE);

        if ( memcmp (compressedkey1,compressedkey2,COMPRESSEDKEYSIZE) )
            Kernel_VTrace() << "Hash Type: " << hashtype << " (0=old,1=fnv,2=simple) PASSED.";
        else
        {
            Kernel_VTrace() << "LockKey collision for type (0=old,1=fnv,2=simple): " << hashtype;
            gg06CalculateHash(key1,key1length,hashtype,key1hashvalue);
            gg06CalculateHash(key2,key2length,hashtype,key2hashvalue);
            if ( key1hashvalue == key2hashvalue )
                Kernel_VTrace() << "Hash collision for type (0=old,1=fnv,2=simple): " << hashtype;
            else
                Kernel_VTrace() << "NO Hash collision ?";
        }
    }
}

/*----------------------------------------------------------------------------*/
void testHashSpreading(tgg00_TransContext& trans,
                       bool                onlyAscii)
{
    const tsp00_Uint4   MAXLOOPS = 100000;
    const tsp00_Uint4   MAXHASHTYPE = 3;

    typedef tsp00_Uint2 CollisionList [MAXLOOPS];

    CollisionList       spreading     [MAXHASHTYPE];
    tsp00_Uint4         collisions    [MAXHASHTYPE];
    
    const tsp00_Uint4   KEYLENGTH         = 1024;
    tsp00_Byte          key           [KEYLENGTH];

    tsp00_Uint4         hashvalue;
    tsp00_Uint4         loop;
    tsp00_Byte          hashtype;

    Kernel_VTrace() << "TEST SPREADING: loops=" << MAXLOOPS << " keylength=" << KEYLENGTH
                    << (onlyAscii?" only ascii keys":" keys 0-255");

    for ( hashtype = 0; hashtype < MAXHASHTYPE; hashtype++ )
    {
        collisions[hashtype] = 0;
        for ( loop = 0; loop < MAXLOOPS; loop++ )
            spreading [hashtype][loop] = 0;
    }
    
    RTE_MySQLPseudoRandomGenerator rand(11);

    for ( loop = 0; loop < MAXLOOPS; loop++ )
    {
        randomKeyAssign (key, KEYLENGTH, rand, onlyAscii);
        for ( hashtype = 0; hashtype < MAXHASHTYPE; hashtype++ )
        {
            gg06CalculateHash(key,KEYLENGTH,hashtype,hashvalue);
            if ( spreading [hashtype][hashvalue%MAXLOOPS] > 0 )
                collisions[hashtype]++;
            (spreading[hashtype][hashvalue%MAXLOOPS])++;
        }
    }
    for ( hashtype = 0; hashtype < MAXHASHTYPE; hashtype++ )
        Kernel_VTrace() << "HASHTYPE (0=old,1=fnv,2=simple) " << hashtype
                        << ": collisions=" << collisions[hashtype];
}
/*----------------------------------------------------------------------------*/
void testHashSpeed (tgg00_TransContext& trans)
{
    tgg00_BasisError   &berr              = trans.trError_gg00;
    const tsp00_Uint4   KEYLENGTH         = 1024;
	const tsp00_Uint4   COMPRESSEDKEYSIZE = 64;
    const tsp00_Uint4   MAXLOOPS          = 100000;
    tsp00_Byte          key           [KEYLENGTH];
    tsp00_Byte          compressedkey [COMPRESSEDKEYSIZE];
    tsp00_Uint4         loop;
    SAPDB_UInt8         timeBegin;
    SAPDB_UInt8         timeEnd;

    memset (key, 0xFF, KEYLENGTH);
    memset (compressedkey, 0x00, COMPRESSEDKEYSIZE);

    Kernel_VTrace() << "TEST HASHES:";
    Kernel_VTrace() << "KEYLENGTH = " << KEYLENGTH << ", COMPRESSEDKEYSIZE = " << COMPRESSEDKEYSIZE;
    Kernel_VTrace() << "MAXLOOPS  = " << MAXLOOPS;

    timeBegin = RTESys_MicroSecTimer();
    for ( loop = 0; loop < MAXLOOPS; loop++ )
    {
        (void)gg06buildCompressedKey (key,KEYLENGTH,1,KEYLENGTH,COMPRESSEDKEYSIZE,compressedkey);
    }
    timeEnd = RTESys_MicroSecTimer();
    Kernel_VTrace() << "time = " << timeEnd-timeBegin;
}

/*----------------------------------------------------------------------------*/
void testMessageList (tgg00_TransContext& trans)
{
    SAPDBErr_MessageList &errlist = SQLMan_Context::GetContext()->GetErrorList();
    errlist =  SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                                  "DIAGNOSE CHECK INSTANCE 6: e_system_error 1");
    errlist = errlist
              + 
              SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                                 "DIAGNOSE CHECK INSTANCE 6: e_system_error 2");
}

/*----------------------------------------------------------------------------*/

externPascal void gg942ComponentTests (tgg00_TransContext& Trans,
                                       tsp00_Int4          TestNo)
{
    ROUTINE_DBG_MSP00 ("gg942ComponentTests");
    
    Kernel_Version::ComponentType    componentType;
    Kernel_Version::BuildKind        buildInfo;
    Kernel_Version::DevelopmentPhase developPhase;
    Kernel_Version::InstanceType     builtForInstance;
    
    Kernel_Version::GetExtraInfoFromSoftware (componentType, buildInfo, developPhase, builtForInstance);
    if ( developPhase != Kernel_Version::DevelopPhase ) 
    {
        Trans.trError_gg00 = e_invalid_command;
        return;
    }
    else
    {
        switch (TestNo)
        {
        case 1:
            Data_Test (Trans);
            break;
        case 2:
            Kernel_Test (Trans);
            break;
        case 3:
            {
                Log_Test testRunner;
                testRunner.PerformTest(Trans, Log_Test::Log_TestIOSequences);
                break;
            }
        case 4:
            RTE_Test (Trans);
            break;
        case 5:
            Table_Test (Trans);
            break;
        case 6:
            testMessageList (Trans);
            break;
        case 7:
            SQLMan_Test (Trans);
            break;
        case 8:
            SAPDBAlgo_Test (Trans);
            break;
        case 9:
            bd03TestTree(Trans);
            break;
        case 10:
            testHashSpeed(Trans);
            testHashSpreading(Trans,false);
            testHashSpreading(Trans,true);
            testKnownKeys(Trans);
            testCompression(Trans);
            break;
        case 11:
            testRedoFile(Trans);
            break;
        case 12: case 13: case 14:
        {
            // PTS 1134810 UH 2005-04-12 new test
            SAPDBMem_IRawAllocator &allocator = *reinterpret_cast<SAPDBMem_IRawAllocator*>(Trans.trAllocator_gg00);
            Log_Queue              &queue     = *Log_Volume::Instance().GetQueue(Trans.trTaskId_gg00);
            Log_AfterImageSpace     space (allocator);
            Log_Page::OffsetType    entrypos;
            Log_IOSequenceNo        wioseqno;
            Log_RawDeviceOffset     offset;
            
            SAPDB_UInt8             timeBegin;
            SAPDB_UInt8             timeEnd;

            const SAPDB_UInt        MAXLOOPS         = 100;
            const SAPDB_UInt        MIN_RESERVE_SIZE = 100;
                  SAPDB_UInt        RESERVE_SIZE;
            
            Kernel_VTrace() << "MAXLOOPS=" << MAXLOOPS << ", MIN_RESERVE_SIZE=" << MIN_RESERVE_SIZE;

            switch (TestNo-12) 
            {
                case 0:
                {
                    RESERVE_SIZE = 200;
                    timeBegin = RTESys_MicroSecTimer();
                    for ( int i = 0; i < MAXLOOPS; i++ )
                    {
                        if ( ! queue.Reserve ( Trans.trTaskId_gg00, Trans.trWaitContext_gg00,
                                               RESERVE_SIZE, MIN_RESERVE_SIZE, false, space) )
                        {
                            Trans.trError_gg00 = e_log_error;
                            break;
                        }
                        queue.UserTaskReady (Trans.trTaskId_gg00, space);
                    }
                    timeEnd = RTESys_MicroSecTimer();
                    Kernel_VTrace() << "RESERVE_SIZE=" << RESERVE_SIZE << ", TIME=" << timeEnd-timeBegin;
                    queue.ReserveForEOT(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, 10, space, entrypos);
                    queue.UserTaskEOTReady(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, space, wioseqno, offset);
                    break;
                }
                case 1:
                {
                    RESERVE_SIZE = 8192;
                    timeBegin = RTESys_MicroSecTimer();
                    for ( int i = 0; i < MAXLOOPS; i++ )
                    {
                        if ( ! queue.Reserve ( Trans.trTaskId_gg00, Trans.trWaitContext_gg00,
                                               RESERVE_SIZE, MIN_RESERVE_SIZE, false, space) )
                        {
                            Trans.trError_gg00 = e_log_error;
                            break;
                        }
                        queue.UserTaskReady (Trans.trTaskId_gg00, space);
                    }
                    timeEnd = RTESys_MicroSecTimer();
                    Kernel_VTrace() << "RESERVE_SIZE=" << RESERVE_SIZE << ", TIME=" << timeEnd-timeBegin;
                    queue.ReserveForEOT(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, 10, space, entrypos);
                    queue.UserTaskEOTReady(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, space, wioseqno, offset);
                    break;
                }
                case 2:
                {
                    RESERVE_SIZE = 8192*4;
                    timeBegin = RTESys_MicroSecTimer();
                    for ( int i = 0; i < MAXLOOPS; i++ )
                    {
                        if ( ! queue.Reserve ( Trans.trTaskId_gg00, Trans.trWaitContext_gg00,
                                               RESERVE_SIZE, MIN_RESERVE_SIZE, false, space) )
                        {
                            Trans.trError_gg00 = e_log_error;
                            break;
                        }
                        queue.UserTaskReady (Trans.trTaskId_gg00, space);
                    }
                    timeEnd = RTESys_MicroSecTimer();
                    Kernel_VTrace() << "RESERVE_SIZE=" << RESERVE_SIZE << ", TIME=" << timeEnd-timeBegin;
                    queue.ReserveForEOT(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, 10, space, entrypos);
                    queue.UserTaskEOTReady(Trans.trTaskId_gg00, Trans.trWaitContext_gg00, space, wioseqno, offset);
                    break;
                }
                default:
                    break;
            }
        }
        default:
            break;
        }
    }
}
