// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "T_PlTransactionManager.h"
_LIT(KTestName, "T_PlTransactionManager"); // Used by Testhelper - gives an warning under ARMv5
#include "../../tsrc/testhelpers.h"
#include <cntdef.h>


CPlTransMgrTest* CPlTransMgrTest::NewLC()
	{
	CPlTransMgrTest* self = new (ELeave) CPlTransMgrTest();
	CleanupStack::PushL(self);

	self->ConstructL();
	return self;	
	}



void CPlTransMgrTest::ConstructL()
	{
	User::LeaveIfError(iFs.Connect());
	iPersistLayer = CPersistenceLayer::NewLC(iFs);
	CleanupStack::Pop(iPersistLayer); 

	MLplContactsFile& cntFile = iPersistLayer->ContactsFileL();
	// Empty loops required to remove winscw warning
	cntFile.CreateL(nsPlTransMgr::KFilename, MLplContactsFile::EPlOverwrite);
	while(cntFile.OpenStepL   (nsPlTransMgr::KFilename)){};

	iCntItemBldr = CCntItemBuilder::NewLC(iPersistLayer->ContactProperties().SystemTemplateL());
	CleanupStack::Pop(iCntItemBldr);

	iMatchAll = CContactItemViewDef::NewLC(CContactItemViewDef::EIncludeFields,
										   CContactItemViewDef::EIncludeHiddenFields);
	iMatchAll->AddL(KUidContactFieldMatchAll);
	CleanupStack::Pop(iMatchAll);
	}



CPlTransMgrTest::~CPlTransMgrTest()
	{
	delete iMatchAll;
	delete iCntItemBldr;
	delete iPersistLayer;
	iFs.Close();
	}



void CPlTransMgrTest::RunTestsL()
	{
	UpdateAndCommitTestL();
	CreateAndCommitTestL(); 
	DeleteAndCommitTestL();
	RollbackAndRecoverTestL();
	OutOfMemoryUpdateTestL();
	OutOfMemoryDeleteTestL();
	OutOfMemoryAddTestL();
	}

// ---------- Tests ---------

// Create/commit & Rollback/recover tests.

void CPlTransMgrTest::CreateAndCommitTestL()
	{
	test.Next(_L("-> Create Contact and Commit Test"));

	TInt cntID = AddAndCommitContactL();
	
	if (IsContactInDbL(cntID))
		{
		test.Next(_L("-> TEST SUCCESS - Contact FOUND after commit test"));

		return;
		}
	test.Next(_L("-> TEST FAILED - Contact NOT FOUND after commit test"));

	}

void CPlTransMgrTest::RollbackAndRecoverTestL()
	{
	test.Next(_L("-> Rollback & Recover Test"));

	iPersistLayer->TransactionManager().StartTransactionL();

	TInt cntID = AddContactL();
	
	iPersistLayer->TransactionManager().RollbackCurrentTransactionL(0);
  	// Close the database to allow recovery
  	iPersistLayer->ContactsFileL().Close();
  		
	iPersistLayer->ContactsFileL().RecoverL(nsPlTransMgr::KFilename);
	
	if (!IsContactInDbL(cntID))
		{
		test.Next(_L("-> TEST SUCCESS - Contact was NOT Found after Rollback test"));

		return;
		}
	test.Next(_L("-> TEST FAILED - Contact WAS Found after ROLLBACK test"));

	}


void CPlTransMgrTest::UpdateAndCommitTestL()
	{
	test.Next(_L("-> Update Contact and Commit Test"));


	TInt cntID = AddAndCommitContactL();

	iPersistLayer->TransactionManager().StartTransactionL();	
	UpdateContactL(cntID);
	iPersistLayer->TransactionManager().CommitCurrentTransactionL(0);
	
	if (CheckChangedFieldL(cntID))
		{
		test.Next(_L("-> TEST SUCCESS - Contact FOUND after Update commit test"));

		return;
		}
	test.Next(_L("-> TEST FAILED - Contact NOT FOUND after Update commit test"));

	}


