--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/server/t_filecache.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1813 @@
+// Copyright (c) 1995-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_filecache.cpp
+//
+//
+#define __E32TEST_EXTENSION__
+#include <f32file.h>
+#include <e32test.h>
+#include <e32svr.h>
+#include <f32dbg.h>
+#include "t_server.h"
+#include <e32twin.h>
+#include <e32rom.h>
+
+
+//----------------------------------------------------------------------------------------------
+//! @SYMTestCaseID PBASE-T_FILECACHE-0189
+//! @SYMTestType UT
+//! @SYMPREQ PREQ914
+//! @SYMTestCaseDesc Unit tests for fair scheduling, read caching and lazy writing
+//! @SYMTestActions 0 setup the environment to execute the tests
+//! 1 Reads a file with different blocksizes
+//! 2 Write a file with different blocksizes
+//! 3 Small reads while controlling the cache
+//! 4 Read operations with and without read ahead
+//! 5 Write operations with write buffer
+//! @SYMTestExpectedResults finishes if the implementation of PREQ914 behaves as expected, panics otherwise
+//! @SYMTestPriority High
+//! @SYMTestStatus Critical
+//----------------------------------------------------------------------------------------------
+
+
+//#define SYMBIAN_TEST_EXTENDED_BUFFER_SIZES
+
+
+GLDEF_D RTest test(_L("T_FILECACHE"));
+
+GLDEF_D RFs TheFs;
+GLDEF_D TChar gDriveToTest;
+GLDEF_D TInt gDrive;
+GLDEF_D TFileName gSessionPath;
+
+GLDEF_D TVolumeInfo gVolInfo; // volume info for current drive
+GLDEF_D TFileCacheFlags gDriveCacheFlags = TFileCacheFlags(0);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+static TFileCacheConfig gFileCacheConfig;
+static TBool gDisplayCacheFlags = EFalse;
+static TBool gWriteCacheFlags = EFalse;
+static TBool gRomPaged = EFalse;
+#endif
+static TBool gRunTests = ETrue;
+static TBool gRunUnitTests = ETrue;
+static TBool gRunPerformanceTests = EFalse;
+static TBool gRunManualTests = EFalse;
+
+
+LOCAL_D TInt KMaxFileSize = 768 * 1024;
+
+// Chosing a file size of 128K ensures entire file will be cached
+//LOCAL_D TInt KMaxFileSize = 128 * 1024;
+
+LOCAL_D TBuf8<256*1024> DataBuf;
+
+const TInt KBufSize = 513 * 1024 - 16;
+HBufC8* gBuf = NULL;
+
+
+LOCAL_D TPtr8 gBufPtr(NULL, 0);
+
+const TReal KOneK = 1024;
+const TReal KOneMeg = 1024 * KOneK;
+
+const TInt KSegmentSize = 4096;
+const TInt KSegmentSizeMask = (4096-1);
+
+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;
+}
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+void PrintFileCacheStats(TFileCacheStats& fileCacheStats, TBool aDisplay = ETrue)
+ {
+ TInt r = controlIo(TheFs,gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test_KErrNone(r);
+ if (!aDisplay)
+ return;
+ test.Printf(_L("File cache: Cachelines (free %d, used %d), Segments(allocated %d locked %d). Closed files(%d) Holes %d Failures (commit %d Lock %d)\n"),
+ fileCacheStats.iFreeCount, fileCacheStats.iUsedCount, fileCacheStats.iAllocatedSegmentCount,fileCacheStats.iLockedSegmentCount,fileCacheStats.iFilesOnClosedQueue, fileCacheStats.iHoleCount, fileCacheStats.iCommitFailureCount, fileCacheStats.iLockFailureCount);
+ test.Printf(_L("File cache: iUncachedPacketsRead %d iUncachedBytesRead %d iUncachedPacketsWritten %d iUncachedBytesWritten %d\n"),
+ fileCacheStats.iUncachedPacketsRead,
+ fileCacheStats.iUncachedBytesRead,
+ fileCacheStats.iUncachedPacketsWritten,
+ fileCacheStats.iUncachedBytesWritten);
+ }
+
+void PrintFileCacheConfig(TFileCacheConfig& aFileCacheConfig, TBool aDisplay = ETrue)
+ {
+ TInt r = controlIo(TheFs,gDrive, KControlIoFileCacheConfig, aFileCacheConfig);
+ test (r == KErrNone);
+ if (!aDisplay)
+ return;
+
+ test.Printf(_L("File cache:\nDrive %c\nFlags %08X\nFileCacheReadAsync %d\nFairSchedulingLen %d\nCacheSize %d\nMaxReadAheadLen %d\nClosedFileKeepAliveTime %d\nDirtyDataFlushTime %d"),
+ aFileCacheConfig.iDrive + 'A',
+ aFileCacheConfig.iFlags,
+ aFileCacheConfig.iFileCacheReadAsync,
+ aFileCacheConfig.iFairSchedulingLen,
+ aFileCacheConfig.iCacheSize,
+ aFileCacheConfig.iMaxReadAheadLen,
+ aFileCacheConfig.iClosedFileKeepAliveTime,
+ aFileCacheConfig.iDirtyDataFlushTime);
+
+ }
+
+void TestDirtyDataWrittenToDisk(TFileCacheStats& fileCacheStats)
+ {
+ test.Next(_L("test dirty data has been written to disk"));
+ // wait a maximum of double KDefaultDirtyDataFlushTime for dirty data to be flushed
+ const TInt KWaitTime = 250; // 250 milisecs
+ for (TInt n=0; n<(gFileCacheConfig.iDirtyDataFlushTime/1000)<<1 ; n+= KWaitTime)
+ {
+ test.Printf(_L("After %d milisecs : "), n );
+ PrintFileCacheStats(fileCacheStats);
+ User::After(KWaitTime * 1000); // wait 100 ms
+ if (fileCacheStats.iLockedSegmentCount == 0)
+ break;
+ }
+ PrintFileCacheStats(fileCacheStats);
+ test(fileCacheStats.iLockedSegmentCount == 0);
+ }
+#endif
+
+void TestsInit()
+ {
+ gBuf = HBufC8::NewL(KBufSize);
+ test(gBuf!=NULL);
+ gBufPtr.Set(gBuf->Des());
+ }
+void TestsEnd()
+ {
+ delete gBuf;
+ gBuf = NULL;
+ }
+
+LOCAL_C void SetSessionPath(TInt aDrive)
+ {
+ gSessionPath=_L("?:\\F32-TST\\");
+ TChar driveLetter;
+ TInt r=TheFs.DriveToChar(aDrive,driveLetter);
+ test_KErrNone(r);
+ gSessionPath[0]=(TText)driveLetter;
+ r=TheFs.SetSessionPath(gSessionPath);
+ test_KErrNone(r);
+ }
+
+LOCAL_C void PrintFileMode(TUint aFileMode)
+ {
+ TBuf<80> buf;
+
+ buf.Format(_L("FileMode = %08X"), aFileMode);
+
+ if (aFileMode & EFileWriteBuffered)
+ buf.Append(_L(", EFileWriteBuffered"));
+
+ if (aFileMode & EFileWriteDirectIO)
+ buf.Append(_L(", EFileWriteDirectIO"));
+
+ if (aFileMode & EFileReadBuffered)
+ buf.Append(_L(", EFileReadBuffered"));
+
+ if (aFileMode & EFileReadDirectIO)
+ buf.Append(_L(", EFileReadDirectIO"));
+
+ if (aFileMode & EFileReadAheadOn)
+ buf.Append(_L(", EFileReadAheadOn"));
+
+ if (aFileMode & EFileReadAheadOff)
+ buf.Append(_L(", EFileReadAheadOff"));
+
+ buf.Append(_L("\n"));
+ test.Printf(buf);
+ }
+
+void FillBuffer(TDes8& aBuffer, TInt aLength)
+ {
+ test (aBuffer.MaxLength() >= aLength);
+ for(TInt i=0; i<aLength; i+=2)
+ {
+ aBuffer[i]=(TUint8) (i >> 8);
+ aBuffer[i+1]=(TUint8) i;
+ }
+ }
+
+void TestBufferFail(TDes8& aBuffer, TInt aPos, TInt aLength)
+ {
+ test.Printf(_L("TestBuffer failed at pos %d len %d\n"), aPos, aLength);
+
+ #define PRINTCH(ch) ((ch >= 0x20 && ch < 0x7F)?ch:' ')
+ TInt startPos = Max(0, aPos - 64);
+ TInt endPos = startPos + 64;
+
+ for(TInt n=startPos; n<=endPos; n+=16)
+ RDebug::Print(_L("%08X: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]"),
+ //&aBuffer[aPos+n],
+ n,
+ aBuffer[n+0], aBuffer[n+1], aBuffer[n+2], aBuffer[n+3], aBuffer[n+4], aBuffer[n+5], aBuffer[n+6], aBuffer[n+7], aBuffer[n+8], aBuffer[n+9], aBuffer[n+10], aBuffer[n+11], aBuffer[n+12], aBuffer[n+13], aBuffer[n+14], aBuffer[n+15],
+ PRINTCH(aBuffer[n+0]), PRINTCH(aBuffer[n+1]), PRINTCH(aBuffer[n+2]), PRINTCH(aBuffer[n+3]), PRINTCH(aBuffer[n+4]), PRINTCH(aBuffer[n+5]), PRINTCH(aBuffer[n+6]), PRINTCH(aBuffer[n+7]), PRINTCH(aBuffer[n+8]), PRINTCH(aBuffer[n+9]), PRINTCH(aBuffer[n+10]), PRINTCH(aBuffer[n+11]), PRINTCH(aBuffer[n+12]), PRINTCH(aBuffer[n+13]), PRINTCH(aBuffer[n+14]), PRINTCH(aBuffer[n+15]));
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TInt x;
+ TInt r = controlIo(TheFs,gDrive, KControlIoFileCacheDump, x);
+ test_KErrNone(r);
+#endif
+ test (0);
+ }
+
+void TestBuffer(TDes8& aBuffer, TInt aPos, TInt aLength)
+ {
+ TInt pos = aPos;
+ for(TInt i=0; i<aLength; i++, pos++)
+ {
+ if (pos & 1)
+ {
+ if (aBuffer[pos] != (TUint8) pos-1)
+ TestBufferFail(aBuffer, pos, aLength);
+ }
+ else
+ {
+ if (aBuffer[pos] != (TUint8) (pos >> 8))
+ TestBufferFail(aBuffer, pos, aLength);
+ }
+ }
+ }
+
+
+LOCAL_C void UnitTests()
+//
+// Test read file handling.
+//
+ {
+
+ test.Start(_L("File cache read and write unit tests"));
+
+//TheFs.SetDebugRegister(KCACHE);
+
+ RFile f;
+ TInt r;
+ TInt pos;
+ TInt len;
+ TInt testNum;
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TBool simulatelockFailureMode;
+ TFileCacheStats fileCacheStats;
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of files on closed queue=%d\n"),fileCacheStats.iFilesOnClosedQueue);
+ test(fileCacheStats.iFilesOnClosedQueue == 0);
+#endif
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // turn OFF lock failure mode
+ simulatelockFailureMode = EFalse;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone);
+#endif
+
+ TFileName testFile = _L("TEST.BIN");
+
+ //**********************************
+ // Test Read-Modify-Write
+ //**********************************
+ test.Next(_L("Test read-modify-write"));
+ gBufPtr.SetLength(KBufSize);
+ FillBuffer(gBufPtr, KBufSize);
+ TestBuffer(gBufPtr, 0,KBufSize);
+ TPtrC8 writePtr;
+ TPtr8 readPtr(gBuf->Des());
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TInt uncachedBytesRead;
+ TInt uncachedPacketsRead;
+#endif
+
+ // create an empty file, so that any writes overlapping segemt boundaries
+ // need a read first
+ // create a test file using directIO and then re-open it in buffered mode,
+ // so that any writes overlapping segemt boundaries need a read first
+ r = f.Replace(TheFs, testFile, EFileWrite | EFileWriteDirectIO);
+ test_KErrNone(r);
+ writePtr.Set(gBuf->Des());
+ r = f.Write(0, writePtr);
+ test_KErrNone(r);
+ f.Close();
+ r = f.Open(TheFs, testFile, EFileReadBuffered | EFileWrite | EFileWriteBuffered);
+ test_KErrNone(r);
+
+ TInt cacheLineLen = 128*1024;
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ // write 1 partial segment at offset 0 to 7 in segment
+ test.Next(_L("Test read-modify-write #1"));
+ pos = cacheLineLen*0 + KSegmentSize*2 + 0;
+ len = 7;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back & verify whole segment
+ pos&= ~KSegmentSizeMask; len = (len + KSegmentSize-1) & ~KSegmentSizeMask;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ readPtr.FillZ();
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ test (fileCacheStats.iUncachedBytesRead - uncachedBytesRead == KSegmentSize);
+ test (fileCacheStats.iUncachedPacketsRead - uncachedPacketsRead == 1);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ // write 1 partial segment at offset 7 to 4096 in segment
+ test.Next(_L("Test read-modify-write #2"));
+ pos = cacheLineLen*0 + KSegmentSize*3 + 7;
+ len = KSegmentSize - 7;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back & verify whole segment
+ pos&= ~KSegmentSizeMask; len = (len + KSegmentSize-1) & ~KSegmentSizeMask;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ readPtr.FillZ();
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ test (fileCacheStats.iUncachedBytesRead - uncachedBytesRead == KSegmentSize);
+ test (fileCacheStats.iUncachedPacketsRead - uncachedPacketsRead == 1);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ // write 1 partial segment at offset 7 to 37 in segment
+ test.Next(_L("Test read-modify-write #3"));
+ pos = cacheLineLen*0 + KSegmentSize*4 + 7;
+ len = 30;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back & verify whole segment
+ pos&= ~KSegmentSizeMask; len = (len + KSegmentSize-1) & ~KSegmentSizeMask;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ readPtr.FillZ();
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ test (fileCacheStats.iUncachedBytesRead - uncachedBytesRead == KSegmentSize);
+ test (fileCacheStats.iUncachedPacketsRead - uncachedPacketsRead == 1);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ // write 2 segments, first and last both partial
+ test.Next(_L("Test read-modify-write #4"));
+ pos = cacheLineLen*1 + KSegmentSize*2 + 3;
+ len = KSegmentSize * 1;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back & verify whole segment
+ pos&= ~KSegmentSizeMask; len = (len + KSegmentSize-1) & ~KSegmentSizeMask;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ readPtr.FillZ();
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ test (fileCacheStats.iUncachedBytesRead - uncachedBytesRead == KSegmentSize*2);
+ // should read both segments in one read as they are contiguous
+ test (fileCacheStats.iUncachedPacketsRead - uncachedPacketsRead == 1);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ // write 3 segments, first and last both partial
+ test.Next(_L("Test read-modify-write #5"));
+ pos = cacheLineLen*2 + KSegmentSize*2 + 7;
+ len = KSegmentSize * 2;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back & verify whole segment
+ pos&= ~KSegmentSizeMask; len = (len + KSegmentSize-1) & ~KSegmentSizeMask;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ readPtr.FillZ();
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ test (fileCacheStats.iUncachedBytesRead - uncachedBytesRead == KSegmentSize*2);
+ test (fileCacheStats.iUncachedPacketsRead - uncachedPacketsRead == 2);
+ uncachedBytesRead = fileCacheStats.iUncachedBytesRead;
+ uncachedPacketsRead = fileCacheStats.iUncachedPacketsRead;
+#endif
+
+ f.Close();
+
+ //**************************************************************
+ // Test dirty data NOT written to disk if continuously writing to a file which fits in tke cache
+ //**************************************************************
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Printf(_L("Test dirty data NOT written to disk if continuously writing to a file which fits in tke cache...\n"));
+
+ // flush closed files queue to empty cache
+ test.Printf(_L("Flushing close queue to empty cache...\n"));
+ r = TheFs.ControlIo(gDrive, KControlIoFlushClosedFiles);
+ test_KErrNone(r);
+
+ r = f.Replace(TheFs, testFile, EFileReadBuffered | EFileWrite | EFileWriteBuffered);
+ test_KErrNone(r);
+
+ RTimer timer;
+ timer.CreateLocal();
+ TRequestStatus reqStat;
+ timer.After(reqStat,gFileCacheConfig.iClosedFileKeepAliveTime*3); // write 3 times the flush timer
+
+ pos = 0;
+ TInt bufLen = 7;
+ TInt maxLockedSegmentCount = 0;
+ while (reqStat == KRequestPending)
+ {
+ bufLen = (bufLen + 1023) & 0x3FFF;
+ len = Min(bufLen, KBufSize - pos);
+ len = Min(len, gFileCacheConfig.iCacheSize-pos);
+ TPtrC8 writePtr = gBuf->Mid(pos, len);
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ pos+= len;
+ TInt r = controlIo(TheFs,gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test_KErrNone(r);
+ // test the locked (i.e. dirty) count always goes up & never down (down would indicate a flush)
+ if (fileCacheStats.iLockedSegmentCount != maxLockedSegmentCount)
+ test.Printf(_L("iLockedSegmentCount %d...\n"), fileCacheStats.iLockedSegmentCount);
+
+ test(fileCacheStats.iLockedSegmentCount >= maxLockedSegmentCount);
+ maxLockedSegmentCount = Max(maxLockedSegmentCount, fileCacheStats.iLockedSegmentCount);
+ // wrap to start of file
+ if (pos >= gFileCacheConfig.iCacheSize)
+ pos = 0;
+ }
+ timer.Close();
+
+ test(fileCacheStats.iLockedSegmentCount > 0);
+
+ if (gDriveCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))
+ TestDirtyDataWrittenToDisk(fileCacheStats);
+ f.Close();
+#endif
+
+
+ //**************************************************************
+ // Test dirty data written to disk
+ //**************************************************************
+ enum {ETestDataWrittenAfterTimeout, ETestDataWrittenAfterFileClose, ETestDataWrittenAfterSessionClose, ETestEnd};
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ for (testNum = 0; testNum < ETestEnd; testNum++)
+#else
+ for (testNum = 0; testNum < 1; testNum++)
+#endif
+ {
+ switch(testNum)
+ {
+ case ETestDataWrittenAfterTimeout : test.Next(_L("ETestDataWrittenAfterTimeout")); break;
+ case ETestDataWrittenAfterFileClose : test.Next(_L("ETestDataWrittenAfterFileClose")); break;
+ case ETestDataWrittenAfterSessionClose : test.Next(_L("ETestDataWrittenAfterSessionClose")); break;
+ };
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // flush closed files queue to empty cache
+ test.Printf(_L("Flushing close queue to empty cache...\n"));
+ r = TheFs.ControlIo(gDrive, KControlIoFlushClosedFiles);
+ test_KErrNone(r);
+#endif
+
+ r = f.Replace(TheFs, testFile, EFileReadBuffered | EFileWrite | EFileWriteBuffered);
+ test_KErrNone(r);
+
+
+ gBufPtr.SetLength(KBufSize);
+
+ test.Printf(_L("writing file in small blocks to test write caching...\n"));
+
+ FillBuffer(gBufPtr, KBufSize);
+ TestBuffer(gBufPtr, 0,KBufSize);
+
+ pos = 0;
+ TInt bufLen = 7;
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TInt maxLockedSegmentCount = 0;
+ TInt maxUsedCount = 0;
+#endif
+ while (pos < KBufSize)
+ {
+ bufLen = (bufLen + 1023) & 0x3FFF;
+ len = Min(bufLen, KBufSize - pos);
+//RDebug::Print(_L("write len %d"), len);
+ TPtrC8 writePtr = gBuf->Mid(pos, len);
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ pos+= len;
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+// PrintFileCacheStats(fileCacheStats);
+ TInt r = controlIo(TheFs,gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test_KErrNone(r);
+ maxLockedSegmentCount = Max(maxLockedSegmentCount, fileCacheStats.iLockedSegmentCount);
+ maxUsedCount = Max(maxUsedCount, fileCacheStats.iUsedCount);
+#endif
+ }
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Next(_L("Test maxiimum locked page and cacheline count is reasonable"));
+ test.Printf(_L("maxLockedSegmentCount %d maxUsedCount %d"), maxLockedSegmentCount, maxUsedCount);
+ test (maxLockedSegmentCount > 0 && maxLockedSegmentCount <= (gFileCacheConfig.iCacheSize * 2) / KSegmentSize );
+ test (maxUsedCount > 0 && maxUsedCount <= 5);
+#endif
+
+
+
+ if (testNum == ETestDataWrittenAfterTimeout)
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ if (gDriveCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))
+ TestDirtyDataWrittenToDisk(fileCacheStats);
+#endif
+ f.Close();
+ }
+
+ if (testNum == ETestDataWrittenAfterFileClose)
+ {
+ f.Close();
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Next(_L("test dirty data has been written to disk after file close"));
+ PrintFileCacheStats(fileCacheStats);
+ test(fileCacheStats.iLockedSegmentCount == 0);
+#endif
+ }
+
+ if (testNum == ETestDataWrittenAfterSessionClose)
+ {
+ // verify that closing the file server session (i.e. not the file)
+ // flushes the data to disk...
+ // *** NB This is likely to complete sometime AFTER returning from RFs::Close()
+ // *** as the file server doesn't wait for the disconnect thread to complete !!!
+
+ TheFs.Close();
+ TheFs.Connect();
+ SetSessionPath(gDrive);
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Next(_L("Test dirty data is eventually written to disk after session close"));
+ TestDirtyDataWrittenToDisk(fileCacheStats);
+ test(fileCacheStats.iLockedSegmentCount == 0);
+#endif
+ }
+
+ } // for (TInt testNum = 0; testNum < ETestEnd; testNum++)
+
+
+//#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+// PrintFileCacheStats(fileCacheStats);
+//#endif
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // flush closed files queue to empty cache
+ test.Printf(_L("Flushing close queue to empty cache...\n"));
+ r = TheFs.ControlIo(gDrive, KControlIoFlushClosedFiles);
+ test_KErrNone(r);
+#endif
+
+ r = f.Open(TheFs, testFile, EFileRead | EFileReadBuffered | EFileWrite);
+ test_KErrNone(r);
+
+ TInt size;
+ r = f.Size(size);
+ test (r == KErrNone);
+ test (size = KBufSize);
+
+ readPtr.Set(gBuf->Des());
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // Allocate full cachelines - so we can enable hole testing
+ TBool allocateAllSegmentsInCacheLine = ETrue;
+ r = controlIo(TheFs, gDrive, KControlIoAllocateMaxSegments, allocateAllSegmentsInCacheLine);
+ test (r == KErrNone);
+ PrintFileCacheStats(fileCacheStats, EFalse);
+ TInt holesDetected = fileCacheStats.iHoleCount;
+ TInt lockFailures = fileCacheStats.iCommitFailureCount + fileCacheStats.iLockFailureCount;
+#endif
+
+ test.Next(_L("Test reading a partially filled cacheline"));
+ // create a cacheline with only the first segment filled
+ TInt startPos = KSegmentSize;
+ pos = startPos;
+ len = KSegmentSize;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+
+ // read from first & second segments
+ pos = startPos;
+ len = 8192;
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+
+
+ test.Next(_L("Test reading an empty segment towards the end of a partially filled cacheline (hole test)"));
+ // read from segment #4 and beyond end of cacheline into next
+ pos = startPos + KSegmentSize * 4;
+ len = KSegmentSize * 33; // 132 K
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+
+
+ test.Next(_L("Test writing to an empty segment towards the end of a partially filled cacheline (hole test)"));
+ // create a cacheline with only the first segment filled
+ startPos = 256 * 1024;
+ pos = startPos;
+ len = KSegmentSize;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+
+
+ // write into segment 3, 4 & 5
+ pos = startPos + KSegmentSize * 2;
+ len = KSegmentSize * 3;
+ writePtr.Set(gBuf->Mid(pos, len));
+ r = f.Write(pos, writePtr, len);
+ test_KErrNone(r);
+ // read back whole cacheline & verify
+ pos = startPos;
+ len = KSegmentSize * 32; // 128 K
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheStats(fileCacheStats);
+ if (fileCacheStats.iCommitFailureCount + fileCacheStats.iLockFailureCount != lockFailures)
+ {
+ if (gRomPaged)
+ {
+ test.Printf(_L("Lock failures detected on paged ROM, abandoning hole test"));
+ }
+ else
+ {
+ test.Printf(_L("Unexpected lock failures detected on unpaged ROM!!!"));
+ test(0);
+ }
+ }
+ else
+ {
+ test(fileCacheStats.iHoleCount > holesDetected);
+ }
+ // Don't allocate full cachelines any more
+ allocateAllSegmentsInCacheLine = EFalse;
+ r = controlIo(TheFs, gDrive, KControlIoAllocateMaxSegments, allocateAllSegmentsInCacheLine);
+ test (r == KErrNone);
+#endif
+
+
+
+ gBufPtr.FillZ();
+ gBufPtr.SetLength(KBufSize);
+
+
+
+ readPtr.SetLength(0);
+
+
+ // read from middle of fifth sector to half way thru seventh
+ test.Next(_L("Test read that spans two pages"));
+ pos = 512*4 + 16;
+ len = 512*2;
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+
+
+
+ // read to end of file
+ test.Next(_L("Test reading past end of file"));
+ pos = KBufSize - 0x100; // 0xfb05;
+ len = KBufSize - pos;
+// pos = KBufSize - 16;
+// len = 512;;
+// len = Min(len, KBufSize-pos);
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len/* + 0x67*/);
+ test_KErrNone(r);
+
+ test.Next(_L("Test async reads"));
+ // issue 2 async reads
+ pos = KSegmentSize*7 + 16;
+ len = KSegmentSize;
+ RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+
+ TInt pos2 = pos + len;
+ TInt len2 = KSegmentSize;
+ TPtr8 readPtr2 = gBuf->Des();
+ readPtr2.Set(gBufPtr.MidTPtr(pos2, len2));
+ readPtr2.SetLength(len2);
+
+ TRequestStatus status1(KRequestPending);
+ TRequestStatus status2(KRequestPending);
+
+ f.Read(pos, readPtr, len, status1);
+ f.Read(readPtr2, len2, status2);
+
+
+ User::WaitForRequest(status1);
+ User::WaitForRequest(status2);
+ test.Printf(_L("status1 %d status2 %d\n"), status1.Int(), status2.Int());
+ TestBuffer(gBufPtr, pos, len);
+ TestBuffer(gBufPtr, pos2, len2);
+
+
+ test.Next(_L("Read entire file"));
+
+ for (pos = 0, len = 1; len <= 1024 && pos < KBufSize; len = (len+1) & 0x3FF)
+ {
+ len = Min(len, KBufSize-pos);
+// RDebug::Print(_L("+%x, %x\n"), pos, len);
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+ pos+= len;
+ }
+
+ TestBuffer(gBufPtr, 0, KBufSize);
+
+ // read from position zero to ensure it's cached
+ pos = 0;
+ len = 512;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+
+ f.Close();
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of files on closed queue=%d\n"),fileCacheStats.iFilesOnClosedQueue);
+ test(fileCacheStats.iFilesOnClosedQueue == 1);
+#endif
+ //
+
+
+ //**************************************************************
+ // Test closed file queue
+ //**************************************************************
+ enum
+ {
+ ETestCloseQueueEmptyAfterTimeout,
+ ETestCloseQueueEmptyAfterOpenWithDirectIo,
+ ETestCloseQueueEmptyAfterDelete,
+ ETestCloseEnd};
+ for (testNum = 0; testNum < ETestCloseEnd; testNum++)
+ {
+
+ test.Next(_L("Reopen file & verify closed queue is empty"));
+
+ r = f.Open(TheFs, testFile, EFileRead | EFileReadBuffered);
+ test_KErrNone(r);
+ pos = 0;
+ len = 512;
+ readPtr.Set(gBufPtr.MidTPtr(pos, len));
+ readPtr.SetLength(len);
+ r = f.Read(pos, readPtr, len);
+ test_KErrNone(r);
+ TestBuffer(gBufPtr, pos, len);
+ test_KErrNone(r);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of files on closed queue=%d\n"),fileCacheStats.iFilesOnClosedQueue);
+ test(fileCacheStats.iFilesOnClosedQueue == 0);
+#endif
+ f.Close();
+
+ test.Next(_L("close & verify file is back on close queue"));
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of files on closed queue=%d\n"),fileCacheStats.iFilesOnClosedQueue);
+ test(fileCacheStats.iFilesOnClosedQueue == 1);
+#endif
+
+ if (testNum == ETestCloseQueueEmptyAfterDelete)
+ {
+ test.Next(_L("delete file and verify closed queue is empty"));
+ r = TheFs.Delete(testFile);
+ test_KErrNone(r);
+ }
+ if (testNum == ETestCloseQueueEmptyAfterTimeout)
+ {
+ test.Next(_L("wait for a while and verify closed queue is empty"));
+ // wait for closed file queue to empty
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ User::After(gFileCacheConfig.iClosedFileKeepAliveTime + 1000000);
+#endif
+ }
+ if (testNum == ETestCloseQueueEmptyAfterOpenWithDirectIo)
+ {
+ test.Next(_L("Re-open file in directIo mode and then close and verify closed queue is empty"));
+ r = f.Open(TheFs, testFile, EFileRead | EFileReadDirectIO);
+ test_KErrNone(r);
+ f.Close();
+ }
+
+ #if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of files on closed queue=%d\n"),fileCacheStats.iFilesOnClosedQueue);
+ test(fileCacheStats.iFilesOnClosedQueue == 0);
+ #endif
+ }
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // turn lock failure mode back ON (if enabled)
+ simulatelockFailureMode = ETrue;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone);
+#endif
+
+ //**************************************************************
+ // Test opening a file with a silly open mode flags combinations
+ //**************************************************************
+ test.Next(_L("Open file with illegal cache on/off bits"));
+ r = f.Open(TheFs, testFile, EFileRead | EFileReadBuffered | EFileReadDirectIO);
+ test_Value(r, r==KErrArgument);
+ r = f.Open(TheFs, testFile, EFileRead | EFileWriteBuffered | EFileWriteDirectIO);
+ test_Value(r, r==KErrArgument);
+ //**********************************
+ // Test that continuously appending to a file yields the correct size...
+ // NB: Must have lock failure more ON in debug mode for this test to pass
+ //**********************************
+ test.Next(_L("Test appending to a file & checking the file size..."));
+ gBufPtr.SetLength(KBufSize);
+
+ r = f.Replace(TheFs, testFile, EFileWrite | EFileWriteBuffered);
+ test_KErrNone(r);
+
+ const TInt KWriteLen = KSegmentSize+1;
+ writePtr.Set(gBuf->Des().Ptr(), KWriteLen);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ test.Printf(_L("Number of Write-throughs with dirty data=%d\n"),fileCacheStats.iWriteThroughWithDirtyDataCount);
+ TInt writeThroughWithDirtyDataCountOld = fileCacheStats.iWriteThroughWithDirtyDataCount;
+ TInt writeThroughWithDirtyDataCountNew = writeThroughWithDirtyDataCountOld;
+#endif
+
+ TInt fileSize = 0;
+ for (TInt i=0; i<4; i++)
+ {
+ fileSize = 0;
+ r = f.SetSize(fileSize);
+ test (r == KErrNone);
+ for (pos = 0; pos < KMaxFileSize; )
+ {
+ r = f.Write(pos, writePtr);
+ if (r != KErrNone)
+ {
+ test.Printf(_L("Iter #%d, write pos %d size %d, Write() returned %d"), i, pos, fileSize, r);
+ r = f.Flush();
+ test.Printf(_L("Flush returned %d"), r);
+ test(0);
+ }
+ test(r == KErrNone);
+ pos+= writePtr.Length();
+
+ r = f.Size(fileSize);
+ test (r == KErrNone);
+ if (fileSize != pos)
+ {
+ test.Printf(_L("Iter #%d, write pos %d != size %d"), i, pos, fileSize);
+ r = f.Flush();
+ test.Printf(_L("Flush returned %d"), r);
+ test(0);
+ }
+ test (fileSize == pos);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ r = controlIo(TheFs, gDrive, KControlIoFileCacheStats, fileCacheStats);
+ test (r == KErrNone);
+ writeThroughWithDirtyDataCountNew = fileCacheStats.iWriteThroughWithDirtyDataCount;
+ if (writeThroughWithDirtyDataCountNew > writeThroughWithDirtyDataCountOld)
+ {
+ test.Printf(_L("Iter #%d, write pos %d size %d"), i, pos, fileSize);
+ test.Printf(_L("Number of Write-throughs with dirty data=%d\n"),fileCacheStats.iWriteThroughWithDirtyDataCount);
+ i = 4;
+ break;
+ }
+#endif
+
+ }
+ }
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test(writeThroughWithDirtyDataCountNew > writeThroughWithDirtyDataCountOld);
+#endif
+
+
+ f.Close();
+
+
+//TheFs.SetDebugRegister(0);
+ test.End();
+ }
+
+
+// This thread is used to test what happens if requests are sent to the file server
+// while the drive thread is hung in the critical notifier server. The requests should
+// complete immediately with KErrNotReady
+TInt ReaderThread(TAny* aFileName)
+ {
+ RTest test(_L("T_FILECACHE, ReaderThread"));
+
+ TDesC& fileName = *(TDesC*) aFileName;
+
+ RFs fs;
+ TInt r = fs.Connect();
+ test (r==KErrNone);
+ r = fs.SetSessionPath(gSessionPath);
+ test (r==KErrNone);
+
+
+ RFile file;
+ r = file.Open(fs, fileName, EFileRead | EFileReadBuffered | EFileShareReadersOrWriters);
+ test_KErrNone(r);
+
+ RTimer timer;
+ timer.CreateLocal();
+ TRequestStatus reqStat;
+ timer.After(reqStat,10000000); // Read for 10 secs
+
+ TInt pos=0;
+ TInt KReadLen = 32768;
+ TInt retCode = KErrNone;
+
+ for (TInt i=0; reqStat == KRequestPending; i++)
+ {
+ r = file.Read(pos, DataBuf, KReadLen);
+ test_Value(r, r == KErrNone || r == KErrNotReady);
+
+ //test.Printf(_L("ReaderThread: iter %d, read at %d ret %d\n"), i, pos, r);
+ if (r == KErrNotReady)
+ retCode = r;
+ else if (r == KErrNone)
+ pos = DataBuf.Length() == 0 ? 0: pos + DataBuf.Length();
+ if (retCode == KErrNotReady)
+ test.Printf(_L("ReaderThread: iter %d, read at %d ret %d \n"), i, pos, r);
+ }
+
+ timer.Close();
+ file.Close();
+ fs.Close();
+
+ return retCode;
+ }
+
+
+void ManualTests()
+ {
+ RFile f;
+ TInt r;
+ gBufPtr.SetLength(KBufSize);
+ FillBuffer(gBufPtr, KBufSize);
+ TestBuffer(gBufPtr, 0,KBufSize);
+
+ TPtrC8 writePtr;
+ writePtr.Set(gBuf->Des());
+
+ TFileName testFile = _L("TEST.BIN");
+
+#if defined(_DEBUG)
+ test.Next(_L("Testing writing and then closing with an immediate simulated card eject"));
+ test.Printf(_L("**********************************************************\n"));
+ test.Printf(_L("When critical notifier message appears, PRESS RETRY\n"));
+ test.Printf(_L("**********************************************************\n"));
+
+ r = f.Replace(TheFs, testFile, EFileWrite | EFileWriteBuffered | EFileShareReadersOrWriters);
+ test_KErrNone(r);
+
+ test.Printf(_L("Writing...."));
+ r = f.Write(0, writePtr);
+ test.Printf(_L("Write returned %d\n"), r);
+ test_KErrNone(r);
+
+ r=TheFs.ControlIo(gDrive, KControlIoSimulateFileCacheWriteFailure);
+ test_KErrNone(r);
+
+ f.Close();
+
+ TEntry entry;
+
+ // wait for re-insertion....
+ do
+ {
+ r = TheFs.Entry(testFile, entry);
+ User::After(500000);
+ }
+ while (r == KErrNotReady);
+ test_KErrNone(r);
+
+ test.Printf(_L("FileSize %d expected %d\n"), entry.iSize, writePtr.Length());
+
+ // test all data is written to disk
+ test (entry.iSize == writePtr.Length());
+#endif
+
+
+ test.Next(_L("Testing writing and then ejecting card"));
+
+// r = f.Replace(TheFs, testFile, EFileWrite | EFileWriteDirectIO);
+ r = f.Replace(TheFs, testFile, EFileWrite | EFileWriteBuffered | EFileShareReadersOrWriters);
+ test_KErrNone(r);
+ writePtr.Set(gBuf->Des());
+
+
+ // set the file size first so that the cluster chain already exists
+ r = f.SetSize(writePtr.Length());
+ test.Printf(_L("SetSize returned %d"));
+
+ // Create another thread which will attempt to issue reads while this thread is suspended
+ // These reads should complete with KErrNotReady
+ const TInt KHeapSize = 32768;
+ test.Printf(_L("Creating file-reading thread\n"));
+ RThread readerThread;
+ readerThread.Create(_L("FileReaderThread"), ReaderThread, KDefaultStackSize, KHeapSize, KHeapSize, &testFile);
+
+ test.Printf(_L("Writing...."));
+ r = f.Write(0, writePtr);
+ test.Printf(_L("Write returned %d\n"), r);
+
+
+ test.Printf(_L("Waiting 5 seconds.\n"));
+ test.Printf(_L("**********************************************************\n"));
+ test.Printf(_L("Please press a key, EJECT MEDIA BEFORE WRITE TIMER EXPIRES,\n"));
+ test.Printf(_L("wait for critical notifier, replace media and retry\n"));
+ test.Printf(_L("**********************************************************\n"));
+// test.Printf(_L("press any key..."));
+ test.Getch();
+ test.Printf(_L("\n"));
+
+ test.Printf(_L("Writing...."));
+ r = f.Write(0, writePtr);
+ test.Printf(_L("Write returned %d\n"), r);
+ readerThread.Resume();
+
+ User::After(5000000);
+
+ do
+ {
+ test.Printf(_L("Flushing....\n"));
+ r = f.Flush();
+ test.Printf(_L("Flush returned %d\n"), r);
+ if (r != KErrNone)
+ User::After(5000000);
+ }
+ while (r != KErrNone);
+
+
+// r = f.Write(0, writePtr);
+// test.Printf(_L("Write returned %d"));
+
+// test_KErrNone(r);
+
+ test.Next(_L("Testing reader thread completed with KErrNotReady"));
+ TRequestStatus status;
+ readerThread.Logon(status);
+ readerThread.Resume();
+ User::WaitForRequest(status);
+ test.Printf(_L("ReaderThread status %d\n"), status.Int());
+ test (status.Int() == KErrNotReady);
+ readerThread.Close();
+
+ test.Printf(_L("press any key..."));
+ test.Getch();
+ test.Printf(_L("\n"));
+
+ f.Close();
+ }
+
+
+LOCAL_C void DoTestFileRead(TUint aFileMode, TInt aReadBlockSize)
+//
+// Do Read Test
+//
+ {
+
+ RFile file;
+ TInt r=file.Open(TheFs,_L("READTEST.XXX"),EFileStream | aFileMode);
+ test_KErrNone(r);
+
+ TInt maxReadCount=KMaxFileSize/aReadBlockSize;
+ TInt loopCount=0;
+
+ TTime startTime;
+ TTime endTime;
+
+ RTimer timer;
+ timer.CreateLocal();
+ TRequestStatus reqStat;
+ timer.After(reqStat,10000000); // After 10 secs
+ startTime.HomeTime();
+
+ FOREVER
+ {
+ TInt pos=0;
+ file.Seek(ESeekStart,pos);
+ for(TInt ii=0;ii<maxReadCount;ii++)
+ {
+ r = file.Read(DataBuf,aReadBlockSize);
+ test_KErrNone(r);
+#if defined(_DEBUG)
+ for(TInt jj=0;jj<DataBuf.Size();jj++)
+ {
+ if (DataBuf[jj] != ((jj+ii*aReadBlockSize)%256))
+ {
+ test.Printf(_L("len %d size %d jj %d ii %d"), DataBuf.Length(), DataBuf.Size(), jj, ii);
+ test(0);
+ }
+// test(DataBuf[jj] == ((jj+ii*aReadBlockSize)%256) );
+ }
+#endif
+ if (reqStat!=KRequestPending)
+ {
+ endTime.HomeTime();
+
+ TInt functionCalls=loopCount*maxReadCount+ii;
+// TReal rate = ( TReal32(functionCalls) * TReal32(aReadBlockSize) ) / 10;
+// test.Printf(_L("Read %5d byte blocks:\t%11.3f KBytes/s\n"), aReadBlockSize, rate / (KOneK));
+
+ TReal transferRate =
+ (TReal32(functionCalls) * TReal32(aReadBlockSize)) /
+ TReal(endTime.MicroSecondsFrom(startTime).Int64()) * TReal(KOneMeg) / TReal(KOneK);
+ test.Printf(_L("Read %5d byte blocks:\t%11.3f KBytes/s\n"), aReadBlockSize,transferRate);
+
+ timer.Close();
+ file.Close();
+
+ return;
+ }
+ }
+ loopCount++;
+ }
+ }
+
+LOCAL_C void DoTestFileWrite(TUint aFileMode, TInt aWriteBlockSize)
+//
+// Do Write benchmark
+//
+ {
+ DataBuf.SetLength(aWriteBlockSize);
+ TInt maxWriteCount=KMaxFileSize/aWriteBlockSize;
+ TInt loopCount=0;
+
+ RFile file;
+ TInt r = file.Replace(TheFs,_L("WRITETST"),EFileStream | aFileMode);
+ test (r == KErrNone);
+
+ TTime startTime;
+ TTime endTime;
+
+ RTimer timer;
+ timer.CreateLocal();
+ TRequestStatus reqStat;
+ timer.After(reqStat,10000000); // After 10 secs
+ startTime.HomeTime();
+
+
+
+ FOREVER
+ {
+ TInt pos=0;
+ file.Seek(ESeekStart,pos);
+ for(TInt ii=0;ii<maxWriteCount;ii++)
+ {
+ r = file.Write(DataBuf);
+ test_KErrNone(r);
+ if (reqStat!=KRequestPending)
+ {
+
+// TReal rate = (TReal32(functionCalls) * TReal32(aWriteBlockSize)) / 10;
+// test.Printf(_L("Write %5d byte blocks:\t%11.3f KBytes/s\n"), aWriteBlockSize, rate / (KOneK));
+
+ r = file.Flush();
+ test_KErrNone(r);
+
+ endTime.HomeTime();
+
+ TInt functionCalls=loopCount*maxWriteCount+ii;
+ TReal transferRate =
+ (TReal32(functionCalls) * TReal32(aWriteBlockSize)) /
+ TReal(endTime.MicroSecondsFrom(startTime).Int64()) * TReal(KOneMeg) / TReal(KOneK);
+ test.Printf(_L("Write %5d byte blocks:\t%11.3f KBytes/s\n"), aWriteBlockSize,transferRate);
+
+ file.Close();
+ timer.Close();
+ TInt r=TheFs.Delete(_L("WRITETST"));
+ test_KErrNone(r);
+ return;
+ }
+ }
+ loopCount++;
+ }
+ }
+
+LOCAL_C void TestFileRead(TUint aFileMode)
+//
+// Benchmark read method
+//
+ {
+
+// ClearSessionDirectory();
+ test.Next(_L("Benchmark read method"));
+// test.Printf(_L("aFileMode %08X\n"), aFileMode);
+ PrintFileMode(aFileMode);
+
+// Create test data
+ TBuf8<1024> testdata(1024);
+ for (TInt i=0;i<testdata.MaxSize();i++)
+ testdata[i]=TText8(i%256);
+
+ // create & fill the file
+ RFile file;
+ TInt r=file.Replace(TheFs,_L("READTEST.XXX"),EFileStream | aFileMode);
+ test_KErrNone(r);
+
+ test.Printf(_L("Creating test file....."));
+ TInt count=KMaxFileSize/testdata.Length();
+ while (count--)
+ file.Write(testdata);
+ file.Close();
+ test.Printf(_L("done.\n"));
+
+#ifdef SYMBIAN_TEST_EXTENDED_BUFFER_SIZES
+
+ DoTestFileRead(aFileMode, 1);
+ DoTestFileRead(aFileMode, 2);
+ DoTestFileRead(aFileMode, 4);
+ DoTestFileRead(aFileMode, 8);
+ DoTestFileRead(aFileMode, 16);
+ DoTestFileRead(aFileMode, 32);
+ DoTestFileRead(aFileMode, 64);
+ DoTestFileRead(aFileMode, 128);
+ DoTestFileRead(aFileMode, 256);
+ DoTestFileRead(aFileMode, 512);
+ DoTestFileRead(aFileMode, 1024);
+ DoTestFileRead(aFileMode, 2048);
+ DoTestFileRead(aFileMode, 4096);
+ DoTestFileRead(aFileMode, 8192);
+ DoTestFileRead(aFileMode, 16384);
+ DoTestFileRead(aFileMode, 32768);
+ DoTestFileRead(aFileMode, 65536);
+ DoTestFileRead(aFileMode, 131072);
+#else
+//TheFs.SetDebugRegister(KCACHE);
+ DoTestFileRead(aFileMode, 1);
+ DoTestFileRead(aFileMode, 16);
+ DoTestFileRead(aFileMode, 512);
+//TheFs.SetDebugRegister(0);
+ DoTestFileRead(aFileMode, 4 * 1024);
+ DoTestFileRead(aFileMode, 32 * 1024);
+ DoTestFileRead(aFileMode, 64 * 1024);
+ DoTestFileRead(aFileMode, 128 * 1024);
+ DoTestFileRead(aFileMode, 256 * 1024);
+#endif
+
+ r=TheFs.Delete(_L("READTEST.XXX"));
+
+
+
+ test_KErrNone(r);
+ }
+
+
+LOCAL_C void TestFileWrite(TUint aFileMode)
+//
+// Benchmark write method
+//
+ {
+// ClearSessionDirectory();
+ test.Next(_L("Benchmark write method"));
+// test.Printf(_L("aFileMode %08X\n"), aFileMode);
+ PrintFileMode(aFileMode);
+
+
+// RFile file;
+// TInt r = file.Replace(TheFs,_L("WRITETST"),EFileStream | aFileMode);
+// test_KErrNone(r);
+
+
+#ifdef SYMBIAN_TEST_EXTENDED_BUFFER_SIZES
+ DoTestFileWrite(aFileMode, 1);
+ DoTestFileWrite(aFileMode, 2);
+ DoTestFileWrite(aFileMode, 4);
+ DoTestFileWrite(aFileMode, 8);
+ DoTestFileWrite(aFileMode, 16);
+ DoTestFileWrite(aFileMode, 32);
+ DoTestFileWrite(aFileMode, 64);
+ DoTestFileWrite(aFileMode, 128);
+ DoTestFileWrite(aFileMode, 256);
+ DoTestFileWrite(aFileMode, 512);
+ DoTestFileWrite(aFileMode, 1024);
+ DoTestFileWrite(aFileMode, 2048);
+ DoTestFileWrite(aFileMode, 4096);
+ DoTestFileWrite(aFileMode, 8192);
+ DoTestFileWrite(aFileMode, 16384);
+ DoTestFileWrite(aFileMode, 32768);
+ DoTestFileWrite(aFileMode, 65536);
+ DoTestFileWrite(aFileMode, 131072);
+#else
+ DoTestFileWrite(aFileMode, 1);
+ DoTestFileWrite(aFileMode, 16);
+ DoTestFileWrite(aFileMode, 512);
+ DoTestFileWrite(aFileMode, 4 * 1024);
+ DoTestFileWrite(aFileMode, 32 * 1024);
+ DoTestFileWrite(aFileMode, 64 * 1024);
+ DoTestFileWrite(aFileMode, 128 * 1024);
+ DoTestFileWrite(aFileMode, 256 * 1024);
+#endif
+ }
+
+enum CommandId {
+ CMD_NO_COMMAND,
+ CMD_HELP,
+ CMD_DISPLAY_CACHE_FLAGS,
+ CMD_WRITE_CACHE_FLAGS,
+ CMD_PERFORMANCE_TEST,
+ CMD_MANUAL_TEST
+};
+
+
+class CommandLineOption {
+public:
+ CommandLineOption(const TPtrC &s, CommandId i) : str(s), id(i) { }
+
+ const TPtrC str;
+ CommandId id;
+
+private:
+ CommandLineOption() { }
+};
+
+// Lists of command line options
+static const CommandLineOption commandList[] = {
+ CommandLineOption(_L("-help"), CMD_HELP),
+ CommandLineOption(_L("-h"), CMD_HELP),
+ CommandLineOption(_L("-?"), CMD_HELP),
+ CommandLineOption(_L("/?"), CMD_HELP),
+ CommandLineOption(_L("-d"), CMD_DISPLAY_CACHE_FLAGS),
+ CommandLineOption(_L("-w"), CMD_WRITE_CACHE_FLAGS),
+ CommandLineOption(_L("-p"), CMD_PERFORMANCE_TEST),
+ CommandLineOption(_L("-m"), CMD_MANUAL_TEST),
+};
+
+
+
+LOCAL_C void printHelp() {
+ test.Printf(_L("Option Explanation\r\n"));
+ test.Printf(_L("-help Print this text\r\n"));
+ test.Printf(_L("-d Display cache flags\n"));
+ test.Printf(_L("-p Performance test\n"));
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Function:
+// bool parseCommandLine()
+//
+// Parameters:
+// -
+//
+// Return value:
+// true if command line options was correct, false if any command was
+// invalid
+//
+// Purpose:
+// Parses the command line
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//TBuf<512> commandLine;
+
+
+LOCAL_C bool ParseCommandLine( const TDesC& aCommand )
+ {
+ TLex lex(aCommand);
+ TInt tokenCount = 0;
+
+ gDriveToTest='C';
+ gRunTests = ETrue;
+
+ for (TPtrC token=lex.NextToken(); token.Length() != 0;token.Set(lex.NextToken()))
+ {
+ tokenCount++;
+
+ //
+ // Search the list of commands for a match.
+ //
+ CommandId commandId = CMD_NO_COMMAND;
+ for (TUint i = 0; i < sizeof commandList / sizeof commandList[0]; i++)
+ {
+ if (token.CompareF(commandList[i].str) == 0)
+ {
+ //
+ // Found a matching string
+ //
+ commandId = commandList[i].id;
+ break;
+ }
+ }
+
+ switch (commandId)
+ {
+ case CMD_NO_COMMAND:
+ {
+ TFileName thisfile=RProcess().FileName();
+ if (token.MatchF(thisfile)==0)
+ {
+ token.Set(lex.NextToken());
+ }
+ test.Printf(_L("CLP=%S\n"),&token);
+
+ TChar ch = token[0];
+ if (ch.IsAlpha())
+ {
+ if(token.Length()!=0)
+ {
+ gDriveToTest=token[0];
+ gDriveToTest.UpperCase();
+ }
+ else
+ gDriveToTest='C';
+ lex.NextToken();
+ }
+ }
+ break;
+
+ case CMD_HELP:
+ printHelp();
+ gRunTests = EFalse;
+ break;
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ case CMD_WRITE_CACHE_FLAGS:
+ {
+ token.Set(lex.NextToken());
+ if (token.Length() == 0) {
+ printHelp();
+ return false;
+ }
+ //
+ // Extract flags (in hex) from next token.
+ //
+ TPtrC numStr(token);
+ TUint val;
+ TInt r = TLex(numStr).Val(val, EHex);
+ if (r != KErrNone) {
+ printHelp();
+ return false;
+ }
+ gDriveCacheFlags = TFileCacheFlags(val);
+ gWriteCacheFlags = ETrue;
+ gRunTests = EFalse;
+ break;
+ }
+
+ case CMD_DISPLAY_CACHE_FLAGS:
+ gDisplayCacheFlags = ETrue;
+ gRunTests = EFalse;
+ break;
+#endif
+ case CMD_PERFORMANCE_TEST:
+ gRunPerformanceTests = ETrue;
+ gRunUnitTests = EFalse;
+ break;
+
+ case CMD_MANUAL_TEST:
+ gRunManualTests = ETrue;
+ gRunUnitTests = EFalse;
+ break;
+
+ default:
+ test.Printf(_L("Sorry, command option '%S' not implemented\n"),
+ &token);
+ break;
+ }
+
+ }
+ return true;
+}
+
+
+LOCAL_C TBool parseCommandLine() {
+ //
+ // Loop through all tokens in the command line
+ //
+ TInt cmdLineLen = User::CommandLineLength();
+ HBufC* cmdLineBuf = HBufC::New( cmdLineLen );
+ if( !cmdLineBuf )
+ {
+ return false;
+ }
+ TPtr ptr = cmdLineBuf->Des();
+ User::CommandLine(ptr);
+
+ bool err = ParseCommandLine( *cmdLineBuf );
+ delete cmdLineBuf;
+ return err;
+ }
+
+GLDEF_C void CallTestsL()
+//
+// Do tests relative to the session path
+//
+ {
+ TestsInit();
+
+ TVolumeInfo volInfo;
+ TInt r = TheFs.Volume(volInfo, gDrive);
+ test (r == KErrNone);
+
+ TFullName extName;
+ r = TheFs.ExtensionName(extName,gDrive, 0);
+ if (r == KErrNone)
+ {
+ test.Printf(_L("File system extension present (%S)\n"), &extName);
+ }
+
+ if ((volInfo.iDrive.iType == EMediaRam) ||
+ ((gDriveCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn)) == 0))
+ {
+ if (gRunPerformanceTests)
+ {
+ TestFileRead(EFileReadDirectIO);
+ TestFileWrite(EFileWriteDirectIO);
+ }
+ TestsEnd();
+ return;
+ }
+
+ if (gRunUnitTests)
+ UnitTests();
+
+ if (gRunManualTests)
+ {
+ ManualTests();
+ }
+
+ if (gRunPerformanceTests)
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // turn OFF lock failure mode
+ TBool simulatelockFailureMode = EFalse;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone);
+#endif
+
+ TestFileRead(EFileReadDirectIO);
+ if (gDriveCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))
+ {
+ TestFileRead(EFileReadBuffered | EFileReadAheadOff);
+ TestFileRead(EFileReadBuffered | EFileReadAheadOn);
+ }
+
+ TestFileWrite(EFileWriteDirectIO);
+
+ if (gDriveCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))
+ TestFileWrite(EFileWriteBuffered);
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ // turn lock failure mode back ON (if enabled)
+ simulatelockFailureMode = ETrue;
+ r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode);
+ test (r == KErrNone);
+#endif
+ } // if (gRunPerformanceTests)
+
+ TestsEnd();
+ }
+
+
+
+LOCAL_C void DoTests(TInt aDrive)
+//
+// Do testing on aDrive
+//
+ {
+
+ SetSessionPath(aDrive);
+
+// !!! Disable platform security tests until we get the new APIs
+// if(User::Capability() & KCapabilityRoot)
+// CheckMountLFFS(TheFs,driveLetter);
+
+ User::After(1000000);
+
+ TInt r=TheFs.MkDirAll(gSessionPath);
+ test_Value(r, r == KErrNone || r == KErrAlreadyExists);
+ TheFs.ResourceCountMarkStart();
+
+ TRAP(r,CallTestsL());
+ test_KErrNone(r);
+
+ TheFs.ResourceCountMarkEnd();
+ }
+
+
+void Format(TInt aDrive)
+//
+// Format current drive
+//
+ {
+
+ test.Next(_L("Format"));
+ TBuf<4> driveBuf=_L("?:\\");
+ driveBuf[0]=(TText)(aDrive+'A');
+ RFormat format;
+ TInt count;
+ TInt r=format.Open(TheFs,driveBuf,EQuickFormat,count);
+ //TInt r=format.Open(TheFs,driveBuf,EFullFormat,count);
+ test.Printf(_L("RFormat::Open() returned %d\n"), r);
+ test_KErrNone(r);
+ while(count)
+ {
+ TInt r=format.Next(count);
+ test_KErrNone(r);
+ }
+ format.Close();
+ }
+
+GLDEF_C TInt E32Main()
+//
+// Test with drive nearly full
+//
+ {
+
+ CTrapCleanup* cleanup;
+ cleanup=CTrapCleanup::New();
+
+ __UHEAP_MARK;
+
+ TBool parseOk = parseCommandLine();
+ if (!parseOk)
+ User::Leave(KErrNotSupported);
+
+
+ TInt r = TheFs.Connect();
+ test_KErrNone(r);
+
+ r=TheFs.CharToDrive(gDriveToTest,gDrive);
+ test_KErrNone(r);
+
+#if !defined(__WINS__)
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ test.Start(_L("Check that the rom is paged"));
+ TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+ if (romHeader->iPageableRomStart != NULL)
+ {
+ test.Printf(_L("ROM is paged\n"));
+ gRomPaged = ETrue;
+ }
+#endif
+#endif
+
+ // Get the TFileCacheFlags for this drive
+ r = TheFs.Volume(gVolInfo, gDrive);
+ if (r == KErrNotReady)
+ {
+ TDriveInfo info;
+ TInt err = TheFs.Drive(info,gDrive);
+ test_KErrNone(err);
+ if (info.iType == EMediaNotPresent)
+ test.Printf(_L("%c: Medium not present - cannot perform test.\n"), (TUint)gDriveToTest);
+ 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)gDriveToTest);
+ }
+ else if (r == KErrCorrupt)
+ {
+ test.Printf(_L("%c: Media corruption; previous test may have aborted; else, check hardware\n"), (TUint)gDriveToTest);
+ }
+ test_KErrNone(r);
+ gDriveCacheFlags = gVolInfo.iFileCacheFlags;
+ test.Printf(_L("DriveCacheFlags for drive %C = %08X\n"), (TInt) gDriveToTest, gDriveCacheFlags);
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ PrintFileCacheConfig(gFileCacheConfig, gDrive);
+
+ if (gDisplayCacheFlags)
+ {
+ test.Printf(_L("Press any key...\n"));
+ test.Getch();
+ }
+
+ if (gWriteCacheFlags)
+ {
+ test.Printf(_L("Writing DriveCacheFlags for drive %C = %08X\n"), (TInt) gDriveToTest, gDriveCacheFlags);
+ r = controlIo(TheFs,gDrive, KControlIoFileCacheFlagsWrite, gDriveCacheFlags);
+ test (r == KErrNone);
+ }
+#endif
+
+ if (gRunTests)
+ {
+ test.Title();
+
+
+ test.Start(_L("Starting tests..."));
+
+ if ((gVolInfo.iDrive.iMediaAtt & KMediaAttFormattable))
+ Format(gDrive);
+
+//TheFs.SetDebugRegister(KCACHE);
+ DoTests(gDrive);
+//TheFs.SetDebugRegister(0);
+ test.End();
+// }
+ }
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TFileCacheStats fileCacheStats;
+ PrintFileCacheStats(fileCacheStats);
+#endif
+
+ TheFs.Close();
+ test.Close();
+ __UHEAP_MARKEND;
+ delete cleanup;
+ return(KErrNone);
+ }