// Copyright (c) 2006-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 <stdlib.h>
#include <string.h>
#include <hal.h>
#include "SqliteSymbian.h"
#ifdef __cplusplus
extern "C" {
#endif
	#include "sqliteInt.h"
	#include "os.h"
#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#include "SqliteUtil.h"

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

const char* KSymbianVfsNameZ = "SymbianSql";

RTest TheTest(_L("t_sqloslayer test"));
RFs   TheFs;

_LIT(KTestDir, "c:\\test\\");
_LIT(KTestFile1, "c:\\test\\t_sqloslayer.bin");
_LIT(KTestFile3, "c:\\test\\t_sqloslayer.db");
const char* KTestFile1Z = "c:\\test\\t_sqloslayer.bin";
const char* KTestFile2Z = "z:\\test\\TestDb1.db";
const char* KTestFile3Z = "c:\\test\\t_sqloslayer.db";
_LIT(KPrivateDir, "c:\\private\\21F12127\\");
const char* KTestFile4Z = "c:\\test\\t_sqloslayer2.db";

//In order to be able to compile the test, the following variables are defined (used inside the OS porting layer, when _SQLPROFILER macro is defined)
#ifdef _SQLPROFILER
TInt TheSqlSrvProfilerFileRead = 0;
TInt TheSqlSrvProfilerFileWrite = 0;
TInt TheSqlSrvProfilerFileSync = 0;
TInt TheSqlSrvProfilerFileSetSize = 0;
#endif

//SQLite panic category.
_LIT(KSqlitePanicCategory, "Sqlite");

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

void DeleteTestFiles()
	{
	(void)TheFs.Delete(KTestFile3);
	(void)TheFs.Delete(KTestFile1);
	}

void TestEnvDestroy()
	{
	sqlite3SymbianLibFinalize();
	DeleteTestFiles();
	TheFs.Close();
	}

///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
//Test macros and functions
void Check1(TInt aValue, TInt aLine)
	{
	if(!aValue)
		{
		TestEnvDestroy();
		RDebug::Print(_L("*** Line %d\r\n"), aLine);
		TheTest(EFalse, aLine);
		}
	}
void Check2(TInt aValue, TInt aExpected, TInt aLine)
	{
	if(aValue != aExpected)
		{
		TestEnvDestroy();
		RDebug::Print(_L("*** Line %d, Expected error: %d, got: %d\r\n"), aLine, aExpected, aValue);
		TheTest(EFalse, aLine);
		}
	}
#define TEST(arg) ::Check1((arg), __LINE__)
#define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__)

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

static TInt TheProcessHandleCount = 0;
static TInt TheThreadHandleCount = 0;
static TInt TheAllocatedCellsCount = 0;

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

static void MarkHandles()
    {
    RThread().HandleCount(TheProcessHandleCount, TheThreadHandleCount);
    }

static void MarkAllocatedCells()
    {
    TheAllocatedCellsCount = User::CountAllocCells();
    }

static void CheckAllocatedCells()
    {
    TInt allocatedCellsCount = User::CountAllocCells();
    TEST2(allocatedCellsCount, TheAllocatedCellsCount);
    }

static void CheckHandles()
    {
    TInt endProcessHandleCount;
    TInt endThreadHandleCount;
    
    RThread().HandleCount(endProcessHandleCount, endThreadHandleCount);

    TEST2(TheProcessHandleCount, endProcessHandleCount);
    TEST2(TheThreadHandleCount, endThreadHandleCount);
    }

static void OomPreStep(TInt
#ifdef _DEBUG        
    aFailingAllocationNo
#endif
                      )
    {
    MarkHandles();
    MarkAllocatedCells();
    __UHEAP_MARK;
    __UHEAP_SETBURSTFAIL(RAllocator::EBurstFailNext, aFailingAllocationNo, KBurstRate);
    }

static void OomPostStep()
    {
    __UHEAP_RESET;
    __UHEAP_MARKEND;
    CheckAllocatedCells();
    CheckHandles();
    }

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

void TestEnvInit()
    {
	TInt err = TheFs.Connect();
	TEST2(err, KErrNone);

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

	err = sqlite3SymbianLibInit();
	TEST2(err, KErrNone);
	}

TInt CalcMs(TUint32 aStartTicks, TUint32 aEndTicks)
	{
	static TInt freq = 0;
	if(freq == 0)
		{
		TEST2(HAL::Get(HAL::EFastCounterFrequency, freq), KErrNone);
		}
	TInt64 diffTicks = (TInt64)aEndTicks - (TInt64)aStartTicks;
	if(diffTicks < 0)
		{
		diffTicks = KMaxTUint32 + diffTicks + 1;
		}
	const TInt KMicroSecIn1Sec = 1000000;
	TInt32 us = (diffTicks * KMicroSecIn1Sec) / freq;
	return us / 1000;
	}
	
///////////////////////////////////////////////////////////////////////////////////////

//Create/open/close/delete a file
void Test1()
	{
	sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ);
	TEST(vfs != NULL);

  	sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
	TEST(osFile != NULL);
		
	//Creating a new file
	int outFlags = 0;
	int err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, &outFlags);
	TEST2(err, SQLITE_OK);
	TEST(outFlags & SQLITE_OPEN_READWRITE);
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//Opening an existing file for R/W
	err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE, &outFlags);
	TEST2(err, SQLITE_OK);
	TEST(outFlags & SQLITE_OPEN_READWRITE);
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//Opening a read-only file
	err = sqlite3OsOpen(vfs, KTestFile2Z, osFile, SQLITE_OPEN_READWRITE, &outFlags);
	TEST2(err, SQLITE_OK);
	TEST(outFlags & SQLITE_OPEN_READONLY);
	//Truncate a read-only file
	err = osFile->pMethods->xTruncate(osFile, 0);
	TEST2(err, SQLITE_IOERR);
	//xAccess - read-only file
	int res = 0;
	err = vfs->xAccess(vfs, KTestFile2Z, SQLITE_ACCESS_READ, &res);
	TEST2(err, SQLITE_OK);
	TEST(res != 0);
	//xAccess - invalid request
	res = 0;
	err = vfs->xAccess(vfs, KTestFile2Z, 122, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	//
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//Creating a new file
	err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, &outFlags);
	TEST2(err, SQLITE_OK);
	TEST(outFlags & SQLITE_OPEN_READWRITE);
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//Open a file for a read-only access
	err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READONLY, &outFlags);
	TEST2(err, SQLITE_OK);
	TEST(outFlags & SQLITE_OPEN_READONLY);
	err = sqlite3OsWrite(osFile, "1234", 4, 0);
	TEST(err != SQLITE_OK);
	err = sqlite3SymbianLastOsError();
	TEST2(err, KErrAccessDenied);
	err = vfs->xGetLastError(vfs, 0, 0);
	TEST2(err, 0);//Default implementation
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//Delete KTestFile3Z file
	err = sqlite3OsDelete(vfs, KTestFile3Z, 0);
	TEST2(err, SQLITE_OK);
	res = 0;
	err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	//Open a file for an exclusive access
	err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE, &outFlags);
	TEST2(err, SQLITE_OK);
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//The file should not exist now
	err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	//Open a file for an exclusive access without deleting it after 
	err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE, &outFlags);
	TEST2(err, SQLITE_OK);
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//The file should exist now
	err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 1);
	//Delete KTestFile3Z file
	err = sqlite3OsDelete(vfs, KTestFile3Z, 0);
	TEST2(err, SQLITE_OK);
	err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	//
	User::Free(osFile);
	}

//Read/Write/Seek/Truncate test	
void Test2()
	{
	sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ);
	TEST(vfs != NULL);

  	sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
	TEST(osFile != NULL);
	
	//Creating a new file
	int err = sqlite3OsDelete(vfs, KTestFile1Z, 0);
	TEST2(err, SQLITE_OK);
	int res = 0;
	err = sqlite3OsAccess(vfs, KTestFile1Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
	TEST2(err, SQLITE_OK);
	//Writing at the beginning of the file
	err = sqlite3OsWrite(osFile, "123456", 6, 0);
	TEST2(err, SQLITE_OK);
	//Verify the written data
	char data[20];
	err = sqlite3OsRead(osFile, data, 6, 0);
	TEST2(err, SQLITE_OK);
	err = memcmp(data, "123456", 6);
	TEST2(err, 0);
	//Writing at beyond the end of the file
	err = sqlite3OsWrite(osFile, "abcdefgh", 8, 100);
	TEST2(err, SQLITE_OK);
	//Verify the written data
	err = sqlite3OsRead(osFile, data, 8, 100);
	TEST2(err, SQLITE_OK);
	err = memcmp(data, "abcdefgh", 8);
	TEST2(err, 0);
	//Truncate the file
	err = sqlite3OsTruncate(osFile, 3);
	TEST2(err, SQLITE_OK);
	//Write more data
	err = sqlite3OsWrite(osFile, "xyz", 3, 3);
	TEST2(err, SQLITE_OK);
	//Verify the written data
	err = sqlite3OsRead(osFile, data, 6, 0);
	TEST2(err, SQLITE_OK);
	err = memcmp(data, "123xyz", 6);
	TEST2(err, 0);
	//Check the file size
	TInt64 fileSize = 0;
	err = sqlite3OsFileSize(osFile, &fileSize);
	TEST2(err, SQLITE_OK);
	TEST(fileSize == 6);
	//FileControl - lock type
	int lockType = -1;
	err = osFile->pMethods->xFileControl(osFile, SQLITE_FCNTL_LOCKSTATE, &lockType);
	TEST2(err, SQLITE_OK);
	TEST2(lockType, NO_LOCK);
	//FileControl - set callback - NULL callback
	err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, 0);
	TEST2(err, SQLITE_ERROR);
	//FileControl - set callback - invalid callback object
	TSqlFreePageCallback cbck;
	err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, &cbck);
	TEST2(err, SQLITE_ERROR);
	//FileControl - invalid op-code
	err = osFile->pMethods->xFileControl(osFile, 90234, 0);
	TEST2(err, SQLITE_ERROR);
	//Close the file
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//
	err = sqlite3OsDelete(vfs, KTestFile1Z, 0);
	TEST2(err, SQLITE_OK);
	User::Free(osFile);
	}

