// 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_DBTransactionTest.h"
_LIT(KTestName, "T_Database_Transaction_Tests"); // Used by Testhelper - gives an warning under ARMv5
const TInt MAX_TRY = 10;
#include "..\..\tsrc\testhelpers.h"
#include <e32base.h>

#include "T_CAsyncTest.h"

/*
					How to Add a new concurrent Test
	++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Add a new header and cpp file for each new test class.
	Additions to the CConcurrentController
		1. Add a new private Method called Start-TestName
		2. Call the new method from StartTestL	
		3. Add a new enum value called E-TestName to TTestCodes in 
			the header file.
		4. Within the new Start-TestName method, call RunTestThreadL passing in
			the new enum. Refer to StartTimeOutTestL
	Additions to the CConcurrentTester
		1. Instantiate and execute your test class in the RunTestL method.
	
*/

CConcurrentController::~CConcurrentController()
	{
	iStore.ResetAndDestroy();
	}
	
CConcurrentController* CConcurrentController::NewLC()
	{
	CConcurrentController* self = new (ELeave) CConcurrentController();
	CleanupStack::PushL(self);
	return self;	
	}
	
CConcurrentController::CConcurrentController()
	{}
	

void CConcurrentController::StartTestL(TInt aNoOfTesters)
	{
//	StartTransactionTestL(aNoOfTesters);
	StartTimeOutTestL(aNoOfTesters);
//	StartAsyncOpenTestL(aNoOfTesters);
	}
	
void CConcurrentController::StartTransactionTestL(TInt aNoOfTesters)
	{
	test.Printf(_L("Starting Transaction Tests"));
	CreateTestersL(aNoOfTesters);
	// Create the tests and kick off the thread.
	for (TInt ii = 0; ii < aNoOfTesters; ++ii)
		{
		iStore[ii]->RunTestThreadL(); 
		}
	
	while (!Completed())	
		User::WaitForAnyRequest();
	}

void CConcurrentController::CreateTestersL(TInt aNoOfTesters)
	{
	if (iStore.Count() == 0)
		{
		_LIT(KThreadName,"Thread_%d");
		TBuf<256> buf;

		for (TInt ii = 0; ii < aNoOfTesters; ++ii)
			{
			buf.Format(KThreadName, ii);
			iStore.AppendL(CConcurrentTester::NewL(CEventResponse::NewL(*this), buf));
			}
		}
	}

void CConcurrentController::StartAsyncOpenTestL(TInt aNoOfTesters)
	{
	test.Printf(_L("Starting Async Open Tests"));
	CreateTestersL(aNoOfTesters);
	// Create the tests and kick off the thread.
	for (TInt ii = 0; ii < aNoOfTesters; ++ii)
		{
		iStore[ii]->RunTestThreadL(EAsyncOpenTest); 
		}
	
	while (!Completed())	
		User::WaitForAnyRequest();
	
	}

void CConcurrentController::StartTimeOutTestL(TInt aNoOfTesters)
	{
	test.Printf(_L("Starting TimeOut Test\n\r"));
	CreateTestersL(aNoOfTesters);
	
	for (TInt ii = 0; ii < aNoOfTesters; ++ii)
		{
		if (ii%2 == 0)
			{
			iStore[ii]->RunTestThreadL(ENonTimeOutTest);	
			}
		else
			{
			iStore[ii]->RunTestThreadL(ETimeOutTest);				
			}
		}
	while (!Completed())	
		User::WaitForAnyRequest();
	}


TBool CConcurrentController::Completed()
	{
	TInt noOfTesters = iStore.Count();
	for (TInt ii = 0; ii < noOfTesters; ++ii)
		{
		if (iStore[ii]->IsCompleted())
			return EFalse;	
		}
	return ETrue;
	}
	
void CConcurrentController::PublishError(TInt aError)
	{
	test.Printf(_L(" ->  TEST FAILED - Error %d was reported during execution"), aError);
	}





CEventResponse* CEventResponse::NewL(CConcurrentController& aController)
	{
	CEventResponse* self = new (ELeave) CEventResponse(aController);
	return self;	
	}


