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

  module      : Data_Test
  responsible : UweH
  special area: DataAccess
  created     : 2000-11-22
  last changed: 2000-11-22 12:00
  copyright   : (c) 2000-2004 SAP AG
  description : entrypoint for component tests



    ========== 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 <memory.h> // memset

#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_IRawAllocator.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include "DataAccess/Data_PageAccessManager.hpp"
#include "DataAccess/Data_Chain.hpp"
#include "DataAccess/Data_PageSplitSpace.hpp"
#include "DataAccess/Data_ChainSplitSpaceBackwardRead.hpp"
#include "DataAccess/Data_ChainSplitSpaceForwardRead.hpp"
#include "DataAccess/Data_ChainFixSizeSpace.hpp"
#include "DataAccess/Data_PrimaryFile.hpp"
#include "DataAccess/Data_TempFile.hpp"
#include "DataAccess/Data_Test.hpp"
#include "KernelCommon/Kernel_IAdminInfo.hpp"
#include "Container/Container_Vector.hpp"

/* --------------------------------------------------------------------------- */
typedef Data_ChainSplitSpaceBackwardRead<Data_PageSplitSpaceBackwardRead>
    BackwardChainType;
typedef Data_ChainSplitSpaceForwardRead<Data_PageSplitSpaceForwardRead>
    ForwardChainType;
typedef Data_ChainFixSizeSpace<Data_PageFixSizeSpace>
    FixSizeChain;
    
/* --------------------------------------------------------------------------- */

inline void Check (const char * Message,
                   bool         CheckValue)
{
    if ( ! CheckValue )
        RTE_Crash(SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,Message));
}

/* --------------------------------------------------------------------------- */
/*
 * Create a chain, insert a second page after the root page.
 *
 */
inline void ChainTest (tgg00_TransContext& Trans)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::ChainTest", DataChain_Trace, 5);
    
    // An empty Chainhandler is declared.

    Data_PageId InvalidRootId;

    Data_PageAccessManager PAM (Trans, Data_UnknownFile, 
        Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Data_PageNo());
    
    Data_Chain<Data_ChainPage> Chain (PAM, InvalidRootId);

    // Everything is declared now to create a new page chain.

    Data_ChainIterator<Data_ChainPage> ChainIter (PAM);

    // The create method returns an iterator pointing to the root page.

    Chain.Create (ChainIter);

    // If the Iterator can be dereferenced - it should, test if the root pno of the new chain can be get.

    SAPDBERR_ASSERT_STATE( ChainIter.IsValid() );

    // First,  the Iterator is dereferenced and gives the ChainPage it points to.
    // Second, the Method from base class Page is used to get the PageId of the root ChainPage.

    Data_PageNo AuxPageNo;

    AuxPageNo = (*ChainIter).PageNo();

    // The Iterator should now move to the next page in chain.

    ++ChainIter;

    // There is no next page.

    SAPDBERR_ASSERT_STATE( ! ChainIter.IsValid() );

    // Set to root again.

    Chain.Begin (ChainIter, Data_ForStructureChange);

    // the root must be accessable.

    SAPDBERR_ASSERT_STATE( ChainIter.IsValid() );

    // A new page is inserted into the chain.

    Chain.Insert (ChainIter);

    // The new page just inserted is removed now.

    Chain.Begin  (ChainIter, Data_ForStructureChange); // ROOT
    Chain.Delete (ChainIter);

    // The Iterator should now move to the next page in chain.

    ++ChainIter;

    // There is no next page.

    SAPDBERR_ASSERT_STATE( ! ChainIter.IsValid() );

    // Insert three pages

    Chain.End    (ChainIter, Data_ForStructureChange);
    Chain.Insert (ChainIter); // PAGE1
    ChainIter.Invalidate(); 

    Chain.End    (ChainIter, Data_ForStructureChange);
    Chain.Insert (ChainIter); // PAGE2
    ChainIter.Invalidate(); 

    Chain.End    (ChainIter, Data_ForStructureChange);
    Chain.Insert (ChainIter); // PAGE3
    ChainIter.Invalidate(); 

    // Now: ROOT->PAGE1->PAGE2->PAGE3
    //      LAST=PAGE3

    Chain.Begin  (ChainIter, Data_ForRead);
    ++ChainIter;
    ++ChainIter;
    ++ChainIter;

    // The Iterator points to PAGE1

    SAPDBERR_ASSERT_STATE( ChainIter.IsValid() );

    SAPDBERR_ASSERT_STATE( Chain.LastPageNo() == (*ChainIter).PageNo() );

    // release internal members (the page pointing to the root)

    ChainIter.Invalidate();

    // Store the LastPageNo - be sure not to collide with myself

    Chain.StoreLastPageNo();
    
    // Drop Chain

    Chain.Drop();
}

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

