--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqlcompact1.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,492 @@
+// 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 <hal.h>
+#include "SqlUtil.h"
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+RTest TheTest(_L("t_sqlcompact1 test"));
+
+RSqlDatabase TheDb;
+
+const TInt KTextLen = 400;
+TBuf<KTextLen> TheText;
+TBuf<KTextLen + 100> TheSqlBuf;
+
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KDbName1, "c:\\test\\t_sqlcompact1_1.db");
+_LIT(KDbName2, "c:\\test\\t_sqlcompact1_2.db");
+_LIT(KDbName3, "c:\\test\\t_sqlcompact1_3.db");
+_LIT(KDbName4, "c:\\test\\t_sqlcompact1_4.db");
+
+_LIT(KAttachName1, "UOOO");
+_LIT(KAttachName2, "FOOO");
+_LIT(KAttachName3, "AOOO");
+_LIT(KAttachName4, "EOOO");
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void DestroyTestEnv()
+ {
+ TheDb.Close();
+ (void)RSqlDatabase::Delete(KDbName4);
+ (void)RSqlDatabase::Delete(KDbName3);
+ (void)RSqlDatabase::Delete(KDbName2);
+ (void)RSqlDatabase::Delete(KDbName1);
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//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 Check2(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) ::Check2(aValue, aExpected, __LINE__)
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void CreateTestEnv()
+ {
+ RFs fs;
+ TInt err = fs.Connect();
+ TEST2(err, KErrNone);
+
+ err = fs.MkDir(KTestDir);
+ TEST(err == KErrNone || err == KErrAlreadyExists);
+
+ fs.Close();
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void ReplaceDb(const TDesC& aDbName, TInt aPageSize)
+ {
+ (void)RSqlDatabase::Delete(aDbName);
+ _LIT8(KConfigStr, "compaction=manual;page_size=");
+ TBuf8<50> config;
+ config.Copy(KConfigStr);
+ config.AppendNum(aPageSize);
+ TInt err = TheDb.Create(aDbName, &config);
+ TEST2(err, KErrNone);
+ TheDb.Close();
+ }
+
+void CreateTable(const TDesC& aDbName)
+ {
+ TInt err = TheDb.Open(aDbName);
+ TEST2(err, KErrNone);
+ err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER, T TEXT)"));
+ TEST(err >= 0);
+ TheDb.Close();
+ }
+
+void InsertRecords(const TDesC& aDbName)
+ {
+ TInt err = TheDb.Open(aDbName);
+ TEST2(err, KErrNone);
+ TheText.SetLength(TheText.MaxLength());
+ TheText.Fill(TChar('A'));
+ for(TInt i=0;i<100;++i)
+ {
+ TheSqlBuf.Format(_L("INSERT INTO A VALUES(%d, '%S')"), i + 1, &TheText);
+ err = TheDb.Exec(TheSqlBuf);
+ TEST2(err, 1);
+ }
+ TheDb.Close();
+ }
+
+TInt DeleteRecords(const TDesC& aDbName, TInt aPageCount, TInt aPageSize)
+ {
+ TInt freePageCount = -1;
+ TInt err = TheDb.Open(aDbName);
+ TEST2(err, KErrNone);
+ for(TInt i=0;;++i)
+ {
+ TheSqlBuf.Format(_L("DELETE FROM A WHERE I=%d"), i + 1);
+ err = TheDb.Exec(TheSqlBuf);
+ TEST2(err, 1);
+ RSqlDatabase::TSize s;
+ err = TheDb.Size(s);
+ TEST2(err, KErrNone);
+ freePageCount = s.iFree / aPageSize;
+ if(freePageCount >= aPageCount)
+ {
+ break;
+ }
+ }
+ TheDb.Close();
+ return freePageCount;
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-UT-4072
+@SYMTestCaseDesc Manual compaction on attached databases with different page size.
+ The test creates couple of databases with manual compaction and
+ different page sizes, then inserts some records and deletes part of
+ the just inserted records thus making some free pages.
+ The test opens the first database and attaches all other databases the the first one.
+ Then the test checks that RSqlDatabase::Size() returns correct information
+ about the free database space. The test runs the manual compaction on the
+ databases and checks again that the free database space is reported correctly.
+@SYMTestPriority Medium
+@SYMTestActions Manual compaction on attached databases with different page size.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ10405
+ REQ10407
+*/
+void CompactDbTest1()
+ {
+ const TPtrC KDbName[] = {KDbName1(), KDbName2(), KDbName3(), KDbName4()};
+ const TPtrC KDbAttachName[]={KAttachName1(),KAttachName2(), KAttachName3(), KAttachName4()};
+ const TInt KDbPageSize[] = {8192, 1024, 4096, 2048};
+ const TInt KFreePageCount[]={9, 30, 17, 7};
+ TInt freePageCount[] ={0, 0, 0, 0};
+ const TInt KSize = sizeof(KDbName) / sizeof(KDbName[0]);
+
+ TInt i;
+
+ //Create databases, tables, insert records, delete part of the just inserted records.
+ for(i=0;i<KSize;++i)
+ {
+ ReplaceDb(KDbName[i], KDbPageSize[i]);
+ CreateTable(KDbName[i]);
+ InsertRecords(KDbName[i]);
+ freePageCount[i] = DeleteRecords(KDbName[i], KFreePageCount[i], KDbPageSize[i]);
+ }
+
+ //Open the first database, attach all others.
+ TInt err = TheDb.Open(KDbName1());
+ TEST2(err, KErrNone);
+ for(i=0;i<KSize;++i)
+ {
+ err = TheDb.Attach(KDbName[i], KDbAttachName[i]);
+ TEST2(err, KErrNone);
+ }
+
+ //Check the size of the main database.
+ RSqlDatabase::TSize size;
+ err = TheDb.Size(size);
+ TEST2(err, KErrNone);
+ TEST2((size.iFree / KDbPageSize[0]), freePageCount[0]);
+
+ //For all attached database: check the size of the database, compact, check the size again.
+ for(i=0;i<KSize;++i)
+ {
+ err = TheDb.Size(size, KDbAttachName[i]);
+ TEST2(err, KErrNone);
+ TEST2((size.iFree / KDbPageSize[i]), freePageCount[i]);
+
+ const TInt KCompactPageCount = 3;
+ TInt rc = TheDb.Compact(KCompactPageCount * KDbPageSize[i], KDbAttachName[i]);
+ TInt expected = KCompactPageCount * KDbPageSize[i];
+ TEST2(rc, expected);
+ err = TheDb.Size(size, KDbAttachName[i]);
+ TEST2(err, KErrNone);
+ TInt count = size.iFree / KDbPageSize[i];
+ expected = freePageCount[i] - KCompactPageCount;
+ TEST2(count, expected);
+ }
+
+ //Detach databases and close the main database.
+ for(i=0;i<KSize;++i)
+ {
+ err = TheDb.Detach(KDbAttachName[i]);
+ TEST2(err, KErrNone);
+ }
+ TheDb.Close();
+
+ //Cleanup.
+ for(i=0;i<KSize;++i)
+ {
+ (void)RSqlDatabase::Delete(KDbName[i]);
+ }
+ }
+
+//Creates a test database (with KDbName1 name). Inserts aRecordCount records.
+//The page size is specified in aPageSize parameter. But practically the page size is always 1024 bytes.
+//The record size is such that there is only one record per page.
+void PrepareDb(TInt aPageSize, TInt aRecordCount, TBool aManualCompaction = EFalse)
+ {
+ //Create the database
+ (void)RSqlDatabase::Delete(KDbName1);
+ _LIT8(KConfigStr, "page_size=");
+ TBuf8<100> config;
+ config.Copy(KConfigStr);
+ config.AppendNum(aPageSize);
+ if(aManualCompaction)
+ {
+ config.Append(_L(";compaction=manual;"));
+ }
+ TInt err = TheDb.Create(KDbName1, &config);
+ TEST2(err, KErrNone);
+
+ err = TheDb.Exec(_L("CREATE TABLE A(I INTEGER, T TEXT)"));
+ TEST(err >= 0);
+ //Insert records
+ TheText.SetLength(TheText.MaxLength());
+ TheText.Fill(TChar('A'));
+ for(TInt i=0;i<aRecordCount;++i)
+ {
+ TheSqlBuf.Format(_L("INSERT INTO A VALUES(%d, '%S')"), i + 1, &TheText);
+ err = TheDb.Exec(TheSqlBuf);
+ TEST2(err, 1);
+ }
+ //Delete all records making a lot of free pages. This operation should kick-off the background compaction
+ err = TheDb.Exec(_L("DELETE FROM A WHERE 1"));
+ TEST2(err, aRecordCount);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-UT-4073
+@SYMTestCaseDesc Background compaction steps test.
+ The test creates a database with background compaction mode,
+ then inserts records and deletes all of them. The count of records is such that when
+ the records get deleted, the number of the free pages is very big and all free pages cannot
+ be removed for just one compaction step.
+ The test waits for ("compaction interval"/10 ms) time and checks that no compaction
+ step has been run by the server during the pause and the free space size is the same as before the
+ pause. Then the test waits for ("compaction interval" + "compaction step") time and checks that
+ the background compaction step really happened and removed only part of the free pages.
+ The same test is repeated again and the same check is performed again.
+@SYMTestPriority Medium
+@SYMTestActions Background compaction steps test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ10271
+ REQ10272
+*/
+void CompactDbTest2()
+ {
+ const TInt KPageSize = 1024;
+ //Number of records to be added and removed from database. Need to be increased when testing on a faster
+ // hardware, otherwise at fastest case the background compaction could be finished in just 1 step.
+ const TInt KRecordCount = 2000;
+ PrepareDb(KPageSize, KRecordCount);
+
+ //Check the free space-1
+ RSqlDatabase::TSize size1;
+ TInt err = TheDb.Size(size1);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("===Free space before compaction, pages=%d\r\n"), size1.iFree / KPageSize);
+ TEST(size1.iSize >= (KRecordCount * KPageSize));
+
+ //Wait KSqlCompactStepIntervalMs/10 ms. The background compaction should not be kicked-off.
+ TTime time1;
+ time1.HomeTime();
+ User::After((KSqlCompactStepIntervalMs / 10) * 1000);
+ TTime time2;
+ time2.HomeTime();
+ TTimeIntervalMicroSeconds intervalUs = time2.MicroSecondsFrom(time1);
+ //Check the free space-2
+ RSqlDatabase::TSize size2;
+ err = TheDb.Size(size2);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("=== Wait time: %ld ms. Free space after compaction-1, pages=%d\r\n"), intervalUs.Int64() / 1000 ,size2.iFree / KPageSize);
+ if(intervalUs > KSqlCompactStepIntervalMs * 1000)
+ {
+ TEST(size2.iFree <= size1.iFree);
+ }
+ else
+ {
+ TEST(size2.iFree == size1.iFree);
+ }
+
+ //Wait (KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms. During the pause only part of the free pages
+ //should be removed (whatever can be completed for KSqlCompactStepLengthMs ms).
+ User::After((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) * 1000);
+ //Check the free space-3
+ RSqlDatabase::TSize size3;
+ err = TheDb.Size(size3);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("===Free space after compaction-2, pages=%d\r\n"), size3.iFree / KPageSize);
+ if(size3.iFree == 0)
+ {
+ TheTest.Printf(_L("WARNING: Background compaction finished in 1 step. Initial number of records need to be increased.\r\n"));
+ }
+ TEST(size3.iFree > 0 && size3.iFree < size2.iFree);
+
+ //Wait another (KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms. During the pause only part of the free pages
+ //should be removed (whatever can be completed for KSqlCompactStepLengthMs ms).
+ User::After((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) * 1000);
+ //Check the free space-4
+ RSqlDatabase::TSize size4;
+ err = TheDb.Size(size4);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("===Free space after compaction-3, pages=%d\r\n"), size4.iFree / KPageSize);
+ TEST((size4.iFree > 0 && size4.iFree < size3.iFree) || (size4.iFree == 0));
+
+ //Cleanup
+ TheDb.Close();
+ (void)RSqlDatabase::Delete(KDbName1);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-UT-4074
+@SYMTestCaseDesc Background compaction timer test.
+ The test creates a database with background compaction mode,
+ then inserts records and deletes all of them. The count of records is such that when
+ the records get deleted, the number of the free pages is very big and all free pages cannot
+ be removed for just one compaction step.
+ Then the test executes a set of operations with the server. The amount of time needed for the
+ operations to be executed is bigger than the ("compaction interval" + "compaction step") time.
+ No compaction step should be executed during that time, because every operation resets the background
+ compaction timer.
+@SYMTestPriority Medium
+@SYMTestActions Background compaction timer test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ10271
+ REQ10272
+*/
+void CompactDbTest3()
+ {
+ const TInt KPageSize = 1024;
+ const TInt KRecordCount = 1000;
+ PrepareDb(KPageSize, KRecordCount);
+
+ //Check the free space-1
+ RSqlDatabase::TSize size1;
+ TInt err = TheDb.Size(size1);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("===Free space before operations, pages=%d. Db.Size=%d, Db.Free=%d\r\n"), size1.iFree / KPageSize, size1.iSize, size1.iFree);
+ TEST(size1.iSize >= (KRecordCount * KPageSize));
+
+ //Execute a set of operations. The time needed for the operations to complete is bigger than
+ //(KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs) ms
+ TInt freq = 0;
+ TEST2(HAL::Get(HAL::EFastCounterFrequency, freq), KErrNone);
+ TUint32 begin = User::FastCounter();
+ TInt count = 0;
+ TInt time = -1;
+ for(;;++count)
+ {
+ err = TheDb.Exec(_L("SELECT COUNT(*) FROM A"));
+ TEST(err >= 0);
+ TUint32 current = User::FastCounter();
+ TInt64 diffTicks = (TInt64)current - (TInt64)begin;
+ if(diffTicks < 0)
+ {
+ diffTicks = KMaxTUint32 + diffTicks + 1;
+ }
+ const TInt KMicroSecIn1Sec = 1000000;
+ TInt32 us = (diffTicks * KMicroSecIn1Sec) / freq;
+ time = us / 1000;
+ if(time > ((KSqlCompactStepIntervalMs + KSqlCompactStepLengthMs)))
+ {
+ break;
+ }
+ }
+ //Check the free space-2
+ RSqlDatabase::TSize size2;
+ err = TheDb.Size(size2);
+ TEST2(err, KErrNone);
+ TheTest.Printf(_L("===%d operations completed for %d ms\r\n"), count, time);
+ TheTest.Printf(_L("===Free space after operations, pages=%d\r\n"), size2.iFree / KPageSize);
+ TEST(size1.iFree == size2.iFree);
+
+ //Cleanup
+ TheDb.Close();
+ (void)RSqlDatabase::Delete(KDbName1);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-UT-4103
+@SYMTestCaseDesc Big manual compaction test.
+ The test creates a database with 1000 free pages, then calls
+ RSqlDatabase::Compact(RSqlDatabase::EMaxCompaction).
+@SYMTestPriority Medium
+@SYMTestActions Big manual compaction test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ10271
+ REQ10272
+*/
+void ManualCompactTest()
+ {
+ //Create a database with 1000 free pages
+ const TInt KPageSize = 1024;
+ const TInt KRecordCount = 1000;
+ PrepareDb(KPageSize, KRecordCount, ETrue);//create the database with manual compaction mode
+ //Check the free space-1
+ RSqlDatabase::TSize size1;
+ TInt err = TheDb.Size(size1);
+ TEST2(err, KErrNone);
+ const TInt KFreePageCount = size1.iFree / KPageSize;
+ TheTest.Printf(_L("===Free space before operations, pages=%d\r\n"), KFreePageCount);
+ TEST(size1.iSize >= (KRecordCount * KPageSize));
+ //Compact
+ err = TheDb.Compact(RSqlDatabase::EMaxCompaction);
+ TEST2(err, size1.iFree);
+ //Cleanup
+ TheDb.Close();
+ (void)RSqlDatabase::Delete(KDbName1);
+ }
+
+void DoTestsL()
+ {
+ TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4072 Manual Compact() - attached databases, different page sizes"));
+ CompactDbTest1();
+
+ TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4073 Background compaction steps test"));
+ CompactDbTest2();
+
+ TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4074 Background compaction timer test"));
+ CompactDbTest3();
+
+ TheTest.Next( _L(" @SYMTestCaseID:SYSLIB-SQL-UT-4103 Big manual compaction test"));
+ ManualCompactTest();
+ }
+
+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;
+ }