CEventResponse::CEventResponse(CConcurrentController& aController) : CActive(EPriorityIdle), iController(aController) 
	{
	CActiveScheduler::Add(this);
	}


CEventResponse::~CEventResponse()
	{}


void CEventResponse::RunL()
	{
	test.Next(_L("-> Thread Finished\r"));

	}


void CEventResponse::DoCancel()
	{
	Cancel();
	}


TInt CEventResponse::RunError(TInt aError)
	{
	iController.PublishError(aError);
	return aError;
	}


void CEventResponse::CompleteRequest(TInt aError)
	{
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, aError);
	}
			
TRequestStatus& CEventResponse::RequestStatus()
	{
	return iStatus;
	}
			

CConcurrentTester::~CConcurrentTester()
	{
	delete iResponse;	
	iThread.Close();	
	}


CConcurrentTester* CConcurrentTester::NewL(CEventResponse* aResponse, const TDesC& aThreadName)
	{
	CConcurrentTester* self = new (ELeave) CConcurrentTester(aResponse, aThreadName);
	return self;	
	}

	
TInt CConcurrentTester::ThreadFunction(TAny* aTester)
	{
    CTrapCleanup* cleanup = CTrapCleanup::New();
	if	(!cleanup)
		return KErrNoMemory;
	
	CActiveScheduler*  scheduler = new CActiveScheduler;
	CActiveScheduler::Install(scheduler);
	
	TRAPD(err, static_cast<CConcurrentTester*>(aTester)->RunTestL());	

	delete scheduler;	
	scheduler = NULL;
    delete cleanup;
    cleanup   = NULL;
    
	return err;
	}

TBool CConcurrentTester::IsCompleted()
	{
	return iResponse->RequestStatus() == KRequestPending;	
	}

// Wrapper method for Test class RunTestL()
// This method is called from within the ThreadFunction() method
// and runs in a new thread. Apart from this function all the concurrent
// logic is in the CTransactionThreadTest class.

void CConcurrentTester::RunTestL()
	{
	switch(iTestCode)
		{
		case ETransactionTest:
			{
			CTransactionThreadTest* transTest = CTransactionThreadTest::NewLC();
			transTest->RunTestL();
			CleanupStack::PopAndDestroy(transTest);
			transTest = NULL;
			break;
			}
		case ETimeOutTest:
			{
			CTransTimeOutThread* timeOut = CTransTimeOutThread::NewLC();
			timeOut->RunTestL();
			CleanupStack::PopAndDestroy(timeOut);
			timeOut = NULL;	
			break;
			}
		case ENonTimeOutTest:
			{
			CReqTimeOutThread* nonTimeOut = CReqTimeOutThread::NewLC();
			nonTimeOut->RunTestL();
			CleanupStack::PopAndDestroy(nonTimeOut);
			nonTimeOut = NULL;
			break;
			}
		case EAsyncOpenTest:
			{
			CAsyncTest* asyncTest = CAsyncTest::NewLC();
			CleanupStack::PopAndDestroy(asyncTest);
			asyncTest = NULL;
			break;
			}
		default:
			{
			User::Leave(KErrNotFound);
			}	
		}
	}
	

void CConcurrentTester::RunTestThreadL(TInt aTestCode)
	{
	iTestCode = aTestCode;
	test.Next(_L("-> %s Started \r"));

	User::LeaveIfError(iThread.Create(iThreadName, CConcurrentTester::ThreadFunction, KDefaultStackSize, 0x2000, 0x200000, this, EOwnerThread));
	iThread.SetPriority(EPriorityMuchLess);
	iThread.Logon (iResponse->RequestStatus());
	iThread.Resume();
	}



	


//
// CTransactionThreadTest
CTransactionThreadTest::~CTransactionThreadTest()
	{
	delete iMatchAll;
	delete iCntItemBldr;
	delete iDB;
	delete iGoldenTemplate;
	delete iTest;
	}
	
CTransactionThreadTest* CTransactionThreadTest::NewLC()
	{
	CTransactionThreadTest* self = new (ELeave) CTransactionThreadTest();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;	
	}
	
	
