sl@0: // Copyright (c) 1998-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: const TInt KTestCleanupStack=0x20; sl@0: sl@0: // This is a path specification and should not be used as is sl@0: _LIT(KFileLocationSpec, "Z:\\STOR-TST\\T_COMPACT.DAT"); sl@0: _LIT(KResultsFile,"RESULTS_%d.TXT"); sl@0: sl@0: static TFileName TheResultsFile; sl@0: static TBool AllTests=ETrue; sl@0: static TInt Iterations=10000; sl@0: static TInt DataVolume=160; sl@0: static TInt CompactFrequency=40; sl@0: sl@0: class RLog sl@0: { sl@0: public: sl@0: void CreateL(RFs& aFs, const TDesC& aFile); sl@0: void Close(); sl@0: void PrintL(const char* aFormat, ...); sl@0: private: sl@0: RFileWriteStream iLog; sl@0: }; sl@0: sl@0: LOCAL_D CTrapCleanup* TheTrapCleanup; sl@0: LOCAL_D RTest test(_L("t_storfcomp")); sl@0: LOCAL_D RFs TheFs; sl@0: LOCAL_D RLog TheLog; sl@0: sl@0: class StopWatch sl@0: { sl@0: public: sl@0: void Start(); sl@0: TUint Stop(); sl@0: private: sl@0: TTime iTime; sl@0: }; sl@0: sl@0: void StopWatch::Start() sl@0: { sl@0: iTime.UniversalTime(); sl@0: } sl@0: sl@0: TUint StopWatch::Stop() sl@0: { sl@0: TTime t; sl@0: t.UniversalTime(); sl@0: TInt64 i = ((t.MicroSecondsFrom(iTime).Int64()) + 500)/1000; sl@0: return I64LOW(i); sl@0: } sl@0: sl@0: StopWatch CompactSW; sl@0: sl@0: void CompactL(CFileStore& aStore) sl@0: // sl@0: // Compact the file and record the stats sl@0: // sl@0: { sl@0: aStore.CommitL(); sl@0: TInt startSize; sl@0: User::LeaveIfError(aStore.File().Size(startSize)); sl@0: CompactSW.Start(); sl@0: TInt wasted = aStore.CompactL(); sl@0: aStore.CommitL(); sl@0: TUint ms = CompactSW.Stop(); sl@0: TInt endSize; sl@0: User::LeaveIfError(aStore.File().Size(endSize)); sl@0: TheLog.PrintL("%d\t%d\t%d\t%u\n",startSize, endSize, wasted, ms); sl@0: } sl@0: sl@0: void ReclaimCompactL(CFileStore& aStore, TInt aTrigger) sl@0: // sl@0: // Reclaim the file and compact on trigger sl@0: // sl@0: { sl@0: TInt startSize; sl@0: User::LeaveIfError(aStore.File().Size(startSize)); sl@0: CompactSW.Start(); sl@0: TBool compacted = EFalse; sl@0: if (aTrigger == 0 || aStore.ReclaimL() * 100 > startSize * aTrigger) sl@0: { sl@0: aStore.CompactL(); sl@0: aStore.CommitL(); sl@0: compacted = ETrue; sl@0: } sl@0: TUint ms = CompactSW.Stop(); sl@0: TInt endSize; sl@0: User::LeaveIfError(aStore.File().Size(endSize)); sl@0: TheLog.PrintL("%s\t%d\t%u\n",compacted ? "Compact" : "Reclaim", startSize - endSize, ms); sl@0: } sl@0: sl@0: void WriteBytesL(RWriteStream& s, TInt aCount) sl@0: { sl@0: const TInt KBufSize = 512; sl@0: TUint8 buf[KBufSize]; sl@0: while (aCount > KBufSize) sl@0: { sl@0: s.WriteL(buf, KBufSize); sl@0: aCount -= KBufSize; sl@0: } sl@0: s.WriteL(buf, aCount); sl@0: s.CommitL(); sl@0: } sl@0: sl@0: TStreamId CreateStreamL(CStreamStore& aStore, TInt aSize) sl@0: { sl@0: RStoreWriteStream s; sl@0: TStreamId id = s.CreateLC(aStore); sl@0: WriteBytesL(s, aSize); sl@0: CleanupStack::PopAndDestroy(&s); sl@0: return id; sl@0: } sl@0: sl@0: TInt Random(TUint aLimit) sl@0: { sl@0: return Math::Random() % aLimit; sl@0: } sl@0: sl@0: sl@0: TBool AllocationFailure() sl@0: { sl@0: User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,1); sl@0: TAny* cell = User::Alloc(4); sl@0: User::Free(cell); sl@0: User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1); sl@0: return cell == 0; sl@0: } sl@0: /** sl@0: @SYMTestCaseID SYSLIB-STORE-CT-1144 sl@0: @SYMTestCaseDesc Tests for CFileStore::CompactL() function sl@0: @SYMTestPriority High sl@0: @SYMTestActions Attempt for compaction process on the store.Tests for KErrNone flag sl@0: @SYMTestExpectedResults Test must not fail sl@0: @SYMREQ REQ0000 sl@0: */ sl@0: void BasicCompactionTestsL() sl@0: { sl@0: const TInt KTestCount = 40; sl@0: const TInt KTestSize = 50; sl@0: TParsePtrC parse(KFileLocationSpec); sl@0: // sl@0: test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1144 ")); sl@0: CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite); sl@0: sl@0: store->SetTypeL(store->Layout()); sl@0: // sl@0: TheLog.PrintL("Compact uncommitted empty store\n"); sl@0: TRAPD(r, store->CompactL()); sl@0: test (r == KErrNone); sl@0: // sl@0: TheLog.PrintL("Compact committed empty store\n"); sl@0: r = store->Commit(); sl@0: test (r == KErrNone); sl@0: TRAP(r, store->CompactL()); sl@0: test (r == KErrNone); sl@0: // sl@0: TheLog.PrintL("Compact empty store with full TOC\n"); sl@0: TStreamId streams[KTestCount]; sl@0: TInt i; sl@0: for (i = 0; i < KTestCount; ++i) sl@0: streams[i] = CreateStreamL(*store, KTestSize); sl@0: store->CommitL(); sl@0: for (i = 0; i < KTestCount; ++i) sl@0: store->DeleteL(streams[i]); sl@0: store->CommitL(); sl@0: TRAP(r, store->CompactL()); sl@0: test (r == KErrNone); sl@0: // sl@0: TheLog.PrintL("Compact empty store with delta TOC\n"); sl@0: streams[0] = CreateStreamL(*store, KTestSize); sl@0: store->CommitL(); sl@0: store->DeleteL(streams[0]); sl@0: store->CommitL(); sl@0: TRAP(r, store->CompactL()); sl@0: test (r == KErrNone); sl@0: // sl@0: CleanupStack::PopAndDestroy(store); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-STORE-CT-1145 sl@0: @SYMTestCaseDesc Testing fallback compaction algorithm sl@0: @SYMTestPriority High sl@0: @SYMTestActions Tests for compaction process on the store, sl@0: If we have allocation failure we can test that the fallback algorithm is in place sl@0: @SYMTestExpectedResults Test must not fail sl@0: @SYMREQ REQ0000 sl@0: */ sl@0: void CompactionAlgorithmTestL() sl@0: { sl@0: const TInt KTestCount = 200; sl@0: const TInt KTestSize = 50; sl@0: const TInt KBeyondSuccess = 2; sl@0: TInt stopat = KMaxTInt; sl@0: TInt bestTime = -1; sl@0: TParsePtrC parse(KFileLocationSpec); sl@0: // sl@0: test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1145 ")); sl@0: TheLog.PrintL("Testing fallback compaction algorithm\n"); sl@0: // sl@0: for (TInt failat = 1; failat <= stopat; ++failat) sl@0: { sl@0: TheLog.PrintL("Fail allocation #%d: ", failat); sl@0: // sl@0: // prepare the store. Leave a single hole at the beginning sl@0: CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite); sl@0: store->SetTypeL(store->Layout()); sl@0: TStreamId first = CreateStreamL(*store, KTestSize); sl@0: for (TInt i = 1; i < KTestCount; ++i) sl@0: CreateStreamL(*store, KTestSize); sl@0: store->CommitL(); sl@0: store->DeleteL(first); sl@0: store->CommitL(); sl@0: // sl@0: User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,failat); sl@0: CompactSW.Start(); sl@0: TRAPD(r, store->CompactL(); store->CommitL();) sl@0: TInt ms = CompactSW.Stop(); sl@0: User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1); sl@0: // sl@0: if (r != KErrNone) sl@0: { sl@0: test (bestTime == -1); sl@0: TheLog.PrintL("compaction failed\n"); sl@0: } sl@0: else sl@0: { sl@0: TheLog.PrintL("compaction succeeded in %u ms\n", ms); sl@0: if (bestTime == -1) sl@0: { sl@0: bestTime = ms; sl@0: stopat = failat + KBeyondSuccess; //stop after a few passes after sucsss sl@0: } sl@0: else if (ms < bestTime) sl@0: { sl@0: if (ms < bestTime/2) sl@0: stopat = failat + KBeyondSuccess; // new algorithm has kicked in sl@0: bestTime = ms; sl@0: } sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(store); sl@0: } sl@0: } sl@0: sl@0: sl@0: struct TTracker sl@0: { sl@0: TStreamId iId; sl@0: TInt iSize; sl@0: }; sl@0: sl@0: CFileStore* InitialiseStoreLC(RArray& aStreams, TInt aDataVolume, TInt aAverageSize) sl@0: { sl@0: TParsePtrC parse(KFileLocationSpec); sl@0: CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite); sl@0: store->SetTypeL(store->Layout()); sl@0: for (TInt count = Max(1,(aDataVolume + aAverageSize/2)/aAverageSize); count > 0; --count) sl@0: { sl@0: TInt size; sl@0: if (count == 1) sl@0: size = aDataVolume; sl@0: else sl@0: { sl@0: size = aDataVolume / count; sl@0: TInt spread = Min(aAverageSize, size); sl@0: size += Random(spread) - spread/2; sl@0: } sl@0: TTracker e; sl@0: e.iSize = size; sl@0: e.iId = CreateStreamL(*store, size); sl@0: User::LeaveIfError(aStreams.Append(e)); sl@0: aDataVolume -= size; sl@0: } sl@0: store->CommitL(); sl@0: return store; sl@0: } sl@0: /** sl@0: @SYMTestCaseID SYSLIB-STORE-CT-1146 sl@0: @SYMTestCaseDesc Tests for compaction on store sl@0: @SYMTestPriority High sl@0: @SYMTestActions Tests for CFileStore::CompactL() function sl@0: @SYMTestExpectedResults Test must not fail sl@0: @SYMREQ REQ0000 sl@0: */ sl@0: void CompactionTestL(const TInt aIterations, const TInt aDataVolume, const TInt aAverageSize, const TInt aCommitFrequency, const TInt aCompactionFrequency) sl@0: { sl@0: test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1146 ")); sl@0: TheLog.PrintL("CompactionTest : \n"); sl@0: TheLog.PrintL("\tIterations:\t%d\n",aIterations); sl@0: TheLog.PrintL("\tDataVolume:\t%d\n",aDataVolume); sl@0: TheLog.PrintL("\tAverageSize:\t%d\n",aAverageSize); sl@0: TheLog.PrintL("\tCommitFrequency:\t%d\n",aCommitFrequency); sl@0: TheLog.PrintL("\tCompactionFrequency:\t%d\n\n",aCompactionFrequency); sl@0: sl@0: RArray streams(8); sl@0: CleanupClosePushL(streams); sl@0: CFileStore* store = InitialiseStoreLC(streams, aDataVolume, aAverageSize); sl@0: const TInt maxCount = streams.Count() + (streams.Count() / 5); sl@0: TInt size = aDataVolume; sl@0: const TInt span = aDataVolume / 10; // +- 10% sl@0: for (TInt i = aIterations ; --i >= 0; ) sl@0: { sl@0: TInt from = Random(streams.Count()); sl@0: TInt to = Random(maxCount); sl@0: TInt tfr = to == from ? 0 : Random(streams[from].iSize + 1); sl@0: TInt adj = Random(span - 1) - span/2; sl@0: if (size > aDataVolume + span/2) sl@0: adj -= size - (aDataVolume + span/2); sl@0: else if (size < aDataVolume - span/2) sl@0: adj += (aDataVolume - span/2) - size; sl@0: if (adj < 0) sl@0: { sl@0: if (adj < tfr - streams[from].iSize) sl@0: adj = tfr - streams[from].iSize; sl@0: } sl@0: TInt fromSize = streams[from].iSize - tfr; sl@0: if (adj < 0 || to == from) sl@0: fromSize += adj; sl@0: if (fromSize > 0) sl@0: { sl@0: RStoreWriteStream s; sl@0: s.ReplaceLC(*store, streams[from].iId); sl@0: WriteBytesL(s, fromSize); sl@0: CleanupStack::PopAndDestroy(&s); sl@0: streams[from].iSize = fromSize; sl@0: } sl@0: sl@0: if (to != from) sl@0: { sl@0: if (to < streams.Count()) sl@0: { sl@0: TInt toSize = streams[to].iSize + tfr; sl@0: if (adj > 0) sl@0: toSize += adj; sl@0: RStoreWriteStream s; sl@0: s.ReplaceLC(*store, streams[to].iId); sl@0: WriteBytesL(s, toSize); sl@0: CleanupStack::PopAndDestroy(&s); sl@0: streams[to].iSize = toSize; sl@0: } sl@0: else sl@0: { sl@0: TInt toSize = tfr; sl@0: if (adj > 0) sl@0: toSize += adj; sl@0: TTracker e; sl@0: e.iSize = toSize; sl@0: e.iId = CreateStreamL(*store, toSize); sl@0: User::LeaveIfError(streams.Append(e)); sl@0: } sl@0: } sl@0: if (fromSize <= 0) sl@0: { sl@0: store->DeleteL(streams[from].iId); sl@0: streams.Remove(from); sl@0: } sl@0: size += adj; sl@0: // sl@0: if (Random(aCommitFrequency) == 0) sl@0: { sl@0: store->CommitL(); sl@0: if (aCompactionFrequency <= 0) sl@0: ReclaimCompactL(*store, -aCompactionFrequency); sl@0: } sl@0: if (aCompactionFrequency > 0 && Random(aCompactionFrequency) == 0) sl@0: CompactL(*store); sl@0: } sl@0: CleanupStack::PopAndDestroy(store); sl@0: CleanupStack::PopAndDestroy(&streams); sl@0: TheLog.PrintL("\nCompactionTestEnd\n\n"); sl@0: } sl@0: sl@0: void RLog::CreateL(RFs& aFs, const TDesC& aFile) sl@0: { sl@0: #ifdef __WINS__ sl@0: User::LeaveIfError(iLog.Replace(aFs, aFile, EFileWrite)); sl@0: #endif sl@0: } sl@0: sl@0: void RLog::Close() sl@0: { sl@0: iLog.Close(); sl@0: } sl@0: sl@0: void RLog::PrintL(const char* aFormat, ...) sl@0: { sl@0: VA_LIST list; sl@0: VA_START(list,aFormat); sl@0: TBuf8<256> b; sl@0: b.FormatList(_L8(aFormat),list); sl@0: VA_END(list); sl@0: #ifdef __WINS__ sl@0: iLog.WriteL(b); sl@0: #endif sl@0: TBuf<256> b16; sl@0: b16.Copy(b); sl@0: test.Printf(_L("%S"),&b16); sl@0: } sl@0: sl@0: void GetOpt() sl@0: { sl@0: TBuf<256> options; sl@0: User::CommandLine(options); sl@0: if (options.Length() > 0) sl@0: { sl@0: AllTests = EFalse; sl@0: TLex lex(options); sl@0: lex.SkipSpace(); sl@0: if (lex.Val(DataVolume) == KErrNone) sl@0: { sl@0: lex.SkipSpace(); sl@0: if (lex.Val(Iterations) == KErrNone) sl@0: { sl@0: lex.SkipSpace(); sl@0: lex.Val(CompactFrequency); sl@0: } sl@0: } sl@0: } sl@0: TheResultsFile.Format(KResultsFile,DataVolume); sl@0: } sl@0: sl@0: void testCompactL() sl@0: { sl@0: GetOpt(); sl@0: TheLog.CreateL(TheFs, TheResultsFile); sl@0: if (AllTests) sl@0: { sl@0: BasicCompactionTestsL(); sl@0: if (AllocationFailure()) sl@0: CompactionAlgorithmTestL(); sl@0: } sl@0: CompactionTestL(Iterations, DataVolume<<10, 300, 5, CompactFrequency); sl@0: TheLog.Close(); sl@0: } sl@0: sl@0: LOCAL_C void setupTestDirectory() sl@0: // sl@0: // Prepare the test directory. sl@0: // sl@0: { sl@0: TInt r=TheFs.Connect(); sl@0: test(r==KErrNone); sl@0: // sl@0: TDriveUnit drive(static_cast(RFs::GetSystemDrive())); sl@0: TParse parse; sl@0: parse.Set(drive.Name(), &KFileLocationSpec, NULL); sl@0: sl@0: r=TheFs.MkDir(parse.DriveAndPath()); sl@0: test(r==KErrNone||r==KErrAlreadyExists); sl@0: r=TheFs.SetSessionPath(parse.DriveAndPath()); sl@0: test(r==KErrNone); sl@0: } sl@0: sl@0: LOCAL_C void setupCleanup() sl@0: // sl@0: // Initialise the cleanup stack. sl@0: // sl@0: { sl@0: TheTrapCleanup=CTrapCleanup::New(); sl@0: test(TheTrapCleanup!=NULL); sl@0: TRAPD(r,\ sl@0: {\ sl@0: for (TInt i=KTestCleanupStack;i>0;i--)\ sl@0: CleanupStack::PushL((TAny*)0);\ sl@0: CleanupStack::Pop(KTestCleanupStack);\ sl@0: }); sl@0: test(r==KErrNone); sl@0: } sl@0: sl@0: LOCAL_C void DeleteDataFile(const TDesC& aFullName) sl@0: { sl@0: RFs fsSession; sl@0: TInt err = fsSession.Connect(); sl@0: if(err == KErrNone) sl@0: { sl@0: TEntry entry; sl@0: if(fsSession.Entry(aFullName, entry) == KErrNone) sl@0: { sl@0: RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName); sl@0: err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly); sl@0: if(err != KErrNone) sl@0: { sl@0: RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName); sl@0: } sl@0: err = fsSession.Delete(aFullName); sl@0: if(err != KErrNone) sl@0: { sl@0: RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName); sl@0: } sl@0: } sl@0: fsSession.Close(); sl@0: } sl@0: else sl@0: { sl@0: RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); sl@0: } sl@0: } sl@0: sl@0: class CTestStreamStore : public CStreamStore sl@0: { sl@0: public: sl@0: static CTestStreamStore* NewL(); sl@0: virtual ~CTestStreamStore(); sl@0: sl@0: private: sl@0: CTestStreamStore(); sl@0: virtual MStreamBuf* DoReadL(TStreamId anId) const; sl@0: virtual MStreamBuf* DoCreateL(TStreamId& anId); sl@0: sl@0: }; sl@0: sl@0: CTestStreamStore* CTestStreamStore::NewL() sl@0: { sl@0: return new (ELeave) CTestStreamStore; sl@0: } sl@0: sl@0: CTestStreamStore::~CTestStreamStore() sl@0: { sl@0: } sl@0: sl@0: CTestStreamStore::CTestStreamStore() sl@0: { sl@0: } sl@0: sl@0: MStreamBuf* CTestStreamStore::DoReadL(TStreamId) const sl@0: { sl@0: return NULL; sl@0: } sl@0: sl@0: MStreamBuf* CTestStreamStore::DoCreateL(TStreamId&) sl@0: { sl@0: return NULL; sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID PDS-STORE-CT-4063 sl@0: @SYMTestCaseDesc CStreamStore tests. sl@0: @SYMTestActions CStreamStore provides couple of virtual methods in its private section: sl@0: DoExtendL(), DoDeleteL(), DoReplaceL(), DoReclaimL(). sl@0: They are no-ops and are expected to be overriden in the class derived from sl@0: CStreamStore. Their implementations leave with KErrNotsupported. sl@0: The test uses a class derived from CStreamStore, which class does not implement sl@0: virtuals mentioned above. These virtuals should leave with KErrNotSupported when called. sl@0: @SYMTestPriority High sl@0: @SYMTestExpectedResults Test must not fail sl@0: */ sl@0: void TestStreamStoreVirtualsL() sl@0: { sl@0: CTestStreamStore* store = CTestStreamStore::NewL(); sl@0: TRAPD(err, store->CommitL()); sl@0: test(err == KErrNone); sl@0: TRAP(err, store->RevertL()); sl@0: test(err == KErrNotSupported); sl@0: TRAP(err, store->ReclaimL()); sl@0: test(err == KErrNotSupported); sl@0: TRAP(err, store->CompactL()); sl@0: test(err == KErrNotSupported); sl@0: TRAP(err, store->DeleteL(TStreamId(1))); sl@0: test(err == KErrNotSupported); sl@0: delete store; sl@0: } sl@0: sl@0: // sl@0: // Test permanent file store. sl@0: // sl@0: GLDEF_C TInt E32Main() sl@0: { sl@0: test.Title(); sl@0: setupTestDirectory(); sl@0: setupCleanup(); sl@0: __UHEAP_MARK; sl@0: // sl@0: test.Start(_L("Test compaction")); sl@0: TRAPD(r,testCompactL()); sl@0: test(r==KErrNone); sl@0: test.Next(_L("@SYMTestCaseID:PDS-STORE-CT-4063: Test CStreamStore virtuals")); sl@0: TRAP(r, TestStreamStoreVirtualsL()) sl@0: test(r==KErrNone); sl@0: sl@0: //deletion of data files must be before call to .End() - DEF047652 sl@0: TDriveUnit drive(static_cast(RFs::GetSystemDrive())); sl@0: TParse parse; sl@0: parse.Set(drive.Name(), &KFileLocationSpec, NULL); sl@0: ::DeleteDataFile(parse.FullName()); sl@0: sl@0: test.End(); sl@0: // sl@0: __UHEAP_MARKEND; sl@0: sl@0: delete TheTrapCleanup; sl@0: TheFs.Close(); sl@0: test.Close(); sl@0: return 0; sl@0: } sl@0: