--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/server/t_fsched.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1530 @@
+// Copyright (c) 2006-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_fsched.cpp
+//
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#define __E32TEST_EXTENSION__
+
+#include <f32file.h>
+#include <e32test.h>
+#include <e32svr.h>
+#include <f32dbg.h>
+#include "t_server.h"
+#include <e32twin.h>
+#include <e32cmn.h>
+
+//----------------------------------------------------------------------------------------------
+//! @SYMTestCaseID PBASE-T_FSCHED-0191
+//! @SYMTestType CIT
+//! @SYMPREQ PREQ914
+//! @SYMTestCaseDesc This test case is exercising the Fair Scheduling functionality added to
+//! the File Server. There are negative and positive tests.
+//! @SYMTestActions 0 setup the environment to execute the tests and benchmarks the time
+//! taken to write the two files that are going to be used during the test
+//! 1 TestReadingWhileWriting uses two threads (sync case) to write a big file/
+//! read a small file simultaneously and verifies that the timing is correct.
+//! Same test in the async case.
+//! 2 TestWritingWhileWriting same test as before, but the two operations are
+//! writes.
+//! 3 TestTwoBigOnes does the same test as 1, but with two big files, ensuring
+//! that the first starting is the first finishing.
+//! 4 TestReadingWhileWritingSameFile reads a file while it is being written.
+//! 5 TestClientDies starts a file write and kills it, then checks the integrity
+//! of the volume.
+//! 6 Finally it performs the benchmark of writing the two files again, to see
+//! if it has been degraded during the execution
+//!
+//! @SYMTestExpectedResults finishes if the fair scheduling behaves as expected, panics otherwise
+//! @SYMTestPriority High
+//! @SYMTestStatus Implemented
+//----------------------------------------------------------------------------------------------
+
+
+class RTestThreadSemaphore : public RSemaphore
+ {
+public:
+ inline void Signal(TInt aError = KErrNone) {iError = aError; RSemaphore::Signal();};
+public:
+ TInt iError;
+ };
+
+// macro to be used in place of test(). Signals main thread if assertion fails
+#define TEST(x) \
+if (!(x)) \
+ { \
+ RThread t; \
+ if (t.Id() != gMainThreadId) \
+ client.Signal(KErrGeneral); \
+ test(x); \
+ }
+#define TESTERROR(x) \
+ if (x != KErrNone) \
+ { \
+ RThread t; \
+ if (t.Id() != gMainThreadId) \
+ RDebug::Print(_L("error %d\n"), x); \
+ else \
+ test.Printf(_L("error %d\n"), x); \
+ TEST(x==KErrNone); \
+ }
+
+#define CLIENTWAIT() {client.Wait(); test(client.iError == KErrNone);}
+
+////////////////////////////////////////////////////////////
+// Template functions encapsulating ControlIo magic
+//
+GLDEF_D template <class C>
+GLDEF_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;
+}
+
+
+GLDEF_D RTest test(_L("T_FSCHED"));
+
+GLDEF_D RFs TheFs;
+GLDEF_D TInt gDrive;
+GLDEF_D TFileName gSessionPath;
+GLDEF_D TChar gDriveToTest;
+TThreadId gMainThreadId;
+
+HBufC8* gBuf = NULL;
+TPtr8 gBufReadPtr(NULL, 0);
+HBufC8* gBufSec = NULL;
+TPtr8 gBufWritePtr(NULL, 0);
+
+const TInt KuStomS = 1000;
+const TInt KOneK = 1024;
+const TInt KOneMeg = KOneK * 1024;
+const TInt KBigBlockSize = KOneMeg ;
+const TInt KBlockSize = KOneK * 129 ;
+const TInt KWaitRequestsTableSize = 70;
+
+TInt gBigFileSize = 0;
+TInt gSmallFileSize = 0;
+TInt64 gMediaSize = 0;
+TInt gTotalTimeSync[2];
+TInt gTotalTimeAsync[2];
+
+TTimeIntervalMicroSeconds32 gTimeTakenBigFile(0);
+TTimeIntervalMicroSeconds32 gTimeTakenBigBlock(0);
+TBuf16<45> gSmallFile, gBigFile;
+TInt gNextFile=0;
+TTime gTime1;
+TTime gTime2;
+
+RSemaphore gSync;
+
+// Concurrent Threads
+RThread gBig;
+RThread gSmall;
+RTestThreadSemaphore client;
+const TInt KHeapSize = 0x4000;
+const TInt KMaxHeapSize = 0x200000;
+TRequestStatus gStatus[KWaitRequestsTableSize];
+
+enum TTestState
+ {
+ EThreadWait,
+ EThreadSignal,
+ ENoThreads
+ };
+
+/** 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);
+}
+
+
+/** 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 = TheFs.CharToDrive(gDriveToTest,gDrive);
+ TESTERROR(r);
+ gSessionPath = _L("?:\\F32-TST\\");
+ gSessionPath[0] = (TText)(gDrive+'A');
+ test.Printf(_L("\nCLP=%C\n"), (TInt)gDriveToTest);
+}
+
+/** Verifies the content of a buffer (all the letters are like the first one)
+
+ @param aBuffer Buffer to be verified
+
+ @return KErrNone if all the letters are the same, KErrCorrupt otherwise
+*/
+TInt VerifyBuffer(TDes8& aBuffer, TChar aExpectedChar)
+ {
+ TChar c = aExpectedChar;
+
+ for(TInt i = 1; i<aBuffer.Length(); i++)
+ {
+ if(c != (TChar)(aBuffer[i]))
+ {
+ RDebug::Print(_L("VerifyBuffer() failed at +%d, %02X != %02X"), i, (TInt) c, aBuffer[i] );
+ return KErrCorrupt;
+ }
+ }
+
+ return KErrNone;
+ }
+
+/** Fills a buffer with character aC
+
+ @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(aC);
+ }
+ }
+
+/** Waits for all the TRequestStatus in status[] to complete
+
+ @param status Array of TRequestStatus
+ @param aLength Length to be filled
+ @param aC Character to be used to fill the buffer
+*/
+void WaitForAll(TRequestStatus* status, TInt aFileSize, TInt aBlockSize)
+{
+ TInt i = 0;
+ TInt size = 0;
+
+ if((aFileSize % aBlockSize) == 0 )
+ size = aFileSize/aBlockSize;
+ else
+ size = (aFileSize/aBlockSize) + 1;
+
+ while(i < size)
+ {
+ User::WaitForRequest(status[i++]);
+ }
+}
+
+/** Writes a file synchronously in blocks of aBlockSize size
+
+ @param fs RFs object
+ @param aFile File name
+ @param aSize Size of the file in bytes
+ @param aBlockSize Size of the blocks to be used in bytes
+ @param aState Determines if the operation is being done in the main process
+ or in a thread
+
+ @return Returns KErrNone if everything ok, otherwise it panics
+*/
+TInt WriteFile(RFs& fs, TDes16& aFile, TInt aSize, TInt aBlockSize, TTestState aState)
+ {
+ TInt r = 0;
+ RFile fileWrite;
+
+ TEST(aBlockSize>0);
+
+ r = fileWrite.Replace(fs,aFile,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+ TESTERROR(r);
+
+ if(aState==EThreadWait)
+ {
+ gSync.Wait();
+ User::After(gTimeTakenBigBlock);
+ }
+
+ TInt j = 0;
+ while(j < aSize)
+ {
+ if((j == 0) && (aState == EThreadSignal))
+ {
+ gSync.Signal();
+ }
+
+ if((aSize - j) >= aBlockSize) // All the blocks but the last one
+ r = fileWrite.Write(gBufWritePtr, aBlockSize);
+ else // The last block
+ r = fileWrite.Write(gBufWritePtr, (aSize - j));
+
+ TESTERROR(r);
+ j += aBlockSize;
+ }
+
+ fileWrite.Close();
+
+ return KErrNone;
+ }
+
+/** Read File in blocks of size aBlockSize
+
+ @param fs RFs object
+ @param aFile File name
+ @param aBlockSize Size of the blocks to be used in bytes
+ @param aState Determines if the operation is being done in the main process
+ or in a thread
+
+ @return Returns KErrNone if everything ok, otherwise it panics
+*/
+TInt ReadFile(RFs& fs, TDes16& aFile, TInt aBlockSize, TTestState aState)
+ {
+ RTest test(_L("T_FSCHED"));
+ TInt r = 0, size = 0;
+ RFile fileRead;
+
+ TEST(aBlockSize > 0);
+
+ r = fileRead.Open(fs, aFile, EFileShareAny|EFileRead|EFileReadDirectIO);
+ TESTERROR(r);
+
+ r = fileRead.Size(size);
+ TESTERROR(r);
+
+ if(aState == EThreadWait)
+ {
+ gSync.Wait();
+ User::After(gTimeTakenBigBlock);
+ }
+
+ TInt j = 0;
+ while(j < size)
+ {
+ if((j == 0)&&(aState == EThreadSignal))
+ {
+ gSync.Signal();
+ }
+ r = fileRead.Read(gBufReadPtr, aBlockSize);
+ TESTERROR(r);
+ r = VerifyBuffer(gBufReadPtr, 'B');
+ TESTERROR(r);
+
+ j += aBlockSize;
+ r = fileRead.Size(size);
+ TESTERROR(r);
+ }
+
+ fileRead.Close();
+
+ return KErrNone;
+ }
+
+
+/** Write a file asynchronously in blocks of aBlockSize size
+
+ @param fs 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 aBlockSize Size of the blocks to be used in bytes
+ @param aStatus TRequestStatus array for all the requests
+*/
+void WriteFileAsync(RFs& fs, RFile& aFileWrite, TDes16& aFile, TInt aSize, TInt aBlockSize, TRequestStatus aStatus[])
+ {
+
+ TInt r = 0;
+
+ TEST(aBlockSize > 0); // Block size must be greater than 0
+
+ r = aFileWrite.Replace(fs,aFile,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+ TESTERROR(r);
+
+ TInt j = 0;
+ TInt i = 0;
+ while(j < aSize)
+ {
+ if((aSize - j) >= aBlockSize) // All the blocks but the last one
+ aFileWrite.Write(gBufWritePtr, aBlockSize, aStatus[i]);
+ else // The last block
+ aFileWrite.Write(gBufWritePtr, (aSize - j), aStatus[i]);
+
+ TInt status = aStatus[i].Int();
+ if (status != KErrNone && status != KRequestPending)
+ {
+ test.Printf(_L("RFile::Write() returned %d\n"), aStatus[i].Int());
+ }
+
+ test (status == KErrNone || status == KRequestPending);
+ i++;
+ j += aBlockSize;
+ }
+ }
+
+/** Read a file asynchronously in blocks of aBlockSize size
+
+ @param fs RFs object
+ @param aFileRead RFile object, needs to exist beyond the scope of this function
+ @param aFile File name
+ @param aSize 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
+
+ @return KErrNone
+*/
+TInt ReadFileAsync(RFs& fs, RFile& aFileRead, TDes16& aFile, TInt aBlockSize, TRequestStatus aStatus[])
+ {
+ TInt r = 0, size = 0;
+
+ TEST(aBlockSize > 0); // Block size must be greater than 0
+
+
+ r = aFileRead.Open(fs, aFile, EFileShareAny|EFileRead|EFileReadDirectIO);
+ TESTERROR(r);
+
+ r = aFileRead.Size(size);
+ TESTERROR(r);
+
+ TInt j = 0, i = 0;
+ while(j < size)
+ {
+ aFileRead.Read(gBufReadPtr, aBlockSize, aStatus[i]);
+ test (aStatus[i].Int() == KErrNone || aStatus[i].Int() == KRequestPending);
+ i++;
+ TESTERROR(r);
+ j += aBlockSize;
+ }
+
+ return KErrNone;
+ }
+
+
+/**
+Write a pattern to 2 files simultaneously in blocks of aBlockSize size and verify that fair scheduling
+does not interfere with the write order by reading both files back and verifying the contents.
+
+aBlockSize should be slightly bigger that the fair scheduling size
+
+@param aFs RFs object
+@param aSize Size of the file in bytes
+@param aBlockSize Size of the blocks to be used in bytes, should be slightly bigger that the fair scheduling size
+*/
+void WriteOrderTest(RFs& aFs, TInt aSize, TInt aBlockSize)
+ {
+ TFileName fileName1, fileName2;
+ FileNameGen(fileName1, 8, 99);
+ FileNameGen(fileName2, 8, 100);
+
+ RFile file1, file2;
+
+ TRequestStatus status1[KWaitRequestsTableSize];
+ TRequestStatus status2[KWaitRequestsTableSize];
+
+ TInt r = 0;
+
+ TEST(aBlockSize > 0); // Block size must be greater than 0
+
+ r = file1.Replace(aFs, fileName1,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+ TESTERROR(r);
+
+ r = file2.Replace(aFs, fileName2,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+ TESTERROR(r);
+
+
+ HBufC8* buf1 = NULL;
+ TRAP(r,buf1 = HBufC8::NewL(KBigBlockSize));
+ TEST(r == KErrNone && buf1 != NULL);
+
+ TInt maxBlockNum = KBigBlockSize / aBlockSize;
+ TInt blockNum = 0;
+ TInt blocksFree = maxBlockNum;
+
+ TInt blockHead = 0; // ha ha
+ TInt blockTail = 0;
+
+
+ TPtrC8* writeBuffer = new TPtrC8[maxBlockNum];
+ for (TInt n=0; n<maxBlockNum; n++)
+ {
+ TUint8* addr = (TUint8*) buf1->Des().Ptr() + (n * aBlockSize);
+ writeBuffer[n].Set(addr, aBlockSize);
+ }
+
+ TInt j = 0;
+ TInt i = 0;
+ while(j < aSize)
+ {
+
+ // fill the buffer with a char based on the block number
+ TPtr8 bufWritePtr((TUint8*) writeBuffer[blockHead].Ptr(), 0, writeBuffer[blockHead].Length());
+ TUint8* addr = (TUint8*) buf1->Des().Ptr() + (blockHead * aBlockSize);
+ bufWritePtr.Set(addr, 0, aBlockSize);
+ FillBuffer(bufWritePtr, aBlockSize, 'A'+blockNum);
+
+//test.Printf(_L("j %d, addr %08X, aSize %d, blockNum %d, blockTail %d, blockHead %d, maxBlockNum %d, blocksFree %d\n"),
+// j, writeBuffer[blockHead].Ptr(), aSize, blockNum, blockTail, blockHead, maxBlockNum, blocksFree);
+
+ if((aSize - j) >= aBlockSize) // All the blocks but the last one
+ file1.Write(writeBuffer[blockHead], aBlockSize, status1[blockHead]);
+ else // The last block
+ file1.Write(writeBuffer[blockHead], (aSize - j), status1[blockHead]);
+
+ if((aSize - j) >= aBlockSize) // All the blocks but the last one
+ file2.Write(writeBuffer[blockHead], aBlockSize, status2[blockHead]);
+ else // The last block
+ file2.Write(writeBuffer[blockHead], (aSize - j), status2[blockHead]);
+
+ TInt status = status1[blockHead].Int();
+ if (status != KErrNone && status != KRequestPending)
+ test.Printf(_L("RFile::Write() returned %d\n"), status1[blockHead].Int());
+ test (status == KErrNone || status == KRequestPending);
+
+ status = status2[blockHead].Int();
+ if (status != KErrNone && status != KRequestPending)
+ test.Printf(_L("RFile::Write() returned %d\n"), status1[blockHead].Int());
+ test (status == KErrNone || status == KRequestPending);
+
+ i++;
+ j += aBlockSize;
+ blockNum++;
+ blockHead = (blockHead + 1) % maxBlockNum;
+ blocksFree--;
+
+ if (blocksFree == 0)
+ {
+//test.Printf(_L("Waiting for block %d\n"), blockTail);
+ User::WaitForRequest(status1[blockTail]);
+ User::WaitForRequest(status2[blockTail]);
+ blockTail = (blockTail + 1) % maxBlockNum;
+ blocksFree++;
+ }
+
+ }
+
+ while (blockTail != blockHead)
+ {
+//test.Printf(_L("Waiting for block %d\n"), blockTail);
+ User::WaitForRequest(status1[blockTail]);
+ User::WaitForRequest(status2[blockTail]);
+ blockTail = (blockTail + 1) % maxBlockNum;
+ blocksFree++;
+ }
+
+ file1.Close();
+ file2.Close();
+
+ // Verify file1
+ r = file1.Open(aFs, fileName1, EFileShareAny|EFileRead|EFileReadDirectIO);
+ TESTERROR(r);
+ TInt size;
+ r = file1.Size(size);
+ TESTERROR(r);
+ blockNum = 0;
+ j = 0;
+ while(j < size)
+ {
+ TPtr8 readPtr((TUint8*) writeBuffer[0].Ptr(), aBlockSize, aBlockSize);
+ r = file1.Read(readPtr);
+ TESTERROR(r);
+ r = VerifyBuffer(readPtr, 'A' + blockNum);
+ TESTERROR(r);
+ j += aBlockSize;
+ blockNum++;
+ }
+
+ // Verify file2
+ r = file2.Open(aFs, fileName2, EFileShareAny|EFileRead|EFileReadDirectIO);
+ TESTERROR(r);
+ r = file2.Size(size);
+ TESTERROR(r);
+ blockNum = 0;
+ j = 0;
+ while(j < size)
+ {
+ TPtr8 readPtr((TUint8*) writeBuffer[0].Ptr(), aBlockSize, aBlockSize);
+ r = file2.Read(readPtr);
+ TESTERROR(r);
+ r = VerifyBuffer(readPtr, 'A' + blockNum);
+ TESTERROR(r);
+ j += aBlockSize;
+ blockNum++;
+ }
+
+ file2.Close();
+ file1.Close();
+
+ delete [] writeBuffer;
+ delete buf1;
+
+ r = aFs.Delete(fileName1);
+ TEST(r == KErrNone);
+ r = aFs.Delete(fileName2);
+ TEST(r == KErrNone);
+ }
+
+
+
+/** Measure the time taken for a big block to be written synchronously
+*/
+void TimeTakenToWriteBigBlock()
+{
+ TTime startTime;
+ TTime endTime;
+ RFile fileWrite;
+
+ TInt r = fileWrite.Replace(TheFs,gBigFile,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+ TESTERROR(r);
+
+ // Calculate how long it takes to write a big block to be able to issue at the right time the concurrent writes
+ startTime.HomeTime();
+
+ r = fileWrite.Write(gBufWritePtr, KBigBlockSize);
+
+ endTime.HomeTime();
+
+ fileWrite.Close();
+
+ TESTERROR(r);
+
+ gTimeTakenBigBlock = I64LOW(endTime.MicroSecondsFrom(startTime).Int64()/3);
+
+ test.Printf(_L("\nTime spent to write the big block in isolation: %d ms\n"), gTimeTakenBigBlock.Int() / KuStomS);
+}
+
+
+/** Measure the time taken for this file to be written synchronously
+*/
+void TimeTakenToWriteBigFile(TInt aPos)
+{
+ TTime startTime;
+ TTime endTime;
+
+ test((aPos >= 0) && (aPos <= 1));
+ startTime.HomeTime();
+
+ WriteFile(TheFs,gBigFile, gBigFileSize, KBigBlockSize, ENoThreads);
+
+ endTime.HomeTime();
+
+ gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
+
+
+ test.Printf(_L("\nTime spent to write the big file in isolation: %d ms\n"), gTimeTakenBigFile.Int() / KuStomS);
+
+ gTotalTimeSync[aPos] = Max((gTimeTakenBigFile.Int()/KuStomS), gTotalTimeSync[aPos]) ;
+}
+
+/** Measure the time taken for this file to be written asynchronously
+*/
+void TimeTakenToWriteBigFileAsync(TInt aPos)
+{
+ TTime startTime;
+ TTime endTime;
+ TTime endTime2;
+ TRequestStatus status[KWaitRequestsTableSize];
+ RFile bigFile;
+
+ test((aPos >= 0) && (aPos <= 1));
+
+ startTime.HomeTime();
+ WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize,KBigBlockSize,status);
+ endTime.HomeTime();
+
+
+ WaitForAll(status, gBigFileSize, KBigBlockSize);
+
+ endTime2.HomeTime();
+
+ gTimeTakenBigFile=I64LOW(endTime.MicroSecondsFrom(startTime).Int64());
+ bigFile.Close();
+ test.Printf(_L("\nTime to queue the blocks in isolation asynchronously: %d ms, "), gTimeTakenBigFile.Int() / KuStomS);
+ gTimeTakenBigFile=I64LOW(endTime2.MicroSecondsFrom(startTime).Int64());
+ test.Printf(_L("to actually write it: %d ms\n"),gTimeTakenBigFile.Int() / KuStomS);
+ gTotalTimeAsync[aPos] = Max((gTimeTakenBigFile.Int() / KuStomS), gTotalTimeAsync[aPos]) ;
+}
+
+/** Delete content of directory
+
+ @param aDir Target directory
+
+ @return Error returned if any, otherwise KErrNone
+*/
+TInt DeleteAll(TDes16& aDir)
+ {
+ TBuf16<100> dir;
+ CFileMan* fMan=CFileMan::NewL(TheFs);
+ TInt r=0;
+
+ dir = aDir;
+ dir.Append(_L("F*.*"));
+ r = fMan->Delete(dir);
+
+ delete fMan;
+ return r;
+ }
+
+/** Read a small file sync, in a different thread
+
+ @return ETrue
+*/
+TInt ReadSmallFile(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r=fs.Connect();
+ TESTERROR(r);
+
+ r = fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ ReadFile(fs,gSmallFile,KBlockSize, EThreadWait);
+ gTime2.HomeTime();
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Read the big file sync, in a different thread
+
+ @return ETrue
+*/
+TInt ReadBigFile(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r=fs.Connect();
+ TESTERROR(r);
+
+ r=fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ ReadFile(fs,gBigFile,KBlockSize, EThreadWait);
+ gTime2.HomeTime();
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Write a small file sync, in a different thread
+
+ @return ETrue
+*/
+TInt WriteSmallFile(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r=fs.Connect();
+ TESTERROR(r);
+
+ r=fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ WriteFile(fs,gSmallFile,gSmallFileSize, KBlockSize, EThreadWait);
+ gTime2.HomeTime();
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Write a big file sync, in a different thread
+
+ @return ETrue
+*/
+TInt WriteBigFile(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r=fs.Connect();
+ TESTERROR(r);
+
+ r=fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ WriteFile(fs, gBigFile, gBigFileSize, KBigBlockSize, EThreadSignal);
+ gTime1.HomeTime();
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Write big file after a signalling thread has been scheduled, in a different thread
+
+ @return ETrue
+*/
+TInt WriteBigFile2(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r=fs.Connect();
+ TESTERROR(r);
+
+ r = fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ WriteFile(fs, gSmallFile, gBigFileSize, KBigBlockSize, EThreadWait);
+ gTime2.HomeTime();
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Write a big file async, in a different thread
+
+ @return ETrue
+*/
+TInt WriteBigFileAsync(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r = fs.Connect();
+ TESTERROR(r);
+
+ r=fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+
+ RFile bigFile;
+
+ WriteFileAsync(fs, bigFile, gSmallFile, gBigFileSize, KBlockSize,gStatus);
+ gSync.Signal();
+ WaitForAll(gStatus, gBigFileSize, KBlockSize);
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Delete a big file, in a different thread
+
+ @return ETrue
+*/
+TInt DeleteBigFile(TAny* )
+ {
+ RTest test(_L("T_FSCHED"));
+ RFs fs;
+ TInt r = fs.Connect();
+ TESTERROR(r);
+
+ r = fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ gSync.Wait();
+
+ r = fs.Delete(gBigFile);
+#if defined(__WINS__)
+ TEST(r == KErrInUse || r == KErrNone);
+#else
+ test_Equal(KErrInUse, r);
+#endif
+
+ client.Signal();
+
+ fs.Close();
+ test.Close();
+
+ return ETrue;
+ }
+
+/** Reads a small file while writing a big one
+
+*/
+void TestReadingWhileWriting()
+{
+ TInt r = 0;
+ TTime time1;
+ TTime time2;
+
+ time1.HomeTime();
+
+ // Write the small file and take the appropriate measures
+ WriteFile(TheFs,gSmallFile,KBlockSize, KBlockSize, ENoThreads);
+
+ // Sync test
+ TBuf<20> buf=_L("Big Write");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ buf = _L("Small Read");
+ r = gSmall.Create(buf, ReadSmallFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ gBig.Resume();
+ gSmall.Resume();
+
+ CLIENTWAIT();
+ CLIENTWAIT();
+
+ gBig.Close();
+ gSmall.Close();
+
+
+ TTimeIntervalMicroSeconds timeTaken = gTime1.MicroSecondsFrom(gTime2);
+ test.Printf(_L("\nSync read done %d ms before the write ended\n"),I64LOW(timeTaken.Int64() / KuStomS));
+ TReal time=I64LOW(timeTaken.Int64() / KuStomS);
+ #if !defined(__WINS__)
+ // If this condition fails, means that writing the sync file while fairscheduling a small sync read takes too long
+ test.Printf(_L("time: %f\n"), time);
+// test((time > 0) && (((gTotalTimeSync[0]-time)>0) || ((gTotalTimeSync[1]-time)>0)) );
+ test(time > 0);
+ #endif
+
+ // Async test
+ TRequestStatus status[KWaitRequestsTableSize];
+ TRequestStatus status2[2];
+ RFile bigFile, smallFile;
+
+ WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize ,status);
+ ReadFileAsync(TheFs, smallFile, gSmallFile, KBlockSize, status2 );
+
+ WaitForAll(status2,KBlockSize , KBlockSize );
+ time1.HomeTime();
+
+ WaitForAll(status,gBigFileSize, KBigBlockSize);
+
+ time2.HomeTime();
+ bigFile.Close();
+ smallFile.Close();
+
+ timeTaken = time2.MicroSecondsFrom(time1);
+
+ test.Printf(_L("\nAsync read done %d ms before the write ended\n"),I64LOW(timeTaken.Int64() / KuStomS));
+ time = I64LOW(timeTaken.Int64() / KuStomS);
+
+ #if !defined(__WINS__)
+ // If this condition fails, means that writing the async file while fairscheduling a small async read takes too long
+ test.Printf(_L("time: %f\n"), time);
+ test.Printf(_L("gTotalTimeAsync[0] = %d , gTotalTimeAsync[1] = %d\n"),gTotalTimeAsync[0],gTotalTimeAsync[1] );
+// test((time > 0) && (((gTotalTimeAsync[0]-time)>0) || ((gTotalTimeAsync[1]-time)>0)) );
+ test(time > 0);
+ #endif
+}
+
+/** Writes a small file while writing a big one
+
+*/
+void TestWritingWhileWriting()
+{
+ TInt r = 0;
+ TTime time1;
+ TTime time2;
+
+ // Sync test
+ TBuf<20> buf = _L("Big Write II");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ buf = _L("Small Write II");
+ r = gSmall.Create(buf, WriteSmallFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r==KErrNone);
+
+ gBig.Resume();
+ gSmall.Resume();
+
+ CLIENTWAIT();
+ CLIENTWAIT();
+
+ gBig.Close();
+ gSmall.Close();
+
+ TTimeIntervalMicroSeconds timeTaken = gTime1.MicroSecondsFrom(gTime2);
+ test.Printf(_L("\nSync write done %d ms before the big write ended\n"),I64LOW(timeTaken.Int64() / KuStomS));
+ TReal time=I64LOW(timeTaken.Int64() / KuStomS);
+ #if !defined(__WINS__)
+ // If this condition fails, means that writing the sync file while fairscheduling a small sync write takes too long
+ test.Printf(_L("time: %f\n"), time);
+// test((time > 0) && (((gTotalTimeSync[0]-time)>0) || ((gTotalTimeSync[1]-time)>0)) );
+ test(time > 0);
+ #endif
+
+ // Async test
+ TRequestStatus status[KWaitRequestsTableSize];
+ TRequestStatus status2[1];
+ RFile bigFile, smallFile;
+
+ WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status);
+ WriteFileAsync(TheFs,smallFile, gSmallFile,gSmallFileSize,KBlockSize,status2);
+ WaitForAll(status2,gSmallFileSize, KBlockSize);
+ time1.HomeTime();
+ WaitForAll(status, gBigFileSize, KBigBlockSize);
+ time2.HomeTime();
+
+ timeTaken = time2.MicroSecondsFrom(time1);
+ test.Printf(_L("\nAsync write done %d ms before the big write ended\n"),I64LOW(timeTaken.Int64() / KuStomS));
+ time=I64LOW(timeTaken.Int64() / KuStomS);
+ #if !defined(__WINS__)
+ // If this condition fails, means that writing the async file while fairscheduling a small async write takes too long
+ test.Printf(_L("time: %f\n"), time);
+ test.Printf(_L("gTotalTimeAsync[0] = %d , gTotalTimeAsync[1] = %d\n"),gTotalTimeAsync[0],gTotalTimeAsync[1] );
+// test((time > 0) && (((gTotalTimeAsync[0]-time)>0) || ((gTotalTimeAsync[1]-time)>0)) );
+ test(time > 0);
+ #endif
+ bigFile.Close();
+ smallFile.Close();
+}
+
+/** Writes two big files, ensuring the one that started to be written later ends the last one
+
+*/
+void TestTwoBigOnes()
+{
+ TInt r = 0;
+ TTime time1;
+ TTime time2;
+
+ // Sync test
+ TBuf<20> buf = _L("Big Write IIII");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ buf = _L("Big Write The Second");
+ r = gSmall.Create(buf, WriteBigFile2, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ gBig.Resume();
+ gSmall.Resume();
+
+ CLIENTWAIT();
+ CLIENTWAIT();
+
+ gBig.Close();
+ gSmall.Close();
+
+ TTimeIntervalMicroSeconds timeTaken = gTime2.MicroSecondsFrom(gTime1);
+ test.Printf(_L("\nSync first write ended %d ms before the second write ended (same file size)\n"),I64LOW(timeTaken.Int64() / KuStomS));
+
+ // Async test
+ TRequestStatus status[KWaitRequestsTableSize];
+ TRequestStatus status2[KWaitRequestsTableSize];
+ RFile bigFile, bigFile2;
+
+ WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status);
+ WriteFileAsync(TheFs, bigFile2, gSmallFile, gBigFileSize, KBigBlockSize, status2);
+ WaitForAll(status, gBigFileSize, KBigBlockSize);
+ time1.HomeTime();
+ WaitForAll(status2, gBigFileSize, KBigBlockSize);
+ time2.HomeTime();
+
+ timeTaken = time2.MicroSecondsFrom(time1);
+ test.Printf(_L("\nAsync first write ended %d ms before the second write ended (same file size)\n"),I64LOW(timeTaken.Int64() / KuStomS));
+ bigFile.Close();
+ bigFile2.Close();
+}
+
+/** Reads the file that is being written
+
+*/
+void TestReadingWhileWritingSameFile()
+{
+ TInt r = 0;
+ TTime time1;
+
+ time1.HomeTime();
+
+ // Sync test
+ TBuf<20> buf = _L("Big Write IV");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ buf = _L("Read IV");
+ r = gSmall.Create(buf, ReadBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ gBig.Resume();
+ gSmall.Resume();
+
+ CLIENTWAIT();
+ CLIENTWAIT();
+
+ CLOSE_AND_WAIT(gBig);
+ CLOSE_AND_WAIT(gSmall);
+
+ TTimeIntervalMicroSeconds timeTaken = gTime2.MicroSecondsFrom(gTime1);
+ test.Printf(_L("\nSync write finished %d ms before the read ended\n"),I64LOW(timeTaken.Int64() / KuStomS));
+
+}
+
+/** Client dying uncleanly before the operation is finished
+
+*/
+void TestClientDies()
+{
+ TInt r = 0;
+
+
+ TBuf<20> drive = _L("?:\\");
+ TVolumeInfo volInfo;
+ drive[0]=(TText)(gDrive+'A');
+
+ r = TheFs.CheckDisk(drive);
+ TEST(r == KErrNone || r == KErrNotSupported);
+
+ // Sync test
+ TBuf<20> buf = _L("Big Write V");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ TRequestStatus status;
+ gBig.Logon(status);
+ gBig.Resume();
+ gSync.Wait();
+
+ // Kill the writing thread and wait for it to die.
+
+ if(status.Int() == KRequestPending)
+ {// the people who wrote this test did not consider the case when the file write finishes before they try to kill the thread.
+ gBig.Kill(KErrGeneral);
+ User::WaitForRequest(status);
+ TEST(gBig.ExitReason() == KErrGeneral);
+ TEST(gBig.ExitType() == EExitKill);
+ }
+
+
+ // Make sure the thread is destroyed and the handles it owned and IPCs
+ // it executed are closed/cancelled.
+ CLOSE_AND_WAIT(gBig);
+
+
+ r = TheFs.Volume(volInfo, gDrive);
+ TESTERROR(r);
+
+ r = TheFs.CheckDisk(drive);
+ TEST(r == KErrNone || r == KErrNotSupported);
+
+ r = TheFs.ScanDrive(drive);
+ TEST(r == KErrNone || r == KErrNotSupported);
+
+ test.Printf(_L("Sync operation stopped\n"));
+
+ // Async test
+ buf = _L("Big Write VI");
+ r = gSmall.Create(buf, WriteBigFileAsync, KDefaultStackSize * 2, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ gSmall.Logon(status);
+ gSmall.Resume();
+ gSync.Wait();
+
+
+ if(status.Int() == KRequestPending)
+ {
+ // Kill the writing thread and wait for it to die.
+ gSmall.Kill(KErrGeneral);
+ User::WaitForRequest(status);
+ TEST(gSmall.ExitReason() == KErrGeneral);
+ TEST(gSmall.ExitType() == EExitKill);
+ }
+
+
+ // Make sure the thread is destroyed and the handles it owned and IPCs
+ // it executed are closed/cancelled.
+ CLOSE_AND_WAIT(gSmall);
+
+ r = TheFs.CheckDisk(drive);
+ TEST(r == KErrNone || r == KErrNotSupported);
+
+ r=TheFs.ScanDrive(drive);
+ TEST(r == KErrNone || r == KErrNotSupported);
+
+ test.Printf(_L("Async operation stopped\n"));
+}
+
+/** Reads a small file while writing a big one
+
+*/
+void TestDeletionWhileWriting()
+{
+ TInt r = 0;
+
+ // Sync test
+ TBuf<20> buf = _L("Big Write V");
+ r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ buf = _L("Deletion");
+ r = gSmall.Create(buf, DeleteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL);
+ TEST(r == KErrNone);
+
+ gSmall.Resume();
+ gBig.Resume();
+
+ CLIENTWAIT();
+ CLIENTWAIT();
+
+ gBig.Close();
+ gSmall.Close();
+
+ test.Printf(_L("The file was properly blocked when writing sync, not deleted\n"));
+
+ // Async test
+ TRequestStatus status[KWaitRequestsTableSize];
+ RFile bigFile;
+ RFs fs;
+
+ r = fs.Connect();
+ TESTERROR(r);
+ r = fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status);
+
+ r = fs.Delete(gBigFile);
+ TEST(r == KErrInUse);
+
+ WaitForAll(status, gBigFileSize, KBigBlockSize);
+
+ bigFile.Close();
+ fs.Close();
+
+ test.Printf(_L("The file was properly blocked when writing async, not deleted\n"));
+
+}
+
+
+void TestWriteOrder()
+ {
+ RFs fs;
+
+ TInt r = fs.Connect();
+ TESTERROR(r);
+ r = fs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ WriteOrderTest(TheFs, gBigFileSize, KBlockSize);
+
+ fs.Close();
+ }
+
+
+/** Main tests function
+*/
+void CallTestsL()
+ {
+ TBuf16<45> dir;
+ TInt r = 0;
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Printf(_L("Disabling Lock Fail simulation ...\n"));
+ // turn OFF lock failure mode (if cache is enabled)
+
+ TBool simulatelockFailureMode = EFalse;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone || r == KErrNotSupported);
+#endif
+
+ // FileNames/File generation
+ test.Start(_L("Preparing the environment\n"));
+
+ FileNameGen(gSmallFile, 8, gNextFile++);
+ FileNameGen(gBigFile, 8, gNextFile++);
+
+ dir = gSessionPath;
+ dir.Append(gSmallFile);
+
+ gSmallFile = dir;
+ dir = gSessionPath;
+ dir.Append(gBigFile);
+ gBigFile = dir;
+
+ TRAPD(res,gBuf = HBufC8::NewL(KBigBlockSize));
+ TEST(res == KErrNone && gBuf != NULL);
+
+ gBufWritePtr.Set(gBuf->Des());
+ FillBuffer(gBufWritePtr, KBigBlockSize, 'B');
+
+ TRAPD(res2, gBufSec = HBufC8::NewL(KBlockSize));
+ TEST(res2 == KErrNone && gBufSec != NULL);
+ gBufReadPtr.Set(gBufSec->Des());
+
+ test.Next(_L("Benchmarking\n"));
+ TimeTakenToWriteBigFile(0);
+ TimeTakenToWriteBigFileAsync(0);
+
+ test.Printf(_L("second try, second timings account for the last comparison\n"));
+ TimeTakenToWriteBigFile(0);
+ TimeTakenToWriteBigFileAsync(0);
+
+ TimeTakenToWriteBigBlock();
+
+ test.Next(_L("Big file sync written, small file read from the media at the same time\n"));
+ TestReadingWhileWriting();
+
+ test.Next(_L("Big file written, small file written at the same time\n"));
+ TestWritingWhileWriting();
+
+ test.Next(_L("Big file written async, deletion in the meantime\n"));
+ TestDeletionWhileWriting();
+
+ test.Next(_L("Two big files written at the same time\n"));
+ TestTwoBigOnes();
+
+ test.Next(_L("Big file being written, start reading\n"));
+ TestReadingWhileWritingSameFile();
+
+ test.Next(_L("Client dying unexpectedly\n"));
+ TestClientDies();
+
+ test.Next(_L("Ensure write order is preserved\n"));
+ TestWriteOrder();
+
+ // Format the drive to make sure no blocks are left to be erased in LFFS
+ #if !defined(__WINS__)
+ Format(gDrive);
+ #endif
+ r = TheFs.MkDirAll(gSessionPath);
+
+ TimeTakenToWriteBigFile(1);
+ TimeTakenToWriteBigFileAsync(1);
+
+ // Make sure that the difference between the first time and the last time the files are written doesn't
+ // differ more than 3%
+ test.Printf(_L("Abs(gTotalTimeSync[0]-gTotalTimeSync[1]) :%d\n"), Abs(gTotalTimeSync[0]-gTotalTimeSync[1]));
+ test.Printf(_L("Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1]) :%d\n"), Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1]));
+ test.Printf(_L("gTotalTimeSync[0] :%d\n"), gTotalTimeSync[0]);
+ test.Printf(_L("gTotalTimeAsync[0] :%d\n"), gTotalTimeAsync[0]);
+
+ #if !defined(__WINS__)
+ test((Abs(gTotalTimeSync[0]-gTotalTimeSync[1])/gTotalTimeSync[0]) < 0.03);
+ test((Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1])/gTotalTimeAsync[0]) < 0.03);
+ #endif
+
+ r = DeleteAll(gSessionPath);
+ TESTERROR(r);
+
+ delete gBuf;
+ delete gBufSec;
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // turn lock failure mode back ON (if cache is enabled)
+ simulatelockFailureMode = ETrue;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone || r == KErrNotSupported);
+#endif
+
+ test.End();
+ }
+
+/** Initialises semaphores and call the tests
+*/
+void DoTests()
+ {
+ TInt r = 0;
+
+ r = client.CreateLocal(0);
+ TESTERROR(r);
+
+ r = gSync.CreateLocal(0);
+ TESTERROR(r);
+
+ r = TheFs.SetSessionPath(gSessionPath);
+ TESTERROR(r);
+
+ r = TheFs.MkDirAll(gSessionPath);
+ if (r != KErrNone && r != KErrAlreadyExists)
+ {
+ TESTERROR(r);
+ }
+ TheFs.ResourceCountMarkStart();
+ TRAP(r,CallTestsL());
+ if (r == KErrNone)
+ TheFs.ResourceCountMarkEnd();
+ else
+ {
+ TESTERROR(r);
+ }
+ }
+
+/** Determines the space that can be used for the files
+
+*/
+TBool CheckForDiskSize()
+{
+ TVolumeInfo volInfo;
+ TInt r = TheFs.Volume(volInfo, gDrive);
+ TESTERROR(r);
+
+ gMediaSize = volInfo.iSize;
+ gSmallFileSize = KBlockSize;
+ gBigFileSize = KBlockSize*20;
+
+ while(((2*gBigFileSize)+KOneMeg) > gMediaSize )
+ {
+ gBigFileSize -= (2*KBlockSize);
+ }
+
+ TReal32 small = (TReal32)(gSmallFileSize/KOneK);
+ TReal32 big = (TReal32)(gBigFileSize/KOneK);
+
+
+ test.Printf(_L("Small File Size: %.2f KB\n"), small);
+ test.Printf(_L("Big File Size: %.2f KB (%.2f MB)\n"), big, big / KOneK);
+
+ if(gBigFileSize< (3*gSmallFileSize))
+ return EFalse;
+ else
+ return ETrue;
+}
+
+/** Formats the drive
+
+ @param aDrive Drive to be formatted
+*/
+void Format(TInt aDrive)
+ {
+
+ test.Next(_L("Format"));
+ TBuf<4> driveBuf = _L("?:\\");
+ driveBuf[0] = (TText)(aDrive+'A');
+ RFormat format;
+ TInt count, prevcount = 0;
+ TInt r = format.Open(TheFs, driveBuf, EQuickFormat, count);
+ TESTERROR(r);
+
+ while(count)
+ {
+ TInt r = format.Next(count);
+ if(count != prevcount)
+ {
+ test.Printf(_L("."));
+ prevcount = count;
+ }
+ TESTERROR(r);
+ }
+
+ format.Close();
+ }
+
+/** 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_FSCHED"));
+ parseCommandLine();
+
+ TInt r = TheFs.Connect();
+ TESTERROR(r);
+
+ TDriveInfo info;
+ TVolumeInfo volInfo;
+ r = TheFs.Drive(info, gDrive);
+ TESTERROR(r);
+
+ if(info.iMediaAtt&KMediaAttVariableSize)
+ {
+ test.Printf(_L("Tests skipped in RAM drive\n"));
+ goto out;
+ }
+
+ r = TheFs.Volume(volInfo, gDrive);
+ if (r == KErrNotReady)
+ {
+ if (info.iType == EMediaNotPresent)
+ test.Printf(_L("%c: Medium not present - cannot perform test.\n"), (TUint)gDrive + 'A');
+ else
+ test.Printf(_L("medium found (type %d) but drive %c: not ready\nPrevious test may have hung; else, check hardware.\n"), (TInt)info.iType, (TUint)gDrive + 'A');
+ }
+ else if (r == KErrCorrupt)
+ {
+ test.Printf(_L("%c: Media corruption; previous test may have aborted; else, check hardware\n"), (TUint)gDrive + 'A');
+ }
+ TESTERROR(r);
+#if !defined(__WINS__)
+ if ((volInfo.iDrive.iMediaAtt & KMediaAttFormattable))
+ Format(gDrive);
+#endif
+
+ if(CheckForDiskSize())
+ {
+ DoTests();
+ }
+ else
+ {
+ test.Printf(_L("Skipping tests due to lack of space to perform them in this drive\n"));
+ }
+out:
+ test.End();
+
+ TheFs.Close();
+ test.Close();
+
+ __UHEAP_MARKEND;
+ delete cleanup;
+ return(KErrNone);
+ }