const CContactTemplate&  CTransactionThreadTest::GetSysTemplateL()
	{
	if (!iGoldenTemplate)
		{
		CContactItemViewDef* matchAll = CContactItemViewDef::NewLC(CContactItemViewDef::EIncludeFields,CContactItemViewDef::EIncludeHiddenFields);
		matchAll->AddL(KUidContactFieldMatchAll);

		iGoldenTemplate = static_cast<CContactTemplate*>(iDB->ReadContactL(iDB->TemplateId(), *matchAll));
		CleanupStack::PopAndDestroy(matchAll);
		}
	return *iGoldenTemplate;
	}
	


void CTransactionThreadTest::CreateAndCommitTestL()
	{
	iTest->Next((_L(" %s -> Create Contact and Commit Test"), RThread().Name() ));
	TInt cntID = AddAndCommitContactL();
	
	if (IsContactInDbL(cntID))
		{
		iTest->Next((_L(" %s  -> TEST SUCCESS - Contact FOUND after commit test"), RThread().Name() ));
		return;
		}
	iTest->Next((_L(" %s  -> TEST FAILED - Contact NOT FOUND after commit test"), RThread().Name() ));
	}



void CTransactionThreadTest::RollbackAndRecoverTestL()
	{
	iTest->Next((_L(" %s -> Rollback & Recover Test"), RThread().Name() ));
	iDB->DatabaseBeginLC(EFalse); 

	TInt cntID = AddContactL();
	
	iDB->DatabaseRollback();
	iDB->RecoverL();
	
	CleanupStack::Pop();// Need to pop the rollback
		
	if (!IsContactInDbL(cntID))
		{
		iTest->Next((_L(" %s -> TEST SUCCESS - Contact was NOT Found after Rollback test"), RThread().Name() ));
		return;
		}
	iTest->Next((_L(" %s -> TEST FAILED - Contact WAS Found after ROLLBACK test"), RThread().Name() ));
	}


	
void CTransactionThreadTest::UpdateAndCommitTestL()
	{
	iTest->Next((_L(" %s  -> Update Contact and Commit Test"), RThread().Name() ));

	TInt cntID = AddAndCommitContactL();

	iDB->DatabaseBeginL(EFalse);	
	UpdateContactL(cntID);
	iDB->DatabaseCommitL(EFalse);
	
	if (CheckChangedFieldL(cntID))
		{
		iTest->Next((_L(" %s -> TEST SUCCESS - Contact FOUND after Update commit test"), RThread().Name() ));
		return;
		}
	iTest->Next((_L(" %s  -> TEST FAILED - Contact NOT FOUND after Update commit test"), RThread().Name() ));
	}


void CTransactionThreadTest::DeleteAndCommitTestL()
	{
	iTest->Next((_L(" %s  -> Contact and Commit Test"), RThread().Name() ));

	TInt cntID = AddAndCommitContactL();

	iDB->DatabaseBeginL(EFalse);	
	DeleteContactL(cntID);
	iDB->DatabaseCommitL(EFalse);
	
	if (!IsContactInDbL(cntID))
		{
		iTest->Next((_L(" %s  -> TEST SUCCESS - Contact FOUND after Update commit test"), RThread().Name() ));
		return;
		}
	iTest->Next((_L(" %s  -> TEST FAILED - Contact NOT FOUND after Update commit test"), RThread().Name() ));
	}



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

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

    TInt cntID = 0;
    TRAP_IGNORE(cntID = iDB->AddNewContactL(*cntItem));
		

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


TInt CTransactionThreadTest::AddAndCommitContactL()
	{
	iDB->DatabaseBeginL(EFalse);
	TInt cntID = AddContactL();

	for (TInt ii = 0; ii < 100; ++ii)
	 	AddContactL();

	iDB->DatabaseCommitL(EFalse);
	return cntID;
	}


void CTransactionThreadTest::DeleteContactL(TInt aCntID) 
	{
	// Remove the contact
    iDB->DeleteContactL(aCntID);
	}


void CTransactionThreadTest::UpdateContactL(TInt aCntID)
	{
	CContactItem* cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
	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);

	CContactItem* cntItem2 = iDB->UpdateContactLC(cntItem->Id(), cntItem);
	CleanupStack::PopAndDestroy(cntItem2);
	CleanupStack::PopAndDestroy(cntItem);
	}



