kerneltest/f32test/server/t_wcache.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// f32test\server\t_wcache.cpp
// This file contains a test for the Write Caching functionality of the File Server
// 
//

/**
 @file
 @internalTechnology 
*/

#define __E32TEST_EXTENSION__
#include <f32file.h>
#include <e32test.h>
#include <e32svr.h>
#include <f32dbg.h>
#include "t_server.h"
#include <e32twin.h>

const TInt KTotalCacheSize = 32 * 1024 * 1024;
const TInt KDefaultCacheSize = (128 + 12) * 1024; 	// This size is the default configuration size
const TInt KFilesNeededToFillCache = (KTotalCacheSize / KDefaultCacheSize) + 2;
const TInt KMinSize = 254; // Boundary minim limit
const TInt KMaxSize = 257; // Boundary max limit



//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID      PBASE-T_WCACHE-0271
//! @SYMTestType        CIT
//! @SYMPREQ            PREQ914
//! @SYMTestCaseDesc    This test case is exercising the Write Caching functionality added to 
//!						the File Server. There are negative and positive tests. 
//! @SYMTestActions     0   setup the environment to execute the tests
//!						1	TestBoundaries writes/reads around the write cache boundaries to
//!							the behaviour of the cache in some common cases. 
//!						2 	TestNegative ensures the integrity of data in the cache gets 
//!							preserved under error conditions
//!						3	TestIntegrity is trying to make sure integrity of the data is preserved
//!						4	TestFillCache fills the cache and then executes TestBoundaries. 
//!						5 	TestFillCacheNegative fills the cache with uncommitted data 
//!
//! @SYMTestExpectedResults finishes if the read cache behaves as expected, panics otherwise
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------


////////////////////////////////////////////////////////////
// Template functions encapsulating ControlIo magic
//
template <class C>
TInt controlIo(RFs &fs, TInt drv, TInt fkn, C &c)
{
    TPtr8 ptrC((TUint8 *)&c, sizeof(C), sizeof(C));

    TInt r = fs.ControlIo(drv, fkn, ptrC);

    return r;
}

RTest test(_L("T_WCACHE"));

RFs gTheFs;
TInt gDrive;
TFileName gSessionPath;
TChar gDriveToTest;	
TThreadId gMainThreadId;
TInt gManual = 0;

HBufC8* gBuf = NULL;
TPtr8 gBufReadPtr(NULL, 0);	
HBufC8* gBufSec = NULL;
TPtr8 gBufWritePtr(NULL, 0);	

const TInt KOneK = 1024;
const TInt KOneMeg = KOneK * 1024;
const TInt KBlockSize = KOneK;
const TInt KWaitRequestsTableSize = 256;
const TInt KMs = 1000; 

TInt gSecondFileSize = 0; 
TInt gFirstFileSize = 0;

TInt64 gMediaSize = 0;

TTimeIntervalMicroSeconds gTimeTakenBigFile(0);
TBuf16<25> gFirstFile;
TBuf16<25> gSecondFile;
TBuf16<25> gCurrentFile;

TInt gNextFile = 0;
TTime gTime1;
TTime gTime2;


// Concurrent Threads
RThread gThread1;
RSemaphore gClient;
const TInt KHeapSize = 0x4000;
const TInt KMaxHeapSize = 0x100000;


/** Formats the drive 

	@param aDrive 	Drive to be formatted
	@param aFormatMode Mode for the format operation
*/
void Formatting(TInt aDrive, TUint aFormatMode )
	{

	test.Next(_L("Format"));
	TBuf<4> driveBuf = _L("?:\\");
	driveBuf[0]=(TText)(aDrive+'A');
	RFormat format;
	TInt count;
	TInt r = format.Open(gTheFs,driveBuf,aFormatMode,count);
	test_KErrNone(r);
	while(count)
		{
		TInt r = format.Next(count);
		test_KErrNone(r);
		}
	format.Close();
	
	}

/** Verifies the content of a buffer 
	This function returns KErrNone when all the letters are consecutive in the aBuffer, KErrCorrupt otherwise

	@param aBuffer  Buffer to be verified
	
	@return KErrNone if all the letters are the same, KErrCorrupt otherwise
*/
TInt VerifyBuffer(TDes8& aBuffer)
	{
	TChar c = aBuffer[0];
	
	for(TInt i = 1; i < aBuffer.Length(); i++)
		{
		if(i%32 != 0) 
			{
			if(c != (TChar)(aBuffer[i] - 1)) 
				return KErrCorrupt;
			}
		else
			{
			if(aBuffer[i] != aBuffer[0])		
				return KErrCorrupt;
			}
		c = aBuffer[i];
		}
		
	return KErrNone;
	}

/**  Fills a buffer with character aC, aC+1, aC+2, ..., aC+0x20, aC, etc 

	@param aBuffer  Buffer to be filled, output
	@param aLength  Length to be filled
	@param aC		Character to be used to fill the buffer
*/
void FillBuffer(TDes8& aBuffer, TInt aLength, TChar aC)
	{
	test (aBuffer.MaxLength() >= aLength);
	for(TInt i = 0; i < aLength; i++)
		{
		aBuffer.Append((i%32) + aC);
		}
	}

