diff -r 000000000000 -r a41df078684a kerneltest/f32test/bench/t_notify_perf_impl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/bench/t_notify_perf_impl.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1664 @@ +// Copyright (c) 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\bench\t_notify_perf_impl.cpp +// +// + +#include "t_notify_perf.h" +#include "t_server.h" + +extern void FileNameGen(TFileName& aName, TInt aNum, TBool aIsFile = ETrue); +extern TInt FileOperationThread(TAny* aSetting); +extern TInt NotificationOperationThread(TAny* aSetting); +extern TInt KillerThread(TAny*); +extern TUint OpNotifyMapping(TUint16& aOption, TInt aOperation); +extern TBool CompareEntryName(const TEntry& aEntry1, const TEntry& aEntry2); +extern void SafeTestL(TBool aResult, TInt aId, TInt aLine, TText* aFile); +extern void SafeTestL(TInt aResult, TInt aExpected, TInt aId, TInt aLine, TText* aFile); + +const TInt KNotificationHeaderSize = (sizeof(TUint16)*2)+(sizeof(TUint)); +const TInt KMinNotificationBufferSize = 2*KNotificationHeaderSize + 2*KMaxFileName; + +TBool gPerfMeasure; +TFileName gTestPath; +TFileName gLogFilePath; +RArray gNotiThreads; +RThread gFileThread; +TBuf<50> gLogPostFix; + +// Prints out the filename +#define ExpandMe(X) L ## X +#define Expand(X) ExpandMe(X) + +// Safe test, so that sub-threads are not hanging after check fail +#define SAFETEST0(a) SafeTestL(a,KNoThreadId,__LINE__,(TText*)Expand("t_notify_perf_impl.cpp")) +#define SAFETEST1(a,b) SafeTestL(a,b,__LINE__,(TText*)Expand("t_notify_perf_impl.cpp")) +#define SAFETEST2(a,b,c) SafeTestL(a,b,c,__LINE__,(TText*)Expand("t_notify_perf_impl.cpp")) + + +TTestSetting::TTestSetting() +: iNumFiles(0), iNumCli(0), iOption(0), iOperationList(NULL) + { + } + +TTestSetting::TTestSetting(TInt aNumFiles, TInt aNumCli, TUint16 aOpt, const TUint* aOpList) +: iNumFiles(aNumFiles), iNumCli(aNumCli), iOption(aOpt), iOperationList(aOpList) + { + } + +//=========================================================== + +CTimerLogger* CTimerLogger::NewL(const TFileName& aLogFile) + { + CTimerLogger* self = new(ELeave) CTimerLogger(); + CleanupStack::PushL(self); + self->ConstructL(aLogFile); + CleanupStack::Pop(self); + return self; + } + +void CTimerLogger::ConstructL(const TFileName& aLogFile) + { + User::LeaveIfError(HAL::Get(HALData::ENanoTickPeriod, iTickPeriod)); + iLogFile.Copy(aLogFile); + } + +CTimerLogger::CTimerLogger() +: iTiming(EFalse), iTickNumber(0), iTimeScale(KDefaultTimeScale) + { + iFs.Connect(); + } + +CTimerLogger::~CTimerLogger() + { + iFs.Close(); + } + +// Write Logs +TInt CTimerLogger::Log(const TDesC& aDes, TBool aIsLine) + { + RFile file; + iFs.Connect(); + + TInt err = file.Open(iFs,iLogFile,EFileShareExclusive|EFileWrite); + SAFETEST0(err == KErrNone || err == KErrNotFound || err == KErrPathNotFound); + + if (err != KErrNone) + { + err = iFs.MkDirAll(iLogFile); + SAFETEST0(err == KErrNone || err == KErrAlreadyExists); + err = file.Create(iFs,iLogFile,EFileShareExclusive|EFileWrite); + SAFETEST0(err == KErrNone); + } + + TBuf8<240> data; + data.Copy(aDes); + if (aIsLine) + { + data.Append(_L8("\r\n")); + } + + TInt offset = 0; + err = file.Seek(ESeekEnd, offset); + SAFETEST0(err == KErrNone); + err = file.Write(data); + SAFETEST0(err == KErrNone); + + file.Close(); + iFs.Close(); + return err; + } + +// Write Logs and also print the line written in the console +TInt CTimerLogger::LogAndPrint(const TDesC& aDes, TBool aIsLine) + { + TInt err = KErrNone; + RDebug::Print(aDes); + if (gPerfMeasure) + err = Log(aDes, aIsLine); + return err; + } + +// Write and print the time result +TInt CTimerLogger::LogTestStepTime(TUint aOp, TInt aNum) + { + if (iTiming) + return KErrGeneral; + + TBuf<100> buf; + switch (aOp) + { + case EOpCreate: + buf.Append(_L("Create - ")); + break; + case EOpReplace: + buf.Append(_L("Replace - ")); + break; + case EOpChgAttr: + buf.Append(_L("Change Attribute - ")); + break; + case EOpRename: + buf.Append(_L("Rename - ")); + break; + case EOpWrite: + buf.Append(_L("Write - ")); + break; + case EOpResize: + buf.Append(_L("Resize - ")); + break; + case EOpDelete: + buf.Append(_L("Delete - ")); + break; + case EOpManyChanges: + buf.AppendFormat(_L("%d Changes on Single File - "), aNum); + break; + case EOpManyFiles: + buf.AppendFormat(_L("Small Changes on %d Files - "), aNum); + break; + case EOpCreateDir: + buf.Append(_L("Create(dir) - ")); + break; + case EOpRenameDir: + buf.Append(_L("Rename(dir) - ")); + break; + case EOpDeleteDir: + buf.Append(_L("Delete(dir) - ")); + break; + case EOpMixed: + buf.AppendFormat(_L("%d Mixed Operations - "), aNum*18); + default: + break; + } + + TReal time = (static_cast(iTickNumber) * iTickPeriod) / iTimeScale; + buf.AppendFormat(_L("time: %d ms"), static_cast(time)); + return LogAndPrint(buf); + } + +// write and print the test case discription +TInt CTimerLogger::LogSettingDescription(const TInt aNumFile, const TInt aNumCli, const TUint16 aOption, TBool aNumOpVaries) + { + LogAndPrint(_L("====================================================")); + + TBuf<120> buf; + buf.Append(_L("Test: ")); + if (!aNumOpVaries) + { + buf.AppendFormat(_L("%d files/directories, %d Clients, "), aNumFile, aNumCli); + } + + // Notification Type + switch(aOption & KNotifyOptionMask) + { + case ENoNotify: + buf.Append(_L("No Notification")); + break; + case EEnhanced: + buf.Append(_L("Enhanced")); + break; + case EOriginal: + buf.Append(_L("Original")); + break; + case EPlugin: + buf.Append(_L("Nokia Plug-in")); + break; + default: + return KErrArgument; + } + + if (aOption & EBigFilter) + { + buf.Append(_L(", 100 Filters")); + } + + if (aOption & EReportChg) + { + buf.Append(_L(", Change Reporting")); + } + else + { + buf.Append(_L(", Not Report Changes")); + } + + if (aOption & EEnhanced) + { + if (aOption & EBigBuffer) + { + buf.Append(_L(", Big Buffer")); + } + else + { + buf.Append(_L(", Small Buffer")); + } + + if (aOption & EMultiNoti1) + { + buf.Append(_L(", Multi Notification Mode 1")); + } + else if (aOption & EMultiNoti2) + { + buf.Append(_L(", Multi Notification Mode 2")); + } + } + + LogAndPrint(buf); + LogAndPrint(_L("----------------------------------------------------")); + + return KErrNone; + } + +//=========================================================== + +CTestExecutor::CTestExecutor(TTestSetting& aSetting) +: iTestSetting(aSetting) + { + } + +CTestExecutor::~CTestExecutor() + { + } + +void CTestExecutor::KillAllTestThreads() + { + RThread killer; + killer.Create(_L("KillerThread"), KillerThread, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, NULL); + killer.Resume(); + + TRequestStatus status; + killer.Logon(status); + User::WaitForRequest(status); + TInt err = killer.ExitReason(); + test(err == KErrNone); + } + +// start run a test case +void CTestExecutor::RunTestCaseL() + { + RSemaphore smphF; + smphF.CreateLocal(0); + RSemaphore smphN; + smphN.CreateLocal(0); + + RArray notiThreads; // list of handles of notification threads + RPointerArray loggerList; + + TUint16 count = 0; + TUint16 option = iTestSetting.iOption; + while (count < iTestSetting.iNumCli) + { + test(count < 16); + iTestSetting.iOption = (TUint16)(option + count); // Put Thread ID in option + + TThreadParam param; + param.iSetting = iTestSetting; + param.iSmphFT = &smphF; + param.iSmphNT = &smphN; + + TFileName logName; + logName.FillZ(); + + if (gPerfMeasure) + { + logName.Append(gLogFilePath); + if (iTestSetting.iNumCli == 1) + logName.Append(_L("SingleClient")); + else + logName.AppendFormat(_L("MultiClient%02d"), count); + + logName.Append(gLogPostFix); + } + + CTimerLogger* logger = CTimerLogger::NewL(logName); + CleanupStack::PushL(logger); + + param.iLogger = logger; + param.iLoggerArray = NULL; + + TUint operation = *iTestSetting.iOperationList; + TBool numFilesVaries = EFalse; + + if (operation == EOpManyFiles || operation == EOpManyChanges || operation == EOpMixed) + { + numFilesVaries = ETrue; + } + logger->LogSettingDescription(iTestSetting.iNumFiles, iTestSetting.iNumCli, iTestSetting.iOption, numFilesVaries); + + loggerList.AppendL(logger); + + TBuf<20> threadName; + threadName.AppendFormat(_L("NotificationThread%02d"), count); + + RThread notifyOp; + notifyOp.Create(threadName, NotificationOperationThread, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, ¶m); + + notiThreads.AppendL(notifyOp); + + notifyOp.Resume(); + + smphF.Wait(); // Wait for the parameters being properly passed + + CleanupStack::Pop(logger); + count++; + } + + gNotiThreads = notiThreads; + + if (iTestSetting.iNumCli == 0) // no notification + { + TFileName logName; + logName.Append(gLogFilePath); + logName.Append(_L("SingleClient")); + logName.Append(gLogPostFix); + + CTimerLogger* logger = CTimerLogger::NewL(logName); + CleanupStack::PushL(logger); + + logger->LogSettingDescription(iTestSetting.iNumFiles, iTestSetting.iNumCli, iTestSetting.iOption); + + loggerList.AppendL(logger); + CleanupStack::Pop(logger); + } + + TThreadParam paramFileOp; + paramFileOp.iSetting = iTestSetting; + paramFileOp.iSmphFT = &smphF; + paramFileOp.iSmphNT = &smphN; + paramFileOp.iLogger = NULL; + paramFileOp.iLoggerArray = &loggerList; + + RThread fileOp; + fileOp.Create(_L("FileOperationThread"), FileOperationThread, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, ¶mFileOp); + gFileThread = fileOp; + + fileOp.Resume(); + + TInt err; + + TRequestStatus status; + fileOp.Logon(status); + User::WaitForRequest(status); + err = fileOp.ExitReason(); + test(err == KErrNone); + + count = 0; + while(count < notiThreads.Count()) + { + notiThreads[count].Logon(status); + User::WaitForRequest(status); + err = notiThreads[count].ExitReason(); + test(err == KErrNone); + count++; + } + + CLOSE_AND_WAIT(fileOp); + + count = 0; + while(count < notiThreads.Count()) + { + RThread thread = notiThreads[count]; + CLOSE_AND_WAIT(thread); + count++; + } + + for (TInt i = 0; i < loggerList.Count(); i++) + { + loggerList[i]->LogAndPrint(_L("====================================================")); + } + + smphN.Close(); + smphF.Close(); + loggerList.ResetAndDestroy(); + loggerList.Close(); + notiThreads.Reset(); + notiThreads.Close(); + } + +//=========================================================== + +CFileOperator::CFileOperator(const TTestSetting& aSetting, RPointerArray& aLoggerArray, RSemaphore* aSmphFT, RSemaphore* aSmphNT) +: iFirstFile(0) + { + iNumFiles = aSetting.iNumFiles; + iOption = aSetting.iOption; + iCurrentOp = aSetting.iOperationList; + iNumCli = aSetting.iNumCli; + + iLoggers = aLoggerArray; + iSmphS = aSmphNT; + iSmphW = aSmphFT; + + iFs.Connect(); + } + +CFileOperator::~CFileOperator() + { + iFs.Close(); + } + +// starts measuring for all the notification thread +// the loggers in iLogger are shared by Notification thread +void CFileOperator::MesureStartsAll() + { + TInt i = 0; + while (i < iNumCli) + { + iLoggers[i]->MeasureStart(); + i++; + } + } + +// Wait for all notification threads to signal +void CFileOperator::WaitForSignalsAll() + { + TInt i = 0; + while (i < iNumCli) + { + iSmphW->Wait(); + i++; + } + } + +// prepare each test step +void CFileOperator::TestStepPrepare(TUint aOp) + { + RDebug::Print(_L("Preparing for the next test step...")); + switch (aOp) + { + case EOpReplace: + { + User::After(1500000); // Wait for 1.5 sec so that the new files have different modified time + RFile file; + TInt count = iFirstFile + iNumFiles; + while (count < (iFirstFile + (iNumFiles * 2))) + { + TFileName nextFile; + FileNameGen(nextFile, count); + file.Create(iFs, nextFile, EFileRead); + file.Close(); + count++; + } + break; + } + case EOpManyChanges: + { + RFile file; + TFileName nextFile; + FileNameGen(nextFile, 9999); + file.Create(iFs, nextFile, EFileRead); + file.Close(); + break; + } + case EOpManyFiles: + { + CDir* list; + iFs.GetDir(gTestPath, KEntryAttMaskSupported, ESortNone, list); + TInt count = list->Count(); + delete list; + + RFile file; + + for (TInt i = 0; i < iNumFiles - count; i++) + { + TFileName nextFile; + FileNameGen(nextFile, count + i); + file.Create(iFs, nextFile, EFileRead); + file.Close(); + } + + break; + } + // No preparation for other operations + default: + break; + } + RDebug::Print(_L("Preparation done...")); + } + +// Finish each test step, do some clearing or test setting modification +void CFileOperator::TestStepFinish(TUint aOp) + { + RDebug::Print(_L("Finishing the test step...")); + switch (aOp) + { + case EOpRenameDir: + case EOpRename: + { + iFirstFile += iNumFiles; + break; + } + case EOpDelete: + case EOpDeleteDir: + { + iFirstFile = 0; + break; + } + case EOpManyChanges: + { + TFileName fileName; + FileNameGen(fileName, 9999); + iFs.Delete(fileName); + iNumFiles += 1000; + break; + } + case EOpManyFiles: + { + iNumFiles += 1000; + break; + } + case EOpMixed: + { + iNumFiles += 50; + } + // no clearing for other operations + default: + break; + } + RDebug::Print(_L("***** Test step finished *****")); + RDebug::Print(_L("\n")); + } + +// perform the file operations +void CFileOperator::DoChangesL() + { + while(*iCurrentOp != EOpEnd) + { + TestStepPrepare(*iCurrentOp); + + if (iOption & ENoNotify) + { + iLoggers[0]->MeasureStart(); + } + else + { + iSmphS->Signal(iNumCli); // Signal notification threads that preparation is done + WaitForSignalsAll(); // Wait for notification threads to finish requesting notifications + MesureStartsAll(); + } + + RDebug::Print(_L("Start Timing and File operations...")); + switch(*iCurrentOp) + { + case EOpCreate: + DoCreateL(); + break; + case EOpReplace: + DoReplaceL(); + break; + case EOpChgAttr: + DoChangeAttL(); + break; + case EOpRename: + DoRenameL(); + break; + case EOpWrite: + DoWriteL(); + break; + case EOpResize: + DoResizeL(); + break; + case EOpDelete: + DoDeleteL(); + break; + case EOpManyChanges: + DoManyChangesOnSingleFileL(); + break; + case EOpManyFiles: + DoSmallChangesOnManyFilesL(); + break; + case EOpCreateDir: + DoCreateDirL(); + break; + case EOpRenameDir: + DoRenameDirL(); + break; + case EOpDeleteDir: + DoDeleteDirL(); + break; + case EOpMixed: + DoMixedOperationsL(); + break; + default: + User::Leave(KErrArgument); + } + RDebug::Print(_L("File Operation Ended...")); + + if (iOption & ENoNotify) + { + RDebug::Print(_L("Timing ended...")); + iLoggers[0]->MeasureEnd(); + iLoggers[0]->LogTestStepTime(*iCurrentOp, iNumFiles); + } + else + { + RFile file; + TFileName endFile(gLogFilePath); + endFile.Append(_L("test.end")); + TInt r = file.Replace(iFs, endFile, EFileWrite); + SAFETEST0(r == KErrNone); + file.Close(); + + WaitForSignalsAll(); // Wait for notification threads to receive all the notifications + } + + TestStepFinish(*iCurrentOp); + + iCurrentOp++; + } + } + +void CFileOperator::DoCreateL() + { + RFile file; + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count); + + TInt r = file.Create(iFs, nextFile, EFileRead); + SAFETEST0(r == KErrNone); + file.Close(); + count++; + } + } + +void CFileOperator::DoReplaceL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName newFile; + TFileName oldFile; + FileNameGen(newFile, count); + FileNameGen(oldFile, count + iNumFiles); + + iFs.Replace(oldFile, newFile); + count++; + } + } + +void CFileOperator::DoChangeAttL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count); + iFs.SetAtt(nextFile, KEntryAttNormal, KEntryAttArchive); + count++; + } + } + +void CFileOperator::DoRenameL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName newFile; + TFileName oldFile; + FileNameGen(oldFile, count); + FileNameGen(newFile, count + iNumFiles); + + iFs.Rename(oldFile, newFile); + count++; + } + } + +void CFileOperator::DoWriteL() + { + RFile file; + TInt count = iFirstFile; + _LIT8(KData, "Some text for writing test of enhanced notification performance test."); + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count); + file.Open(iFs, nextFile, EFileWrite); + file.Write(KData); + // Flush file to ensure notification is received + file.Flush(); + file.Close(); + count++; + } + } + +void CFileOperator::DoResizeL() + { + RFile file; + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count); + file.Open(iFs, nextFile, EFileWrite); + TInt size; + file.Size(size); + file.SetSize(size+10); + file.Close(); + count++; + } + } + +void CFileOperator::DoDeleteL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count); + iFs.Delete(nextFile); + count++; + } + } + +void CFileOperator::DoCreateDirL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count, EFalse); + iFs.MkDir(nextFile); + count++; + } + } + +void CFileOperator::DoRenameDirL() + { + TInt count = iFirstFile; + while (count < iFirstFile + iNumFiles) + { + TFileName newFile; + TFileName oldFile; + FileNameGen(oldFile, count, EFalse); + FileNameGen(newFile, count + iNumFiles, EFalse); + + iFs.Rename(oldFile, newFile); + count++; + } + } + +void CFileOperator::DoDeleteDirL() + { + TInt count = iFirstFile; + + while (count < iFirstFile + iNumFiles) + { + TFileName nextFile; + FileNameGen(nextFile, count, EFalse); + iFs.RmDir(nextFile); + count++; + } + } + +void CFileOperator::DoManyChangesOnSingleFileL() + { + TFileName fileName; + FileNameGen(fileName, 9999); + + RFile file; + _LIT8(KData, "a"); + + for(TInt i = 0; i < iNumFiles; i++) + { + TInt offset = 0; + file.Open(iFs, fileName, EFileWrite); + file.Seek(ESeekEnd, offset); + file.Write(KData); + file.Flush(); + file.Close(); + } + } + +void CFileOperator::DoSmallChangesOnManyFilesL() + { + RFile file; + _LIT8(KData, "a"); + + for(TInt i = 0; i < iNumFiles; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + file.Open(iFs, nextFile, EFileWrite); + file.Write(KData); + file.Flush(); + file.Close(); + } + } + +void CFileOperator::DoMixedOperationsL() + { + // will perform 18*iNumFiles operations + RFile file; + TInt firstFile = iFirstFile; + TInt lastFile = iFirstFile + (2 * iNumFiles); + TInt firstDir = iFirstFile; + TInt lastDir = iFirstFile + iNumFiles; + + _LIT8(KData, "Some text."); + + TInt i; + // Create Files - 2*iNumFiles Ops + // we create 2*iNumFiles files here so that we can ensure that at least iNumfiles ops are performed after the replace step + for (i = firstFile; i < lastFile; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + file.Create(iFs, nextFile, EFileRead); + file.Close(); + } + + // Create Directories - iNumFiles Ops + for (i = firstDir; i < lastDir; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i, EFalse); + iFs.MkDir(nextFile); + } + + // Write - 2*iNumFiles Ops + for (i = firstFile; i < lastFile; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + file.Open(iFs, nextFile, EFileWrite); + file.Write(KData); + file.Flush(); + file.Close(); + } + + // Resize - 2*iNumFiles Ops + for (i = firstFile; i < lastFile; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + file.Open(iFs, nextFile, EFileWrite); + TInt size; + file.Size(size); + file.SetSize(size+10); + file.Close(); + } + + // Replace Files - iNumFiles Ops + for (i = firstFile; i < firstFile + iNumFiles; i++) + { + TFileName newFile; + TFileName oldFile; + FileNameGen(oldFile, i); + FileNameGen(newFile, i + iNumFiles); + iFs.Replace(oldFile, newFile); + } + firstFile += iNumFiles; + + // Rename Files - iNumFiles Ops + for (i = firstFile; i < lastFile; i++) + { + TFileName newFile; + TFileName oldFile; + FileNameGen(newFile, i - iNumFiles); + FileNameGen(oldFile, i); + iFs.Rename(oldFile, newFile); + } + firstFile -= iNumFiles; + lastFile -= iNumFiles; + + // Delete Dirs - iNumFiles Ops + for (i = firstDir; i < lastDir; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i, EFalse); + iFs.RmDir(nextFile); + } + + // Delete File - iNumFiles Ops + for (i = firstFile; i < lastFile; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + iFs.Delete(nextFile); + } + + // All-in-one - 7*iNumFiles Ops + for (i = firstFile; i < lastFile; i++) + { + TFileName nextFile; + FileNameGen(nextFile, i); + TFileName nextDir; + FileNameGen(nextDir, i, EFalse); + + iFs.MkDir(nextDir); + + file.Create(iFs, nextFile, EFileWrite); + file.Write(KData); + file.Flush(); + TInt size; + file.Size(size); + file.SetSize(size+10); + file.Close(); + + TFileName newName; + FileNameGen(newName, i + iNumFiles); + iFs.Rename(nextFile, newName); + + iFs.Delete(newName); + iFs.RmDir(nextDir); + } + } + +//========================================================== + +CNotifyOperator::CNotifyOperator(const TTestSetting& aSetting, RSemaphore* aSmphFT, RSemaphore* aSmphNT, CTimerLogger* aLogger) + { + iNumFiles = aSetting.iNumFiles; + iOption = aSetting.iOption; + iCurrentOp = aSetting.iOperationList; + iLogger = aLogger; + iSmphS = aSmphFT; + iSmphW = aSmphNT; + } + +CNotifyOperator::~CNotifyOperator() + { + } + +// start operations in notification thread +// this is the main function called in the thread, +// it creates notification watcher, requests notification, and check test result +void CNotifyOperator::StartOperationL() + { + CNotifyWatcher* notifyWatcher = CNotifyWatcher::NewL(iNumFiles, iOption, *iCurrentOp, iLogger); + CleanupStack::PushL(notifyWatcher); + CTestStopper* stopper = new(ELeave) CTestStopper(); + CleanupStack::PushL(stopper); + + CActiveScheduler::Add(notifyWatcher); + CActiveScheduler::Add(stopper); + + while (*iCurrentOp != EOpEnd) + { + iSmphW->Wait(); // wait for file thread finishing preparation for the current file operation. + + notifyWatcher->FullDirectoryScanL(notifyWatcher->iEntries); + notifyWatcher->RequestNotification(); + stopper->StartWaitingForFile(); + + iSmphS->Signal(); // Signal file thread that the notifications are requested, ready to receive + + CActiveScheduler::Start(); + //////////////////////////////// + // receiving notifications... // + //////////////////////////////// + + notifyWatcher->HandleNotification(ETrue); // handle for the last time + + LogTestResult(notifyWatcher->iCounter, notifyWatcher->iMeanCounter, notifyWatcher->iOverflowCounter); + + if(iOption & EReportChg) + { + TestChangeReport(notifyWatcher->iRecords.Count()); + } + + if (!gPerfMeasure) + { + TInt id = iOption & KNotifyTreadIdMask; + if (iOption & EMultiNoti2) + { + if ( *iCurrentOp == EOpCreate || + (id % 4 == 0 && (*iCurrentOp == EOpRename || *iCurrentOp == EOpReplace)) || + (id % 4 == 1 && *iCurrentOp == EOpChgAttr) || + (id % 4 == 2 && (*iCurrentOp == EOpResize || *iCurrentOp == EOpWrite)) || + (id % 4 == 3 && *iCurrentOp == EOpDelete)) + { + SAFETEST2(notifyWatcher->iCounter, iNumFiles, id); + } + } + else + { + SAFETEST2(notifyWatcher->iCounter, iNumFiles, id); + } + } + + iSmphS->Signal(); // Signal file thread that all notifications are received + + iCurrentOp++; + + notifyWatcher->Reset(*iCurrentOp); + if ((*iCurrentOp == EOpManyFiles) || (*iCurrentOp == EOpManyChanges)) + { + iNumFiles += 1000; + } + else if (*iCurrentOp == EOpMixed) + { + iNumFiles += 50; + } + } + + CleanupStack::PopAndDestroy(2); + + } + +// Write log and print the notification numbers using iLogger +void CNotifyOperator::LogNotificationNumbers(TInt aNumNoti, TInt aNumIter, TInt aNumOverflow) + { + TBuf<100> buf; + + if (iOption & EEnhanced) + { + if (aNumOverflow > 0) + { + buf.AppendFormat(_L("Buffer overflow: %d overflow notifications received."), aNumOverflow, aNumNoti); + iLogger->LogAndPrint(buf); + buf.Zero(); + } + + TReal mean = static_cast(aNumNoti)/aNumIter; + buf.AppendFormat(_L("%.2f mean enhanced notifications per iteration."), mean); + iLogger->LogAndPrint(buf); + buf.Zero(); + } + + buf.AppendFormat(_L("%d notifications received overall."), aNumNoti); + iLogger->LogAndPrint(buf); + } + +// Write log and print the test reault +void CNotifyOperator::LogTestResult(TInt aCounter, TInt aMeanCounter, TInt aOFCounter) + { + if (iOption & EMultiNoti2) + { + TInt id = iOption & KNotifyTreadIdMask; + if ( *iCurrentOp == EOpCreate || + (id % 4 == 0 && (*iCurrentOp == EOpRename || *iCurrentOp == EOpReplace)) || + (id % 4 == 1 && *iCurrentOp == EOpChgAttr) || + (id % 4 == 2 && (*iCurrentOp == EOpWrite || *iCurrentOp == EOpResize)) || + (id % 4 == 3 && *iCurrentOp == EOpDelete)) + { + iLogger->LogTestStepTime(*iCurrentOp, iNumFiles); + LogNotificationNumbers(aCounter, aMeanCounter, aOFCounter); + } + else + iLogger->LogAndPrint(_L("File operation not tested - time: 0 ms")); + } + else if (iOption & EPlugin) + { + if (*iCurrentOp == EOpChgAttr || *iCurrentOp == EOpWrite || *iCurrentOp == EOpResize) + iLogger->LogAndPrint(_L("File operation not tested - time: 0 ms")); + else + { + iLogger->LogTestStepTime(*iCurrentOp, iNumFiles); + LogNotificationNumbers(aCounter, aMeanCounter, aOFCounter); + } + } + else + { + iLogger->LogTestStepTime(*iCurrentOp, iNumFiles); + LogNotificationNumbers(aCounter, aMeanCounter, aOFCounter); + } + } + +// When change report enabled, check the number of changes we detected +void CNotifyOperator::TestChangeReport(TInt aNumChanges) + { + TBuf<100> buf; + buf.AppendFormat(_L("%d file changes detected.\r\n"), aNumChanges); + iLogger->LogAndPrint(buf); + + TInt id = iOption & KNotifyTreadIdMask; + if (iOption & EEnhanced) + { + if (iOption & EMultiNoti2) + { + if ( *iCurrentOp == EOpCreate || + (id % 4 == 0 && (*iCurrentOp == EOpRename || *iCurrentOp == EOpReplace)) || + (id % 4 == 1 && *iCurrentOp == EOpChgAttr) || + (id % 4 == 2 && (*iCurrentOp == EOpResize || *iCurrentOp == EOpWrite)) || + (id % 4 == 3 && *iCurrentOp == EOpDelete)) + { + SAFETEST2(aNumChanges, iNumFiles, id); + } + } + else if (!(iOption & EBigBuffer)) + { + // not testing this case, because the number of file changes detected + // could varies depend on when notifications are received + } + else if (*iCurrentOp == EOpMixed) + { + SAFETEST2(aNumChanges, (18*iNumFiles), id); // On cached drives, the number of write notification could vary. + } + else + { + SAFETEST2(aNumChanges, iNumFiles, id); + } + } + else if (iOption & EOriginal) + { + if ((*iCurrentOp == EOpManyChanges) || (*iCurrentOp == EOpMixed)) + { + // not testing this case, because the number of file changes detected + // could varies depend on when notifications are received + } + else if ((*iCurrentOp == EOpReplace) || (*iCurrentOp == EOpRename) || (*iCurrentOp == EOpRenameDir)) + { + SAFETEST2(aNumChanges, (2*iNumFiles), id); + } + else + { + SAFETEST2(aNumChanges, iNumFiles, id); + } + } + else if (iOption & EPlugin) + { + if (*iCurrentOp == EOpCreate || *iCurrentOp == EOpReplace || *iCurrentOp == EOpRename || + *iCurrentOp == EOpDelete || *iCurrentOp == EOpRenameDir) + { + SAFETEST2(aNumChanges, iNumFiles, id); + } + else if (*iCurrentOp == EOpMixed) + { + SAFETEST2(aNumChanges, (8*iNumFiles), id); // only part of operations can be notified + } + // Other cases are not testable. + } + } + +//=========================================================== + +CNotifyWatcher::CNotifyWatcher(TInt aNumFiles, TUint16 aOption, TUint aCurrentOp, CTimerLogger* aLogger) +: CActive(CActive::EPriorityStandard), iCounter(0), iMeanCounter(0), iOverflowCounter(0), iCurrentOp(aCurrentOp), +iNumFiles(aNumFiles), iOption(aOption), iEntries(aNumFiles), iRecords(aNumFiles), iLogger(aLogger) + { + } + +CNotifyWatcher::~CNotifyWatcher() + { + Cancel(); + delete iNotify; + + iFs.DismountPlugin(KPluginName); + iFs.RemovePlugin(KPluginName); + + iFs.Close(); + iEntries.Close(); + iRecords.Close(); + } + +CNotifyWatcher* CNotifyWatcher::NewL(TInt aNumFiles, TUint16 aOption, TUint aCurrentOp, CTimerLogger* aLogger) + { + CNotifyWatcher* self = new(ELeave) CNotifyWatcher(aNumFiles, aOption, aCurrentOp, aLogger); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CNotifyWatcher::ConstructL() + { + iFs.Connect(); + + if (iOption & EEnhanced) + { + TInt bufferSize; + if(iOption & EBigBuffer) + { + bufferSize = KMinNotificationBufferSize * iNumFiles; + if (iCurrentOp == EOpMixed) + bufferSize = bufferSize * 18; + } + else + bufferSize = KMinNotificationBufferSize; + + iNotify = CFsNotify::NewL(iFs, bufferSize); + } + else + iNotify = NULL; + + if (iOption& EPlugin) + { + TInt r = iFs.AddPlugin(KPluginName); + test(r == KErrNone || r == KErrAlreadyExists); + if (r != KErrAlreadyExists) + { + r = iFs.MountPlugin(KPluginName); + test(r == KErrNone); + } + } + + FullDirectoryScanL(iEntries); + iRecords.Reset(); + } + +// reset certain members so that we can do next test step +void CNotifyWatcher::Reset(TUint aOp) + { + iCurrentOp = aOp; + iRecords.Reset(); + iCounter = 0; + iOverflowCounter = 0; + iMeanCounter = 0; + if ((aOp == EOpManyFiles) || (aOp == EOpManyChanges)) + iNumFiles += 1000; + } + +// perform a full dir scan +void CNotifyWatcher::FullDirectoryScanL(RArray& aArray) + { + aArray.Reset(); + + CDir* list; + iFs.GetDir(gTestPath, KEntryAttMaskSupported, ESortByName, list); + + if (!list) + return; + + CleanupStack::PushL(list); + + for (TInt i = 0; i < list->Count(); i++) + { + TEntry en ((*list)[i]); + aArray.AppendL(en); + } + + CleanupStack::PopAndDestroy(); + } + +// find out what has changed in test path, and save the changes in iRecord +void CNotifyWatcher::MakeChangeRecordL(RArray& aArray) + { + TInt num = aArray.Count(); + TInt r,i; + for (i = 0; i < num; i++) + { + TInt index = iEntries.Find(aArray[i], CompareEntryName); + SAFETEST1((index == KErrNotFound || index >= 0), (iOption & KNotifyTreadIdMask)); + + TFileName name(aArray[i].iName); + + if (index == KErrNotFound) + { + r = iRecords.Append(name); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + else + { + if (!CompareEntry(iEntries[index], aArray[i])) + { + r = iRecords.Append(name); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + iEntries.Remove(index); + } + } + + num = iEntries.Count(); + + for (i = 0; i < num; i++) + { + TFileName name(iEntries[i].iName); + r = iRecords.Append(name); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + + iEntries.Reset(); + num = aArray.Count(); + for (i = 0; i < num; i++) + { + TEntry en (aArray[i]); + iEntries.AppendL(en); + } + } + + +TBool CNotifyWatcher::CompareEntry(const TEntry& aEntry1, const TEntry& aEntry2) + { + // we don't compare name, because names are compared by CompareEntryName() already + if ((aEntry1.iAtt == aEntry2.iAtt) && (aEntry1.iModified == aEntry2.iModified) + && (aEntry1.iSize == aEntry2.iSize) && (aEntry1.iType == aEntry2.iType)) + return ETrue; + + return EFalse; + } + +// add 100 filters for enhanced notification test case +void CNotifyWatcher::AddLotsOfFilters() + { + for (TInt i = 0; i < 100; i++) + { + TFileName path; + path.Copy(gTestPath); + path.Append('*'); + TFileName file; + file.AppendFormat(_L("*.%3d"), i); + TInt r = iNotify->AddNotification((TUint)TFsNotification::EAllOps, gTestPath, file); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + } + +void CNotifyWatcher::RequestNotification() + { + switch (iOption & KNotifyOptionMask) + { + case EEnhanced: + RequestNotificationEnhanced(); + break; + case EOriginal: + RequestNotificationOriginal(); + break; + case EPlugin: + RequestNotificationPlugin(); + break; + case ENoNotify: + default: + return; + } + } + +// request notification using enhanced notification +void CNotifyWatcher::RequestNotificationEnhanced() + { + if (iOption & EBigFilter) + { + AddLotsOfFilters(); + } + + TFileName path; + path.Copy(gTestPath); + path.Append('*'); + TBuf<3> file = _L("*"); + + if (iOption & EMultiNoti1) + { + TInt r = iNotify->AddNotification(TFsNotification::ECreate, path, file); // Create & replace + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + r = iNotify->AddNotification(TFsNotification::ERename, path, file); // Change Attribute + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + r = iNotify->AddNotification(TFsNotification::EAttribute, path, file); // Rename + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + r = iNotify->AddNotification(TFsNotification::EFileChange, path, file); // Write & Setsize + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + r = iNotify->AddNotification(TFsNotification::EDelete, path, file); // Delete + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + else if (iOption & EMultiNoti2) + { + TInt r = iNotify->AddNotification(TFsNotification::ECreate, path, file); // Create & replace + TInt id = iOption & KNotifyTreadIdMask; + + switch (id%4) + { + case 0: + r = iNotify->AddNotification(TFsNotification::ERename, path, file); // Change Attribute + SAFETEST2(r, KErrNone, id); + break; + case 1: + r = iNotify->AddNotification(TFsNotification::EAttribute, path, file); // Rename + SAFETEST2(r, KErrNone, id); + break; + case 2: + r = iNotify->AddNotification(TFsNotification::EFileChange, path, file); // Write & Setsize + SAFETEST2(r, KErrNone, id); + break; + case 3: + r = iNotify->AddNotification(TFsNotification::EDelete, path, file); // Delete + SAFETEST2(r, KErrNone, id); + break; + default: + break; + } + } + else + { + TInt r = iNotify->AddNotification((TUint)TFsNotification::EAllOps, path, file); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + + TInt r = iNotify->RequestNotifications(iStatus); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + + SetActive(); + } + +// request notification using original notification +void CNotifyWatcher::RequestNotificationOriginal() + { + iFs.NotifyChange(ENotifyAll, iStatus, gTestPath); + SetActive(); + } + +void CNotifyWatcher::HandleNotification(TBool aLastTime) + { + switch (iOption & KNotifyOptionMask) + { + case EEnhanced: + HandleNotificationEnhanced(aLastTime); + break; + case EOriginal: + HandleNotificationOriginal(aLastTime); + break; + case EPlugin: + HandleNotificationPlugin(aLastTime); + break; + case ENoNotify: + default: + return; + } + + if(aLastTime) + { + iLogger->MeasureEnd(); + RDebug::Print(_L("Timing ended...")); + Cancel(); + } + } + +// handle enhanced notification +void CNotifyWatcher::HandleNotificationEnhanced(TBool aLastTime) + { + TInt num = iCounter; + iMeanCounter++; + + TFsNotification::TFsNotificationType type; + const TFsNotification* notification; + + while((notification = iNotify->NextNotification())!= NULL) + { + iCounter++; + type = notification->NotificationType(); + if (iOption & EBigBuffer) + { + SAFETEST1((type & OpNotifyMapping(iOption, iCurrentOp)), (iOption & KNotifyTreadIdMask)); + } + else + { + SAFETEST1((type & (OpNotifyMapping(iOption, iCurrentOp) | TFsNotification::EOverflow)), (iOption & KNotifyTreadIdMask)); + } + + if(iOption & EReportChg) + { + if (type & TFsNotification::EOverflow) + { + iOverflowCounter++; + RArray newEntries; + FullDirectoryScanL(newEntries); + MakeChangeRecordL(newEntries); + newEntries.Close(); + } + else + { + TPtrC ptr; + notification->Path(ptr); + TFileName name; + name.Copy(ptr); + TInt r = iRecords.Append(name); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + } + } + } + + if (aLastTime) + { + if (num == iCounter) // no new notification so this iteration doesn't count + { + iMeanCounter--; + } + return; + } + + iNotify->RequestNotifications(iStatus); + SetActive(); + } + +// handle original notification +void CNotifyWatcher::HandleNotificationOriginal(TBool aLastTime) + { + iCounter++; + + if (iOption & EReportChg) + { + RArray newEntries; + FullDirectoryScanL(newEntries); + MakeChangeRecordL(newEntries); + newEntries.Close(); + } + + if (aLastTime) + { + iCounter--; // the last time this function is called is not a notification. + return; + } + + RequestNotificationOriginal(); + } + +// reset the iPluginStatusPkg, so that we can request notification again +void CNotifyWatcher::ResetMdsFSPStatus() + { + TMdsFSPStatus& status = iPluginStatusPkg(); + + status.iDriveNumber = 0; + status.iFileEventType = EMdsFileUnknown; + status.iFileName.Zero(); + status.iNewFileName.Zero(); + status.iProcessId = TUid::Null(); + } + +// request notification using nokia MDS plugin +void CNotifyWatcher::RequestNotificationPlugin() + { + TInt r = iPlugin.Open(iFs, KMdsFSPluginPosition); + SAFETEST2(r, KErrNone, (iOption & KNotifyTreadIdMask)); + iPlugin.AddNotificationPath(gTestPath); + + iPlugin.Enable(); + ResetMdsFSPStatus(); + iPlugin.RegisterNotification(iPluginStatusPkg, iStatus); + SetActive(); + } + +// handle notifications from plugin +void CNotifyWatcher::HandleNotificationPlugin(TBool aLastTime) + { + if (aLastTime) + return; + + if (iOption & EReportChg) + { + TMdsFSPStatus& status = iPluginStatusPkg(); + TFileName name; + name.Copy(status.iFileName); + iRecords.Append(name); + if (iCurrentOp != EOpMixed) + { + TInt type = status.iFileEventType; + SAFETEST1(((TUint)type == OpNotifyMapping(iOption, iCurrentOp)), (iOption & KNotifyTreadIdMask)); + } + } + + iCounter++; + ResetMdsFSPStatus(); + iPlugin.RegisterNotification(iPluginStatusPkg, iStatus); + SetActive(); + } + +// cancel request +void CNotifyWatcher::DoCancel() + { + switch (iOption & KNotifyOptionMask) + { + case EEnhanced: + iNotify->CancelNotifications(iStatus); + iNotify->RemoveNotifications(); + // not breaking here as the Enhanced may change to Original if Overflow + case EOriginal: + iFs.NotifyChangeCancel(iStatus); + break; + case EPlugin: + iPlugin.Disable(); + iPlugin.NotificationCancel(); + iPlugin.Close(); + break; + case ENoNotify: + default: + return; + } + } + +void CNotifyWatcher::RunL() + { + HandleNotification(EFalse); + } + +//======================================================================== + +CTestStopper::CTestStopper() +: CActive(EPriorityLow) + { + iFs.Connect(); + iTestEndFile.Append(gLogFilePath); + iTestEndFile.Append(_L("test.End")); + } + +CTestStopper::~CTestStopper() + { + Cancel(); + iFs.Close(); + } + +void CTestStopper::DoCancel() + { + iFs.NotifyChangeCancel(iStatus); + } + +// stop the scheduler since the test is done +void CTestStopper::RunL() + { + CActiveScheduler::Stop(); + iFs.Delete(iTestEndFile); + Cancel(); + } + +// start waiting for "test.end" file +void CTestStopper::StartWaitingForFile() + { + iFs.NotifyChange(ENotifyAll,iStatus,iTestEndFile); + SetActive(); + }