TBool CTransactionThreadTest::CheckChangedFieldL(TInt aCntID)
	{
	CContactItem* cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
	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 CTransactionThreadTest::IsContactInDbL(TInt aCntID)
	{
	CContactItem* cntItem = NULL;
	TRAPD(err, cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
		CleanupStack::PopAndDestroy(cntItem);
		cntItem = NULL;
		);
	
	if (err == KErrNotFound)
		{
		return FALSE;	
		}
	User::LeaveIfError(err);
	return TRUE;
	}


void CTransactionThreadTest::ConstructL()
	{
	iDB = CContactDatabase::OpenL(); // Default Database.
	const CContactTemplate& sysTempl(GetSysTemplateL() );
	iCntItemBldr = CCntItemBuilder::NewLC(sysTempl);
	CleanupStack::Pop(iCntItemBldr);
		
	// Create and name an RTest
	TPtrC name(RThread().Name());
	iTest = new(ELeave) RTest(name);
	}


void CTransactionThreadTest::RunTestL()
	{

	for (int testIndex = 0; testIndex <= EUpdateAndCommitTest; ++testIndex)
		{
		switch(testIndex)	
			{
			case ECreateAndCommitTest:
				{
				CreateAndCommitTestL();
				break;
				}
			case ERollbackAndRecoverTest:
				{
				RollbackAndRecoverTestL();
				break;
				}
			case EDeleteAndCommitTest:
				{
				DeleteAndCommitTestL();
				break;
				}
			case EUpdateAndCommitTest:
				{
				UpdateAndCommitTestL();
				break;
				}
			
			default:
				{
				User::Leave(KErrNotFound);
				}
			}
		}
	}


	



// Request Timeout test classes
CTransTimeOutThread* CTransTimeOutThread::NewLC()
	{
	CTransTimeOutThread* self = new (ELeave) CTransTimeOutThread();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;					
	}
CTransTimeOutThread::~CTransTimeOutThread()
	{
	delete iDB;
	delete iTest;		
	}
	
void CTransTimeOutThread::RunTestL()
	{
	// Just wait for TimeOut
	iDB->DatabaseBeginL(EFalse);

	iTest->Next(_L("Started Transaction & Waiting for 60 seconds...\n"));
	User::After(KSixtySecondsTimeOut);
	iTest->Next(_L("Timed Out...\n"));

	TRAP_IGNORE(iDB->DatabaseCommitL(EFalse));

	}
	
void CTransTimeOutThread::ConstructL()
	{
	iDB = CContactDatabase::OpenL(); // Default Database.

	// Create and name an RTest
	TPtrC name(RThread().Name());
	iTest = new(ELeave) RTest(name);	
	}




	
	
	

//




CReqTimeOutThread* CReqTimeOutThread::NewLC()
	{
	CReqTimeOutThread* self = new (ELeave) CReqTimeOutThread();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;			
	}

CReqTimeOutThread::~CReqTimeOutThread()
	{
	delete iMatchAll;
	delete iCntItemBldr;
	delete iDB;
	delete iGoldenTemplate;
	delete iTest;
	}

void CReqTimeOutThread::RunTestL()
	{
	TTime finishTime;
	TTime now;
	finishTime.UniversalTime();
	now.UniversalTime();
	finishTime +=(KSixtySecondsTimeOut);
	
	while (now < finishTime)							
		{
		AddContactL(); // Contact the server
		iTest->Printf(_L("Non TimeOut Thread is maintaining contact with the server\n\r"));
		User::After(KOneHalfSecondTimeOut);// Every .25 seconds
		now.UniversalTime();
		}
	}

void CReqTimeOutThread::ConstructL()
	{
	iDB = CContactDatabase::OpenL(); // Default Database.
	const CContactTemplate& sysTempl(GetSysTemplateL() );
	iCntItemBldr = CCntItemBuilder::NewLC(sysTempl);
	CleanupStack::Pop(iCntItemBldr);
		
	// Create and name an RTest
	TPtrC name(RThread().Name());
	iTest = new(ELeave) RTest(name);	
	}

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

    TRAPD(err, iDB->AddNewContactL(*cntItem));
	if(err != KErrNotReady)
		{
		User::LeaveIfError(err); // Propagate the error
		}
		
	CleanupStack::PopAndDestroy(cntItem);
	cntItem = NULL;
	}