/**  Returns true if fat filesystem present on aDrive

	@param aFsSession 	Session on the File Server
	@param aDrive 		Drive to be looked at
	
	@return ETrue if FAT, EFalse otherwise
*/
TBool IsFSFAT(RFs &aFsSession,TInt aDrive)
	{
	TFileName f;
	TInt r = aFsSession.FileSystemName(f,aDrive);
	
	if (r != KErrNone)
		{
		test.Printf(_L("Unable to get file system name\n"));
		return EFalse;
		}
		
	return (f.CompareF(_L("Fat")) == 0);
	}

/** Generates a file name of the form FFFFF*<aPos>.TXT (aLong.3)

	@param aBuffer The filename will be returned here
	@param aLong   Defines the longitude of the file name 
	@param aPos	   Defines the number that will be attached to the filename
*/
void FileNameGen(TDes16& aBuffer, TInt aLong, TInt aPos) 
	{
	TInt padding;
	TInt i = 0;
	TBuf16<10> tempbuf;
	
	_LIT(KNumber,"%d");
	tempbuf.Format(KNumber,aPos);
	padding = aLong-tempbuf.Size()/2;
	aBuffer = _L("");
	while(i < padding)
	{
		aBuffer.Append('F');
		i++;
	}
	aBuffer.Append(tempbuf);

	_LIT(KExtension1, ".TXT");
	aBuffer.Append(KExtension1);
	}

/**  Delete content of directory

	@param aDir	Target directory
	
	@return Error returned if any, otherwise KErrNone
*/
TInt DeleteAllL(TDes16& aDir) 
	{
	TBuf16<100> dir;
	CFileMan* fMan = CFileMan::NewL(gTheFs);
	TInt r=0;
	
	dir = aDir;
	dir.Append(_L("F*.*"));
	r = fMan->Delete(dir);	

	delete fMan;
	return r;
	}

/**  Waits for all the TRequestStatus in status[] to complete

	@param status 	Array of TRequestStatus 
	@param aSize  Length to be filled
*/
void WaitForAll(TRequestStatus* status, TInt aSize) 
	{
	TInt i = 0;

	RTest test(_L("T_WCACHE"));

	while(i < aSize)
		{
		User::WaitForRequest(status[i]);
		if (status[i] != KErrNone)
			{
			test.Printf(_L("status[%d] == %d\n"), i, status[i].Int());
			test(EFalse);
			}
		i++;
		}

	test.Close();
	}

/**  Reads the parameters from the comand line 
	 and updates the appropriate variables
*/
void parseCommandLine() 
	{
	TBuf<0x100> cmd;
	User::CommandLine(cmd);
	TLex lex(cmd);
	TPtrC token = lex.NextToken();
	TInt r=0;

	if(token.Length() != 0)		
		{
		gDriveToTest = token[0];
		gDriveToTest.UpperCase();
		}
	else						
		{
		gDriveToTest = 'C';
		}	
		
	r = gTheFs.CharToDrive(gDriveToTest,gDrive);
	test_KErrNone(r);
	
	if(!lex.Eos()) 
		{
		token.Set(lex.NextToken());	
		if(token.Length() != 0)		
			{
				TChar c = token[0];
				c.UpperCase();
				
				gManual = (c == 'M'); 
			}
		}
	
	gSessionPath = _L("?:\\F32-TST\\");
	gSessionPath[0] = (TUint16) gDriveToTest;
	test.Printf(_L("\nCLP=%C\n"),(TInt)gDriveToTest);
	}



/**  Writes a file synchronously in blocks of aBlockSize size

	@param aFs			RFs object
	@param aFile		File 
	@param aFileName	File name
	@param aSize		Size of the file in bytes
	@param aBlockSize	Size of the blocks to be used in bytes
	@param aBuf			Buffer to be used to write
	@param aMode		Mode in which the file is meant to be opened
						
	@return Returns KErrNone if everything ok, otherwise it panics 
*/
TInt WriteFile(RFs& aFs, RFile& aFile, TDes16& aFileName, TInt aSize, TInt aBlockSize, TDes8& aBuf, TInt aMode) 
	{
	RTest test(_L("T_WCACHE"));

	TInt r = 0;

	test(aBlockSize > 0);	


	r = aFile.Replace(aFs,aFileName,aMode);
	test_KErrNone(r);

	TInt j = 0;
	while(j < aSize)
		{
			r = aFile.Write(aBuf, aBlockSize);
			test_KErrNone(r);
			j += aBlockSize;
		}					

	test.Close();
	return KErrNone;	
	}

/** Write a file that fits in the cache, and dies without proper cleaning

*/
LOCAL_C TInt WriteFileT(TAny* )
	{
	RTest test(_L("T_WCACHE"));
	RFs fs;
	RFile file;
	TInt r = fs.Connect();
	test_KErrNone(r);

	r = fs.SetSessionPath(gSessionPath);
	test_KErrNone(r);
	
	r = WriteFile(fs, file, gFirstFile, KMinSize * KOneK, KBlockSize, gBufWritePtr, EFileShareAny|EFileWrite|EFileWriteBuffered);
	test_KErrNone(r);
	
	gClient.Signal();


	FOREVER
		{
			// waiting for the kill
		}		
	}

