diff -r 000000000000 -r 96e5fb8b040d kerneltest/f32test/server/t_wcache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/server/t_wcache.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1453 @@ +// 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 +#include +#include +#include +#include "t_server.h" +#include + +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 +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*.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); + } +