persistentstorage/sql/TEST/t_sqlcompact4.cpp
changeset 0 08ec8eefde2f
child 13 211563e4b919
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqlcompact4.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1093 @@
+// Copyright (c) 2008-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 <e32test.h>
+#include <bautils.h>
+#include <sqldb.h>
+#include <stdlib.h>
+#include "sqlite3.h"
+#include "SqliteSymbian.h"
+#include "SqlResourceTester.h"
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+RTest TheTest(_L("t_sqlcompact4 test"));
+TParse TheParse;
+TDriveName TheDrive;
+
+RSqlDatabase TheDb;
+sqlite3* TheSqliteDb = NULL;
+TBuf<256> TheCmd;
+
+const TInt KTextLen = 1000;
+const TInt KRecLen = 2000;
+
+TBuf<KTextLen> TheText;
+TBuf8<KRecLen> TheSqlQuery;
+TBuf8<KRecLen> TheSqlFmt;
+TBuf<KTextLen + 50> TheSqlTexLen;
+
+_LIT(KDefaultDriveName, "c:");
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KTestDbTemplate8, "c:\\test\\t_sqlcompact4_tmpl8.dat");
+_LIT(KTestDbTemplate16, "c:\\test\\t_sqlcompact4_tmpl16.dat");
+_LIT(KDbName, "c:\\test\\t_sqlcompact4_1.db");
+_LIT(KDbName2, "c:\\test\\t_sqlcompact4_2.db");
+_LIT(KRoDbName, "z:\\test\\testdb1.db");//Created outside the test app
+TFileName TheTestDbName;
+
+const TInt KMaxThreadCount = 100;
+TInt32 TheTestThreadCount = 8;
+
+const TInt KTestDbPageSize = 1024;
+
+TInt TheOriginalDbSize8 = -1;
+TInt TheCompactedDbSize8 = -1;
+
+TInt TheOriginalDbSize16 = -1;
+TInt TheCompactedDbSize16 = -1;
+
+//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
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void DestroyTestEnv()
+	{
+	if(TheSqliteDb)
+		{
+		sqlite3_close(TheSqliteDb);
+		TheSqliteDb = NULL;
+		}
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(KDbName2);
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	(void)RSqlDatabase::Delete(KTestDbTemplate16);
+	(void)RSqlDatabase::Delete(KTestDbTemplate8);
+	sqlite3SymbianLibFinalize();
+	CloseSTDLIB();
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Test macros and functions
+void Check(TInt aValue, TInt aLine)
+	{
+	if(!aValue)
+		{
+		DestroyTestEnv();
+		RDebug::Print(_L("*** Test failure. Boolean expression evaluates to false.\r\n"));
+		TheTest(EFalse, aLine);
+		}
+	}
+void Check(TInt aValue, TInt aExpected, TInt aLine)
+	{
+	if(aValue != aExpected)
+		{
+		DestroyTestEnv();
+		RDebug::Print(_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__)
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void CreateTestEnv()
+    {
+    RFs fs;
+	TInt err = fs.Connect();
+	TEST2(err, KErrNone);
+
+	err = fs.MkDir(KTestDir);
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+
+	TheParse.Set(TheDrive, &KTestDir, 0);
+
+	err = fs.MkDir(TheParse.DriveAndPath());
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+
+	fs.Close();
+
+	sqlite3SymbianLibInit();
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void CreateTestDatabase8()
+	{
+	TheTest.Printf(_L("Create UTF8 test database: %S\r\n"), &KTestDbTemplate8);
+	(void)RSqlDatabase::Delete(KTestDbTemplate8);
+	TBuf8<KMaxFileName> fname;
+	fname.Copy(KTestDbTemplate8);
+	TheSqliteDb = NULL;
+	TInt rc = sqlite3_open((const char*)fname.PtrZ(), &TheSqliteDb);
+	TEST2(rc, SQLITE_OK);
+	TBuf8<100> sql;
+	_LIT8(KSql, "PRAGMA page_size=%d\x0");
+	sql.Format(KSql, KTestDbPageSize);
+	rc = sqlite3_exec(TheSqliteDb, (const char*)sql.Ptr(), 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	rc = sqlite3_exec(TheSqliteDb, "CREATE TABLE A(Id INTEGER,Data BLOB)", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	//Insert records
+	rc = sqlite3_exec(TheSqliteDb, "BEGIN", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	TheSqlQuery.Copy(_L8("INSERT INTO A VALUES(%d,x'"));
+	for(TInt j=0;j<(KRecLen-50);++j)
+		{
+		TheSqlQuery.Append(_L8("A"));
+		}
+	TheSqlQuery.Append(_L8("')"));
+	const TInt KRecCount = 100;	
+	for(TInt i=0;i<KRecCount;++i)
+		{
+		TheSqlFmt.Format(TheSqlQuery, i + 1);
+		rc = sqlite3_exec(TheSqliteDb, (const char*)TheSqlFmt.PtrZ(), 0, 0, 0);
+		TEST2(rc, SQLITE_OK);
+		}
+	rc = sqlite3_exec(TheSqliteDb, "COMMIT", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	//Free some space
+	rc = sqlite3_exec(TheSqliteDb, "DELETE FROM A WHERE Id > 10", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	sqlite3_close(TheSqliteDb);
+	TheSqliteDb = NULL;
+	}
+
+void CreateTestDatabase16()
+	{
+	TheTest.Printf(_L("Create UTF16 test database: %S\r\n"), &KTestDbTemplate16);
+	(void)RSqlDatabase::Delete(KTestDbTemplate16);
+	TBuf<KMaxFileName> fname;
+	fname.Copy(KTestDbTemplate16);
+	TheSqliteDb = NULL;
+	TInt rc = sqlite3_open16(fname.PtrZ(), &TheSqliteDb);
+	TEST2(rc, SQLITE_OK);
+	TBuf8<100> sql;
+	_LIT8(KSql, "PRAGMA page_size=%d\x0");
+	sql.Format(KSql, KTestDbPageSize);
+	rc = sqlite3_exec(TheSqliteDb, (const char*)sql.Ptr(), 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	rc = sqlite3_exec(TheSqliteDb, "CREATE TABLE A(Id INTEGER,Data BLOB)", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	//Insert records
+	rc = sqlite3_exec(TheSqliteDb, "BEGIN", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	TheSqlQuery.Copy(_L8("INSERT INTO A VALUES(%d,x'"));
+	for(TInt j=0;j<(KRecLen-50);++j)
+		{
+		TheSqlQuery.Append(_L8("A"));
+		}
+	TheSqlQuery.Append(_L8("')"));
+	const TInt KRecCount = 100;	
+	for(TInt i=0;i<KRecCount;++i)
+		{
+		TheSqlFmt.Format(TheSqlQuery, i + 1);
+		rc = sqlite3_exec(TheSqliteDb, (const char*)TheSqlFmt.PtrZ(), 0, 0, 0);
+		TEST2(rc, SQLITE_OK);
+		}
+	rc = sqlite3_exec(TheSqliteDb, "COMMIT", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	//Free some space
+	rc = sqlite3_exec(TheSqliteDb, "DELETE FROM A WHERE Id > 10", 0, 0, 0);
+	TEST2(rc, SQLITE_OK);
+	sqlite3_close(TheSqliteDb);
+	TheSqliteDb = NULL;
+	}
+	
+void CreateDatabase8(const TDesC& aTargetDbName)
+	{
+	RFs fs;
+	TInt err = fs.Connect();
+	TEST2(err, KErrNone);		
+	CFileMan* fm = NULL;
+	TRAP(err, fm = CFileMan::NewL(fs));
+	TEST2(err, KErrNone);
+	err = fm->Copy(KTestDbTemplate8, aTargetDbName);
+	delete fm;
+	fs.Close();
+	TEST2(err, KErrNone);
+	}
+
+void CreateDatabase16(const TDesC& aTargetDbName)
+	{
+	RFs fs;
+	TInt err = fs.Connect();
+	TEST2(err, KErrNone);		
+	CFileMan* fm = NULL;
+	TRAP(err, fm = CFileMan::NewL(fs));
+	TEST2(err, KErrNone);
+	err = fm->Copy(KTestDbTemplate16, aTargetDbName);
+	delete fm;
+	fs.Close();
+	TEST2(err, KErrNone);
+	}
+
+void CalculateMaxCompaction8()
+	{
+	TheTest.Printf(_L("UTF8 test database - calculate max compaction\r\n"));
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase8(TheTestDbName);
+	TInt err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	RSqlDatabase::TSize size1;
+	err = TheDb.Size(size1);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("UTF8.Database before compaction: size %ld, free space %ld\r\n"), size1.iSize, size1.iFree);
+	err = TheDb.Compact(RSqlDatabase::EMaxCompaction);
+	TEST2(err, size1.iFree);
+	RSqlDatabase::TSize size2;
+	err = TheDb.Size(size2);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("UTF8.Database after compaction: size %ld, free space %ld\r\n"), size2.iSize, size2.iFree);
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	TheOriginalDbSize8 = size1.iSize;
+	TheCompactedDbSize8 = size2.iSize;
+	TEST(TheOriginalDbSize8 > 0);
+	TEST(TheCompactedDbSize8 > 0 && TheCompactedDbSize8 < TheOriginalDbSize8);
+	}
+
+void CalculateMaxCompaction16()
+	{
+	TheTest.Printf(_L("UTF16 test database - calculate max compaction\r\n"));
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase16(TheTestDbName);
+	TInt err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	RSqlDatabase::TSize size1;
+	err = TheDb.Size(size1);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("UTF16.Database before compaction: size %ld, free space %ld\r\n"), size1.iSize, size1.iFree);
+	err = TheDb.Compact(RSqlDatabase::EMaxCompaction);
+	TEST2(err, size1.iFree);
+	RSqlDatabase::TSize size2;
+	err = TheDb.Size(size2);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("UTF16.Database after compaction: size %ld, free space %ld\r\n"), size2.iSize, size2.iFree);
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	TheOriginalDbSize16 = size1.iSize;
+	TheCompactedDbSize16 = size2.iSize;
+	TEST(TheOriginalDbSize16 > 0);
+	TEST(TheCompactedDbSize16 > 0 && TheCompactedDbSize16 < TheOriginalDbSize16);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+enum TCompactionType {ESyncCompaction, EAsyncCompaction, EMaxCompactionType};
+
+TInt DoCompact(TCompactionType aType, TInt aSize, const TDesC& aAttachDbName = KNullDesC)
+	{
+	TInt err = KErrGeneral;
+	switch(aType)
+		{
+		case ESyncCompaction:
+			err = TheDb.Compact(aSize, aAttachDbName);
+			break;
+		case EAsyncCompaction:
+			{
+			TRequestStatus stat;				
+			TheDb.Compact(aSize, stat, aAttachDbName);
+			User::WaitForRequest(stat);
+			TEST(stat != KRequestPending);
+			err = stat.Int();
+			break;
+			}
+		default:
+			TEST(0);
+			break;	
+		}
+	return err;
+	}
+
+TInt DoManualCompaction(TCompactionType aType, const TDesC& aMainDb, TInt aSize, TBool aRoFlag = EFalse)
+	{
+	if(!aRoFlag)
+		{
+		(void)RSqlDatabase::Delete(aMainDb);
+		CreateDatabase8(aMainDb);
+		}
+
+	TInt err = TheDb.Open(aMainDb);
+	TEST2(err, KErrNone);
+	
+	err = DoCompact(aType, aSize);
+		
+	TheDb.Close();
+	if(!aRoFlag)
+		{
+		(void)RSqlDatabase::Delete(aMainDb);
+		}
+	return err;
+	}
+
+TInt DoManualCompaction(TCompactionType aType, TInt aSize, const TDesC& aAttachDbName)
+	{
+	return DoCompact(aType, aSize, aAttachDbName);
+	}
+
+void DoManualCompaction(TCompactionType aType, TInt aSize, TInt aCompactedSize)
+	{
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase8(TheTestDbName);
+
+	TInt err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	
+	err = DoCompact(aType, aSize);
+	TEST(err >= 0);
+	
+	RSqlDatabase::TSize size;
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	TEST2(size.iSize, aCompactedSize);
+		
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	}
+
+void DoManualCompaction(TCompactionType aType, TInt aSize, TInt aCompactedSize, const TDesC& aAttachDbName)
+	{
+	TInt err = DoCompact(aType, aSize, aAttachDbName);
+	TEST(err >= 0);
+	
+	RSqlDatabase::TSize size;
+	err = TheDb.Size(size, aAttachDbName);
+	TEST2(err, KErrNone);
+	TEST2(size.iSize, aCompactedSize);
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4064
+@SYMTestCaseDesc		Manual compaction - negative tests.
+						The test creates a database with a manual compaction mode.
+						Then the test executes the following negative tests using both synchronous and
+						asynchronous Compact() methods:
+							- RSqlDatabase::Compact() called with aSize parameter value = KMinTInt;
+							- RSqlDatabase::Compact() called with negative aSize parameter value;
+							- RSqlDatabase::Compact() called on a read-only database;
+							- RSqlDatabase::Compact() called on an attached read-only database;
+							- RSqlDatabase::Compact() called on a nonexisting attached database with very long name;
+							- RSqlDatabase::Compact() called with aSize = 0;
+							- RSqlDatabase::Compact() called on a read-only database where the version number of symbian_settings table is 3;
+@SYMTestPriority		Medium
+@SYMTestActions			Manual compaction - negative tests.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10273
+                        REQ10274
+                        REQ10402
+*/
+void ManualCompactionNegativeTest()
+	{
+	for(TInt i=0;i<EMaxCompactionType;++i)
+		{
+		//Specifying KMaxTInt as aSize argument value.
+		TInt err = DoManualCompaction((TCompactionType)i, TheTestDbName, KMaxTInt);
+		TEST(err > 0);
+		//Specifying KMinTInt as aSize argument value.
+		err = DoManualCompaction((TCompactionType)i, TheTestDbName, KMinTInt);
+		TEST2(err, KErrArgument);
+		//Specifying another negative value as aSize argument value.
+		err = DoManualCompaction((TCompactionType)i, TheTestDbName, -357);
+		TEST2(err, KErrArgument);
+		//Specifying zero as aSize argument value.
+		err = DoManualCompaction((TCompactionType)i, TheTestDbName, 0);
+		TEST2(err, 0);
+		//Read-only database - old format (version 3 of symbian_settings table)
+		err = DoManualCompaction((TCompactionType)i, KRoDbName, RSqlDatabase::EMaxCompaction, ETrue);
+		TEST2(err, KSqlErrReadOnly);
+		//
+		(void)RSqlDatabase::Delete(TheTestDbName);
+		CreateDatabase16(TheTestDbName);
+		err = TheDb.Open(TheTestDbName);
+		TEST2(err, KErrNone);
+		_LIT(KAttachDbName, "Db");
+		//Attached read-only database
+		err = TheDb.Attach(KRoDbName, KAttachDbName);
+		TEST2(err, KErrNone);
+		err = DoManualCompaction((TCompactionType)i, RSqlDatabase::EMaxCompaction, KAttachDbName);
+		TEST2(err, KSqlErrReadOnly);
+		err = TheDb.Detach(KAttachDbName);
+		TEST2(err, KErrNone);
+		//Nonexisting attached database 
+		err = DoManualCompaction((TCompactionType)i, RSqlDatabase::EMaxCompaction, _L("aaa"));
+		TEST2(err, KSqlErrGeneral);
+		//Very long name of a  nonexisting attached database 
+		TBuf<KMaxFileName + 10> fname;
+		fname.SetLength(fname.MaxLength());
+		fname.Fill(0xDD);
+		err = DoManualCompaction((TCompactionType)i, RSqlDatabase::EMaxCompaction, fname);
+		TEST2(err, KErrBadName);
+		//
+		TheDb.Close();
+		(void)RSqlDatabase::Delete(TheTestDbName);
+		}
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4065
+@SYMTestCaseDesc		Manual compaction - functional tests.
+						The test creates a database with a manual compaction mode.
+						Then the test executes the following functional tests using both synchronous and
+						asynchronous Compact() methods:
+							- RSqlDatabase::Compact() called with aSize parameter value = RSqlDatabase::EMaxCompaction;
+							- RSqlDatabase::Compact() called with aSize parameter value = 0. No pages should be removed;
+							- RSqlDatabase::Compact() called with aSize parameter value = 1. 1 page should be removed;
+							- RSqlDatabase::Compact() called with aSize parameter value = "db page size - 1". 1 page should be removed;
+							- RSqlDatabase::Compact() called with aSize parameter value = "db page size * <cnt>". <cnt> pages should be removed;
+							- RSqlDatabase::Compact() called with aSize parameter value > the free db space. All free pages should be removed;
+						The same functional tests are repeated with an attached database.
+@SYMTestPriority		Medium
+@SYMTestActions			Manual compaction - functional tests.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10273
+                        REQ10274
+                        REQ10402
+*/
+void ManualCompactionTest()
+	{
+	for(TInt i=0;i<EMaxCompactionType;++i)
+		{
+		//Calling Compact() with aSize = RSqlDatabase::EMaxCompaction
+		DoManualCompaction((TCompactionType)i, RSqlDatabase::EMaxCompaction, TheCompactedDbSize8);
+		//Calling Compact() with aSize = 0. 0 pages expected to be removed
+		DoManualCompaction((TCompactionType)i, 0, TheOriginalDbSize8);
+		//Calling Compact() with aSize = 1. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, 1, TheOriginalDbSize8 - KTestDbPageSize);
+		//Calling Compact() with aSize = KTestDbPageSize - 1. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize - 1, TheOriginalDbSize8 - KTestDbPageSize);
+		//Calling Compact() with aSize = KTestDbPageSize. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize, TheOriginalDbSize8 - KTestDbPageSize);
+		const TInt KPagesCnt1 = 17;
+		//Calling Compact() with aSize = KTestDbPageSize * KPagesCnt1. KPagesCnt1 pages expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize * KPagesCnt1, TheOriginalDbSize8 - KTestDbPageSize * KPagesCnt1);
+		//Calling Compact() with aSize > TheOriginalDbSize8. All free pages expected to be removed
+		DoManualCompaction((TCompactionType)i, TheOriginalDbSize8 + 2000, TheCompactedDbSize8);
+		//Attached database
+		(void)RSqlDatabase::Delete(KDbName2);
+		TInt err = TheDb.Create(KDbName2);
+		TEST2(err, KErrNone);
+		(void)RSqlDatabase::Delete(TheTestDbName);
+		CreateDatabase16(TheTestDbName);
+		_LIT(KAttachDbName, "Db");
+		err = TheDb.Attach(TheTestDbName, KAttachDbName);
+		TEST2(err, KErrNone);
+		TInt newDatabaseSize = TheOriginalDbSize16;
+		//Calling Compact() with aSize = 0. 0 pages expected to be removed
+		DoManualCompaction((TCompactionType)i, 0, newDatabaseSize, KAttachDbName);
+		//Calling Compact() with aSize = 1. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, 1, TheOriginalDbSize16 - KTestDbPageSize, KAttachDbName);
+		newDatabaseSize -= KTestDbPageSize;
+		//Calling Compact() with aSize = KTestDbPageSize - 1. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize - 1, newDatabaseSize - KTestDbPageSize, KAttachDbName);
+		newDatabaseSize -= KTestDbPageSize;
+		//Calling Compact() with aSize = KTestDbPageSize. 1 page expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize, newDatabaseSize - KTestDbPageSize, KAttachDbName);
+		newDatabaseSize -= KTestDbPageSize;
+		//Calling Compact() with aSize = KTestDbPageSize * KPagesCnt1. KPagesCnt1 pages expected to be removed
+		DoManualCompaction((TCompactionType)i, KTestDbPageSize * KPagesCnt1, newDatabaseSize - KTestDbPageSize * KPagesCnt1, KAttachDbName);
+		newDatabaseSize -= KTestDbPageSize * KPagesCnt1;
+		//Calling Compact() with aSize > newDatabaseSize. All free pages expected to be removed
+		DoManualCompaction((TCompactionType)i, newDatabaseSize + 2000, TheCompactedDbSize16, KAttachDbName);
+		//
+		err = TheDb.Detach(KAttachDbName);
+		TEST2(err, KErrNone);
+		TheDb.Close();
+		(void)RSqlDatabase::Delete(KDbName2);
+		}
+	}
+
+
+enum TSizeTestType {EManualSizeTest, EAutoSizeTest};
+
+void DoCompactionDbSizeTest(TSizeTestType aType)
+	{
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	_LIT8(KConfig1, "compaction=manual");
+	_LIT8(KConfig2, "compaction=auto");
+	TInt err = TheDb.Create(TheTestDbName, aType == EManualSizeTest ? &KConfig1 : &KConfig2);
+	TEST2(err, KErrNone);
+	err = TheDb.Exec(_L("CREATE TABLE A(T TEXT)"));
+	TEST2(err, 1);
+	//
+	RSqlDatabase::TSize size;
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	TEST2(size.iFree, 0);
+	//
+	const TInt KRecCnt = 50;
+	for(TInt i=0;i<KRecCnt;++i)
+		{
+		err = TheDb.Exec(_L("INSERT INTO A VALUES('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaa')"));
+		TEST2(err, 1);
+		}
+	//
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	TEST2(size.iFree, 0);
+	//
+	err = TheDb.Exec(_L("DELETE FROM A WHERE 1"));
+	TEST2(err, KRecCnt);
+	//
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	if(aType == EManualSizeTest)
+		{
+		TEST(size.iFree > 0);
+		}
+	else
+		{
+		TEST2(size.iFree, 0);
+		}
+	//
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4066
+@SYMTestCaseDesc		RSqlDatabase::Size(TSize&) called on a database with manual compaction mode.
+						The test creates a database with a manual compaction mode.
+						Then the test inserts some records and deletes the records making some free database pages.
+						The test calls RSqlDatabase::Size(TSize&) before and after the delete operation and verifies
+						that the database file size stays unchanged.
+@SYMTestPriority		Medium
+@SYMTestActions			RSqlDatabase::Size(TSize&) called on a database with manual compaction mode.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10407
+*/
+void ManualCompactionSizeTest()
+	{
+	DoCompactionDbSizeTest(EManualSizeTest);
+	}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////         OOM testing        ////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void PrintEndOfOomTest(TInt aFailingAllocationNo)
+	{
+	TheTest.Printf(_L("=== OOM Test succeeded at heap failure rate of %d ===\r\n"), aFailingAllocationNo);
+	}
+
+void SetDbHeapFailure(TInt aFailingAllocationNo)
+	{
+	const TInt KDelayedDbHeapFailureMask = 0x1000;
+	TSqlResourceTester::SetDbHeapFailure(RHeap::EDeterministic | KDelayedDbHeapFailureMask, aFailingAllocationNo);
+	}
+	
+void ResetDbHeapFailure()
+	{
+	TSqlResourceTester::SetDbHeapFailure(RHeap::ENone, 0);
+	}
+
+static TInt TheHandleCount1B;
+static TInt TheHandleCount2B;
+static TInt TheAllocatedCellsCountB;
+
+void MarkHandles()
+	{
+	RThread().HandleCount(TheHandleCount1B, TheHandleCount2B);
+	}
+
+void CheckHandles()
+	{
+	TInt endHandleCount1E;
+	TInt endHandleCount2E;
+
+	RThread().HandleCount(endHandleCount1E, endHandleCount2E);
+
+	TEST(TheHandleCount1B == endHandleCount1E);
+	TEST(TheHandleCount2B == endHandleCount2E);
+	}
+
+void MarkAllocatedCells()
+	{
+	TheAllocatedCellsCountB = User::CountAllocCells();
+	}
+
+void CheckAllocatedCells()
+	{
+	TInt allocatedCellsCountE = User::CountAllocCells();
+	TEST(allocatedCellsCountE == TheAllocatedCellsCountB);
+	}
+
+typedef void (*TDbFuncPtrL)(const TDesC& aDbName);
+
+void DoManualCompactionOomTest(TDbFuncPtrL aTestFunctionPtrL, const TDesC& aDbFileName, const TDesC& aAttachDbFileName, const TDesC& aDbName)
+	{
+	const TInt KDoDbOomTestAllocLimitServer = 1000;
+	TInt failingAllocation = 0;
+	TInt allocation = 0;
+	TInt err = KErrNoMemory;
+	while(allocation < KDoDbOomTestAllocLimitServer)
+		{
+		MarkHandles();
+		MarkAllocatedCells();
+		
+		__UHEAP_MARK;
+
+		SetDbHeapFailure(++allocation);
+
+		err = TheDb.Open(aDbFileName);
+		TEST2(err, KErrNone);
+		if(aAttachDbFileName != KNullDesC)
+			{
+			TEST(aDbName != KNullDesC);
+			err = TheDb.Attach(aAttachDbFileName, aDbName);
+			TEST(err == KErrNone || err == KErrNoMemory);
+			}
+		if(err == KErrNone)
+			{
+			TRAP(err, (*aTestFunctionPtrL)(aDbName));
+			if(err != KErrNoMemory)
+				{			
+				TEST2(err, KErrNone);
+				}
+			else
+				{
+				failingAllocation = allocation;	
+				}
+			}
+			
+		ResetDbHeapFailure();
+
+		if(aAttachDbFileName != KNullDesC)
+			{
+			(void)TheDb.Detach(aDbName);
+			}
+		TheDb.Close();
+
+		__UHEAP_MARKEND;
+
+		CheckAllocatedCells();	    	
+		CheckHandles();	
+		}
+	TEST2(err, KErrNone);
+	PrintEndOfOomTest(failingAllocation + 1);
+	}
+
+void OomTest1L(const TDesC&)
+	{
+	User::LeaveIfError(TheDb.Compact(RSqlDatabase::EMaxCompaction));
+	}
+
+void OomTest2L(const TDesC& aDbName)
+	{
+	TEST(aDbName != KNullDesC);
+	User::LeaveIfError(TheDb.Compact(RSqlDatabase::EMaxCompaction, aDbName));
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4068
+@SYMTestCaseDesc		RSqlDatabase::Compact() - OOM test.
+						The test creates a database with a manual compaction mode.
+						Then the test calls Compact() in an OOM loop.
+						The same OOM test is repeated for Compact() called an attached database.
+@SYMTestPriority		Medium
+@SYMTestActions			RSqlDatabase::Compact() - OOM test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10405
+*/
+void ManualCompactionOomTest()
+	{
+	TheTest.Printf(_L("Main database - manual compaction - OOM test\r\n"));
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase8(TheTestDbName);
+	DoManualCompactionOomTest(&OomTest1L, TheTestDbName, KNullDesC, KNullDesC);
+	TInt err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	RSqlDatabase::TSize size;
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	TEST2(size.iSize, TheCompactedDbSize8);
+	TheDb.Close();
+
+	TheTest.Printf(_L("Attached database - manual compaction - OOM test\r\n"));
+	(void)RSqlDatabase::Delete(KDbName2);
+	err = TheDb.Create(KDbName2);
+	TEST2(err, KErrNone);
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase16(TheTestDbName);
+	DoManualCompactionOomTest(&OomTest2L, KDbName2, TheTestDbName, _L("Db"));
+	err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	err = TheDb.Size(size);
+	TEST2(err, KErrNone);
+	TEST2(size.iSize, TheCompactedDbSize16);
+	TheDb.Close();
+
+	(void)RSqlDatabase::Delete(KDbName2);
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4067
+@SYMTestCaseDesc		RSqlDatabase::Size(TSize&) called on a database with auto compaction mode.
+						The test creates a database with an auto compaction mode.
+						Then the test inserts some records and deletes the records.
+						The test calls RSqlDatabase::Size(TSize&) after the delete operation and verifies
+						that the database file does not contain any free pages.
+@SYMTestPriority		Medium
+@SYMTestActions			RSqlDatabase::Size(TSize&) called on a database with auto compaction mode.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10407
+                        REQ10400
+*/
+void AutoCompactionSizeTest()
+	{
+	DoCompactionDbSizeTest(EAutoSizeTest);
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4069
+@SYMTestCaseDesc		Background compaction functional test.
+						The test executes a 10 iterations loop with a "sleep" time 1000000 us at the beginning.
+						The "sleep" time is divided by 2 on each iteration.
+						In each iteration the test creates a database with free pages count big enough to kick-off the
+						background compaction. Then the test executes enough Exec()s in order to kick-off the background compaction.
+						Then the test "sleeps" the calculated "sleep" time and checks after that the database size and free pages
+						count and prints them out. After the last iteration the same test is repeated with no "sleep" time.
+						The test verifies how the client connection activity affects the possibility of the server to run the
+						background compaction. 
+@SYMTestPriority		Medium
+@SYMTestActions			Background compaction functional test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10271
+                        REQ10407
+*/
+void BackgroundCompactionTest()
+	{
+	TInt interval = 1000000;//us
+	const TInt KIterationCnt = 10;
+	TheTest.Printf(_L("===Sleep after Exec()\r\n"));
+	for(TInt i=0;i<KIterationCnt;++i)
+		{
+		(void)RSqlDatabase::Delete(TheTestDbName);
+		CreateDatabase8(TheTestDbName);
+		TInt err = TheDb.Open(TheTestDbName);
+		TEST2(err, KErrNone);
+		RSqlDatabase::TSize size1;
+		err = TheDb.Size(size1);
+		TEST2(err, KErrNone);
+		TheTest.Printf(_L("---------------------------------\r\n"));
+		TheTest.Printf(_L("===Sleep time %d ms. Database before background compaction: size %ld, free space %ld\r\n"), interval / 1000, size1.iSize, size1.iFree);
+		//Simulate Exec() activities
+		for(TInt j=0;j<100;++j)
+			{
+			err = TheDb.Exec(_L8("SELECT Id FROM A LIMIT 1"));
+			TEST(err >= 0);
+			}
+		User::After(interval);
+		RSqlDatabase::TSize size2;
+		err = TheDb.Size(size2);
+		TEST2(err, KErrNone);
+		TheTest.Printf(_L("===Database after background compaction: size %ld, free space %ld\r\n"), size2.iSize, size2.iFree);
+		TEST(size2.iSize <= size1.iSize);
+		TEST(size2.iFree <= size1.iFree);
+		interval /= 2;
+		TheDb.Close();
+		}
+	TheTest.Printf(_L("===No sleep\r\n"));
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	CreateDatabase8(TheTestDbName);
+	TInt err = TheDb.Open(TheTestDbName);
+	TEST2(err, KErrNone);
+	RSqlDatabase::TSize size1;
+	err = TheDb.Size(size1);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("===Database before background compaction: size %ld, free space %ld\r\n"), size1.iSize, size1.iFree);
+	//Simulate Exec() activities
+	for(TInt j=0;j<100;++j)
+		{
+		err = TheDb.Exec(_L8("SELECT Id FROM A LIMIT 1"));
+		TEST(err >= 0);
+		}
+	RSqlDatabase::TSize size2;
+	err = TheDb.Size(size2);
+	TEST2(err, KErrNone);
+	TheTest.Printf(_L("===Database after background compaction: size %ld, free space %ld\r\n"), size2.iSize, size2.iFree);
+	TEST(size2.iSize <= size1.iSize);
+	TEST(size2.iFree <= size1.iFree);
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(TheTestDbName);
+	}
+
+struct TThreadData
+	{
+	TThreadData(const TDesC& aFileName, TInt aSleepInterval) :
+		iDbName(aFileName),
+		iSleepInterval(aSleepInterval)
+		{
+		TInt err = iCritSection.CreateLocal();
+		TEST2(err, KErrNone);
+		iCritSection.Wait();
+		Mem::FillZ(&iSize1, sizeof(iSize1));
+		Mem::FillZ(&iSize2, sizeof(iSize2));
+		}
+	TFileName iDbName;	
+	RCriticalSection iCritSection;
+	RSqlDatabase::TSize iSize1;
+	RSqlDatabase::TSize iSize2;
+	TInt	iSleepInterval;
+	};
+
+TInt ThreadFunc(void* aPrm)
+	{
+	TEST(aPrm != NULL);
+
+	__UHEAP_MARK;
+	CTrapCleanup* tc = CTrapCleanup::New();
+	TheTest(tc != NULL);
+
+	//Wait for a signal from the main thread
+	TThreadData* thrdat = (TThreadData*)aPrm;
+	thrdat->iCritSection.Wait();
+
+	RSqlDatabase db;
+	TInt err = db.Open(thrdat->iDbName);
+	TEST2(err, KErrNone);
+	err = db.Size(thrdat->iSize1);
+	TEST2(err, KErrNone);
+	//Simulate Exec() activities
+	for(TInt j=0;j<100;++j)
+		{
+		err = db.Exec(_L8("SELECT Id FROM A LIMIT 1"));
+		TEST(err >= 0);
+		if((j % 10) == 0 && thrdat->iSleepInterval > 0)
+			{
+			User::After(thrdat->iSleepInterval);
+			}
+		}
+	err = db.Size(thrdat->iSize2);
+	TEST2(err, KErrNone);
+	db.Close();
+
+	delete tc;	
+	__UHEAP_MARKEND;
+	return KErrNone;
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4070
+@SYMTestCaseDesc		Background compaction load test.
+						The test runs 8 threads. Each thread connects to a different database.
+						Each database has space in the free pages above the "free pages" threshold - 
+						the background compaction will be scheduled at the moment when the database is opened.
+						Every thread executes some operations on the opened database - that will delay the background compaction.
+						After every 10 operations the thread sleeps for a specified interval of a time.
+						After all threads complete, the test checks the database size and free pages count and
+						prints them out.
+						The test verifies the ability of the SQL server to run the background compaction under a load.
+@SYMTestPriority		Medium
+@SYMTestActions			Background compaction load test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10271
+                        REQ10407
+*/
+void BackgroundCompactionLoadTest()
+	{
+	RThread threads[KMaxThreadCount];
+	TThreadData* thrdata[KMaxThreadCount] = {NULL};
+	TRequestStatus thrstat[KMaxThreadCount];
+	
+	const TInt KSleepInterval[] = {0, 50000, 100000, 300000, 500000, 800000};//us
+	const TInt KTestCnt = sizeof(KSleepInterval) / sizeof(KSleepInterval[0]);
+	
+	for(TInt k=0;k<KTestCnt;++k)
+		{
+		TheTest.Printf(_L("=================================================\r\n"));
+		TheTest.Printf(_L("===Sleep interval %d ms\r\n"), KSleepInterval[k] / 1000);
+		TheTest.Printf(_L("=================================================\r\n"));
+		//Create test databases and threads
+		for(TInt i=0;i<TheTestThreadCount;++i)
+			{
+			//Database
+			TBuf<16> fname;
+			fname.Copy(_L("\\test\\a"));
+			fname.AppendNum(i + 1);
+			fname.Append(_L(".db"));
+			TheParse.Set(TheDrive, &fname, 0);
+			(void)RSqlDatabase::Delete(TheParse.FullName());
+			CreateDatabase8(TheParse.FullName());
+			//Thread data
+			thrdata[i] = new TThreadData(TheParse.FullName(), KSleepInterval[k]);
+			TEST(thrdata[i] != NULL);
+			//Thread
+			TBuf<16> thrname;
+			thrname.Copy(_L("Thread"));
+			thrname.AppendNum(i + 1);
+			TInt err = threads[i].Create(thrname, &ThreadFunc, 0x2000, 0x1000, 0x10000, thrdata[i], EOwnerProcess);
+			TEST2(err, KErrNone);
+			threads[i].Logon(thrstat[i]);
+			TEST2(thrstat[i].Int(), KRequestPending);
+			threads[i].Resume();
+			}
+		//Enable the threads
+		for(TInt i=0;i<TheTestThreadCount;++i)
+			{
+			thrdata[i]->iCritSection.Signal();	
+			}
+		//Wait for cmpletion
+		for(TInt i=0;i<TheTestThreadCount;++i)
+			{
+			User::WaitForRequest(thrstat[i]);
+			}
+		//Report
+		for(TInt i=0;i<TheTestThreadCount;++i)
+			{
+			TheTest.Printf(_L("===Thread %d, database %S\r\n"), i + 1, &(thrdata[i]->iDbName));
+			TheTest.Printf(_L("===Before background compaction: size %6ld, free space %6ld\r\n"), thrdata[i]->iSize1.iSize, thrdata[i]->iSize1.iFree);
+			TheTest.Printf(_L("===After background compaction:  size %6ld, free space %6ld\r\n"), thrdata[i]->iSize2.iSize, thrdata[i]->iSize2.iFree);
+			TEST(thrdata[i]->iSize2.iSize <= thrdata[i]->iSize1.iSize);
+			TEST(thrdata[i]->iSize2.iFree <= thrdata[i]->iSize1.iFree);
+			}
+		//Destroy
+		for(TInt i=0;i<TheTestThreadCount;++i)
+			{
+			(void)RSqlDatabase::Delete(thrdata[i]->iDbName);
+			thrdata[i]->iCritSection.Close();
+			delete thrdata[i];
+			thrdata[i] = NULL;
+			CLOSE_AND_WAIT(threads[i]);
+			}
+		}
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4071
+@SYMTestCaseDesc		Background compaction in a DDL transaction test.
+						The test creates a database, begins a transaction that modifies the database structure  
+						and executes enough operations in order free enough space to kick-off the background compaction. 
+						The test should not report any failures caused by the fact that the main database connection is
+						in a DML transaction and at the same time the background connection may try to execute
+						a "PRAGMA freelist_count" statement.
+@SYMTestPriority		Medium
+@SYMTestActions			Background compaction in a DDL transaction test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10271
+*/
+void BackgroundCompactionInDDLTransactionTest()
+	{
+	const TInt KOperationCount = 100;
+	(void)RSqlDatabase::Delete(KDbName);
+	TInt err = TheDb.Create(KDbName);
+	TEST2(err, KErrNone);
+	err = TheDb.Exec(_L("BEGIN"));
+	TEST(err >= 0);
+	err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER, T TEXT)"));
+	TEST2(err, 1);
+	TheText.SetLength(KTextLen);
+	TheText.Fill(TChar('A'));
+	for(TInt i=0;i<=KOperationCount;++i)	
+		{
+		TheSqlTexLen.Format(_L("INSERT INTO A VALUES(%d, '%S')"), i + 1, &TheText);
+		err = TheDb.Exec(TheSqlTexLen);
+		TEST2(err, 1);
+		}
+	err = TheDb.Exec(_L("COMMIT"));
+	TEST(err >= 0);
+	TheDb.Close();
+	(void)RSqlDatabase::Delete(KDbName);
+	}
+
+void DoTestsL()
+	{
+	CreateTestDatabase8();
+	CalculateMaxCompaction8();
+	CreateTestDatabase16();
+	CalculateMaxCompaction16();
+
+	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4064 Manual Compact() - negative tests"));	
+	ManualCompactionNegativeTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4065 Manual Compact() tests"));	
+	ManualCompactionTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4066 Manual compaction db size test"));	
+	ManualCompactionSizeTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4068 Manual compaction - OOM test"));	
+	ManualCompactionOomTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4067 Auto compaction db size test"));	
+	AutoCompactionSizeTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4069 Background compaction test"));	
+	BackgroundCompactionTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4070 Background compaction - load test"));	
+	BackgroundCompactionLoadTest();
+
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4071 Background compaction activated inside a DDL transaction - test"));	
+	BackgroundCompactionInDDLTransactionTest();
+	}
+
+TInt E32Main()
+	{
+	TheTest.Title();
+	
+	CTrapCleanup* tc = CTrapCleanup::New();
+	TheTest(tc != NULL);
+	
+	TheTest.Printf(_L("Usage:\r\n  t_sqlcompact4 [[drive:] [test thread count]]\r\n"));
+	TheDrive.Copy(KDefaultDriveName);
+	User::CommandLine(TheCmd);
+	TheCmd.TrimAll();
+	if(TheCmd.Length() > 0)
+		{
+		TInt pos = TheCmd.Locate(TChar(' '));
+		TheTest(pos > 0);
+		TPtrC prm1(TheCmd.Left(pos));
+		TPtrC prm2(TheCmd.Mid(pos + 1));
+		
+		TheDrive.Copy(prm1);
+		
+		TLex lex(prm2);
+		lex.Val(TheTestThreadCount);
+		}
+	TheParse.Set(TheDrive, &KDbName, 0);
+	TheTestDbName.Copy(TheParse.FullName());
+	TheTest.Printf(_L("Test database: %S\r\n"), &TheTestDbName);
+	
+	__UHEAP_MARK;
+	
+	CreateTestEnv();
+	TRAPD(err, DoTestsL());
+	DestroyTestEnv();
+	TEST2(err, KErrNone);
+	
+	__UHEAP_MARKEND;
+	
+	TheTest.End();
+	TheTest.Close();
+	
+	delete tc;
+	
+	User::Heap().Check();
+	return KErrNone;
+	}