/**  Read File in blocks of size aBlockSize

	@param aFs			RFs object
	@param aFile		File 
	@param aFileName	File name
	@param aSize		Expected file size
	@param aBlockSize	Size of the blocks to be used in bytes
	@param aMode		Mode in which the file is meant to be opened
						
	@return Returns KErrNone if everything ok, otherwise it panics 
*/
TInt ReadFile(RFs& aFs, RFile& aFile, TDes16& aFileName, TInt aSize, TInt aBlockSize, TInt aMode) 
	{
	RTest test(_L("T_WCACHE"));

	TInt r = 0, size = 0;
	
	test(aBlockSize>0);				// Block size must be greater than 0

	r = aFile.Open(aFs,aFileName,aMode);
	test_KErrNone(r);

	// Make sure the size of the file is the right one at this stage
	r = aFile.Size(size);
	test.Printf(_L("size of the file: %d \n"), size/KOneK); 
	test(size == aSize);
	
	TInt j = 0;
	while(j < size) 
	{
		r = aFile.Read(gBufReadPtr, aBlockSize);
		test_KErrNone(r);
		j += aBlockSize;
	}					

	test.Close();
	return KErrNone;	
	}

/** Write a file asynchronously in blocks of aBlockSize size

	@param aFs			RFs object
	@param aFileWrite	RFile object, needs to exist beyond the scope of this function
	@param aFile		File name
	@param aSize		Size of the file in bytes
	@param aMode		Specifies the mode in which the file should be openned
	@param aStatus		TRequestStatus array for all the requests
*/
void WriteFileAsync(RFs& aFs, RFile& aFileWrite, TDes16& aFile, TInt aSize, TInt aMode, TRequestStatus aStatus[]) 
	{
	RTest test(_L("T_WCACHE"));

	TInt r = 0;
	
	r = aFileWrite.Replace(aFs,aFile,aMode);
	test_KErrNone(r);

	TInt j = 0, i = 0;
	while(j < aSize)
		{
		aFileWrite.Write(gBufWritePtr, KBlockSize, aStatus[i]);
		r = aStatus[i].Int();
		if (r != KErrNone && r != KRequestPending)
			{
			test.Printf(_L("Write %d returned %d\n"), i, r);
			test(0);
			}
		i++;

		j += KBlockSize;	
		}					
	test.Close();
	}

/**  Read a file asynchronously in blocks of aBlockSize size

	@param aFs			RFs object
	@param aFileRead	RFile object, needs to exist beyond the scope of this function
	@param aFile		File name
	@param aFileSize		Size of the file in bytes
	@param aBlockSize	Size of the blocks to be used in bytes
	@param aStatus		TRequestStatus array for all the requests
	@param aMode		Specifies the mode in which the file should be openned
	
	@return KErrNone
*/
TInt ReadFileAsync(RFs& aFs,RFile& aFileRead, TDes16& aFile, TInt aFileSize, TInt aBlockSize,TRequestStatus aStatus[],  TInt aMode) 
	{
	RTest test(_L("T_WCACHE"));

	TInt r = 0;
	TInt size = 0;
	
	test(aBlockSize > 0);			
	

	r = aFileRead.Open(aFs,aFile, aMode); 
	test_KErrNone(r);
	
	r = aFileRead.Size(size);
	test_KErrNone(r);

	test.Printf(_L("size of the file %d\n"), size/KOneK);
	test(size == aFileSize);
	
	TInt j = 0, i = 0;
	while(j < size) 
		{
		aFileRead.Read(gBufReadPtr, aBlockSize, aStatus[i]);
		r = aStatus[i].Int();
		if (r != KErrNone && r != KRequestPending)
			{
			test.Printf(_L("Read %d returned %d\n"), i, r);
			test(0);
			}
		i++;

		j += aBlockSize;
		}					

	test.Close();
	return KErrNone;	
	}

/** Measure the time taken for this file to be written synchronously

	@param aFile 	 	File object
	@param aFileName 	File Name
	@param aSize 		Size in kilobytes
	@param aBlockSize 	Size of the block
	@param aMode 		Mode in which the file is going to be opened
	
	@return time taken to perform the operation in uS
*/
TInt WriteTestFile(RFile& aFile, TDes16& aFileName, TInt aSize, TInt aBlockSize, TInt aMode) 
	{
	RTest test(_L("T_WCACHE"));

	TTime startTime;
	TTime endTime;
	TInt r = 0;
	
	startTime.HomeTime();
	
	r = WriteFile(gTheFs,aFile, aFileName , aSize * KOneK, aBlockSize, gBufWritePtr, aMode);
	test_KErrNone(r);
	
	endTime.HomeTime();
	
	gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
	
	test.Close();
	return I64LOW(gTimeTakenBigFile.Int64());
	}

/** Measure the time taken for this file to be read synchronously

	@param aFile 	 	File object
	@param aFileName 	File Name
	@param aSize 		Size in kilobytes
	@param aBlockSize 	Size of the block
	@param aMode 		Mode in which the file is going to be opened

	@return time taken to perform the operation in uS

*/
TInt ReadTestFile(RFile& aFile, TDes16& aFileName, TInt aSize, TInt aBlockSize, TInt aMode) 
	{
	TTime startTime;
	TTime endTime;
	
	startTime.HomeTime();
	ReadFile(gTheFs,aFile, aFileName, aSize * KOneK, aBlockSize, aMode);
	endTime.HomeTime();
	
	gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
	
	return I64LOW(gTimeTakenBigFile.Int64()) ;
	}