//Miscellaneous tests
void Test3()
	{
	sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ);
	TEST(vfs != NULL);
	//Full path name - the full file name is specified
	char fname[KMaxFileName];
	const char* testFileName1 = "c:\\test\\abv.db";
	int err = sqlite3OsFullPathname(vfs, testFileName1, KMaxFileName, fname);
	TEST2(err, SQLITE_OK);
	err = strncasecmp(fname, testFileName1, strlen(testFileName1));
	TEST2(err, 0);
	//Full path name - no drive, the full file name is not specified
	const char* testFileName2 = "abv.db";
	const char* expectedFileName2 = "c:\\private\\21F12127\\abv.db";//"21F12127", the UID of the current test app
	err = sqlite3OsFullPathname(vfs, testFileName2, KMaxFileName, fname);
	TEST2(err, SQLITE_OK);
	err = strncasecmp(fname, expectedFileName2, strlen(expectedFileName2));
	TEST2(err, 0);
	//Full path name - drive present, the full file name is not specified
	const char* testFileName3 = "z:abv.db";
	const char* expectedFileName3 = "z:\\private\\21F12127\\abv.db";//"21F12127", the UID of the current test app
	err = sqlite3OsFullPathname(vfs, testFileName3, KMaxFileName, fname);
	TEST2(err, SQLITE_OK);
	err = strncasecmp(fname, expectedFileName3, strlen(expectedFileName3));
	TEST2(err, 0);
	//Is directory writable - test 1
	int res = 0;
	err = sqlite3OsAccess(vfs, "c:\\test", SQLITE_ACCESS_READWRITE, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 1);
	//Is directory writable - test 2
	err = sqlite3OsAccess(vfs, "c:\\test\\", SQLITE_ACCESS_READWRITE, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 1);
	//Is directory writable - test 3
	//Not a valid test. It is the media that's read only, not the directory.
	//err = sqlite3OsAccess(vfs, "z:\\test\\", SQLITE_ACCESS_READWRITE, &res);
	//TEST2(err, SQLITE_OK);
	//TEST2(res, 0);
	//Random seed
	const int KBufLen = 100;
	char buf[KBufLen];
	err = sqlite3OsRandomness(vfs, KBufLen, buf);
	TEST2(err, KBufLen);
	//Sleep
	TUint32 start = User::FastCounter();
	const TInt KSleepTime = 200000;//us
	(void)sqlite3OsSleep(vfs, KSleepTime);
	TUint32 end = User::FastCounter();
	TInt ms = CalcMs(start, end);
	TheTest.Printf(_L(" === sqlite3OsSleep() time=%d ms\r\n"), ms);
	TEST(ms >= 80 && ms <= 320);// -60%..+60%
	}

