// Copyright (c) 2010 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 <e32test.h>
#include <bautils.h>
#include <sqldb.h>
#include <e32math.h>
#include "SqlBur.h"

//CSqlSrvTestBurInterface - test implementation of the MSqlSrvBurInterface, implemented in the production code by the SQL server.
class CSqlSrvTestBurInterface : public CBase, public MSqlSrvBurInterface
	{
	public:
		static CSqlSrvTestBurInterface* New();
		virtual ~CSqlSrvTestBurInterface();
		virtual RFs& Fs();
		virtual void GetBackUpListL(TSecureId aUid, TDriveNumber aDrive, RArray<HBufC*>& aFileList);
		
	private:
		void Construct();
		
	private:
		RFs iFs;
		
	};

///////////////////////////////////////////////////////////////////////////////////////

RTest TheTest(_L("t_sqlbur2"));

#ifdef _DEBUG
static const TInt KBurstRate = 100;
#endif

TDriveNumber KTestDrive = EDriveC;
_LIT(KTestDir, "c:\\test\\");
CActiveScheduler* TheScheduler = NULL;
CSqlSrvTestBurInterface* TheSqlSrvTestBurInterface = NULL;
TInt TestModeSqlBurError = KErrNone;//The CSqlBurEventMonitor code will set the error here

///////////////////////////////////////////////////////////////////////////////////////

void DestroyTestEnv()
	{
	delete TheSqlSrvTestBurInterface;
	TheSqlSrvTestBurInterface = NULL;		
	
	delete TheScheduler;
	TheScheduler = NULL;
	
	(void)RProperty::Delete(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey);  
	}

///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
//Test macros and functions
void Check(TInt aValue, TInt aLine)
	{
	if(!aValue)
		{
		DestroyTestEnv();
		TheTest.Printf(_L("*** Boolean expression evaluated to false.\r\n"));
		TheTest(EFalse, aLine);
		}
	}
void Check(TInt aValue, TInt aExpected, TInt aLine)
	{
	if(aValue != aExpected)
		{
		DestroyTestEnv();
		TheTest.Printf(_L("*** Expected error: %d, got: %d.\r\n"), aExpected, aValue);
		TheTest(EFalse, aLine);
		}
	}
#define TEST(arg) ::Check((arg), __LINE__)
#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__)

///////////////////////////////////////////////////////////////////////////////////////

CSqlSrvTestBurInterface* CSqlSrvTestBurInterface::New()
	{
	CSqlSrvTestBurInterface* self = new CSqlSrvTestBurInterface;
	TEST(self != NULL);
	self->Construct();
	return self;
	}

void CSqlSrvTestBurInterface::Construct()
	{
	TInt err = iFs.Connect();
	TEST2(err, KErrNone);

	err = iFs.MkDir(KTestDir);
	TEST(err == KErrNone || err == KErrAlreadyExists);

	err = iFs.CreatePrivatePath(KTestDrive);
	TEST(err == KErrNone || err == KErrAlreadyExists);
	}

CSqlSrvTestBurInterface::~CSqlSrvTestBurInterface()
	{
	iFs.Close();
	}

RFs& CSqlSrvTestBurInterface::Fs()
	{
	return iFs;
	}

//No-op. Not needed in this test app.
void CSqlSrvTestBurInterface::GetBackUpListL(TSecureId, TDriveNumber, RArray<HBufC*>&)
	{
	TEST(EFalse);
	}

///////////////////////////////////////////////////////////////////////////////////////

void CreateTestEnv()
    {
	TheScheduler = new CActiveScheduler;
	TEST(TheScheduler != NULL);
	CActiveScheduler::Install(TheScheduler);
	
	TheSqlSrvTestBurInterface = CSqlSrvTestBurInterface::New();
	TEST(TheSqlSrvTestBurInterface != NULL);
	
	TInt err = RProperty::Define(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, 0);  
	TEST(err == KErrNone || err == KErrAlreadyExists);
	}

///////////////////////////////////////////////////////////////////////////////////////