/** Read asynchronously the test file from the disc

	@param aFile 	 	File object
	@param aFileName 	File Name
	@param aSize 		Size in kilobytes
	@param aBlockSize 	Size of the block
	@param aMode 		Mode in which the file is going to be opened

	@return time taken to perform the operation in uS
*/
TInt ReadAsyncTestFile(RFile& file, TDes16& aFile, TInt aSize, TInt aBlockSize, TInt aMode) 
	{
	TTime startTime;
	TTime endTime;
	TRequestStatus status[KWaitRequestsTableSize];
	
	startTime.HomeTime();
	
	ReadFileAsync(gTheFs, file, aFile, aSize * KOneK, aBlockSize, status, aMode);
	WaitForAll(status,  (aSize * KOneK)/KBlockSize);
	
	endTime.HomeTime();
	
	gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
	
	return I64LOW(gTimeTakenBigFile.Int64());
	}

/** Read asynchronously the test file from the disc

	@param aFile 	 	File object
	@param aFileName 	File Name
	@param aSize 		Size in kilobytes
	@param aMode 		Mode in which the file is going to be opened

	@return time taken to perform the operation in uS
*/
TInt WriteAsyncTestFile(RFile& aFile, TDes16& aFileName, TInt aSize, TInt aMode) 
	{
	TTime startTime;
	TTime endTime;
	TRequestStatus status[KWaitRequestsTableSize];
	
	startTime.HomeTime();
	
	WriteFileAsync(gTheFs, aFile, aFileName, aSize * KOneK, aMode, status );
	WaitForAll(status, (aSize * KOneK)/KBlockSize);
	
	endTime.HomeTime();
	
	gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
	
	return I64LOW(gTimeTakenBigFile.Int64());
	}

/**  Test Boundaries

	This function is testing the behaviour on the boundaries of the write cache size
*/
void TestBoundaries()
	{
	TInt r = 0;
	TInt time = 0;
	TInt rtime = 0;
	TInt tcreate = 0;
	RFile fileWriter;
	RFile fileWriter2;
	RFile fileReader;

	test.Start(_L("Test Boundaries"));
	
	// Test boundaries from 254K to 256K, synchronous operations 
	TInt i = KMinSize;
	
	
	test.Printf(_L("\n\n\n"));
	
	while(i < KMaxSize) 
		{
		test.Printf(_L("\nSync: Write from 1 K to %d K \n"), i); 

		tcreate = WriteTestFile(fileWriter, gSecondFile, i, KBlockSize, EFileShareAny|EFileWrite|EFileWriteDirectIO);
		test.Printf(_L("Time to write %d K without caching: %d mS\n"), i, tcreate/KMs);	
		fileWriter.Close();

		time =  WriteTestFile(fileWriter2, gFirstFile, i, KBlockSize, EFileShareAny|EFileWrite|EFileWriteBuffered);
		test.Printf(_L("Time to write %d K WITH caching: %d mS\n"), i, time/KMs);

		rtime = ReadTestFile(fileReader, gFirstFile, i, KBlockSize, EFileShareAny|EFileRead|EFileReadBuffered);
		test.Printf(_L("Time to read %d K from the cache: %d mS\n"), i, rtime/KMs);


		fileReader.Close();	
		fileWriter2.Close();
		
		#if !defined(__WINS__)
			test((tcreate > time) || (tcreate > rtime)); 
		#endif

		r = gTheFs.Delete(gFirstFile);
		test_KErrNone(r);
		r = gTheFs.Delete(gSecondFile);
		test_KErrNone(r);
		
		i++;
		}

	test.Printf(_L("\n\n\n"));
	
	// Test boundaries from 254K to 256K, asynchronous operations 
	i = KMinSize;
	
	while(i < KMaxSize) 
		{
		test.Printf(_L("\nAsync: Write from 1 K to %d K \n"), i); 

		tcreate = WriteAsyncTestFile(fileWriter, gSecondFile, i, EFileShareAny|EFileWrite|EFileWriteDirectIO);
		test.Printf(_L("Time to write %d K without caching: %d mS\n"), i, tcreate/KMs);	
		fileWriter.Close();

		time =  WriteAsyncTestFile(fileWriter2, gFirstFile, i,EFileShareAny|EFileWrite|EFileWriteBuffered);
		test.Printf(_L("Time to write %d K WITH caching: %d mS\n"), i, time/KMs);


		rtime = ReadAsyncTestFile(fileReader, gFirstFile, i, KBlockSize, EFileShareAny|EFileRead|EFileReadBuffered);
		test.Printf(_L("Time to read %d K from the cache: %d mS\n"), i, rtime/KMs);

		fileReader.Close();	
		fileWriter2.Close();
		
		#if !defined(__WINS__)
			test((tcreate > time) || (tcreate > rtime));  
		#endif

		r = gTheFs.Delete(gFirstFile);
		test_KErrNone(r);
		r = gTheFs.Delete(gSecondFile);
		test_KErrNone(r);
		
		i++;
		}

	test.End();
	}

/**  Test negative cases

*/
void TestNegative()
	{
	TInt r = 0;
	RFile file;
	TInt size =0;
		
	TBuf<20> buf = _L("Write File");


	test.Start(_L("Test Negative"));

	test.Next(_L("Kill a simple operation"));
	
	r = gThread1.Create(buf,WriteFileT,KDefaultStackSize,KHeapSize,KMaxHeapSize,NULL);
	test_KErrNone(r);

	gThread1.Resume();
	gClient.Wait();

	gThread1.Kill(KErrGeneral);

	r = file.Open(gTheFs,gFirstFile,EFileShareAny|EFileRead|EFileReadBuffered|EFileReadAheadOff);
	test_KErrNone(r);

	r = file.Size(size);
	test_KErrNone(r);
	
	test.Printf(_L("The size of the file is %d KB\n\n"), size/KOneK);
	test(size == (KMinSize * KOneK));
	
	file.Close();

	test.End();
	}