void CPlTransMgrTest::DeleteAndCommitTestL()
	{
	test.Next(_L("-> Contact and Commit Test"));


	TInt cntID = AddAndCommitContactL();

	iPersistLayer->TransactionManager().StartTransactionL();	
	DeleteContactL(cntID);
	iPersistLayer->TransactionManager().CommitCurrentTransactionL(0);
	
	if (!IsContactInDbL(cntID))
		{
		test.Next(_L("-> TEST SUCCESS - Contact FOUND after Update commit test"));

		return;
		}
	test.Next(_L("-> TEST FAILED - Contact NOT FOUND after Update commit test"));

	}



// Out Of Memory/Rollback/Recover Tests

void CPlTransMgrTest::OutOfMemoryAddTestL()
	{

	TInt ret=KErrNoMemory;
	TInt failAt=0;	  

	while (ret!=KErrNone)
		{
		// Add a successful contact, guess the next contact id
		TInt cntID = AddAndCommitContactL();
		++cntID;
		iPersistLayer->TransactionManager().StartTransactionL();
		failAt++;
		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, AddContactL());
		if (err==KErrNone)
			{
			TRAP(err, iPersistLayer->TransactionManager().CommitCurrentTransactionL(0));
			if (err==KErrNone)
				{
				test.Next(_L("-> TEST SUCCESS - Contact committed during OOM Test"));

				__UHEAP_RESET;
				return;
				}
			}
		__UHEAP_RESET;
		if (err!=KErrNoMemory && err!=KErrNone)
			{
			test.Printf(_L("Non standard error: %d\n"),err);
			}
		if (err == KErrNoMemory)
			{
			iPersistLayer->TransactionManager().RollbackCurrentTransactionL(0);
			 	// Close the database to allow recovery
  			iPersistLayer->ContactsFileL().Close();
			iPersistLayer->ContactsFileL().RecoverL(nsPlTransMgr::KFilename);
			if (!IsContactInDbL(cntID))
				{
				test.Printf(_L(" ->Correct result - Create rollback during OOM - Contact NOT Found... Step: %d \n\r"),failAt); 
				}
				else
				{
				test.Printf(_L(" ->Error - Delete rollback during OOM - Contact Found... Step: %d \n\r"),failAt);
				User::Leave(KErrNotFound);
				}
			}
		}
	}


void CPlTransMgrTest::OutOfMemoryDeleteTestL()
	{
	TInt ret	= KErrNoMemory;
	TInt failAt = 0;	  

	while (ret!=KErrNone)
		{
		failAt++;
		TInt cntID = AddAndCommitContactL();
		iPersistLayer->TransactionManager().StartTransactionL();
		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, DeleteContactL(cntID));
		
		if (err==KErrNone)
			{
			TRAP(err, iPersistLayer->TransactionManager().CommitCurrentTransactionL(0));
			if (err==KErrNone)
				{
				test.Next(_L("-> TEST SUCCESS - Delete Contact committed during OOM Test"));

				__UHEAP_RESET;
				return;
				}
			}
		__UHEAP_RESET;
		if (err!=KErrNoMemory && err!=KErrNone)
			{
			test.Printf(_L("Non standard error: %d\n"),err);
			}
		if (err == KErrNoMemory)
			{
			iPersistLayer->TransactionManager().RollbackCurrentTransactionL(0);
			// Close the database to allow recovery
  			iPersistLayer->ContactsFileL().Close();
			iPersistLayer->ContactsFileL().RecoverL(nsPlTransMgr::KFilename);
			if (IsContactInDbL(cntID))
				{
				test.Printf(_L(" ->Correct result - Delete rollback during OOM - Contact Found... Step: %d \n\r"),failAt); 
				}
				else
				{
				test.Printf(_L(" ->Error - Delete rollback during OOM - Contact NOT Found... Step: %d \n\r"),failAt);
				User::Leave(KErrNotFound);
				}
			}
		}
	}