/**
@SYMTestCaseID			PDS-SQL-UT-4233
@SYMTestCaseDesc		CSqlBurEventMonitor object creation - OOM test
						The test runs CSqlBurEventMonitor::NewL() in an OOM loop.
@SYMTestActions			CSqlBurEventMonitor object creation - OOM test
@SYMTestExpectedResults Test must not fail
@SYMTestPriority		High
*/
void SqlBurEventMonitorOomTest()
	{
	TInt err = KErrNoMemory;
	TInt failingAllocationNo = 0;
	TheTest.Printf(_L("Iteration:\r\n"));
	while(err == KErrNoMemory)
		{
		TheTest.Printf(_L(" %d"), ++failingAllocationNo);
		
		TInt startProcessHandleCount;
		TInt startThreadHandleCount;
		RThread().HandleCount(startProcessHandleCount, startThreadHandleCount);
		__UHEAP_MARK;
		__UHEAP_SETBURSTFAIL(RAllocator::EBurstFailNext, failingAllocationNo, KBurstRate);

		CSqlBurEventMonitor* monitor = NULL;
		TRAP(err, monitor = CSqlBurEventMonitor::NewL(*TheSqlSrvTestBurInterface));
		delete monitor;
		
		__UHEAP_RESET;
		__UHEAP_MARKEND;
		TInt endProcessHandleCount;
		TInt endThreadHandleCount;
		RThread().HandleCount(endProcessHandleCount, endThreadHandleCount);
		
		TEST2(startProcessHandleCount, endProcessHandleCount);
		TEST2(startThreadHandleCount, endThreadHandleCount);
		}
	TEST2(err, KErrNone);
	TheTest.Printf(_L("\r\n=== OOM Test succeeded at heap failure rate of %d ===\r\n"), failingAllocationNo);
	}

/**
@SYMTestCaseID			PDS-SQL-UT-4234
@SYMTestCaseDesc		CSqlBurEventMonitor functional test
						The test sets the backup & restore property status and then checks
						how the backup & restore property monitor (CSqlBurEventMonitor) reacts to the event.
@SYMTestActions			CSqlBurEventMonitor functional test
@SYMTestExpectedResults Test must not fail
@SYMTestPriority		High
*/
void SqlBurEventMonitorFunctionalTest()
	{
	CSqlBurEventMonitor* monitor = NULL;
	TRAPD(err, monitor = CSqlBurEventMonitor::NewL(*TheSqlSrvTestBurInterface));
	TEST2(err, KErrNone);
	TEST(!monitor->ActiveBackupClient());
	TEST(!monitor->SqlBurCallback());
	//Set the property to conn::EBURBackupFull, conn::EBURBackupPartial, conn::EBURRestoreFull, conn::EBURRestorePartial, 
	//then start the scheduler. CSqlBurEventMonitor::RunL() gets called and 
	//CSqlBurCallback and CActiveBackupClient interfaces get created (if the interfaces do exist, they are destroyed first).
	TInt burPropertyStatus[] = {conn::EBURBackupFull, conn::EBURBackupPartial, conn::EBURRestoreFull, conn::EBURRestorePartial};
	for(TInt i=0;i<(sizeof(burPropertyStatus)/sizeof(burPropertyStatus[0]));++i)
		{
		err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, burPropertyStatus[i]);
		TEST2(err, KErrNone);
		TestModeSqlBurError = KErrNone;
		if(i > 0)
			{
			__UHEAP_MARK;
			}
		CActiveScheduler::Start();
		if(i > 0)
			{
			__UHEAP_MARKEND;
			}
		TEST2(TestModeSqlBurError, KErrNone);
		TEST(monitor->ActiveBackupClient() != NULL);
		TEST(monitor->SqlBurCallback() != NULL);
		}
	//Set the property to conn::EBURUnset, start the scheduler. CSqlBurEventMonitor::RunL() gets called 
	//and CSqlBurCallback interface gets destroyed.
	err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, conn::EBURUnset);
	TEST2(err, KErrNone);
	TestModeSqlBurError = KErrNone;
	CActiveScheduler::Start();
	TEST2(TestModeSqlBurError, KErrNone);
	TEST(!monitor->ActiveBackupClient());
	TEST(!monitor->SqlBurCallback());
	//Set the property to conn::EBURNormal, start the scheduler. CSqlBurEventMonitor::RunL() gets called. 
	//CSqlBurCallback interface has been destroyed alread. No memory deallocations should be made during this call.
	err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, conn::EBURNormal);
	TEST2(err, KErrNone);
	__UHEAP_MARK;
	TestModeSqlBurError = KErrNone;
	CActiveScheduler::Start();
	TEST2(TestModeSqlBurError, KErrNone);
	TEST(!monitor->ActiveBackupClient());
	TEST(!monitor->SqlBurCallback());
	__UHEAP_MARKEND;
	//Set the property, then delete it. CSqlBurEventMonitor::RunL() should get called, but the call should
	//fail because the property does not exist.
	err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, conn::EBURBackupFull);
	TEST2(err, KErrNone);
	err = RProperty::Delete(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey);  
	TEST2(err, KErrNone);
	__UHEAP_MARK;
	TestModeSqlBurError = KErrNone;
	CActiveScheduler::Start();
	TEST2(TestModeSqlBurError, KErrNotFound);
	TEST(!monitor->ActiveBackupClient());
	TEST(!monitor->SqlBurCallback());
	__UHEAP_MARKEND;
	//Restore the property
	err = RProperty::Define(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, 0);  
	TEST2(err, KErrNone);
	//
	delete monitor;
	}