/** Read the file verifying content

	@param aFile file name to verify 
	
	@return returns the time that took to do the verification in mS, fails if the file is not corrupted/modified
*/
TInt ReadTestFileVerif(TDes16& aFile)
	{
	TTime startTime;
	TTime endTime;
	TInt r = 0;
	TInt size = 0;
	RFile fileRead;
	TInt corrupt = 0;
	TBool isFat=IsFSFAT(gTheFs,gDrive);
	
	startTime.HomeTime();
	
	r = fileRead.Open(gTheFs,aFile,EFileShareAny|EFileRead|EFileReadBuffered|EFileReadAheadOff);
	test_KErrNone(r);

	r = fileRead.Size(size);
	test_KErrNone(r);
	
	TInt j = 0;
	
	while(j < size) 
		{
			r = fileRead.Read(gBufReadPtr, KBlockSize);
			if(isFat)
			{
				test_KErrNone(r);
			}
			else 
			{
			if(r == KErrCorrupt) 
				corrupt++;
			}
			
			j += KBlockSize;
			r = VerifyBuffer(gBufReadPtr);
			if(r == KErrCorrupt) 
				corrupt++;
		}					

	fileRead.Close();
	
	test(corrupt>0); // Ensure the cache returns the changed content 
	
	endTime.HomeTime();
	
	gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
	
	return I64LOW(gTimeTakenBigFile.Int64()) / KMs;
	}

/**  Modifies the second file

*/
LOCAL_C TInt CorruptSecondFile()
	{
	TInt r = 0;
	RFile fileWrite;
	HBufC8* dummy = NULL;
	TPtr8 dummyPtr(NULL, 0);	

	TRAPD(res,dummy = HBufC8::NewL(4));
	test(res == KErrNone && dummy != NULL);
		
	dummyPtr.Set(dummy->Des());
	FillBuffer(dummyPtr, 4, '1');

	r = fileWrite.Open(gTheFs,gSecondFile,EFileShareAny|EFileWrite|EFileWriteBuffered);
	if(r != KErrNone) 
		return r;
	TInt pos = 30;
	r = fileWrite.Seek(ESeekStart,pos);
	test_KErrNone(r);
	
	r = fileWrite.Write(dummyPtr);
	if(r != KErrNone) 
		return r;
	
	fileWrite.Close();

	delete dummy;

	return KErrNone;
	}


/** Integrity testing

*/
LOCAL_C void TestIntegrity()
	{
	TInt r = 0;
	TInt time;
	TInt tcreate = 0;
	RFile file;
	
	// Modify file in some position 
	test.Printf(_L("Overwrite partially a file\n"));
	
	test.Printf(_L("\nSync: Write from 1 K to %d K \n"), 255); 

	tcreate = WriteTestFile(file, gSecondFile, 255, KBlockSize, EFileShareAny|EFileWrite|EFileWriteBuffered);
	test.Printf(_L("Time to write %d K with caching: %d mS\n"), 255, tcreate/KMs);	
	file.Close();

	test.Printf(_L("Mess the content that is still in the cache\n"));
	CorruptSecondFile(); 
	
	time = ReadTestFileVerif(gSecondFile);	
	test.Printf(_L("Time taken to verify: %d\n"),time);
	
	test.Printf(_L("Integrity verified\n"));

	r = DeleteAllL(gSessionPath);
	test_KErrNone(r);
	}

/**  Creates the files to fill the cache with dirty data
	
	@return KErrNone
*/
TInt CreateFilesThread(TAny *)
	{
	TInt i = 0;
	TInt r = 0;
	TBuf16<50> directory;
	
	TBuf16<50> path;
	TBuf16<50> buffer(50); 	
	RFile file[KFilesNeededToFillCache];
	
	RTest test(_L("T_WCACHE2"));
	RFs fs;
	
	fs.Connect();
	
	directory = gSessionPath;
	
	test.Printf(_L("Creating %d files for filling the cache (size %d)\n"), KFilesNeededToFillCache, KDefaultCacheSize);

	// create a big buffer to speed things up
	HBufC8* bigBuf = NULL;
	TInt KBigBifferSize = 32 * KOneK;
	
	TRAPD(res,bigBuf = HBufC8::NewL(KBigBifferSize));
	test(res == KErrNone && bigBuf != NULL);
		
	TPtr8 bigBufWritePtr(NULL, 0);	
	bigBufWritePtr.Set(bigBuf->Des());
	FillBuffer(bigBufWritePtr, KBigBifferSize, 'A');
	
	i = 0;		
	while(i < KFilesNeededToFillCache) 
		{
		if (i % 10 == 0)
			test.Printf(_L("Creating file %d of %d...\r"), i, KFilesNeededToFillCache);
		FileNameGen(buffer, 8, i+3) ;
		path = directory;
		path.Append(buffer);

		r = file[i].Create(fs,path,EFileShareAny|EFileWrite|EFileWriteBuffered);
		if(r == KErrAlreadyExists) 
			r = file[i].Open(fs,path,EFileShareAny|EFileWrite|EFileWriteBuffered);
		test_KErrNone(r);
		TInt j = 0;
	
		while(j < KDefaultCacheSize)
			{
			bigBufWritePtr.SetLength(Min(KBigBifferSize, KDefaultCacheSize - j));
			
			r = file[i].Write(bigBufWritePtr);
			test_KErrNone(r);
			j += bigBufWritePtr.Length();
			}					

		// Not closing the files is done on purpose, as part of the test

		i++;
		}
	test.Printf(_L("\nFiles created\n"));
	delete bigBuf;
	
	gClient.Signal();
	
	return KErrNone;
	}