inline void TestBackwardChainSplitSpace (BackwardChainType           &Chain,
                                         BackwardChainType::Iterator &Iter,
                                         SAPDB_UInt                   NumberOfWrites)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::TestBackwardChainSplitSpace", DataChain_Trace, 5);

    SAPDB_UInt             i,j,k;
    SAPDB_Byte             Block [256];
    const SAPDB_UInt       BlockCount = 10;
    SAPDB_UInt             AddedPages;
    Data_SplitRecordLength WantedSize = BlockCount * sizeof(Block);

    for (i=0;i<sizeof(Block);++i) Block [i] = i;
    
    Data_SplitSpaceWriter::Result WriteResult;
    
    for (i=0;i<NumberOfWrites;++i)
    {
        if ( ! Chain.ReserveSpace (WantedSize, 10, Iter, AddedPages) )
            RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"ReserveSpace"));
        Data_SplitSpaceWriter Writer (*Iter);

        for (j=0;j<BlockCount;++j)
        {
            Writer.Write (Block, sizeof(Block), WriteResult);
            if ( WriteResult != Data_SplitSpaceWriter::moreSpaceAvailable
                 &&
                 WriteResult != Data_SplitSpaceWriter::ok )
                RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"TestReserveWriteRead::Write1"));
        }
        if ( WriteResult != Data_SplitSpaceWriter::ok )
            RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"TestReserveWriteRead::Write2"));
    }
    
    Chain.End (Iter, Data_ForRead);

    Data_SplitSpaceReader::Result ReadResult;

    for (i=0;i<NumberOfWrites;++i)
    {
        Data_SplitSpaceReader Reader (*Iter);

        for (j=0;j<BlockCount;++j)
        {
            memset (Block, 0, sizeof(Block));
            
            Reader.Read (Block, sizeof(Block), ReadResult);
            
            if ( ReadResult != Data_SplitSpaceReader::moreSpaceAvailable
                 &&
                 ReadResult != Data_SplitSpaceReader::ok )
                RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"TestReserveWriteRead::Read1"));
                    
            for (k=0;k<sizeof(Block);++k)
                if ( Block [k] != k )
                    RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"TestReserveWriteRead::ReadContents"));
        }
        if ( ReadResult != Data_SplitSpaceWriter::ok )
            RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"TestReserveWriteRead::Read2"));
        --Iter;
    }
}


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

inline void TestBackwardChainSplitSpaceNegative
                (BackwardChainType           &Chain,
                 BackwardChainType::Iterator &Iter,
                 SAPDB_UInt                   NumberOfWrites)
{
    // PTS 1117126 UH 2002-08-07 new
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::TestBackwardChainSplitSpaceNegative", DataChain_Trace, 5);

    SAPDB_Byte                    Block [65536];
    Data_SplitSpaceWriter::Result WriteResult;

    Iter.WriteToTrace ("TestBackwardChainSplitSpaceNegative1");

    memset (Block, 'X', sizeof(Block));

    Data_PageNo testPageNoBefore = Chain.LastPageNo();
    SAPDB_UInt  AddedPages;
    if ( ! Chain.ReserveSpace (sizeof(Block), 10, Iter, AddedPages) )
        RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"ReserveSpace"));
        
    BackwardChainType::RecordSpace &space = *Iter;
    Data_SplitSpaceWriter Writer (space);
    Writer.Write (Block, sizeof(Block), WriteResult);
    
    Iter.WriteToTrace ("TestBackwardChainSplitSpaceNegative2");

    if ( WriteResult != Data_SplitSpaceWriter::ok )
        RTE_Crash( Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,
                                  "TestReserveWriteReadNegative::Write") );

    Chain.UndoReserveSpace(space);
	Iter.Invalidate(false);

    Iter.WriteToTrace ("TestBackwardChainSplitSpaceNegative3");

    if ( testPageNoBefore != Chain.LastPageNo() )
        RTE_Crash( Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,
                                  "TestReserveWriteReadNegative::Invalidate") );
}


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