//Lock/unlock tests
void Test5()
	{
	sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ);
	TEST(vfs != NULL);

  	sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
	TEST(osFile != NULL);
	
	//Creating a new file
	int res = 0;
	int err = sqlite3OsAccess(vfs, KTestFile1Z, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
	TEST2(err, SQLITE_OK);
	//Lock/unlock
	//SHARED_LOCK
	err = sqlite3OsLock(osFile, SHARED_LOCK);
	TEST2(err, SQLITE_OK);
	err = sqlite3OsCheckReservedLock(osFile, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 0);
	//RESERVED_LOCK
	err = sqlite3OsLock(osFile, RESERVED_LOCK);
	TEST2(err, SQLITE_OK);
	err = sqlite3OsCheckReservedLock(osFile, &res);
	TEST2(err, SQLITE_OK);
	TEST2(res, 1);
	//PENDING_LOCK
	err = sqlite3OsLock(osFile, PENDING_LOCK);
	TEST2(err, SQLITE_OK);
	//EXCLUSIVE_LOCK
	err = sqlite3OsLock(osFile, EXCLUSIVE_LOCK);
	TEST2(err, SQLITE_OK);
	//back to SHARED_LOCK
	err = sqlite3OsLock(osFile, SHARED_LOCK);
	TEST2(err, SQLITE_OK);
	//UNLOCK
	err = sqlite3OsUnlock(osFile, NO_LOCK);
	//Close the file
	err = sqlite3OsClose(osFile);
	TEST2(err, SQLITE_OK);
	//
	err = sqlite3OsDelete(vfs, KTestFile1Z, 0);
	TEST2(err, SQLITE_OK);
	User::Free(osFile);
	}

//sqlite3SymbianLibInit() - OOM test
void sqlite3SymbianLibInitOomTest()
	{
	sqlite3SymbianLibFinalize();
	
	TInt failingAllocNum = 0;
	TInt err = KErrNoMemory;
	while(err == KErrNoMemory)
		{
		OomPreStep(++failingAllocNum);
		err = sqlite3SymbianLibInit();
        sqlite3SymbianLibFinalize();
        OomPostStep();
        if(err != KErrNoMemory)
            {
            TEST2(err, KErrNone);
            }
		}
	TEST2(err, KErrNone);
	TheTest.Printf(_L("=== sqlite3SymbianLibInit() OOM test succeeded at allcoation %d\r\n"), failingAllocNum);
	}