/**  Creates the files to fill the read cache 

	@param aFiles 	 Number of files needed to fill the cache
	@param aFileSize The file size
*/
void CreateFiles(TInt aFiles, TInt aFileSize)
	{
	TInt i = 0;
	TInt r = 0;
	RFile file;
	TBuf16<50> directory;
	
	TBuf16<50> path;
	TBuf16<50> buffer(50); 	
	
	directory = gSessionPath;
	
	test.Printf(_L("Creating %d files for filling the cache (size %d)\n"), aFiles, aFileSize);

	// create a big buffer to speed things up
	HBufC8* bigBuf = NULL;
	const TInt KBigBifferSize = 32 * 1024;
	TRAPD(res,bigBuf = HBufC8::NewL(KBigBifferSize));
	test(res == KErrNone && bigBuf != NULL);
		
	TPtr8 bigBufWritePtr(NULL, 0);	
	bigBufWritePtr.Set(bigBuf->Des());
	FillBuffer(bigBufWritePtr, KBigBifferSize, 'A');
	
	i = 0;		
	while(i < aFiles) 
		{
		if (i % 10 == 0)
			test.Printf(_L("Creating file %d of %d...\r"), i, aFiles);
		FileNameGen(buffer, 8, i+3) ;
		path = directory;
		path.Append(buffer);

		// delete file first to ensure it's contents are not in the cache (file may be be on the closed file queue)
		r = gTheFs.Delete(path);
		test_Value(r, r == KErrNone || r == KErrNotFound);

		r = file.Create(gTheFs,path,EFileShareAny|EFileWrite|EFileWriteDirectIO);
		if(r == KErrAlreadyExists) 
			r = file.Open(gTheFs,path,EFileShareAny|EFileWrite|EFileWriteDirectIO);
		test_KErrNone(r);
		TInt j = 0;
		while(j < aFileSize)
			{
			bigBufWritePtr.SetLength(Min(KBigBifferSize, aFileSize - j));
			r = file.Write(bigBufWritePtr);
			test_KErrNone(r);
			j += bigBufWritePtr.Length();
			}					

		file.Close();
		i++;
		}
	test.Printf(_L("\nFiles created\n"));
	delete bigBuf;
	}

/**  Fills the read cache 

	@param aFile	 Array of files needed to fill the cache
	@param aFiles 	 Number of files needed to fill the cache
	@param aFileSize The file size
*/
void FillCache(RFile aFile[KFilesNeededToFillCache], TInt aFiles, TInt aFileSize)
	{
	TInt i = 0;
	TInt r = 0;
	TBuf16<50> directory;
	
	TBuf16<50> path;
	TBuf16<50> buffer(50); 	
	HBufC8* buf = NULL;
	TPtr8 bufPtr(NULL, 0);	
	
	TRAPD(res,buf = HBufC8::NewL(2));
	test(res == KErrNone && buf != NULL);
	bufPtr.Set(buf->Des());
	
	directory = gSessionPath;
	
	i = 0;		
	while(i < aFiles) 
	{
		FileNameGen(buffer, 8, i+3) ;
		path = directory;
		path.Append(buffer);
		r = aFile[i].Open(gTheFs,path,EFileShareAny|EFileRead|EFileReadBuffered|EFileReadAheadOff);
		test_KErrNone(r);
		
		TInt j = 0;
		while(j < aFileSize)
			{
				r = aFile[i].Read(j,bufPtr);
				test_KErrNone(r);
				j += 4*KOneK;
			}					

		i++;
	}
	
	delete buf;
	test.Printf(_L("Cache filled\n"));
	}

/** Fills the default cache

*/
void TestFillCache()
	{	
	TInt nFiles = KFilesNeededToFillCache;
	TInt fSize = KDefaultCacheSize;
	RFile file[KFilesNeededToFillCache];
	TInt r = 0;
	
	if(gMediaSize> ((fSize * nFiles)+gSecondFileSize+gFirstFileSize))
		{
		test.Start(_L("Creating files for filling the cache\n"));
		CreateFiles(nFiles,fSize);
#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		TFileCacheStats startPageCacheStats;
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list at beginning=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list at beginning=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
#endif
		FillCache(file,nFiles,fSize); 

#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list at end=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list at end=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
#endif

	TestBoundaries();
	
#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list after the boundary testing=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list after the boundary testing=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
#endif

		TInt i = 0;
		while( i < KFilesNeededToFillCache )
			{
			file[i++].Close();
			}
			
		r = DeleteAllL(gSessionPath);
		test_KErrNone(r);

		test.End();
		}
	else 
		test.Printf(_L("Skipping the fill of the cache due to lack of space in the current drive\n"));
	}