inline void TestForwardChainSplitSpace (ForwardChainType           &Chain,
                                        ForwardChainType::Iterator &Iter,
                                        SAPDB_UInt                  NumberOfWrites)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::TestForwardChainSplitSpace", DataChain_Trace, 5);

    SAPDB_UInt             i,j,k;
    SAPDB_Byte             Block [256];
    const SAPDB_UInt       BlockCount = 10;
    Data_SplitRecordLength WantedSize = BlockCount * sizeof(Block);
    SAPDB_UInt             AddedPages;
    
    for (i=0;i<sizeof(Block);++i) Block [i] = i;
    
    Data_SplitSpaceWriter::Result WriteResult;
    
    for (i=0;i<NumberOfWrites;++i)
    {
        if ( ! Chain.ReserveSpace (WantedSize, 10, Iter, AddedPages) )
            RTE_Crash(Data_Exception(__CONTEXT__,DATA_COMPONENT_TEST_FAILED,"ReserveSpace"));
        Data_SplitSpaceWriter Writer (*Iter);

        for (j=0;j<BlockCount;++j)
        {
            Writer.Write (Block, sizeof(Block), WriteResult);
            Check ( "TestReserveWriteRead::Write1",
                    WriteResult == Data_SplitSpaceWriter::moreSpaceAvailable
                    ||
                    WriteResult == Data_SplitSpaceWriter::ok );
        }
        Check ( "TestReserveWriteRead::Write2",
                WriteResult == Data_SplitSpaceWriter::ok );
    }
    
    Chain.Begin (Iter, Data_ForRead);

    Data_SplitSpaceReader::Result ReadResult;

    for (i=0;i<NumberOfWrites;++i)
    {
        Data_SplitSpaceReader Reader (*Iter);

        for (j=0;j<BlockCount;++j)
        {
            memset (Block, 0, sizeof(Block));
            
            Reader.Read (Block, sizeof(Block), ReadResult);
            
            Check ( "TestReserveWriteRead::Read1",
                    ReadResult == Data_SplitSpaceReader::moreSpaceAvailable
                    ||
                    ReadResult == Data_SplitSpaceReader::ok );
                    
            for (k=0;k<sizeof(Block);++k)
                Check ( "TestReserveWriteRead::ReadContents", Block [k] == k );
        }
        Check ( "TestReserveWriteRead::Read2",
                ReadResult == Data_SplitSpaceReader::ok );
        ++Iter;
    }
}


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

inline void TestFixSizeChain (FixSizeChain           &Chain,
                              FixSizeChain::Iterator &Iter,
                              SAPDB_UInt              NumberOfRecords)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::TestFixSizeChain", DataChain_Trace, 5);

    for (SAPDB_UInt i = 0; i < NumberOfRecords; ++i)
    {
        Chain.ReserveSpace (Iter);
        Check ("Chain::ReserveSpace", Iter.IsValid());
        Check ("Iter::IsValid", Iter.IsValid() );
        Chain.UnLock();
        // memset
    }
}


/* --------------------------------------------------------------------------- */
inline void StartTestBackwardChainSplitSpace (tgg00_TransContext     &Trans,
                                              SAPDBMem_IRawAllocator &Allocator)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::StartTestBackwardChainSplitSpace", DataChain_Trace, 5);

    Data_PageId                 InvalidRootId;
    Data_PageAccessManager      PAM   (Trans, Data_UndoFile, 
                                       Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Data_PageNo());
    BackwardChainType           Chain (PAM, InvalidRootId);
    BackwardChainType::Iterator Iter  (PAM, Allocator);
    
    Check ("Iter.Initialize()", Iter.Initialize() );
    Chain.Create ();

    TestBackwardChainSplitSpace         (Chain, Iter, 10);
    TestBackwardChainSplitSpaceNegative (Chain, Iter, 10);

    Iter.Invalidate();
    Chain.Drop ();
    Iter.Delete();
}