//sqlite3SymbianLibInit() - 'File I/O error simulation' test
void sqlite3SymbianLibInitFsErrTest()
	{
	sqlite3SymbianLibFinalize();
	
	TInt sysDrive = static_cast<TInt>(RFs::GetSystemDrive());
	TDriveUnit drvUnit(sysDrive);
	TDriveName drvName = drvUnit.Name();
	
	TFileName path;
	TInt err = TheFs.PrivatePath(path);
	TEST2(err, KErrNone);
	
	TParse privDataCage;
	err = privDataCage.Set(drvName, &path, 0);
	TEST2(err, KErrNone);
	
	err = KErrNotFound;
	TInt cnt = 1;
	for(;err<KErrNone;++cnt)
		{
		for (TInt fsError=KErrNotFound;fsError>=KErrDied;--fsError)
			{
			(void)TheFs.RmDir(privDataCage.FullName());
	
			TInt processHandleCnt = 0;
			TInt threadHandleCnt = 0;
			RThread().HandleCount(processHandleCnt, threadHandleCnt);
			TInt allocCellsCnt = User::CountAllocCells();
			
			(void)TheFs.SetErrorCondition(fsError, cnt);
			err = sqlite3SymbianLibInit();
			(void)TheFs.SetErrorCondition(KErrNone);
			
			if(err != KErrNone)
				{
				TInt processHandleCnt2 = 0;
				TInt threadHandleCnt2 = 0;
				RThread().HandleCount(processHandleCnt2, threadHandleCnt2);
				TEST2(processHandleCnt2, processHandleCnt);
				TEST2(threadHandleCnt2, threadHandleCnt);
				TInt allocCellsCnt2 = User::CountAllocCells();
				TEST2(allocCellsCnt2, allocCellsCnt);
				}
			else
				{
				sqlite3SymbianLibFinalize();
				}
			}
		}
	sqlite3SymbianLibFinalize();
	TheTest.Printf(_L("=== sqlite3SymbianLibInit() 'File I/O error simulation' test succeeded at iteration %d\r\n"), cnt);
	}

//If _SQLPROFILER macro is not defined then all profiling PS porting layer functions should return KErrNotSupported.
void ProfilerDisabledTest()
	{
#ifndef _SQLPROFILER
	TInt err = sqlite3SymbianProfilerStart(0);
	TEST2(err, KErrNotSupported);
	
	err = sqlite3SymbianProfilerStop(0);
	TEST2(err, KErrNotSupported);
	
	err = sqlite3SymbianProfilerReset(0);
	TEST2(err, KErrNotSupported);
	
	TBuf8<1> res;
	err = sqlite3SymbianProfilerQuery(0, res);
	TEST2(err, KErrNotSupported);
	res = res;
#else	
	TheTest.Printf(_L("  The _SQLPROFILER macro is defined. The Profliling OS porting layer functions are tested in t_sqlapi2\r\n"));
#endif
	}

void NegativeTest()
	{
	TInt err = sqlite3SymbianLibInit();
	TEST2(err, KErrNone);
	
	sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ);
	TEST(vfs != NULL);

  	sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
	TEST(osFile != NULL);
		
	//Creating a new file - the name is too long
	const char* KLongFileNameZ = "c:\\test\\0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789.bin";
	int outFlags = 0;
	err = sqlite3OsOpen(vfs, KLongFileNameZ, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, &outFlags);
	TEST2(err, SQLITE_CANTOPEN);

	//Open a file - the name contains the '|', but not at position 0 (private database - bad name)
	const char* KBadFileNameZ = "c:\\test\\01|23456.bin";
	err = sqlite3OsOpen(vfs, KBadFileNameZ, osFile, SQLITE_OPEN_READWRITE, &outFlags);
	TEST2(err, SQLITE_CANTOPEN);

	//FullPathName() - the output buffer is too small
	const char* KFileNameZ = "c:\\test\\0123456.bin";
	char buf[5];
	err = vfs->xFullPathname(vfs, KFileNameZ, 5, buf);
	TEST2(err, SQLITE_ERROR);

	//FullPathName() - NULL output buffer
	err = vfs->xFullPathname(vfs, KFileNameZ, 5, NULL);
	TEST2(err, SQLITE_ERROR);

	//FullPathName() - NULL file name
	err = vfs->xFullPathname(vfs, NULL, 5, buf);
	TEST2(err, SQLITE_ERROR);

	//FullPathName() - the file name is too long
	err = vfs->xFullPathname(vfs, KLongFileNameZ, 5, buf);
	TEST2(err, SQLITE_ERROR);

	//FullPathName() - NULL file name
	err = vfs->xFullPathname(vfs, NULL, 5, buf);
	TEST2(err, SQLITE_ERROR);

	//Dellete file - NULL file name
	err = vfs->xDelete(vfs, 0, 0);
	TEST2(err, SQLITE_ERROR);

	//Delete file - the output buffer for the unicode file name is too small	
	err = vfs->xDelete(vfs, KLongFileNameZ, 0);
	TEST2(err, SQLITE_ERROR);

	//Open file - NULL file name - this is a temp file name
	err = sqlite3OsOpen(vfs, NULL, osFile, SQLITE_OPEN_READWRITE, &outFlags);
	TEST2(err, SQLITE_OK);
	err = osFile->pMethods->xClose(osFile);
	TEST2(err, SQLITE_OK);
	
	//xAccess - too long file name
	int res = -1;
	err = vfs->xAccess(vfs, KLongFileNameZ, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_IOERR_ACCESS);

	//xAccess - NULL file name
	err = vfs->xAccess(vfs, 0, SQLITE_ACCESS_EXISTS, &res);
	TEST2(err, SQLITE_IOERR_ACCESS);

	User::Free(osFile);
	}