/** Fills the cache and generate error situations

*/
void TestFillCacheNegative()
	{	
	TInt nFiles = KFilesNeededToFillCache;
	TInt r = 0;

	if(gMediaSize> ((KDefaultCacheSize * nFiles)+gSecondFileSize+gFirstFileSize))
		{
#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		TFileCacheStats startPageCacheStats;
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list at beginning=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list at beginning=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
#endif
	test.Start(_L("Creating files for filling the cache, with uncommitted data\n"));
	
	TBuf<20> buf = _L("FillCache");
	
	 r = gThread1.Create(buf,CreateFilesThread,KDefaultStackSize,KHeapSize,KMaxHeapSize,NULL);
	test_KErrNone(r);

	gThread1.Resume();
	gClient.Wait();

#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list at end=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list at end=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
#endif

	TestBoundaries();
	
#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
		// get number of items on Page Cache
		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list after the boundary testing=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list after the boundary testing=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);
		
		User::After(180000);

		r = controlIo(gTheFs,gDrive, KControlIoFileCacheStats, startPageCacheStats);
		test_Value(r, r == KErrNone || r == KErrNotSupported);
		test.Printf(_L("Number of page cache lines on free list after the boundary testing=%d\n"),startPageCacheStats.iFreeCount);
		test.Printf(_L("Number of page cache lines on used list after the boundary testing=%d\n"),startPageCacheStats.iUsedCount);
		test.Printf(_L("Number of files on closed queue=%d\n"),startPageCacheStats.iFilesOnClosedQueue);

#endif
		test.End();
		
		r = DeleteAllL(gSessionPath);
		test_KErrNone(r);

		}
	else 
		test.Printf(_L("Skipping the fill of the cache due to lack of space in the current drive\n"));
	}