void CPlTransMgrTest::OutOfMemoryUpdateTestL()
	{

	TInt ret=KErrNoMemory;
	TInt failAt=0;	  
	
	iTestNo = 0;
	while (ret!=KErrNone)
		{
		++iTestNo;
		//if (iTestNo == 790)
		//	TInt i = 0;
		TInt cntID = AddAndCommitContactL();
		iPersistLayer->TransactionManager().StartTransactionL();
		failAt++;
		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, UpdateContactL(cntID));
		if (err==KErrNone)
			{
			TRAP(err, iPersistLayer->TransactionManager().CommitCurrentTransactionL(0));
			if (err==KErrNone)
				{
				test.Next(_L("-> TEST SUCCESS - Contact committed during OOM Test"));

				__UHEAP_RESET;
				return;
				}
			}
			
		__UHEAP_RESET;
		if (err!=KErrNoMemory && err!=KErrNone)
			{
			test.Printf(_L("Non standard error: %d\n"),err);
			}
		if (err == KErrNoMemory)
			{
			iPersistLayer->TransactionManager().RollbackCurrentTransactionL(0);
			// Close the database to allow recovery
  			iPersistLayer->ContactsFileL().Close();
			iPersistLayer->ContactsFileL().RecoverL(nsPlTransMgr::KFilename);
			
			// Has the contact changed?
			if (!CheckChangedFieldL(cntID))
				{
				test.Printf(_L(" ->Correct result - Update rollback during OOM - Updated Contact NOT Found... Step: %d \n\r"),failAt);			
				}
				else
				{
				test.Printf(_L(" ->Error - Update rollback during OOM - Updated Contact Found... Step: %d \n\r"),failAt);
				User::Leave(KErrNotFound);
				}
			}
		}
	}



// ----------- Helper methods -----------

TInt CPlTransMgrTest::AddContactL()
	{
	// Create the contact
	CContactItem* cntItem = iCntItemBldr->GetCntItemLC();    

    TInt cntID = iPersistLayer->PersistenceBroker().CreateL(*cntItem, 0);

	CleanupStack::PopAndDestroy(cntItem);
	cntItem = NULL;
	return cntID;
	}


TInt CPlTransMgrTest::AddAndCommitContactL()
	{
	iPersistLayer->TransactionManager().StartTransactionL();
	TInt cntID = AddContactL();
	iPersistLayer->TransactionManager().CommitCurrentTransactionL(0);
	return cntID;
	}


void CPlTransMgrTest::DeleteContactL(TInt aCntID) 
	{
    CContactItem* cntItem = iPersistLayer->PersistenceBroker().DeleteLC(aCntID, 0, ESendEvent);
	CleanupStack::PopAndDestroy(cntItem);
	}


void CPlTransMgrTest::UpdateContactL(TInt aCntID)
	{
	CContactItem* cntItem = iPersistLayer->PersistenceBroker().ReadLC(aCntID, *iMatchAll, EPlAllInfo, 0);
	CCntItemModifier* cntModifier = CCntItemModifier::NewLC();
	
	cntModifier->SetTextFieldL(KUidContactFieldGivenName, nsPlTransMgr::KName,
				 *cntItem);

	cntModifier->SetTextFieldL(KUidContactFieldPhoneNumber,
				KUidContactFieldVCardMapWORK, 
				KUidContactFieldVCardMapVOICE,
				KUidContactFieldVCardMapCELL,
		 		nsPlTransMgr::KNumber, *cntItem);

	cntModifier->SetTextFieldL(KUidContactFieldEMail,
			    KUidContactFieldVCardMapWORK,
			    nsPlTransMgr::KEmail, *cntItem);


	cntModifier->SetTextFieldL(KUidContactFieldAddress, 
				nsPlTransMgr::KHomeAdd, *cntItem);

	CleanupStack::PopAndDestroy(cntModifier);

	iPersistLayer->PersistenceBroker().UpdateL(*cntItem, 0);
	CleanupStack::PopAndDestroy(cntItem);
	}