const CContactTemplate& CReqTimeOutThread::GetSysTemplateL()
	{
	if (!iGoldenTemplate)
		{
		CContactItemViewDef* matchAll = CContactItemViewDef::NewLC(CContactItemViewDef::EIncludeFields,CContactItemViewDef::EIncludeHiddenFields);
		matchAll->AddL(KUidContactFieldMatchAll);

		iGoldenTemplate = static_cast<CContactTemplate*>(iDB->ReadContactL(iDB->TemplateId(), *matchAll));
		CleanupStack::PopAndDestroy(matchAll);
		}
	return *iGoldenTemplate;
	}
	








































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

	self->ConstructL();
	return self;	
	}



void CDBTransTest::ConstructL()
	{
	iDB = CContactDatabase::ReplaceL(); // Default Database.
	const CContactTemplate& sysTempl(GetSysTemplateL() );
	iCntItemBldr = CCntItemBuilder::NewLC(sysTempl);
	CleanupStack::Pop(iCntItemBldr);
	}


const CContactTemplate&  CDBTransTest::GetSysTemplateL()
	{
	if (!iGoldenTemplate)
		{
		CContactItemViewDef* matchAll = CContactItemViewDef::NewLC(CContactItemViewDef::EIncludeFields,CContactItemViewDef::EIncludeHiddenFields);
		matchAll->AddL(KUidContactFieldMatchAll);

		iGoldenTemplate = static_cast<CContactTemplate*>(iDB->ReadContactL(iDB->TemplateId(), *matchAll));
		CleanupStack::PopAndDestroy(matchAll);
		}
	return *iGoldenTemplate;
	}
	
	

CDBTransTest::~CDBTransTest()
	{
	delete iMatchAll;
	delete iCntItemBldr;
	delete iDB;
	delete iGoldenTemplate;
	}



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

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

// Create/commit & Rollback/recover tests.

void CDBTransTest::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 CDBTransTest::RollbackAndRecoverTestL()
	{
	test.Next(_L("-> Rollback & Recover Test"));

	iDB->DatabaseBeginLC(EFalse); 

	TInt cntID = AddContactL();
	
	iDB->DatabaseRollback();
	iDB->RecoverL();
	
	CleanupStack::Pop();// Need to pop the rollback
		
	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 CDBTransTest::UpdateAndCommitTestL()
	{
	test.Next(_L("-> Update Contact and Commit Test"));


	TInt cntID = AddAndCommitContactL();

	iDB->DatabaseBeginL(EFalse);	
	UpdateContactL(cntID);
	iDB->DatabaseCommitL(EFalse);
	
	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 CDBTransTest::DeleteAndCommitTestL()
	{
	test.Next(_L("-> Contact and Commit Test"));


	TInt cntID = AddAndCommitContactL();

	iDB->DatabaseBeginL(EFalse);	
	DeleteContactL(cntID);
	iDB->DatabaseCommitL(EFalse);
	
	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 CDBTransTest::OutOfMemoryAddTestL()
	{

	TInt ret=KErrNoMemory;
	TInt failAt=0;	  
	TInt index =0;
	
	while (ret!=KErrNone && index < MAX_TRY)
		{
		++index;
		// Add a successful contact, guess the next contact id
		TInt cntID = AddAndCommitContactL();
		++cntID;
		iDB->DatabaseBeginL(EFalse);	
		++failAt;
		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, AddContactL());
		if (err==KErrNone)
			{
			iDB->DatabaseCommitL(EFalse);
			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)
			{
			iDB->DatabaseRollback();
			iDB->RecoverL();
			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);
				}
			}
		}
		if (index == MAX_TRY)
			{
			test.Printf(_L(" ->Gave up after %d attempts\n\r"),index); 
			}
	}


