--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/store/TFILE/t_storfcomp.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,546 @@
+// Copyright (c) 1998-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 <s32file.h>
+#include <e32test.h>
+#include <e32math.h>
+
+const TInt KTestCleanupStack=0x20;
+
+// This is a path specification and should not be used as is
+_LIT(KFileLocationSpec, "Z:\\STOR-TST\\T_COMPACT.DAT");
+_LIT(KResultsFile,"RESULTS_%d.TXT");
+
+static TFileName TheResultsFile;
+static TBool AllTests=ETrue;
+static TInt Iterations=10000;
+static TInt DataVolume=160;
+static TInt CompactFrequency=40;
+
+class RLog
+ {
+public:
+ void CreateL(RFs& aFs, const TDesC& aFile);
+ void Close();
+ void PrintL(const char* aFormat, ...);
+private:
+ RFileWriteStream iLog;
+ };
+
+LOCAL_D CTrapCleanup* TheTrapCleanup;
+LOCAL_D RTest test(_L("t_storfcomp"));
+LOCAL_D RFs TheFs;
+LOCAL_D RLog TheLog;
+
+class StopWatch
+ {
+public:
+ void Start();
+ TUint Stop();
+private:
+ TTime iTime;
+ };
+
+void StopWatch::Start()
+ {
+ iTime.UniversalTime();
+ }
+
+TUint StopWatch::Stop()
+ {
+ TTime t;
+ t.UniversalTime();
+ TInt64 i = ((t.MicroSecondsFrom(iTime).Int64()) + 500)/1000;
+ return I64LOW(i);
+ }
+
+StopWatch CompactSW;
+
+void CompactL(CFileStore& aStore)
+//
+// Compact the file and record the stats
+//
+ {
+ aStore.CommitL();
+ TInt startSize;
+ User::LeaveIfError(aStore.File().Size(startSize));
+ CompactSW.Start();
+ TInt wasted = aStore.CompactL();
+ aStore.CommitL();
+ TUint ms = CompactSW.Stop();
+ TInt endSize;
+ User::LeaveIfError(aStore.File().Size(endSize));
+ TheLog.PrintL("%d\t%d\t%d\t%u\n",startSize, endSize, wasted, ms);
+ }
+
+void ReclaimCompactL(CFileStore& aStore, TInt aTrigger)
+//
+// Reclaim the file and compact on trigger
+//
+ {
+ TInt startSize;
+ User::LeaveIfError(aStore.File().Size(startSize));
+ CompactSW.Start();
+ TBool compacted = EFalse;
+ if (aTrigger == 0 || aStore.ReclaimL() * 100 > startSize * aTrigger)
+ {
+ aStore.CompactL();
+ aStore.CommitL();
+ compacted = ETrue;
+ }
+ TUint ms = CompactSW.Stop();
+ TInt endSize;
+ User::LeaveIfError(aStore.File().Size(endSize));
+ TheLog.PrintL("%s\t%d\t%u\n",compacted ? "Compact" : "Reclaim", startSize - endSize, ms);
+ }
+
+void WriteBytesL(RWriteStream& s, TInt aCount)
+ {
+ const TInt KBufSize = 512;
+ TUint8 buf[KBufSize];
+ while (aCount > KBufSize)
+ {
+ s.WriteL(buf, KBufSize);
+ aCount -= KBufSize;
+ }
+ s.WriteL(buf, aCount);
+ s.CommitL();
+ }
+
+TStreamId CreateStreamL(CStreamStore& aStore, TInt aSize)
+ {
+ RStoreWriteStream s;
+ TStreamId id = s.CreateLC(aStore);
+ WriteBytesL(s, aSize);
+ CleanupStack::PopAndDestroy(&s);
+ return id;
+ }
+
+TInt Random(TUint aLimit)
+ {
+ return Math::Random() % aLimit;
+ }
+
+
+TBool AllocationFailure()
+ {
+ User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,1);
+ TAny* cell = User::Alloc(4);
+ User::Free(cell);
+ User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1);
+ return cell == 0;
+ }
+/**
+@SYMTestCaseID SYSLIB-STORE-CT-1144
+@SYMTestCaseDesc Tests for CFileStore::CompactL() function
+@SYMTestPriority High
+@SYMTestActions Attempt for compaction process on the store.Tests for KErrNone flag
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ0000
+*/
+void BasicCompactionTestsL()
+ {
+ const TInt KTestCount = 40;
+ const TInt KTestSize = 50;
+ TParsePtrC parse(KFileLocationSpec);
+//
+ test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1144 "));
+ CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
+
+ store->SetTypeL(store->Layout());
+//
+ TheLog.PrintL("Compact uncommitted empty store\n");
+ TRAPD(r, store->CompactL());
+ test (r == KErrNone);
+//
+ TheLog.PrintL("Compact committed empty store\n");
+ r = store->Commit();
+ test (r == KErrNone);
+ TRAP(r, store->CompactL());
+ test (r == KErrNone);
+//
+ TheLog.PrintL("Compact empty store with full TOC\n");
+ TStreamId streams[KTestCount];
+ TInt i;
+ for (i = 0; i < KTestCount; ++i)
+ streams[i] = CreateStreamL(*store, KTestSize);
+ store->CommitL();
+ for (i = 0; i < KTestCount; ++i)
+ store->DeleteL(streams[i]);
+ store->CommitL();
+ TRAP(r, store->CompactL());
+ test (r == KErrNone);
+//
+ TheLog.PrintL("Compact empty store with delta TOC\n");
+ streams[0] = CreateStreamL(*store, KTestSize);
+ store->CommitL();
+ store->DeleteL(streams[0]);
+ store->CommitL();
+ TRAP(r, store->CompactL());
+ test (r == KErrNone);
+//
+ CleanupStack::PopAndDestroy(store);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-STORE-CT-1145
+@SYMTestCaseDesc Testing fallback compaction algorithm
+@SYMTestPriority High
+@SYMTestActions Tests for compaction process on the store,
+ If we have allocation failure we can test that the fallback algorithm is in place
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ0000
+*/
+void CompactionAlgorithmTestL()
+ {
+ const TInt KTestCount = 200;
+ const TInt KTestSize = 50;
+ const TInt KBeyondSuccess = 2;
+ TInt stopat = KMaxTInt;
+ TInt bestTime = -1;
+ TParsePtrC parse(KFileLocationSpec);
+//
+ test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1145 "));
+ TheLog.PrintL("Testing fallback compaction algorithm\n");
+//
+ for (TInt failat = 1; failat <= stopat; ++failat)
+ {
+ TheLog.PrintL("Fail allocation #%d: ", failat);
+//
+ // prepare the store. Leave a single hole at the beginning
+ CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
+ store->SetTypeL(store->Layout());
+ TStreamId first = CreateStreamL(*store, KTestSize);
+ for (TInt i = 1; i < KTestCount; ++i)
+ CreateStreamL(*store, KTestSize);
+ store->CommitL();
+ store->DeleteL(first);
+ store->CommitL();
+ //
+ User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,failat);
+ CompactSW.Start();
+ TRAPD(r, store->CompactL(); store->CommitL();)
+ TInt ms = CompactSW.Stop();
+ User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1);
+ //
+ if (r != KErrNone)
+ {
+ test (bestTime == -1);
+ TheLog.PrintL("compaction failed\n");
+ }
+ else
+ {
+ TheLog.PrintL("compaction succeeded in %u ms\n", ms);
+ if (bestTime == -1)
+ {
+ bestTime = ms;
+ stopat = failat + KBeyondSuccess; //stop after a few passes after sucsss
+ }
+ else if (ms < bestTime)
+ {
+ if (ms < bestTime/2)
+ stopat = failat + KBeyondSuccess; // new algorithm has kicked in
+ bestTime = ms;
+ }
+ }
+
+ CleanupStack::PopAndDestroy(store);
+ }
+ }
+
+
+struct TTracker
+ {
+ TStreamId iId;
+ TInt iSize;
+ };
+
+CFileStore* InitialiseStoreLC(RArray<TTracker>& aStreams, TInt aDataVolume, TInt aAverageSize)
+ {
+ TParsePtrC parse(KFileLocationSpec);
+ CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
+ store->SetTypeL(store->Layout());
+ for (TInt count = Max(1,(aDataVolume + aAverageSize/2)/aAverageSize); count > 0; --count)
+ {
+ TInt size;
+ if (count == 1)
+ size = aDataVolume;
+ else
+ {
+ size = aDataVolume / count;
+ TInt spread = Min(aAverageSize, size);
+ size += Random(spread) - spread/2;
+ }
+ TTracker e;
+ e.iSize = size;
+ e.iId = CreateStreamL(*store, size);
+ User::LeaveIfError(aStreams.Append(e));
+ aDataVolume -= size;
+ }
+ store->CommitL();
+ return store;
+ }
+/**
+@SYMTestCaseID SYSLIB-STORE-CT-1146
+@SYMTestCaseDesc Tests for compaction on store
+@SYMTestPriority High
+@SYMTestActions Tests for CFileStore::CompactL() function
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ0000
+*/
+void CompactionTestL(const TInt aIterations, const TInt aDataVolume, const TInt aAverageSize, const TInt aCommitFrequency, const TInt aCompactionFrequency)
+ {
+ test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1146 "));
+ TheLog.PrintL("CompactionTest : \n");
+ TheLog.PrintL("\tIterations:\t%d\n",aIterations);
+ TheLog.PrintL("\tDataVolume:\t%d\n",aDataVolume);
+ TheLog.PrintL("\tAverageSize:\t%d\n",aAverageSize);
+ TheLog.PrintL("\tCommitFrequency:\t%d\n",aCommitFrequency);
+ TheLog.PrintL("\tCompactionFrequency:\t%d\n\n",aCompactionFrequency);
+
+ RArray<TTracker> streams(8);
+ CleanupClosePushL(streams);
+ CFileStore* store = InitialiseStoreLC(streams, aDataVolume, aAverageSize);
+ const TInt maxCount = streams.Count() + (streams.Count() / 5);
+ TInt size = aDataVolume;
+ const TInt span = aDataVolume / 10; // +- 10%
+ for (TInt i = aIterations ; --i >= 0; )
+ {
+ TInt from = Random(streams.Count());
+ TInt to = Random(maxCount);
+ TInt tfr = to == from ? 0 : Random(streams[from].iSize + 1);
+ TInt adj = Random(span - 1) - span/2;
+ if (size > aDataVolume + span/2)
+ adj -= size - (aDataVolume + span/2);
+ else if (size < aDataVolume - span/2)
+ adj += (aDataVolume - span/2) - size;
+ if (adj < 0)
+ {
+ if (adj < tfr - streams[from].iSize)
+ adj = tfr - streams[from].iSize;
+ }
+ TInt fromSize = streams[from].iSize - tfr;
+ if (adj < 0 || to == from)
+ fromSize += adj;
+ if (fromSize > 0)
+ {
+ RStoreWriteStream s;
+ s.ReplaceLC(*store, streams[from].iId);
+ WriteBytesL(s, fromSize);
+ CleanupStack::PopAndDestroy(&s);
+ streams[from].iSize = fromSize;
+ }
+
+ if (to != from)
+ {
+ if (to < streams.Count())
+ {
+ TInt toSize = streams[to].iSize + tfr;
+ if (adj > 0)
+ toSize += adj;
+ RStoreWriteStream s;
+ s.ReplaceLC(*store, streams[to].iId);
+ WriteBytesL(s, toSize);
+ CleanupStack::PopAndDestroy(&s);
+ streams[to].iSize = toSize;
+ }
+ else
+ {
+ TInt toSize = tfr;
+ if (adj > 0)
+ toSize += adj;
+ TTracker e;
+ e.iSize = toSize;
+ e.iId = CreateStreamL(*store, toSize);
+ User::LeaveIfError(streams.Append(e));
+ }
+ }
+ if (fromSize <= 0)
+ {
+ store->DeleteL(streams[from].iId);
+ streams.Remove(from);
+ }
+ size += adj;
+//
+ if (Random(aCommitFrequency) == 0)
+ {
+ store->CommitL();
+ if (aCompactionFrequency <= 0)
+ ReclaimCompactL(*store, -aCompactionFrequency);
+ }
+ if (aCompactionFrequency > 0 && Random(aCompactionFrequency) == 0)
+ CompactL(*store);
+ }
+ CleanupStack::PopAndDestroy(store);
+ CleanupStack::PopAndDestroy(&streams);
+ TheLog.PrintL("\nCompactionTestEnd\n\n");
+ }
+
+void RLog::CreateL(RFs& aFs, const TDesC& aFile)
+ {
+#ifdef __WINS__
+ User::LeaveIfError(iLog.Replace(aFs, aFile, EFileWrite));
+#endif
+ }
+
+void RLog::Close()
+ {
+ iLog.Close();
+ }
+
+void RLog::PrintL(const char* aFormat, ...)
+ {
+ VA_LIST list;
+ VA_START(list,aFormat);
+ TBuf8<256> b;
+ b.FormatList(_L8(aFormat),list);
+ VA_END(list);
+#ifdef __WINS__
+ iLog.WriteL(b);
+#endif
+ TBuf<256> b16;
+ b16.Copy(b);
+ test.Printf(_L("%S"),&b16);
+ }
+
+void GetOpt()
+ {
+ TBuf<256> options;
+ User::CommandLine(options);
+ if (options.Length() > 0)
+ {
+ AllTests = EFalse;
+ TLex lex(options);
+ lex.SkipSpace();
+ if (lex.Val(DataVolume) == KErrNone)
+ {
+ lex.SkipSpace();
+ if (lex.Val(Iterations) == KErrNone)
+ {
+ lex.SkipSpace();
+ lex.Val(CompactFrequency);
+ }
+ }
+ }
+ TheResultsFile.Format(KResultsFile,DataVolume);
+ }
+
+void testCompactL()
+ {
+ GetOpt();
+ TheLog.CreateL(TheFs, TheResultsFile);
+ if (AllTests)
+ {
+ BasicCompactionTestsL();
+ if (AllocationFailure())
+ CompactionAlgorithmTestL();
+ }
+ CompactionTestL(Iterations, DataVolume<<10, 300, 5, CompactFrequency);
+ TheLog.Close();
+ }
+
+LOCAL_C void setupTestDirectory()
+//
+// Prepare the test directory.
+//
+ {
+ TInt r=TheFs.Connect();
+ test(r==KErrNone);
+//
+ TDriveUnit drive(static_cast<TUint>(RFs::GetSystemDrive()));
+ TParse parse;
+ parse.Set(drive.Name(), &KFileLocationSpec, NULL);
+
+ r=TheFs.MkDir(parse.DriveAndPath());
+ test(r==KErrNone||r==KErrAlreadyExists);
+ r=TheFs.SetSessionPath(parse.DriveAndPath());
+ test(r==KErrNone);
+ }
+
+LOCAL_C void setupCleanup()
+//
+// Initialise the cleanup stack.
+//
+ {
+ TheTrapCleanup=CTrapCleanup::New();
+ test(TheTrapCleanup!=NULL);
+ TRAPD(r,\
+ {\
+ for (TInt i=KTestCleanupStack;i>0;i--)\
+ CleanupStack::PushL((TAny*)0);\
+ CleanupStack::Pop(KTestCleanupStack);\
+ });
+ test(r==KErrNone);
+ }
+
+LOCAL_C void DeleteDataFile(const TDesC& aFullName)
+ {
+ RFs fsSession;
+ TInt err = fsSession.Connect();
+ if(err == KErrNone)
+ {
+ TEntry entry;
+ if(fsSession.Entry(aFullName, entry) == KErrNone)
+ {
+ RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
+ err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
+ if(err != KErrNone)
+ {
+ RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
+ }
+ err = fsSession.Delete(aFullName);
+ if(err != KErrNone)
+ {
+ RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
+ }
+ }
+ fsSession.Close();
+ }
+ else
+ {
+ RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
+ }
+ }
+
+GLDEF_C TInt E32Main()
+//
+// Test permanent file store.
+//
+ {
+ test.Title();
+ setupTestDirectory();
+ setupCleanup();
+ __UHEAP_MARK;
+//
+ test.Start(_L("Test compaction"));
+ TRAPD(r,testCompactL());
+ test(r==KErrNone);
+
+ //deletion of data files must be before call to .End() - DEF047652
+ TDriveUnit drive(static_cast<TUint>(RFs::GetSystemDrive()));
+ TParse parse;
+ parse.Set(drive.Name(), &KFileLocationSpec, NULL);
+ ::DeleteDataFile(parse.FullName());
+
+ test.End();
+//
+ __UHEAP_MARKEND;
+
+ delete TheTrapCleanup;
+ TheFs.Close();
+ test.Close();
+ return 0;
+ }
+