/**
@SYMTestCaseID			PDS-SQL-UT-4235
@SYMTestCaseDesc		CSqlBurEventMonitor::RunL() - OOM test
						The test sets the backup & restore property status and then checks
						how the backup & restore property monitor (CSqlBurEventMonitor) reacts to the event.
						The test is performed in an OOM loop.
@SYMTestActions			CSqlBurEventMonitor::RunL() - OOM test
@SYMTestExpectedResults Test must not fail
@SYMTestPriority		High
*/
void SqlBurEventMonitorRunOomTest()
	{
	CSqlBurEventMonitor* monitor = NULL;
	TRAPD(err, monitor = CSqlBurEventMonitor::NewL(*TheSqlSrvTestBurInterface));
	
	err = KErrNoMemory;
	TInt failingAllocationNo = 0;
	TheTest.Printf(_L("Iteration:\r\n"));
	while(err == KErrNoMemory)
		{
		TheTest.Printf(_L(" %d"), ++failingAllocationNo);
		
		TInt startProcessHandleCount;
		TInt startThreadHandleCount;
		RThread().HandleCount(startProcessHandleCount, startThreadHandleCount);
		__UHEAP_MARK;
		__UHEAP_SETBURSTFAIL(RAllocator::EBurstFailNext, failingAllocationNo, KBurstRate);

		TEST(!monitor->ActiveBackupClient());
		TEST(!monitor->SqlBurCallback());
		//Set the property, start the scheduler. CSqlBurEventMonitor::RunL() gets called and CSqlBurCallback
		//interface gets created.
		err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, conn::EBURBackupFull);
		if(err == KErrNone)
			{
			TestModeSqlBurError = KErrNone;
			CActiveScheduler::Start();
			err = TestModeSqlBurError;
			if(err == KErrNone)
				{
				TEST(monitor->ActiveBackupClient() != NULL);
				TEST(monitor->SqlBurCallback() != NULL);
				//Destroy the SQL backup & restore callback
				err = RProperty::Set(KSqlBurPropertyCategoryUid, KSqlBurBackupRestoreKey, conn::EBURNormal);
				TestModeSqlBurError = KErrNone;
				CActiveScheduler::Start();
				err = TestModeSqlBurError;
				if(err == KErrNone)
					{
					TEST(!monitor->ActiveBackupClient());
					TEST(!monitor->SqlBurCallback());
					}
				}
			}
		
		__UHEAP_RESET;
		__UHEAP_MARKEND;
		TInt endProcessHandleCount;
		TInt endThreadHandleCount;
		RThread().HandleCount(endProcessHandleCount, endThreadHandleCount);
		
		TEST2(startProcessHandleCount, endProcessHandleCount);
		TEST2(startThreadHandleCount, endThreadHandleCount);
		}
	TEST2(err, KErrNone);
	TheTest.Printf(_L("\r\n=== OOM Test succeeded at heap failure rate of %d ===\r\n"), failingAllocationNo);
	delete monitor;
	}

void DoTestsL()
	{
	TheTest.Start(_L(" @SYMTestCaseID:PDS-SQL-UT-4233 CSqlBurEventMonitor object creation - OOM test"));
	SqlBurEventMonitorOomTest();
	TheTest.Next(_L(" @SYMTestCaseID:PDS-SQL-UT-4234 CSqlBurEventMonitor functional test"));
	SqlBurEventMonitorFunctionalTest();
	TheTest.Next(_L(" @SYMTestCaseID:PDS-SQL-UT-4235 CSqlBurEventMonitor::RunL() - OOM test"));
	SqlBurEventMonitorRunOomTest();
	}

TInt E32Main()
	{
	TheTest.Title();

	CTrapCleanup* tc = CTrapCleanup::New();
	TheTest(tc != NULL);

	__UHEAP_MARK;

	CreateTestEnv();
	TRAPD(err, DoTestsL());
	DestroyTestEnv();
	TEST2(err, KErrNone);

	__UHEAP_MARKEND;

	TheTest.End();
	TheTest.Close();

	delete tc;

	User::Heap().Check();
	return KErrNone;
	}
