--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/server/t_wcache.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -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 <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);
+ }
+