/** Manual test for card removal

*/
void TestRemoval()
	{	
	TInt time = 0, rtime = 0;
	RFile file1, file2;
	
		 	
	TInt r = gClient.CreateLocal(0);
 	test_KErrNone(r);
 	
	r = gTheFs.SetSessionPath(gSessionPath);
	test_KErrNone(r);
	
	r = gTheFs.MkDirAll(gSessionPath);
	test_Value(r, r == KErrNone || r == KErrAlreadyExists);

#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
	test.Printf(_L("Disabling Lock Fail simulation ...\n"));
	// turn OFF lock failure mode
	TBool simulatelockFailureMode = EFalse;
	r = controlIo(gTheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
	test_KErrNone(r);
#endif

	TBuf16<45> dir;
		
	// FileNames/File generation
	test.Start(_L("Preparing the environmnet\n"));
	FileNameGen(gFirstFile, 8, gNextFile++);
	FileNameGen(gSecondFile, 8, gNextFile++);
	dir = gSessionPath;
	dir.Append(gFirstFile);
	gFirstFile = dir;
	dir = gSessionPath;
	dir.Append(gSecondFile);
	gSecondFile = dir;


	TRAPD(res,gBuf = HBufC8::NewL(KBlockSize+1));
	test(res == KErrNone && gBuf != NULL);
		
	gBufWritePtr.Set(gBuf->Des());
	FillBuffer(gBufWritePtr, KBlockSize, 'A');
	
	TRAPD(res2,gBufSec = HBufC8::NewL(KBlockSize+1));
	test(res2 == KErrNone && gBufSec != NULL);
	gBufReadPtr.Set(gBufSec->Des());

	
	test.Printf(_L("\nSync: Write from 1 K to 254 K \n")); 

	time =  WriteTestFile(file1, gSecondFile, KMinSize, KBlockSize, EFileShareAny|EFileWrite|EFileWriteBuffered);
	test.Printf(_L("Time to write %d K WITH caching: %d mS\n"), KMinSize, time/KMs);
	test.Printf(_L("Remove MMC card,! and then press a key\n"));
	test.Getch();

	test.Printf(_L("Wait 3 seconds and insert MMC card! and then press a key\n"));
	test.Getch();

	rtime = ReadTestFile(file2, gSecondFile, KMinSize, KBlockSize, EFileShareAny|EFileRead|EFileReadBuffered);
	test.Printf(_L("Time to read %d K from the cache: %d mS\n"), KMinSize, rtime/KMs);

	test.Printf(_L("Remove MMC card! and then press a key\n"));
	test.Getch();

	test.Printf(_L("Wait 3 seconds and insert MMC card! and then press a key\n"));
	test.Getch();


	test.Printf(_L("\nSync: Write from 1 K to 255 K \n")); 

	time =  WriteTestFile(file1, gFirstFile, KMinSize + 1 , KBlockSize, EFileShareAny|EFileWrite|EFileWriteBuffered);
	test.Printf(_L("Time to write %d K WITH caching: %d mS\n"), KMinSize + 1, time/KMs);
	test.Printf(_L("Remove MMC card and delete the file //F32-TST//FFFFFFF0.TXT and then press a key\n"));
	test.Getch();

	test.Printf(_L("Wait 3 seconds and insert MMC card! and then press a key\n"));
	test.Getch();

	rtime = ReadTestFile(file2, gFirstFile, KMinSize + 1, KBlockSize, EFileShareAny|EFileRead|EFileReadBuffered);
	test.Printf(_L("Time to read %d K from the cache: %d mS\n"), KMinSize + 1, rtime/KMs);

	test.Printf(_L("Remove MMC card! and then press a key\n"));
	test.Getch();

	test.Printf(_L("Wait 3 seconds and insert MMC card! and then press a key\n"));
	test.Getch();
	
		
	file1.Close();	
	file2.Close();
	delete gBuf;
	delete gBufSec;
	}


/** Main tests function
*/ 
void CallTestsL()
	{
	
#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
	test.Printf(_L("Disabling Lock Fail simulation ...\n"));
	// turn OFF lock failure mode
	TBool simulatelockFailureMode = EFalse;
	TInt r = controlIo(gTheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
	test_KErrNone(r);
#endif

	TBuf16<45> dir;
		
	// FileNames/File generation
	test.Start(_L("Preparing the environmnet\n"));
	FileNameGen(gFirstFile, 8, gNextFile++);
	FileNameGen(gSecondFile, 8, gNextFile++);
	dir = gSessionPath;
	dir.Append(gFirstFile);
	gFirstFile = dir;
	dir = gSessionPath;
	dir.Append(gSecondFile);
	gSecondFile = dir;


	TRAPD(res,gBuf = HBufC8::NewL(KBlockSize+1));
	test(res == KErrNone && gBuf != NULL);
		
	gBufWritePtr.Set(gBuf->Des());
	FillBuffer(gBufWritePtr, KBlockSize, 'A');
	
	TRAPD(res2,gBufSec = HBufC8::NewL(KBlockSize+1));
	test(res2 == KErrNone && gBufSec != NULL);
	gBufReadPtr.Set(gBufSec->Des());

	test.Next(_L("Boundary test"));
	TestBoundaries();
	
	test.Next(_L("Negative test\n"));
	TestNegative(); 

	test.Next(_L("Integrity test\n"));
	TestIntegrity(); 
	

	test.Next(_L("Fill the cache, boundary testing\n"));
	TestFillCache();
	
	test.Next(_L("Fill the cache negative, boundary testing\n"));
	TestFillCacheNegative();
	
	test.End();
	delete gBuf;
	delete gBufSec;

#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
	// turn lock failure mode back ON (if enabled)
	simulatelockFailureMode = ETrue;
	r = controlIo(gTheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
	test_KErrNone(r);
#endif

	}

/** Initialises semaphores and call the tests
*/ 
void DoTests()
	{
	TInt r = 0;
	 	
	r = gClient.CreateLocal(0);
 	test_KErrNone(r);
 	
	r = gTheFs.SetSessionPath(gSessionPath);
	test_KErrNone(r);
	
	r = gTheFs.MkDirAll(gSessionPath);
	test_Value(r, r == KErrNone || r == KErrAlreadyExists);
	gTheFs.ResourceCountMarkStart();
	
	TRAP(r,CallTestsL());
	
	test_KErrNone(r);
	gTheFs.ResourceCountMarkEnd();
	}

/** Determines the space that can be used for the files

*/
TBool CheckForDiskSize()
	{
	TVolumeInfo volInfo;
	TInt r = gTheFs.Volume(volInfo, gDrive);
	test_KErrNone(r);
	gMediaSize = volInfo.iSize;
	
	test.Printf(_L("\nMedia size: %d MB\n"), gMediaSize/KOneMeg  );
	
	return ETrue;
	}

/** Main function

	@return KErrNone if everything was ok, panics otherwise
*/
TInt E32Main()
	{
	RThread t;
	gMainThreadId = t.Id();
	
	CTrapCleanup* cleanup;
	cleanup = CTrapCleanup::New();

	__UHEAP_MARK;
	test.Start(_L("Starting tests... T_WCACHE"));
	parseCommandLine();
	
	TInt r = gTheFs.Connect();
	test_KErrNone(r);
	
	TDriveInfo info;
	TVolumeInfo volInfo;
	r = gTheFs.Drive(info,gDrive);
	test_KErrNone(r);
	
	if(info.iMediaAtt&KMediaAttVariableSize)
		{
		test.Printf(_L("Tests skipped in RAM drive\n"));
		goto out;
		}

	r = gTheFs.Volume(volInfo, gDrive);
	if (r == KErrNotReady)
		{
		if (info.iType == EMediaNotPresent)
			test.Printf(_L("%c: Medium not present - cannot perform test.\n"), (TUint)gDriveToTest);
		else
			test.Printf(_L("%c: medium found (type %d) but drive not ready\nPrevious test may have hung; else, check hardware.\n"), (TUint)gDriveToTest, (TInt)info.iType);
		}
	else if (r == KErrCorrupt)
		{
		test.Printf(_L("%c: Media corruption; previous test may have aborted; else, check hardware\n"), (TUint)gDriveToTest);
		}
	test_KErrNone(r);	

	if(!(volInfo.iFileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadAheadEnabled))) 
		{
		test.Printf(_L("Skipping tests, Read caching not enabled in this drive\n"));
		goto out;
		}

	if (((volInfo.iDrive.iMediaAtt & KMediaAttFormattable)))
		Formatting(gDrive,ESpecialFormat);

	if(!CheckForDiskSize())
		{
		test.Printf(_L("Skipping tests due to lack of space to perform them in this drive\n"));
		}
	else if(!gManual)
		{
		DoTests();
		}
	else
		{
		TestRemoval();
		}

out:	
	test.End();

	gTheFs.Close();
	test.Close();

	__UHEAP_MARKEND;
	delete cleanup;
	return(KErrNone);
    }