kerneltest/f32test/concur/t_cfstest.cpp
changeset 9 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/concur/t_cfstest.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,697 @@
+// Copyright (c) 2002-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:
+//
+
+//! @file f32test\concur\t_csfsoak.cpp
+
+#include <f32file.h>
+#include <e32test.h>
+#include <e32math.h>
+#include <f32dbg.h>
+
+#include "t_server.h"
+#include "t_chlffs.h"
+#include "t_tdebug.h"
+#include "t_cfssoak.h"
+#include "f32_test_utils.h"
+
+using namespace F32_Test_Utils;
+
+
+
+
+GLDEF_D RTest test(_L("T_CFSTEST"));
+
+LOCAL_D TFullName gFsName;
+LOCAL_D TFullName gFsName1;
+LOCAL_D TFullName gFsName2;
+LOCAL_D TFullName gOldFsName;
+LOCAL_D TFullName gNewFsName;
+LOCAL_D TBool     gNoMedia = ETrue;
+
+_LIT(KFsFile,   "CFAFSDLY");
+_LIT(KFsName,   "DelayFS");
+
+LOCAL_D const TInt32 KSecond = 1000000;
+LOCAL_D const TInt32 KTenthS = KSecond/10;
+LOCAL_D const TInt   KNumBuf = 10;
+LOCAL_D const TInt   KBufLen = 50000;
+
+LOCAL_D TInt KSessionWaitSlow = 50 * KTenthS;
+
+LOCAL_D TBuf8<KBufLen> gBuff[KNumBuf];
+LOCAL_D TRequestStatus gStat[KNumBuf];
+
+LOCAL_D TChar gRemFsChr = 0;
+LOCAL_D TInt  gRemFsDrv = 0;
+
+LOCAL_D TChar gDrvCh0 = 0;
+LOCAL_D TChar gDrvCh1 = 0;
+LOCAL_D TChar gDrvCh2 = 0;
+
+LOCAL_D	TInt  gWaitTime = KSessionWaitSlow;
+
+GLDEF_D TExtension gPrimaryExtensions[KMaxDrives];
+
+inline void TraceError(const char* aFile, TInt aLine, const TDesC& aStr, TInt aRsp)
+//
+// 'Helper' routine to output the file and line of an error as well as a string
+// and a response value.
+//
+    {
+    TBuf<256> fbuf;
+    TPtrC8    fptr((const TUint8*)aFile);
+    fbuf.Copy(fptr);
+    RDebug::Print(_L("%S:%d  %S: r = %d"), &fbuf, aLine, &aStr, aRsp);
+    }
+
+#define TESTSTR(r,s,cond) if (!(cond)) { TraceError(__FILE__, __LINE__, (s), (r)); test(0); }
+#define TESTLIT(r,s,cond) { _LIT(KStr,s); TESTSTR(r,KStr,cond); }
+#define TESTRES(r,cond)   TESTLIT(r,"ERROR",cond)
+
+LOCAL_C TChar MountTestFileSystem(TInt aDrive)
+///
+/// Mount a new CTestFileSystem on the drive under test.
+/// @param aDrive Drive number (EDriveC etc.) to be used.
+///
+	{
+	TInt r;
+	// Check attributes
+	TBuf<64> b;
+	TChar c;
+	r=TheFs.DriveToChar(aDrive,c);
+	TESTLIT(r, "DriveToChar", r==KErrNone);
+
+	b.Format(_L("Mount test file system on %c:"),(TUint)c);
+	test.Next(b);
+
+	r=TheFs.AddFileSystem(KFsFile);
+	TESTLIT(r, "AddFileSystem", r==KErrNone || r==KErrAlreadyExists);
+
+	r=TheFs.FileSystemName(gOldFsName,aDrive);
+	TESTLIT(r, "FileSystemName", r==KErrNone || r==KErrNotFound);
+
+	TDriveInfo drv;
+	r = TheFs.Drive(drv, gRemFsDrv);
+	TESTLIT(r, "Drive()", r == KErrNone);
+
+	gNoMedia = (drv.iType == EMediaUnknown || drv.iType == EMediaNotPresent);
+
+	if (gOldFsName.Length() > 0)
+		{
+		r = TheFs.ExtensionName(gPrimaryExtensions[aDrive].iName, aDrive, 0);
+		if (r == KErrNone)
+			gPrimaryExtensions[aDrive].iExists = ETrue;
+
+		TTest::Printf(_L("Dismount %C: %S"), (TUint)c, &gOldFsName);
+		r=TheFs.DismountFileSystem(gOldFsName,aDrive);
+		TESTLIT(r, "DismountFileSystem", r==KErrNone);
+		}
+	
+	if (gPrimaryExtensions[aDrive].iExists == EFalse)
+		r=TheFs.MountFileSystem(KFsName,aDrive);
+	else
+		r=TheFs.MountFileSystem(KFsName,gPrimaryExtensions[aDrive].iName,aDrive);
+
+	TESTLIT(r, "MountFileSystem", r==KErrNone);
+
+	r=TheFs.FileSystemName(gNewFsName,aDrive);
+	TESTLIT(r, "FileSystemName", r==KErrNone);
+		
+	r = gNewFsName.CompareF(KFsName);
+	TESTLIT(r, "gNewFsName.Compare", r==0);
+		
+	return c;
+	}
+
+LOCAL_C void UnmountFileSystem(TInt aDrive)
+///
+/// Dismount the filesystem and remount the original one.
+/// @param aDrive Drive number (EDriveC etc.) to be unmounted.
+///
+	{
+	TChar c;
+	TInt r=TheFs.DriveToChar(aDrive,c);
+	TESTLIT(r, "DriveToChar", r==KErrNone);
+
+	if (gNewFsName.Length() > 0)
+		{
+		TTest::Printf(_L("Dismount %C: %S"), (TUint)c, &gNewFsName);
+		r=TheFs.DismountFileSystem(gNewFsName,aDrive);
+		TESTLIT(r, "DismountFileSystem", r==KErrNone);
+
+		// if there's no media present, don't try to mount it
+		if (gNoMedia)
+			{
+			TTest::Printf(_L("No media on %C: so don't remount it"), (TUint)c);
+			}
+		else if (gOldFsName.Length() > 0)
+			{
+			TTest::Printf(_L("Mount    %C: %S"), (TUint)c, &gOldFsName);
+			if (gPrimaryExtensions[aDrive].iExists == EFalse)
+				r=TheFs.MountFileSystem(gOldFsName,aDrive);
+			else
+				r=TheFs.MountFileSystem(gOldFsName,gPrimaryExtensions[aDrive].iName,aDrive);
+			TESTLIT(r, "MountFileSystem", r==KErrNone);
+			}
+		}
+	}
+
+LOCAL_C TInt TestCalibrate(TChar aDrvCh)
+///
+/// Calibrate the timing for writing the data buffers.  This also sets up
+/// the buffers to the appropriate length (fast file systems such as LFFS
+/// and FAT need big buffers, the slow test filesystem needs small ones).
+///
+    {
+	TFileName name;
+	RFile file;
+	RFs   fs;
+	TBool fast = (aDrvCh != gRemFsChr);
+	TInt  drive;
+	TInt  r;
+
+	fs.Connect();
+
+	name.Format(_L("%C:\\cfstest.txt"), (TUint)aDrvCh);
+
+	r=fs.CharToDrive(aDrvCh, drive);
+	TESTLIT(r, "CharToDrive", r==KErrNone);
+
+	TVolumeInfo info;
+	r = fs.Volume(info, drive);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("Getting volume info"));
+	TTest::Printf(_L("Total space on drive %C = %ld (0x%lX)"), (TUint) aDrvCh, info.iSize, info.iSize);
+	TTest::Printf(_L("Free space on drive %C = %ld (0x%lX)"), (TUint) aDrvCh, info.iFree, info.iFree);
+	
+	TInt buffsize;
+	if (fast)
+		{
+		TInt64 bufSize64 = info.iFree / KNumBuf;
+		buffsize = bufSize64 > KBufLen ? KBufLen : I64LOW(bufSize64);
+		}
+	else
+		{
+		buffsize = 100;
+		}
+
+	TTest::Printf(_L("Writing %d buffers of size %d"), KNumBuf, buffsize);
+
+
+    TInt i;
+    for (i = 0; i < KNumBuf; i++)
+		gBuff[i].Fill('*', fast ? buffsize : 100);
+
+    r = file.Replace(fs, name, EFileStreamText | EFileWrite);
+    if (r != KErrNone)
+        TTest::Fail(HERE, r, _L("opening %S for writing"), name.Ptr());
+
+    TTime startTime;
+    TTime endTime;
+    TTimeIntervalMicroSeconds timeTaken;
+    startTime.HomeTime();
+
+
+    for (i = 0; i < KNumBuf; i++)
+        {
+        r = file.Write(gBuff[i]);
+		if (r != KErrNone)
+			{
+			TTest::Printf(_L("Error writing file, r %d buffsize %d"), r, buffsize);
+			file.Close();
+			fs.Close();
+			return r;
+			}
+        }
+
+	file.Close();
+	fs.Close();
+
+    endTime.HomeTime();
+    timeTaken=endTime.MicroSecondsFrom(startTime);
+
+    TInt64 dtime = timeTaken.Int64();
+	gWaitTime = I64LOW(dtime);
+
+	if (gWaitTime < KTenthS)
+		{
+		TTest::Printf(_L("Time to complete writes (%d uS) too short"), gWaitTime);
+		return KErrGeneral;
+		}
+
+	while (gWaitTime > 50*KSecond && buffsize > 100)
+		{
+		gWaitTime /= 10;
+		buffsize /= 10;
+		}
+
+	for (i = 0; i < KNumBuf; i++)
+		gBuff[i].Fill('*', buffsize);
+
+	TTest::Printf(_L("Time to complete writes %d mS"), gWaitTime / 1000);
+
+	return KErrNone;
+	}
+
+void PrintBufStatus()
+	{
+    for (TInt i = 0; i < KNumBuf; i++)
+		{
+		TTest::Printf(_L("Req %d r %08X\n"), i, gStat[i].Int());
+		}
+	}
+
+LOCAL_C TInt TestClose(TChar aDrvCh, TBool aCloseFs)
+///
+/// Test what happens when a file is closed in the middle of writing it.  Note
+/// that this assumes that the filesystem under test has been calibrated and
+/// the buffers set up already.
+///
+/// @return KErrGeneral if an error was detected, KErrNone otherwise
+///
+    {
+	TFileName fsname;
+	TFileName name;
+	RFile file;
+	RFs   fs;
+	TInt  drive;
+	TInt  r;
+
+	fs.Connect();
+
+	r=TheFs.CharToDrive(aDrvCh, drive);
+	TESTLIT(r, "CharToDrive", r==KErrNone);
+
+	r=fs.FileSystemName(fsname, drive);
+	TESTLIT(r, "FileSystemName", r==KErrNone);
+
+	name.Format(_L("%C:\\cfstest.txt"), (TUint)aDrvCh);
+	TTest::Printf(_L("Testing %S (%S)"), &name, &fsname);
+
+    r = file.Replace(fs, name, EFileStreamText | EFileWrite);
+    if (r != KErrNone)
+        TTest::Fail(HERE, r, _L("opening %S for writing"), name.Ptr());
+
+    TInt i;
+    for (i = 0; i < KNumBuf; i++)
+        {
+        file.Write(gBuff[i], gStat[i]);
+        }
+
+	// wait for around a half of the writes to complete, then close the file
+	// before the others finish.
+	// If the media is unexpectedly slow and hasn't written a single buffer, wait a little bit longer
+	TInt KMaxWaitTime =  Max(gWaitTime * 3, KSessionWaitSlow);
+	TInt totalTimeWaited = 0;
+	for (TInt waitTime = gWaitTime/2; 
+		totalTimeWaited < KMaxWaitTime && gStat[0].Int() == KRequestPending;
+		totalTimeWaited+= waitTime, waitTime = gWaitTime / 10)
+		{
+		TTest::Printf(_L("Waiting %d mS"), waitTime / 1000);
+		User::After(waitTime);
+		}
+
+    TTest::Printf(_L("Wait ended"));
+	if (aCloseFs)
+		{
+		TTest::Printf(_L("Closing file server session"));
+		fs.Close();
+		}
+		
+	else
+		{
+		TTest::Printf(_L("Closing file"));
+		file.Close();
+		}
+
+    // test what has happened
+	TInt nFinished  = 0;
+	TInt nCancelled = 0;
+	TInt nPending   = 0;
+    TBool bad = EFalse;
+	TRequestStatus tstat;
+	RTimer timer;
+	timer.CreateLocal();
+	timer.After(tstat, 2 * gWaitTime);
+	TInt requestsCompleted = 0;
+    for (i = 0; i < KNumBuf; i++)
+        {
+        User::WaitForRequest(tstat, gStat[i]);
+		++requestsCompleted;
+		if (tstat != KRequestPending)
+			{
+			TTest::Printf(_L("Timer expired"));
+			break;
+			}
+        r = gStat[i].Int();
+        switch (r)
+            {
+            case KErrNone:
+                TTest::Printf(_L("write %d finished\n"), i);
+				nFinished++;
+				if (nCancelled > 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nCancelled > 0"));
+					}
+                break;
+            case KErrCancel:
+                TTest::Printf(_L("write %d cancelled\n"), i);
+				nCancelled++;
+#if !defined(__WINS__) // under some loads the calibration is wrong by now and no writes have finished yet
+				if (nFinished == 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nFinished == 0"));
+					}
+#endif
+                break;
+			case KRequestPending:
+                TTest::Printf(_L("write %d pending\n"), i);
+				nPending++;
+				if (nCancelled > 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nCancelled > 0"));
+					}
+#if !defined(__WINS__) // timing issue as above
+				if (nFinished == 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nFinished == 0"));
+					}
+#endif
+                break;
+            default:
+				TTest::Fail(HERE, r, _L("incorrect status for write %d"), i);
+            }
+        }
+
+	while (i < KNumBuf)
+		{
+        r = gStat[i].Int();
+        switch (r)
+            {
+            case KErrNone:
+                TTest::Printf(_L("write %d finished\n"), i);
+				nFinished++;
+				if (nCancelled > 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nCancelled > 0"));
+					}
+                break;
+            case KErrCancel:
+                TTest::Printf(_L("write %d cancelled\n"), i);
+				nCancelled++;
+#if !defined(__WINS__) // under some loads the calibration is wrong by now and no writes have finished yet
+				if (nFinished == 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nFinished == 0"));
+					}
+#endif
+                break;
+			case KRequestPending:
+                TTest::Printf(_L("write %d pending\n"), i);
+				nPending++;
+				if (nCancelled > 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nCancelled > 0"));
+					}
+#if !defined(__WINS__) // timing issue as above
+				if (nFinished == 0)
+					{
+					bad = ETrue;
+					PrintBufStatus();
+					TTest::Fail(HERE, _L("nFinished == 0"));
+					}
+#endif
+                break;
+            default:
+				TTest::Fail(HERE, r, _L("incorrect status for write %d"), i);
+			}
+		i++;
+		}
+
+	if (!aCloseFs)
+		{
+		// If we HAVEN'T closed the file server session, we must wait for all the remaining
+		// requests, including the timer.
+		
+		TTest::Printf(_L("Waiting for %d uncompleted requests\n"), KNumBuf + 1 - requestsCompleted);
+		for (i = requestsCompleted ; i < KNumBuf + 1 ; ++i)
+			User::WaitForAnyRequest();
+		}
+	else
+		{
+		// If we have closed the session, then we need to work out if the timer went off (by
+		// checking if there are any writes still pending) and if not then we need to wait for it.
+		
+		if (nPending == 0)
+			{
+			TTest::Printf(_L("Waiting for timer to complete\n"));
+			User::WaitForRequest(tstat);
+			}
+		}
+
+	if (aCloseFs)
+		fs.Connect();
+
+	TEntry entry;
+	r = fs.Entry(name, entry);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("Entry(%S)"), &name);
+
+	TInt nWritten = entry.iSize / gBuff[0].Length();
+	TTest::Printf(_L("Created file %d bytes, %d complete buffers written"),
+				  entry.iSize, nWritten);
+	
+    fs.Delete(name);
+
+	fs.Close();
+
+	if (nCancelled < 2)
+		{
+		// At present, undefined whether cancelled or left pending
+		// TTest::Fail(HERE, _L("Not enough buffers (%d) cancelled"), nCancelled);
+		}
+
+	if (nPending > 0)
+		{
+		// At present, undefined whether cancelled or left pending
+		// TTest::Fail(HERE, _L("Too many buffers (%d) still pending"), nPending);
+		}
+
+	// Get the TFileCacheFlags for this drive
+	TVolumeInfo volInfo;	// volume info for current drive
+	r = TheFs.Volume(volInfo, drive);
+	test(r==KErrNone);
+	test.Printf(_L("DriveCacheFlags = %08X\n"), volInfo.iFileCacheFlags);
+
+	// There's no point testing the numbers of buffers written if write caching enabled, as
+	// in this case, the write requests are completed successfully before the data is written to disk
+	if ((volInfo.iFileCacheFlags & EFileCacheWriteOn) && aCloseFs)
+		{
+		test.Printf(_L("%d write requests completed, %d of %d buffers written to disk\n"), nFinished, nWritten, KNumBuf);
+		test.Printf(_L("Skipping test for buffers written as write caching enabled and FS session destroyed\n"));
+		}
+	else
+		{
+
+#if !defined(__WINS__) // timing issue as above
+		if (nWritten < 2)
+			{
+			TTest::Fail(HERE, _L("Not enough buffers (%d) written"), nFinished);
+			}
+#endif
+		if (nWritten < nFinished)
+			{
+			TTest::Fail(HERE, _L("Only %d buffers written, should be %d"), nWritten, nFinished);
+			}
+		}
+
+    if (bad)
+        TTest::Printf(_L("Test %c: FAILED\n"), name[0]);
+    else
+        TTest::Printf(_L("Test %c: OK\n"), name[0]);
+	
+    return (bad ? KErrGeneral : KErrNone);
+    }
+
+LOCAL_C TInt TestCloseOperation()
+///
+/// Task to exercise the remote (special) filesystem.
+///
+	{
+	TSoakRemote rem(gDrvCh1);
+	TInt r = KErrNone;
+	TBool ok = ETrue;
+
+	test.Next(_L("Mount as synchronous"));
+	rem.Remount(ETrue);
+	r = TestCalibrate(gDrvCh1);
+	if (r == KErrNone)
+		{
+		test.Next(_L("Test SubSession synchronous"));
+		r = TestClose(gDrvCh1, EFalse);
+		if (r != KErrNone)
+			ok = EFalse;
+		test.Next(_L("Test Session synchronous"));
+		r = TestClose(gDrvCh1, ETrue);
+		if (r != KErrNone)
+			ok = EFalse;
+		}
+
+	test.Next(_L("Mount as asynchronous"));
+	rem.Remount(EFalse);
+	r = TestCalibrate(gDrvCh1);
+	if (r == KErrNone)
+		{
+		test.Next(_L("Test SubSession asynchronous"));
+		r = TestClose(gDrvCh1, EFalse);
+		if (r != KErrNone)
+			ok = EFalse;
+		test.Next(_L("Test Session asynchronous"));
+		r = TestClose(gDrvCh1, ETrue);
+		if (r != KErrNone)
+			ok = EFalse;
+		}
+
+	return (ok ? KErrNone : KErrGeneral);
+	}
+
+LOCAL_C TInt parseCmd(TInt aArgC, TPtrC aArgV[], TChar& dr0, TChar& dr1, TChar& dr2)
+///
+/// Get drive characters from the command line.
+///
+    {
+	if (aArgC > 1)
+		dr0 = User::UpperCase(aArgV[1][0]);
+	if (aArgC > 2)
+		dr1 = User::UpperCase(aArgV[2][0]);
+	if (aArgC > 3)
+		dr2 = User::UpperCase(aArgV[3][0]);
+
+    if (dr1 < 'A' || dr1 > 'Z')
+		dr1 = dr0;
+
+    return KErrNone;
+    }
+
+GLDEF_C void CallTestsL()
+///
+/// Do all tests.
+///
+	{
+    //-- set up console output 
+    F32_Test_Utils::SetConsole(test.Console()); 
+
+	for (TInt i = 0; i < KMaxDrives; i++)
+		{
+		gPrimaryExtensions[i].iExists = EFalse;
+		}
+
+
+	// first thing, initialise local test class to make sure it gets done.
+	TInt r = TTest::Init();
+	test(r == KErrNone);
+	
+	const TInt KMaxArgs = 4;
+	TPtrC argv[KMaxArgs];
+	TInt  argc = TTest::ParseCommandArguments(argv, KMaxArgs);
+
+	gDrvCh0 = TTest::DefaultDriveChar();
+
+	r = parseCmd(argc, argv, gDrvCh0, gDrvCh1, gDrvCh2);
+	if (r != KErrNone)
+		{
+		User::Exit(KErrAbort);
+		}
+
+	gRemFsChr = gDrvCh0;
+	r = TheFs.CharToDrive(gRemFsChr, gRemFsDrv);
+
+    test(r == KErrNone);
+    
+    PrintDrvInfo(TheFs, gRemFsDrv);
+
+	if (r != KErrNone || gRemFsDrv == EDriveZ)
+		{
+		test.Printf(_L("Test cannnot run on drive %C:\n"), (TUint)gRemFsChr);
+		return;
+		}
+
+	TDriveInfo drv;
+	r = TheFs.Drive(drv, gRemFsDrv);
+	test(r == KErrNone);
+
+    if(Is_Automounter(TheFs, gRemFsDrv))
+    {
+		//-- this test tries to mount the curent drive as a synchronous one, 
+        //-- automounter can't work on synchronous drives.
+        test.Printf(_L("This test can't be performed on AutoMounter FS, Skipping.\n"));
+		return;
+    }
+
+	// if it's a RAM drive or no media present, use the slow FS
+	if (drv.iType == EMediaRam)
+		{
+		test.Printf(_L("Test can't run on RAM drive %C:\n"), (TUint)gRemFsChr);
+		return;
+		}
+	if (drv.iType == EMediaUnknown || drv.iType == EMediaNotPresent)
+		{
+		// Mount test filesystem on that drive
+		test.Printf(_L("Using slow filesystem on drive %C:\n"), (TUint)gRemFsChr);
+		MountTestFileSystem(gRemFsDrv);
+		}
+	else if (argc > 2)
+		{
+		// two parameters, mount the test filesystem on the first
+		MountTestFileSystem(gRemFsDrv);
+		}
+	else
+		{
+		// Use the existing filesystem on the drive
+		gRemFsChr = 0;
+		}
+
+	// If we can do it, check whether the drive is LFFS and is OK
+// !!! Disable platform security tests until we get the new APIs
+//	if (User::Capability() & KCapabilityRoot)
+//		CheckMountLFFS(TheFs, gDrvCh1);
+
+    //!!!!! Warning: MountTestFileSystem() and UnmountFileSystem() code is lousy! it always mounts back the drive as SYNCHRONOUS.
+    //!!!!! anyway, this code seems not to be called, because of "else if (argc > 2)"
+
+	// Do tests
+	r = TestCloseOperation();
+
+	// restore the original mounted drive if anything changed
+	UnmountFileSystem(gRemFsDrv);
+
+	test(r == KErrNone);
+	}
+
+