TInt DoDeleteTempFiles()
	{
    CFileMan* fm = NULL;
    TRAPD(err, fm = CFileMan::NewL(TheFs));
    TEST2(err, KErrNone);
    TFileName path;
    path.Copy(KPrivateDir);
    path.Append(_L("temp\\"));
    path.Append(_L("*.$$$"));
    err = fm->Delete(path);
    delete fm;
    return err;
	}

void VfsOpenTempFileOomTest()
    {
    //Delete all temp files in this test private data cage.
	TInt err = DoDeleteTempFiles();
    TEST(err == KErrNone || err == KErrNotFound);
    
    sqlite3_vfs* vfs = sqlite3_vfs_find(NULL);
    TEST(vfs != NULL);

    sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
    TEST(osFile != NULL);
    
    TheTest.Printf(_L("Iteration: "));
    TInt failingAllocNum = 0;
    err = SQLITE_IOERR_NOMEM;
    while(err == SQLITE_IOERR_NOMEM)
        {
        ++failingAllocNum;
        TheTest.Printf(_L("%d "), failingAllocNum);
        OomPreStep(failingAllocNum);
        int outFlags = 0;
        err = sqlite3OsOpen(vfs, NULL, osFile, SQLITE_OPEN_READWRITE, &outFlags);
        if(err == SQLITE_OK)
            {
			//Since this is a temp file, its creation will be delayed till the first file write operation.
			err = sqlite3OsWrite(osFile, "1234", 4, 0);
			(void)sqlite3OsClose(osFile);
            }
        OomPostStep();
        if(err != SQLITE_OK)
            {
            TEST2(err, SQLITE_IOERR_NOMEM);
            }
        //If the iteration has failed, then no temp file should exist in the test private data cage.
        //If the iteration has succeeded, then sqlite3OsClose() should have deleted the temp file.
        TInt err2 = DoDeleteTempFiles();
        TEST2(err2, KErrNotFound);
        }
    TEST2(err, SQLITE_OK);
    TheTest.Printf(_L("\r\n=== TVfs::Open(<temp file>) OOM test succeeded at allcoation %d\r\n"), failingAllocNum);
    
    User::Free(osFile);
    }