TBool CPlTransMgrTest::CheckChangedFieldL(TInt aCntID)
	{
	CContactItem* cntItem = iPersistLayer->PersistenceBroker().ReadLC(aCntID, *iMatchAll, EPlAllInfo, 0);
	CCntItemModifier* cntModifier = CCntItemModifier::NewLC();

	TBool retval = cntModifier->FindTextFieldL(KUidContactFieldGivenName, nsPlTransMgr::KName,
				 *cntItem) & 
		cntModifier->FindTextFieldL(KUidContactFieldPhoneNumber,
				KUidContactFieldVCardMapWORK, 
				KUidContactFieldVCardMapVOICE,
				KUidContactFieldVCardMapCELL,
		 		nsPlTransMgr::KNumber, *cntItem) &
		cntModifier->FindTextFieldL(KUidContactFieldAddress, 
				nsPlTransMgr::KHomeAdd, *cntItem)&
		cntModifier->FindTextFieldL(KUidContactFieldEMail,
			    KUidContactFieldVCardMapWORK,
			    nsPlTransMgr::KEmail, *cntItem); 

	CleanupStack::PopAndDestroy(cntModifier);
	CleanupStack::PopAndDestroy(cntItem);
	return retval;
	}



TBool CPlTransMgrTest::IsContactInDbL(TInt aCntID)
	{
	CContactItem* cntItem = NULL;
	TRAPD(err, cntItem = iPersistLayer->PersistenceBroker().ReadLC(aCntID, *iMatchAll, EPlAllInfo, 0);
		CleanupStack::PopAndDestroy(cntItem);
		cntItem = NULL;
		);
	
	if (err == KErrNotFound)
		{
		return FALSE;	
		}
	User::LeaveIfError(err);
	return TRUE;
	}



/**

@SYMTestCaseID     PIM-T-PLTRANSACTIONMANAGER-0001

*/

LOCAL_C void DoTestsL()
	{
    CleanupClosePushL(test);
	test.Start(_L("@SYMTESTCaseID:PIM-T-PLTRANSACTIONMANAGER-0001 ----------- PLTransactionManager tests ----------"));


	CPlTransMgrTest* tranMgrTest = CPlTransMgrTest::NewLC();	

	tranMgrTest->RunTestsL();

	test.End();
    CleanupStack::PopAndDestroy(2); // test.Close & tranMgrTest
	}

LOCAL_C void CleanupFilesL()
	{
    // delete the database file
    RFs fs;
    CleanupClosePushL(fs);
    User::LeaveIfError(fs.Connect() );
	CPersistenceLayer* persistLayer = CPersistenceLayer::NewLC(fs);
    MLplContactsFile& cntFile = persistLayer->ContactsFileL();
    if (cntFile.DatabaseExistsL(nsPlTransMgr::KFilename() ) )
    	{
    	cntFile.DeleteL(nsPlTransMgr::KFilename() );
    	}
    CleanupStack::PopAndDestroy(2, &fs); // and persistLayer
	}


GLDEF_C TInt E32Main()
	{
    // Init
    CTrapCleanup* cleanupStack = CTrapCleanup::New();
    if (!cleanupStack)
        return KErrNoMemory;
          
    CActiveScheduler* activeScheduler = new CActiveScheduler;
    if (!activeScheduler)
        {
        return KErrNoMemory;
        }
    CActiveScheduler::Install(activeScheduler);

    // Run the tests
    __UHEAP_MARK;
    TRAPD(err, DoTestsL());
    __UHEAP_MARKEND;

    TRAP_IGNORE(CleanupFilesL() );

    test(__NB_failures == 0);

    // Cleanup
	delete activeScheduler;
    delete cleanupStack;
	return err;
    }