/* --------------------------------------------------------------------------- */
inline void StartTestForwardChainSplitSpace (tgg00_TransContext     &Trans,
                                             SAPDBMem_IRawAllocator &Allocator)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::StartTestForwardChainSplitSpace", DataChain_Trace, 5);

    Data_PageId                InvalidRootId;
    Data_PageAccessManager     PAM   (Trans, Data_RedoFile, 
                                      Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Data_PageNo());
    ForwardChainType           Chain (PAM, InvalidRootId);
    ForwardChainType::Iterator Iter  (PAM, Allocator);
    
    Check ("Iter.Initialize()", Iter.Initialize() );
    Chain.Create ();

    TestForwardChainSplitSpace (Chain, Iter, 10);

    Iter.Invalidate();
    Chain.Drop ();
    Iter.Delete();
}

/* --------------------------------------------------------------------------- */
inline void StartTestFixSizeChain (tgg00_TransContext &Trans)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test::StartTestFixSizeChain", DataChain_Trace, 5);

    Data_PageId                InvalidRootId;
    Data_PageAccessManager     PAM    (Trans, Data_OpenTransFile, 
                                       Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Data_PageNo());
    FixSizeChain               Chain  (PAM, InvalidRootId, 2000);
    FixSizeChain::Iterator     Iter   (PAM);
    
    Chain.Create ();

    TestFixSizeChain(Chain, Iter, 10);

    Iter.Invalidate();
    Chain.Drop ();
}


/* --------------------------------------------------------------------------- */
inline void TestTempFile (tgg00_TransContext &trans)
{
    const int     MaxLoopCount = 10000;
    SAPDB_UInt4   record;
    bool          result = true;
    int           loop; 
    Data_TempFile file (trans, sizeof(record) );
    //-----------------------------------------------------------------------
    result = file.Create();
    Check ("file.Create()", result);
    //-----------------------------------------------------------------------
    for ( loop = 0; loop < MaxLoopCount; ++loop )
    {
        record = loop;
        result = file.Append (reinterpret_cast<const SAPDB_Byte*>(&record));
        Check ("file.Append()", result);
    }
    //-----------------------------------------------------------------------
    result = file.Verify(Kernel_IAdminInfo::Instance().KernelStateIsAdmin());
    Check ("file.Verify()", result);
    //-----------------------------------------------------------------------
    Data_TempFile::RecordIterator iter (file.PageAccessManager());

    result = file.GetFirst (iter);
    Check ("file.GetFirst()",  result);
 
    for ( loop = 0; loop < MaxLoopCount; ++loop )
    {
        Check ("iter.IsValid()", iter.IsValid());
        Check ("*iter == loop", *(reinterpret_cast<SAPDB_UInt4*>(*iter)) == loop );
        ++iter;
    }
    Check ("! iter.IsValid()", ! iter.IsValid());
    // iter.Invalidate();
    //-----------------------------------------------------------------------
    file.Drop();
    Check ("! file.IsCreated()", ! file.IsCreated());
}

/* --------------------------------------------------------------------------- */
externCpp void Data_Test (tgg00_TransContext& Trans)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Data_Test", DataChain_Trace, 5);

    SAPDBMem_IRawAllocator& Allocator = *( REINTERPRET_CAST(SAPDBMem_IRawAllocator*, Trans.trAllocator_gg00) );

    ChainTest                        (Trans);
    StartTestBackwardChainSplitSpace (Trans, Allocator);
    StartTestForwardChainSplitSpace  (Trans, Allocator);
    StartTestFixSizeChain            (Trans);
    TestTempFile                     (Trans);

    Data_PrimaryFile file (Trans); // fast compiler and linker test of new primary file implementation
}