void CDBTransTest::OutOfMemoryDeleteTestL()
	{
	TInt ret	= KErrNoMemory;
	TInt failAt = 0;	  
	TInt index =0;
	
	while (ret!=KErrNone && index < MAX_TRY)
		{
		++index;
		++failAt;
		TInt cntID = AddAndCommitContactL();
		iDB->DatabaseBeginL(EFalse);
		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, DeleteContactL(cntID));
		
		if (err==KErrNone)
			{
			iDB->DatabaseCommitL(EFalse);
			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)
			{
			iDB->DatabaseRollback();
			iDB->RecoverL();
		
			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);
				}
			}
		}
		if (index == MAX_TRY)
			{
			test.Printf(_L(" ->Gave up after %d attempts\n\r"),index); 
			}
	}


void CDBTransTest::OutOfMemoryUpdateTestL()
	{

	TInt ret=KErrNoMemory;
	TInt failAt=0;	  
	TInt index =0;
	
	while (ret!=KErrNone && index < MAX_TRY)
		{
		++index;
		TInt cntID = AddAndCommitContactL();
		iDB->DatabaseBeginL(EFalse);
		++failAt;

		__UHEAP_SETFAIL(RHeap::EDeterministic,failAt);
		__UHEAP_MARK;			
		TRAPD(err, UpdateContactL(cntID));
		if (err==KErrNone)
			{
			iDB->DatabaseCommitL(EFalse);
			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)
			{
			iDB->DatabaseRollback();
			iDB->RecoverL();
			
			// 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);
				}
			}
		}
		if (index == MAX_TRY)
			{
			test.Printf(_L(" ->Gave up after %d attempts\n\r"),index); 
			}

	}



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

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

    TInt cntID = iDB->AddNewContactL(*cntItem);

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


TInt CDBTransTest::AddAndCommitContactL()
	{
	iDB->DatabaseBeginL(EFalse);
	TInt cntID = AddContactL();
	iDB->DatabaseCommitL(EFalse);
	return cntID;
	}


void CDBTransTest::DeleteContactL(TInt aCntID) 
	{
	// Remove the contact
    iDB->DeleteContactL(aCntID);
	}


void CDBTransTest::UpdateContactL(TInt aCntID)
	{
	CContactItem* cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
	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);

	CContactItem* cntItem2 = iDB->UpdateContactLC(cntItem->Id(), cntItem);
	CleanupStack::PopAndDestroy(cntItem2);
	CleanupStack::PopAndDestroy(cntItem);
	}



TBool CDBTransTest::CheckChangedFieldL(TInt aCntID)
	{
	CContactItem* cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
	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 CDBTransTest::IsContactInDbL(TInt aCntID)
	{
	CContactItem* cntItem = NULL;
	TRAPD(err, cntItem = iDB->ReadContactLC(aCntID, *iMatchAll);
		CleanupStack::PopAndDestroy(cntItem);
		cntItem = NULL;
		);
	
	if (err == KErrNotFound)
		{
		return FALSE;	
		}
	User::LeaveIfError(err);
	return TRUE;
	}

/**

@SYMTestCaseID     PIM-T-DBTRANSACTIONTEST-0001

*/

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


//	CDBTransTest* tranTest = CDBTransTest::NewLC();	
//	tranTest->RunTestsL();
	CConcurrentController* testController = CConcurrentController::NewLC();
	testController->StartTestL(6);

	test.End();
	CleanupStack::PopAndDestroy(testController);
//	CleanupStack::PopAndDestroy(tranTest);
    CleanupStack::Pop(); // test.Close & tranMgrTest
	}




GLDEF_C TInt E32Main()
	{
	__UHEAP_MARK;

	CActiveScheduler* scheduler=new CActiveScheduler;
	if (scheduler)
		{
		CActiveScheduler::Install(scheduler);
		CTrapCleanup* cleanup = CTrapCleanup::New();
		if (cleanup)
			{
			TRAPD(err,DoTestsL());	
			__ASSERT_ALWAYS(err == KErrNone, User::Panic(_L("DBDumper Failed"),err) ); 
			delete cleanup;
			}
		delete scheduler;
		}
	test.Close();	
	
	__UHEAP_MARKEND;
	return KErrNone;
	
	}



