os/persistentdata/persistentstorage/store/TFILE/t_storfcomp.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 1998-2010 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include <s32file.h>
    17 #include <e32test.h>
    18 #include <e32math.h>
    19 
    20 const TInt KTestCleanupStack=0x20;
    21 
    22 // This is a path specification and should not be used as is
    23 _LIT(KFileLocationSpec, "Z:\\STOR-TST\\T_COMPACT.DAT");
    24 _LIT(KResultsFile,"RESULTS_%d.TXT");
    25 
    26 static TFileName TheResultsFile;
    27 static TBool AllTests=ETrue;
    28 static TInt Iterations=10000;
    29 static TInt DataVolume=160;
    30 static TInt CompactFrequency=40;
    31 
    32 class RLog
    33 	{
    34 public:
    35 	void CreateL(RFs& aFs, const TDesC& aFile);
    36 	void Close();
    37 	void PrintL(const char* aFormat, ...);
    38 private:
    39 	RFileWriteStream iLog;
    40 	};
    41 
    42 LOCAL_D CTrapCleanup* TheTrapCleanup;
    43 LOCAL_D RTest test(_L("t_storfcomp"));
    44 LOCAL_D RFs TheFs;
    45 LOCAL_D RLog TheLog;
    46 
    47 class StopWatch
    48 	{
    49 public:
    50 	void Start();
    51 	TUint Stop();
    52 private:
    53 	TTime iTime;
    54 	};
    55 
    56 void StopWatch::Start()
    57 	{
    58 	iTime.UniversalTime();
    59 	}
    60 
    61 TUint StopWatch::Stop()
    62 	{
    63  	TTime t;
    64  	t.UniversalTime();
    65  	TInt64 i = ((t.MicroSecondsFrom(iTime).Int64()) + 500)/1000;
    66  	return I64LOW(i);
    67 	}
    68 
    69 StopWatch CompactSW;
    70 
    71 void CompactL(CFileStore& aStore)
    72 //
    73 // Compact the file and record the stats
    74 //
    75 	{
    76 	aStore.CommitL();
    77 	TInt startSize;
    78 	User::LeaveIfError(aStore.File().Size(startSize));
    79 	CompactSW.Start();
    80 	TInt wasted = aStore.CompactL();
    81 	aStore.CommitL();
    82 	TUint ms = CompactSW.Stop();
    83 	TInt endSize;
    84 	User::LeaveIfError(aStore.File().Size(endSize));
    85 	TheLog.PrintL("%d\t%d\t%d\t%u\n",startSize, endSize, wasted, ms);
    86 	}
    87 
    88 void ReclaimCompactL(CFileStore& aStore, TInt aTrigger)
    89 //
    90 // Reclaim the file and compact on trigger
    91 //
    92 	{
    93 	TInt startSize;
    94 	User::LeaveIfError(aStore.File().Size(startSize));
    95 	CompactSW.Start();
    96 	TBool compacted = EFalse;
    97 	if (aTrigger == 0 || aStore.ReclaimL() * 100 > startSize * aTrigger)
    98 		{
    99 		aStore.CompactL();
   100 		aStore.CommitL();
   101 		compacted = ETrue;
   102 		}
   103 	TUint ms = CompactSW.Stop();
   104 	TInt endSize;
   105 	User::LeaveIfError(aStore.File().Size(endSize));
   106 	TheLog.PrintL("%s\t%d\t%u\n",compacted ? "Compact" : "Reclaim", startSize - endSize, ms);
   107 	}
   108 
   109 void WriteBytesL(RWriteStream& s, TInt aCount)
   110 	{
   111 	const TInt KBufSize = 512;
   112 	TUint8 buf[KBufSize];
   113 	while (aCount > KBufSize)
   114 		{
   115 		s.WriteL(buf, KBufSize);
   116 		aCount -= KBufSize;
   117 		}
   118 	s.WriteL(buf, aCount);
   119 	s.CommitL();
   120 	}
   121 
   122 TStreamId CreateStreamL(CStreamStore& aStore, TInt aSize)
   123 	{
   124 	RStoreWriteStream s;
   125 	TStreamId id = s.CreateLC(aStore);
   126 	WriteBytesL(s, aSize);
   127 	CleanupStack::PopAndDestroy(&s);
   128 	return id;
   129 	}
   130 
   131 TInt Random(TUint aLimit)
   132 	{
   133 	return Math::Random() % aLimit;
   134 	}
   135 
   136 
   137 TBool AllocationFailure()
   138 	{
   139 	User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,1);
   140 	TAny* cell = User::Alloc(4);
   141 	User::Free(cell);
   142 	User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1);
   143 	return cell == 0;
   144 	}
   145 /**
   146 @SYMTestCaseID          SYSLIB-STORE-CT-1144
   147 @SYMTestCaseDesc	    Tests for CFileStore::CompactL() function
   148 @SYMTestPriority 	    High
   149 @SYMTestActions  	    Attempt for compaction process on the store.Tests for KErrNone flag
   150 @SYMTestExpectedResults Test must not fail
   151 @SYMREQ                 REQ0000
   152 */
   153 void BasicCompactionTestsL()
   154 	{
   155 	const TInt KTestCount = 40;
   156 	const TInt KTestSize = 50;
   157 	TParsePtrC parse(KFileLocationSpec);
   158 //
   159 	test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1144 "));
   160 	CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
   161 
   162 	store->SetTypeL(store->Layout());
   163 //
   164 	TheLog.PrintL("Compact uncommitted empty store\n");
   165 	TRAPD(r, store->CompactL());
   166 	test (r == KErrNone);
   167 //
   168 	TheLog.PrintL("Compact committed empty store\n");
   169 	r = store->Commit();
   170 	test (r == KErrNone);
   171 	TRAP(r, store->CompactL());
   172 	test (r == KErrNone);
   173 //
   174 	TheLog.PrintL("Compact empty store with full TOC\n");
   175 	TStreamId streams[KTestCount];
   176 	TInt i;
   177 	for (i = 0; i < KTestCount; ++i)
   178 		streams[i] = CreateStreamL(*store, KTestSize);
   179 	store->CommitL();
   180 	for (i = 0; i < KTestCount; ++i)
   181 		store->DeleteL(streams[i]);
   182 	store->CommitL();
   183 	TRAP(r, store->CompactL());
   184 	test (r == KErrNone);
   185 //
   186 	TheLog.PrintL("Compact empty store with delta TOC\n");
   187 	streams[0] = CreateStreamL(*store, KTestSize);
   188 	store->CommitL();
   189 	store->DeleteL(streams[0]);
   190 	store->CommitL();
   191 	TRAP(r, store->CompactL());
   192 	test (r == KErrNone);
   193 //
   194 	CleanupStack::PopAndDestroy(store);
   195 	}
   196 
   197 /**
   198 @SYMTestCaseID          SYSLIB-STORE-CT-1145
   199 @SYMTestCaseDesc	    Testing fallback compaction algorithm
   200 @SYMTestPriority 	    High
   201 @SYMTestActions  	    Tests for compaction process on the store,
   202                         If we have allocation failure we can test that the fallback algorithm is in place
   203 @SYMTestExpectedResults Test must not fail
   204 @SYMREQ                 REQ0000
   205 */
   206 void CompactionAlgorithmTestL()
   207 	{
   208 	const TInt KTestCount = 200;
   209 	const TInt KTestSize = 50;
   210 	const TInt KBeyondSuccess = 2;
   211 	TInt stopat = KMaxTInt;
   212 	TInt bestTime = -1;
   213 	TParsePtrC parse(KFileLocationSpec);
   214 //
   215 	test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1145 "));
   216 	TheLog.PrintL("Testing fallback compaction algorithm\n");
   217 //
   218 	for (TInt failat = 1; failat <= stopat; ++failat)
   219 		{
   220 		TheLog.PrintL("Fail allocation #%d: ", failat);
   221 //
   222 		// prepare the store. Leave a single hole at the beginning
   223 		CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
   224 		store->SetTypeL(store->Layout());
   225 		TStreamId first = CreateStreamL(*store, KTestSize);
   226 		for (TInt i = 1; i < KTestCount; ++i)
   227 			CreateStreamL(*store, KTestSize);
   228 		store->CommitL();
   229 		store->DeleteL(first);
   230 		store->CommitL();
   231 		//
   232 		User::__DbgSetAllocFail(RHeap::EUser,RHeap::EFailNext,failat);
   233 		CompactSW.Start();
   234 		TRAPD(r, store->CompactL();	store->CommitL();)
   235 		TInt ms = CompactSW.Stop();
   236 		User::__DbgSetAllocFail(RHeap::EUser,RHeap::ENone,1);
   237 		//
   238 		if (r != KErrNone)
   239 			{
   240 			test (bestTime == -1);
   241 			TheLog.PrintL("compaction failed\n");
   242 			}
   243 		else
   244 			{
   245 			TheLog.PrintL("compaction succeeded in %u ms\n", ms);
   246 			if (bestTime == -1)
   247 				{
   248 				bestTime = ms;
   249 				stopat = failat + KBeyondSuccess;		//stop after a few passes after sucsss
   250 				}
   251 			else if (ms < bestTime)
   252 				{
   253 				if (ms < bestTime/2)
   254 					stopat = failat + KBeyondSuccess;	// new algorithm has kicked in
   255 				bestTime = ms;
   256 				}
   257 			}
   258 
   259 		CleanupStack::PopAndDestroy(store);
   260 		}
   261 	}
   262 
   263 
   264 struct TTracker
   265 	{
   266 	TStreamId iId;
   267 	TInt iSize;
   268 	};
   269 
   270 CFileStore* InitialiseStoreLC(RArray<TTracker>& aStreams, TInt aDataVolume, TInt aAverageSize)
   271 	{
   272 	TParsePtrC parse(KFileLocationSpec);	
   273 	CFileStore* store = CPermanentFileStore::ReplaceLC(TheFs, parse.NameAndExt(), EFileRead|EFileWrite);
   274 	store->SetTypeL(store->Layout());
   275 	for (TInt count = Max(1,(aDataVolume + aAverageSize/2)/aAverageSize); count > 0; --count)
   276 		{
   277 		TInt size;
   278 		if (count == 1)
   279 			size = aDataVolume;
   280 		else
   281 			{
   282 			size = aDataVolume / count;
   283 			TInt spread = Min(aAverageSize, size);
   284 			size += Random(spread) - spread/2;
   285 			}
   286 		TTracker e;
   287 		e.iSize = size;
   288 		e.iId = CreateStreamL(*store, size);
   289 		User::LeaveIfError(aStreams.Append(e));
   290 		aDataVolume -= size;
   291 		}
   292 	store->CommitL();
   293 	return store;
   294 	}
   295 /**
   296 @SYMTestCaseID          SYSLIB-STORE-CT-1146
   297 @SYMTestCaseDesc	    Tests for compaction on store
   298 @SYMTestPriority 	    High
   299 @SYMTestActions  	    Tests for CFileStore::CompactL() function
   300 @SYMTestExpectedResults Test must not fail
   301 @SYMREQ                 REQ0000
   302 */
   303 void CompactionTestL(const TInt aIterations, const TInt aDataVolume, const TInt aAverageSize, const TInt aCommitFrequency, const TInt aCompactionFrequency)
   304 	{
   305 	test.Next(_L(" @SYMTestCaseID:SYSLIB-STORE-CT-1146 "));
   306 	TheLog.PrintL("CompactionTest : \n");
   307 	TheLog.PrintL("\tIterations:\t%d\n",aIterations);
   308 	TheLog.PrintL("\tDataVolume:\t%d\n",aDataVolume);
   309 	TheLog.PrintL("\tAverageSize:\t%d\n",aAverageSize);
   310 	TheLog.PrintL("\tCommitFrequency:\t%d\n",aCommitFrequency);
   311 	TheLog.PrintL("\tCompactionFrequency:\t%d\n\n",aCompactionFrequency);
   312 
   313 	RArray<TTracker> streams(8);
   314 	CleanupClosePushL(streams);
   315 	CFileStore* store = InitialiseStoreLC(streams, aDataVolume, aAverageSize);
   316 	const TInt maxCount = streams.Count() + (streams.Count() / 5);
   317 	TInt size = aDataVolume;
   318 	const TInt span = aDataVolume / 10;		// +- 10%
   319 	for (TInt i = aIterations ; --i >= 0; )
   320 		{
   321 		TInt from = Random(streams.Count());
   322 		TInt to = Random(maxCount);
   323 		TInt tfr = to == from ? 0 : Random(streams[from].iSize + 1);
   324 		TInt adj = Random(span - 1) - span/2;
   325 		if (size > aDataVolume + span/2)
   326 			adj -= size - (aDataVolume + span/2);
   327 		else if (size < aDataVolume - span/2)
   328 			adj += (aDataVolume - span/2) - size;
   329 		if (adj < 0)
   330 			{
   331 			if (adj < tfr - streams[from].iSize)
   332 				adj = tfr - streams[from].iSize;
   333 			}
   334 		TInt fromSize = streams[from].iSize - tfr;
   335 		if (adj < 0 || to == from)
   336 			fromSize += adj;
   337 		if (fromSize > 0)
   338 			{
   339 			RStoreWriteStream s;
   340 			s.ReplaceLC(*store, streams[from].iId);
   341 			WriteBytesL(s, fromSize);
   342 			CleanupStack::PopAndDestroy(&s);
   343 			streams[from].iSize = fromSize;
   344 			}
   345 
   346 		if (to != from)
   347 			{
   348 			if (to < streams.Count())
   349 				{
   350 				TInt toSize = streams[to].iSize + tfr;
   351 				if (adj > 0)
   352 					toSize += adj;
   353 				RStoreWriteStream s;
   354 				s.ReplaceLC(*store, streams[to].iId);
   355 				WriteBytesL(s, toSize);
   356 				CleanupStack::PopAndDestroy(&s);
   357 				streams[to].iSize = toSize;
   358 				}
   359 			else
   360 				{
   361 				TInt toSize = tfr;
   362 				if (adj > 0)
   363 					toSize += adj;
   364 				TTracker e;
   365 				e.iSize = toSize;
   366 				e.iId = CreateStreamL(*store, toSize);
   367 				User::LeaveIfError(streams.Append(e));
   368 				}
   369 			}
   370 		if (fromSize <= 0)
   371 			{
   372 			store->DeleteL(streams[from].iId);
   373 			streams.Remove(from);
   374 			}
   375 		size += adj;
   376 //
   377 		if (Random(aCommitFrequency) == 0)
   378 			{
   379 			store->CommitL();
   380 			if (aCompactionFrequency <= 0)
   381 				ReclaimCompactL(*store, -aCompactionFrequency);
   382 			}
   383 		if (aCompactionFrequency > 0 && Random(aCompactionFrequency) == 0)
   384 			CompactL(*store);
   385 		}
   386 	CleanupStack::PopAndDestroy(store);
   387 	CleanupStack::PopAndDestroy(&streams);
   388 	TheLog.PrintL("\nCompactionTestEnd\n\n");
   389 	}
   390 
   391 void RLog::CreateL(RFs& aFs, const TDesC& aFile)
   392 	{
   393 #ifdef __WINS__
   394 	User::LeaveIfError(iLog.Replace(aFs, aFile, EFileWrite));
   395 #endif
   396 	}
   397 
   398 void RLog::Close()
   399 	{
   400 	iLog.Close();
   401 	}
   402 
   403 void RLog::PrintL(const char* aFormat, ...)
   404 	{
   405 	VA_LIST list;
   406 	VA_START(list,aFormat);
   407 	TBuf8<256> b;
   408 	b.FormatList(_L8(aFormat),list);
   409 	VA_END(list);
   410 #ifdef __WINS__
   411 	iLog.WriteL(b);
   412 #endif
   413 	TBuf<256> b16;
   414 	b16.Copy(b);
   415 	test.Printf(_L("%S"),&b16);
   416 	}
   417 
   418 void GetOpt()
   419 	{
   420 	TBuf<256> options;
   421     User::CommandLine(options);
   422 	if (options.Length() > 0)
   423 		{
   424 		AllTests = EFalse;
   425 		TLex lex(options);
   426 		lex.SkipSpace();
   427 		if (lex.Val(DataVolume) == KErrNone)
   428 			{
   429 			lex.SkipSpace();
   430 			if (lex.Val(Iterations) == KErrNone)
   431 				{
   432 				lex.SkipSpace();
   433 				lex.Val(CompactFrequency);
   434 				}
   435 			}
   436 		}
   437 	TheResultsFile.Format(KResultsFile,DataVolume);
   438 	}
   439 
   440 void testCompactL()
   441 	{
   442 	GetOpt();
   443 	TheLog.CreateL(TheFs, TheResultsFile);
   444 	if (AllTests)
   445 		{
   446 		BasicCompactionTestsL();
   447 		if (AllocationFailure())
   448 			CompactionAlgorithmTestL();
   449 		}
   450 	CompactionTestL(Iterations, DataVolume<<10, 300, 5, CompactFrequency);
   451 	TheLog.Close();
   452 	}
   453 
   454 LOCAL_C void setupTestDirectory()
   455 //
   456 // Prepare the test directory.
   457 //
   458     {
   459 	TInt r=TheFs.Connect();
   460 	test(r==KErrNone);
   461 //
   462 	TDriveUnit drive(static_cast<TUint>(RFs::GetSystemDrive()));	
   463 	TParse parse;
   464 	parse.Set(drive.Name(), &KFileLocationSpec, NULL);
   465 	
   466 	r=TheFs.MkDir(parse.DriveAndPath());
   467 	test(r==KErrNone||r==KErrAlreadyExists);
   468 	r=TheFs.SetSessionPath(parse.DriveAndPath());
   469 	test(r==KErrNone);
   470 	}
   471 
   472 LOCAL_C void setupCleanup()
   473 //
   474 // Initialise the cleanup stack.
   475 //
   476     {
   477 	TheTrapCleanup=CTrapCleanup::New();
   478 	test(TheTrapCleanup!=NULL);
   479 	TRAPD(r,\
   480 		{\
   481 		for (TInt i=KTestCleanupStack;i>0;i--)\
   482 			CleanupStack::PushL((TAny*)0);\
   483 		CleanupStack::Pop(KTestCleanupStack);\
   484 		});
   485 	test(r==KErrNone);
   486 	}
   487 
   488 LOCAL_C void DeleteDataFile(const TDesC& aFullName)
   489 	{
   490 	RFs fsSession;
   491 	TInt err = fsSession.Connect();
   492 	if(err == KErrNone)
   493 		{
   494 		TEntry entry;
   495 		if(fsSession.Entry(aFullName, entry) == KErrNone)
   496 			{
   497 			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
   498 			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
   499 			if(err != KErrNone)
   500 				{
   501 				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
   502 				}
   503 			err = fsSession.Delete(aFullName);
   504 			if(err != KErrNone)
   505 				{
   506 				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
   507 				}
   508 			}
   509 		fsSession.Close();
   510 		}
   511 	else
   512 		{
   513 		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
   514 		}
   515 	}
   516 
   517 class CTestStreamStore : public CStreamStore
   518 	{
   519 public:
   520 	static CTestStreamStore* NewL();
   521 	virtual ~CTestStreamStore();
   522 
   523 private:
   524 	CTestStreamStore();
   525 	virtual MStreamBuf* DoReadL(TStreamId anId) const;
   526 	virtual MStreamBuf* DoCreateL(TStreamId& anId);
   527 	
   528 	};
   529 
   530 CTestStreamStore* CTestStreamStore::NewL()
   531 	{
   532 	return new (ELeave) CTestStreamStore; 
   533 	}
   534 
   535 CTestStreamStore::~CTestStreamStore()
   536 	{
   537 	}
   538 
   539 CTestStreamStore::CTestStreamStore()
   540 	{
   541 	}
   542 
   543 MStreamBuf* CTestStreamStore::DoReadL(TStreamId) const
   544 	{
   545 	return NULL;
   546 	}
   547 
   548 MStreamBuf* CTestStreamStore::DoCreateL(TStreamId&)
   549 	{
   550 	return NULL;
   551 	}
   552 
   553 /**
   554 @SYMTestCaseID          PDS-STORE-CT-4063
   555 @SYMTestCaseDesc        CStreamStore tests.
   556 @SYMTestActions         CStreamStore provides couple of virtual methods in its private section:
   557                         DoExtendL(), DoDeleteL(), DoReplaceL(), DoReclaimL().
   558                         They are no-ops and are expected to be overriden in the class derived from
   559                         CStreamStore. Their implementations leave with KErrNotsupported. 
   560                         The test uses a class derived from CStreamStore, which class does not implement
   561                         virtuals mentioned above. These virtuals should leave with KErrNotSupported when called.
   562 @SYMTestPriority        High
   563 @SYMTestExpectedResults Test must not fail
   564 */
   565 void TestStreamStoreVirtualsL()
   566 	{
   567 	CTestStreamStore* store = CTestStreamStore::NewL();
   568 	TRAPD(err, store->CommitL());
   569 	test(err == KErrNone);
   570 	TRAP(err, store->RevertL());
   571 	test(err == KErrNotSupported);
   572 	TRAP(err, store->ReclaimL());
   573 	test(err == KErrNotSupported);
   574 	TRAP(err, store->CompactL());
   575 	test(err == KErrNotSupported);
   576 	TRAP(err, store->DeleteL(TStreamId(1)));
   577 	test(err == KErrNotSupported);
   578 	delete store;
   579 	}
   580 
   581 //
   582 // Test permanent file store.
   583 //
   584 GLDEF_C TInt E32Main()
   585     {
   586 	test.Title();
   587 	setupTestDirectory();
   588 	setupCleanup();
   589 	__UHEAP_MARK;
   590 //
   591 	test.Start(_L("Test compaction"));
   592 	TRAPD(r,testCompactL());
   593 	test(r==KErrNone);
   594 	test.Next(_L("@SYMTestCaseID:PDS-STORE-CT-4063: Test CStreamStore virtuals"));
   595 	TRAP(r, TestStreamStoreVirtualsL())
   596 	test(r==KErrNone);
   597 	
   598 	//deletion of data files must be before call to .End() - DEF047652
   599 	TDriveUnit drive(static_cast<TUint>(RFs::GetSystemDrive()));	
   600 	TParse parse;
   601 	parse.Set(drive.Name(), &KFileLocationSpec, NULL);
   602 	::DeleteDataFile(parse.FullName());
   603 
   604 	test.End();
   605 //
   606 	__UHEAP_MARKEND;
   607 
   608 	delete TheTrapCleanup;
   609 	TheFs.Close();
   610 	test.Close();
   611 	return 0;
   612     }
   613