void VfsOpenTempFileFileIoErrTest()
	{
    //Delete all temp files in this test private data cage.
	TInt err = DoDeleteTempFiles();
    TEST(err == KErrNone || err == KErrNotFound);
    
    sqlite3_vfs* vfs = sqlite3_vfs_find(NULL);
    TEST(vfs != NULL);

    sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
    TEST(osFile != NULL);
    
	err = SQLITE_ERROR;
	TInt cnt = 1;
	while(err != SQLITE_OK)
		{
		TInt processHandleCnt = 0;
		TInt threadHandleCnt = 0;
		RThread().HandleCount(processHandleCnt, threadHandleCnt);
		TInt allocCellsCnt = User::CountAllocCells();

        TheTest.Printf(_L("%d "), cnt);
		(void)TheFs.SetErrorCondition(KErrGeneral, cnt);
        int outFlags = 0;
        err = sqlite3OsOpen(vfs, NULL, osFile, SQLITE_OPEN_READWRITE, &outFlags);
        if(err == SQLITE_OK)
            {
			//Since this is a temp file, its creation will be delayed till the first file write operation.
			err = sqlite3OsWrite(osFile, "1234", 4, 0);
			(void)sqlite3OsClose(osFile);
            }
		(void)TheFs.SetErrorCondition(KErrNone);
		if(err != SQLITE_OK)
			{
			TInt processHandleCnt2 = 0;
			TInt threadHandleCnt2 = 0;
			RThread().HandleCount(processHandleCnt2, threadHandleCnt2);
			TEST2(processHandleCnt2, processHandleCnt);
			TEST2(threadHandleCnt2, threadHandleCnt);
			TInt allocCellsCnt2 = User::CountAllocCells();
			TEST2(allocCellsCnt2, allocCellsCnt);
	        ++cnt;
			}
        //If the iteration has failed, then no temp file should exist in the test private data cage.
        //If the iteration has succeeded, then sqlite3OsClose() should have deleted the temp file.
        TInt err2 = DoDeleteTempFiles();
        TEST2(err2, KErrNotFound);
		}
    TEST2(err, SQLITE_OK);
    TheTest.Printf(_L("\r\n=== TVfs::Open(<temp file>) file I/O error simulation test succeeded at iteration %d\r\n"), cnt);
    User::Free(osFile);
	}

void VfsCreateDeleteOnCloseFileOomTest()
    {
    sqlite3_vfs* vfs = sqlite3_vfs_find(NULL);
    TEST(vfs != NULL);

    sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile);
    TEST(osFile != NULL);
    
    TheTest.Printf(_L("Iteration: "));
    TInt failingAllocNum = 0;
    TInt err = SQLITE_IOERR_NOMEM;
    while(err == SQLITE_IOERR_NOMEM)
        {
        ++failingAllocNum;
        TheTest.Printf(_L("%d "), failingAllocNum);
        OomPreStep(failingAllocNum);
        int outFlags = 0;
        err = sqlite3OsOpen(vfs, KTestFile4Z, osFile, SQLITE_OPEN_CREATE | SQLITE_OPEN_DELETEONCLOSE, &outFlags);
        if(err == SQLITE_OK)
            {
            err = sqlite3OsClose(osFile);
            }
        OomPostStep();
        if(err != SQLITE_OK)
            {
            TEST2(err, SQLITE_IOERR_NOMEM);
            }
        //Whether the iteration has failed or succeeded, the file should not exist.
        TPtrC8 ptrname((const TUint8*)KTestFile4Z);
        TBuf<50> fname;
        fname.Copy(ptrname);
        TInt err2 = TheFs.Delete(fname);
        TEST2(err2, KErrNotFound);
        }
    TEST2(err, SQLITE_OK);
    TheTest.Printf(_L("\r\n=== TVfs::Open(<delete on close file>) OOM test succeeded at allcoation %d\r\n"), failingAllocNum);
    User::Free(osFile);
    }

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

//Panic thread function. 
//It will cast aData parameter to a TFunctor pointer and call it.
//The expectation is that the called function will panic and kill the panic thread.
TInt ThreadFunc(void* aData)
	{
	CTrapCleanup* tc = CTrapCleanup::New();
	TEST(tc != NULL);
	
	User::SetJustInTime(EFalse);	// disable debugger panic handling
	
	TFunctor* obj = reinterpret_cast<TFunctor*> (aData);
	TEST(obj != NULL);
	(*obj)();//call the panic function
	
	delete tc;
	
	return KErrNone;		
	}

//Panic test.
//PanicTest function will create a new thread - panic thread, giving it a pointer to the function which has to
//be executed and the expectation is that the function will panic and kill the panic thread.
//PanicTest function will check the panic thread exit code, exit category and the panic code.
void PanicTest(TFunctor& aFunctor, TExitType aExpectedExitType, const TDesC& aExpectedCategory, TInt aExpectedPanicCode)
	{
	RThread thread;
	_LIT(KThreadName,"OsLayerPanicThread");
	TEST2(thread.Create(KThreadName, &ThreadFunc, 0x2000, 0x1000, 0x10000, (void*)&aFunctor, EOwnerThread), KErrNone);
	
	TRequestStatus status;
	thread.Logon(status);
	TEST2(status.Int(), KRequestPending);
	thread.Resume();
	User::WaitForRequest(status);
	User::SetJustInTime(ETrue);	// enable debugger panic handling

	TEST2(thread.ExitType(), aExpectedExitType);
	TEST(thread.ExitCategory() == aExpectedCategory);
	TEST2(thread.ExitReason(), aExpectedPanicCode);
	
	CLOSE_AND_WAIT(thread);
	}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////     Panic test functions    /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG    

//Panic when calling COsLayerData::Create() is called and the OS layer data has been already created.
class TOsLayerDataDuplicated : public TFunctor
	{
private:		
	virtual void operator()()
		{
		(void)sqlite3SymbianLibInit();//This should crash the thread in debug mode (because the Os layer
		                              //data was created already in TestEnvInit()).
		}
	};
static TOsLayerDataDuplicated TheOsLayerDataDuplicated;

#endif //_DEBUG

/**
@SYMTestCaseID			SYSLIB-SQL-CT-1650
@SYMTestCaseDesc		SQL, OS porting layer tests.
@SYMTestPriority		High
@SYMTestActions			SQL, OS porting layer tests.
@SYMTestExpectedResults Test must not fail
@SYMREQ					REQ5792
                        REQ5793
*/	
void DoTests()
	{
	TheTest.Printf(_L("OS porting layer test - create/open/close/delete a file\r\n"));
	Test1();
	TheTest.Printf(_L("OS porting layer test - read/write/seek/truncate\r\n"));
	Test2();
	TheTest.Printf(_L("OS porting layer test - miscellaneous tests\r\n"));
	Test3();
	TheTest.Printf(_L("OS porting layer test - lock/unlock\r\n"));
	Test5();
	TheTest.Printf(_L("OS porting layer test - sqlite3SymbianLibInit() - OOM test\r\n"));
	sqlite3SymbianLibInitOomTest();
	TheTest.Printf(_L("OS porting layer test - sqlite3SymbianLibInit() - 'File I/O error simulation' test\r\n"));
	sqlite3SymbianLibInitFsErrTest();
	TheTest.Printf(_L("OS porting layer test - 'profiler disabled' tests\r\n"));
	ProfilerDisabledTest();
	TheTest.Printf(_L("OS porting layer test - negative tests\r\n"));
	NegativeTest();
    TheTest.Printf(_L("TVfs::Open(<temp file>) OOM test\r\n"));
    VfsOpenTempFileOomTest();
    TheTest.Printf(_L("TVfs::Open(<temp file>) file I/O error simulation test\r\n"));
    VfsOpenTempFileFileIoErrTest();
    TheTest.Printf(_L("TVfs::Open(<'delete on close' file>) OOM test\r\n"));
    VfsCreateDeleteOnCloseFileOomTest();
#ifdef _DEBUG    
	TheTest.Printf(_L("'An attempt to create the OS layer data again' panic\r\n"));
	PanicTest(TheOsLayerDataDuplicated, EExitPanic, KSqlitePanicCategory, ESqliteOsPanicOsLayerDataExists);
#endif //_DEBUG	
	}

TInt E32Main()
	{
	TheTest.Title();
	
	CTrapCleanup* tc = CTrapCleanup::New();
	TheTest(tc != NULL);
	
	__UHEAP_MARK;
	
	TestEnvInit();
	DeleteTestFiles();
	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1650 OS porting layer tests"));
	DoTests();
	TestEnvDestroy();

	__UHEAP_MARKEND;
	
	TheTest.End();
	TheTest.Close();
	
	delete tc;

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