kerneltest/f32test/concur/t_cfssoakfn.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/concur/t_cfssoakfn.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,903 @@
+// 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:
+// f32test\server\t_csfsoakfn.cpp
+// 
+//
+
+#include <f32file.h>
+#include <e32test.h>
+#include <e32math.h>
+#include <f32dbg.h>
+#include "t_server.h"
+#include "t_tdebug.h"
+#include "t_cfssoak.h"
+
+/// Time value constants
+LOCAL_D const TInt KSecond   = 1000000;
+LOCAL_D const TInt KTenthSec = KSecond / 10;
+
+/// Time to wait before session/subsession close, to allow some writes to
+/// complete but not all (1 second per write with slow FS)
+LOCAL_D const TInt KSessionWaitTime = 25 * KTenthSec;
+
+/// Number of writes we expect to have completed
+LOCAL_D const TInt KSessionNumEnded = KSessionWaitTime / KSecond;
+
+GLREF_D TExtension gPrimaryExtensions[];
+// -----------------------------------------------------------------
+
+void TSoakStats::Print()
+///
+/// Print a statistics header.
+///
+	{
+	TTest::Printf(_L("                    Total                Fail\n"));
+	}
+
+void TSoakStats::Print(const TDesC& aTitle)
+///
+/// Print the statistics for this item (title, total executed, number executed
+/// this time, total failures, failures this time) then reset the "this time"
+/// values.
+///
+	{
+	TTest::Printf(_L("  %-8S %8d %+8d   %8d %+8d\n"),
+				  &aTitle, iTotal, iThis, iFail, iThisF);
+	iThis = 0;
+	iThisF = 0;
+	}
+
+void TSoakStats::Inc()
+///
+/// Increment both the total and "this time" execution numbers.
+///
+	{
+	iTotal++;
+	iThis++;
+	}
+
+void TSoakStats::Fail()
+///
+/// Increment both the total and "this time" failure numbers.
+///
+	{
+	iFail++;
+	iThisF++;
+	}
+
+
+// -----------------------------------------------------------------
+
+TSoakReadOnly::TSoakReadOnly() : iDrive(-1)
+///
+/// Initialise the "read only" tests, connect the file session.
+///
+	{
+	TInt r = iFs.Connect();
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("iFs connect"));
+	}
+
+TSoakReadOnly::TSoakReadOnly(TInt aDriveCh) : iDrive(aDriveCh)
+///
+/// Initialise the "read only" tests, connect the file session, setting up
+/// a drive to be excluded from the reading.
+/// @param aDriveCh Drive letter to be excluded.
+///
+	{
+	TInt r = iFs.Connect();
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("iFs connect"));
+	}
+
+TSoakReadOnly::~TSoakReadOnly()
+///
+/// Destructor -- close the file session.
+///
+	{
+	iFs.Close();
+	}
+
+TInt TSoakReadOnly::ReadFile(const TDesC& aName, TInt aSize)
+///
+/// Read a file, expecting a certain size.  No check is done on the actual
+/// data, just that it can be read.
+/// @param aName Name of the file.
+/// @param aSize Expected file size.
+///
+	{
+	TInt r = KErrNone;
+	RFile f;
+	iReads.Inc();
+	r = f.Open(iFs, aName, EFileStreamText);
+	if (r == KErrNotFound || r == KErrPathNotFound)
+		return KErrNone;
+	TBuf<256> errbuf;
+	if (r != KErrNone)
+		{
+		if (r != KErrInUse)
+			{
+			iFiles.Fail();
+			TTest::Printf(_L("%S -- open failed %S\n"), &aName, &TTest::ErrStr(r, errbuf));
+			}
+		else
+			TTest::Printf(_L("Warning: %S -- open failed %S\n"), &aName, &TTest::ErrStr(r, errbuf));
+		return r;
+		}
+	TBuf8<256> buf;
+	TInt len = 0;
+	while ((r = f.Read(buf)) == KErrNone && buf.Length() > 0)
+		len += buf.Length();
+	f.Close();
+	if (r != KErrNone && r != KErrEof)
+		{
+		iReads.Fail();
+		TTest::Printf(_L("ERROR %S -- READ FAILED %S\n"), &aName, &TTest::ErrStr(r, errbuf));
+		}
+	else if (len < aSize)
+		{
+		iReads.Fail();
+		TTest::Printf(_L("ERROR %S -- %d READ, %d EXPECTED\n"), &aName, len, aSize);
+		}
+	else if (len > aSize)
+		TTest::Printf(_L("Warning: %S -- %d read, %d expected\n"), &aName, len, aSize);
+	return r;
+	}
+
+TInt TSoakReadOnly::ScanDirs(TInt aDriveCh, TInt aReadInterval)
+///
+/// Scan directories on a drive, reading a selection of the files found.
+/// @param aDriveCh Drive letter to be scanned.
+/// @param aReadInterval Approximate number of files to be read (they are
+///                      read at random, so the actual number read may be
+///                      less than this number).
+///
+	{
+	TInt r = KErrNone;
+	CDirScan* scanner=NULL;
+	TRAP(r, scanner=CDirScan::NewL(iFs));
+	ScanDirFunc(scanner, aDriveCh, aReadInterval);
+	delete scanner;
+	return r;
+	}
+
+TInt TSoakReadOnly::ScanDirFunc(CDirScan* aScanner, TInt aDriveCh, TInt aReadInterval)
+///
+/// Scan directories on a drive, reading a selection of the files found.
+/// @param aDriveCh Drive letter to be scanned.
+/// @param aReadInterval Approximate number of files to be read (they are
+///                      read at random, so the actual number read may be
+///                      less than this number).
+///
+	{
+	TInt r = KErrNone;
+	TBuf<8> name;
+	name.Format(_L("%c:\\"), aDriveCh);
+	// TTest::Printf(_L("Scanning %S\n"), &name);
+	TParse dirName;
+	r=iFs.Parse(name, dirName);
+	if (r != KErrNone)
+		{
+		iDrives.Fail();
+		return r;
+		}
+	CDir* entryList = NULL;
+	TInt nfiles = 0;
+	TRAP(r, aScanner->SetScanDataL(dirName.FullName(),KEntryAttDir,ESortByName));
+	if (r != KErrNone)
+		{
+		TTest::Fail(HERE, r, _L("SetScanData %S"), &dirName.FullName());
+		}
+	TRAP(r, aScanner->NextL(entryList));
+	if (r == KErrPathNotFound)
+		return KErrNone;
+	if (r != KErrNone)
+		{
+		TTest::Fail(HERE, r, _L("Scan Next %S"), &dirName.FullName());
+		}
+	while (entryList)
+		{
+		for (TInt i = 0; i < entryList->Count(); i++)
+			{
+			TEntry e = (*entryList)[i];
+			if (!e.IsDir())
+				++nfiles;
+			}
+		delete entryList;
+		TRAP(r, aScanner->NextL(entryList));
+		if (r == KErrPathNotFound)
+			break;
+		if (r != KErrNone)
+			{
+			TTest::Fail(HERE, r, _L("Scan Next %S"), &dirName.FullName());
+			}
+		}
+	TInt prob = (nfiles / aReadInterval) + 1;
+	TRAP(r, aScanner->SetScanDataL(dirName.FullName(),KEntryAttDir,ESortByName));
+	if (r == KErrPathNotFound)
+		return KErrNone;
+	if (r != KErrNone)
+		{
+		TTest::Fail(HERE, r, _L("Scan Next %S"), &dirName.FullName());
+		}
+	for (;;)
+		{
+		TRAP(r, aScanner->NextL(entryList));
+		if (r == KErrPathNotFound)
+			return KErrNone;
+		if (r != KErrNone)
+			{
+			TTest::Fail(HERE, r, _L("Scan Next %S"), &dirName.FullName());
+			}
+		if (entryList==NULL)
+			break;
+		TInt count=entryList->Count();
+		while (count--)
+			{
+			TEntry e = (*entryList)[count];
+			TFileName path=aScanner->FullPath();
+			path.Append(e.iName);
+			TBuf<16> attr;
+			for (TInt j = 0; j < 16; j++)
+				{
+				if ((1 << j) & e.iAtt)
+					attr.Append(TText("RHSVDA X        "[j]));
+				}
+			if (e.IsDir())
+				{
+				iDirs.Inc();
+				// TTest::Printf(_L("%c:%-50S    <DIR>  %S\n"), aDriveCh, &path, &attr);
+				}
+			else
+				{
+				iFiles.Inc();
+				TBool read = (aReadInterval && Math::Rand(iSeed) % prob == 0);
+				if (read)
+					{
+					// TTest::Printf(_L("%-50S %8d  %-8S  test\n"), &path, e.iSize, &attr);
+					r = ReadFile(path, e.iSize);
+					}
+				else
+					{
+					// TTest::Printf(_L("%c:%-50S %8d  %S\n"), aDriveCh, &path, e.iSize, &attr);
+					}
+				}
+			}
+		delete entryList;
+		entryList=NULL;
+		}
+	return r;
+	}
+
+TInt TSoakReadOnly::ScanDrives(TBool aScanDirs, TInt aReadInterval)
+///
+/// Scan all drives (except the one excluded) for directories and files.
+/// @param aScanDirs If true, read subdirectories, if false don't.
+/// @param aReadInterval approximate number of files to read.
+///
+	{
+	TInt r = KErrNone;
+	TInt i;
+	for (i = EDriveA; i <= EDriveZ; i++)
+		{
+		TChar drv;
+		r=iFs.DriveToChar(i, drv);
+		if (r != KErrNone)
+			return r;
+		TDriveInfo info;
+		r=iFs.Drive(info, i);
+		if (r != KErrNone)
+			return r;
+		if (i != iDrive
+			&& info.iDriveAtt != 0
+			&& info.iType != EMediaUnknown
+			&& info.iType != EMediaNotPresent)
+			{
+			iDrives.Inc();
+			// TTest::Printf(_L("Drive %c:"), drv);
+			if (aScanDirs)
+				{
+				iDirs.Inc();
+				r = ScanDirs(drv, aReadInterval);
+				if (r != KErrNone)
+					return r;
+				}
+			}
+		}
+	return r;
+	}
+
+void
+TSoakReadOnly::ExcludeDrive(TInt aDriveCh)
+///
+/// Set a drive to be excluded from scanning.
+/// @param aDriveCh Drive letter to be excluded.
+///
+	{
+	iDrive = aDriveCh;
+	}
+
+
+// --------------------------------------------------------------------------
+
+TSoakFill::TSoakFill()
+///
+/// Initialise the fill/clean cycle.
+///
+	{
+	iSeed = 186483;
+	TInt r = iFs.Connect();
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("iFs connect"));
+	}
+
+TInt TSoakFill::SetDrive(TInt aDriveCh)
+///
+/// Set a drive to be stressed.  The test directory is created and then it is
+/// cleaned (in case anything was left over from previous tests).  Note that
+/// other directories on the drive are untouched.
+/// @param aDriveCh Drive letter to be used.
+/// @leave The drive scanning can cause the function to leave.
+///
+	{
+	TInt  r = KErrNone;
+	iDrive = aDriveCh;
+	if (aDriveCh)
+		{
+		iDrvCh = aDriveCh;
+		iFs.CharToDrive(iDrvCh, iDrive);
+		r=iFs.Volume(iInfo, iDrive);
+		if (r != KErrNone)
+			TTest::Fail(HERE, r, _L("volume info for %C:"), aDriveCh);
+		iFree = iInfo.iSize;
+		iName.Format(_L("%c:\\SOAK\\"), iDrvCh);
+		r = iFs.MkDir(iName);
+		if (r != KErrNone && r != KErrAlreadyExists)
+			TTest::Fail(HERE, r, _L("mkdir %S"), &iName);
+		r = KErrNone;
+		}
+	return r;
+	}
+
+TInt TSoakFill::FillDrive()
+///
+/// Fill the drive with files and directories of random lengths.
+/// @param aDriveCh Drive letter to be tested, or zero (absent) to use the one
+///        set up with SetDrive().
+/// @leave Setting up a new drive can cause this function to leave.
+///
+	{
+	TInt  r = KErrNone;
+	r=iFs.Volume(iInfo, iDrive);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("volume info for %C:"), iDrvCh);
+	iFree = iInfo.iFree;
+	r = iFs.MkDir(iName);
+	if (r != KErrNone && r != KErrAlreadyExists && r != KErrNotSupported)
+		TTest::Fail(HERE, r, _L("mkdir %S"), &iName);
+	do
+		{
+		r = Fill(iName);
+		if (r == KErrDiskFull)
+			{
+			// TTest::Printf(_L("Disk full on %c:\n"), iDrvCh);
+			break;
+			}
+		if (r != KErrNone)
+			TTest::Fail(HERE, r, _L("Filling drive %c"), iDrvCh);
+		r=iFs.Volume(iInfo, iDrive);
+		if (r != KErrNone)
+			TTest::Fail(HERE, r, _L("volume info for %c:"), iDrvCh);
+		} while (iInfo.iFree > 0);
+	r=iFs.Volume(iInfo, iDrive);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("volume info for %c:"), iDrvCh);
+	if (iInfo.iFree > 0)
+		TTest::Printf(_L("Free space on %c: %S %ld KB (out of %ld KB)\n"),
+					  iDrvCh,
+					  &iInfo.iName,
+					  I64LOW(iInfo.iFree / 1024),
+					  I64LOW(iInfo.iSize / 1024));
+	return r;
+	}
+
+TInt TSoakFill::CleanDrive()
+///
+/// Clean the test directory on the selected drive, removing all files and
+/// subdirectories.  Other directories on the drive are left untouched.
+/// @leave Scanning the drive can cause the function to leave.
+///
+	{
+	TInt r = KErrNone;
+	CDirScan* scanner=0;
+	TRAP(r, scanner=CDirScan::NewL(iFs));
+	if (r != KErrNone || !scanner)
+		TTest::Fail(HERE, r, _L("creating scanner"));
+	TParse dirName;
+	r=iFs.Parse(iName, dirName);
+	if (r != KErrNone)
+		{
+		if (r == KErrInUse || r == KErrNotFound || r == KErrPathNotFound)
+			{
+			// valid conditions, don't report error
+			r = KErrNone;
+			}
+		else
+			TTest::Fail(HERE, r, _L("Parse %S"), &iName);
+		}
+	else
+		{
+		// TTest::Printf(_L("scan %S\n"), &iName);
+		CDir* entryList = NULL;
+		TRAP(r, scanner->SetScanDataL(dirName.FullName(),KEntryAttDir,ESortByName,CDirScan::EScanUpTree));
+		if (r != KErrNone)
+			TTest::Fail(HERE, r, _L("SetScanDataL(%S)"), &dirName.FullName());
+		TRAP(r, scanner->NextL(entryList));
+		if (r != KErrNone && r != KErrPathNotFound)
+			TTest::Fail(HERE, r, _L("Scan NextL()"));
+		while (entryList)
+			{
+			for (TInt i = 0; i < entryList->Count(); i++)
+				{
+				TEntry e = (*entryList)[i];
+				TFileName path = iName;
+				if (path.Right(1) == _L("\\"))
+					path.SetLength(path.Length()-1);
+				path.Append(scanner->AbbreviatedPath());
+				path.Append(e.iName);
+				// TTest::Printf(_L("remove %S\n"), &path);
+				TBuf<256> buf;
+				TInt maxInUseCount = 120;
+				do
+					{
+					if (e.IsDir())
+						{
+						if (path.Right(1) != _L("\\"))
+							path.Append(_L("\\"));
+						r = iFs.RmDir(path);
+						}
+					else
+						r = iFs.Delete(path);
+					if (r == KErrInUse)
+						{
+						TTest::Printf(_L("Warning: %S deleting %S\n"), &TTest::ErrStr(r, buf), &path);
+						User::After(1*KSecond);
+						}
+					}
+					while (r == KErrInUse && maxInUseCount-- > 0);
+				if (r != KErrNone)
+					{
+					TTest::Printf(_L("ERROR %S deleting %S\n"), &TTest::ErrStr(r, buf), &path);
+					}
+				}
+			delete entryList;
+			TRAP(r, scanner->NextL(entryList));
+			if (r != KErrNone)
+				TTest::Fail(HERE, r, _L("Scan NextL()"));
+			}
+		}
+	delete scanner;
+	r = iFs.RmDir(iName);
+	if (r != KErrNone && r != KErrNotFound && r != KErrPathNotFound)
+		{
+		TTest::Fail(HERE, r, _L("volume info for %C:"), iName[0]);
+		}
+	r=iFs.Volume(iInfo, iDrive);
+	if (r != KErrNone)
+		{
+		TTest::Fail(HERE, r, _L("volume info for %C:"), iName[0]);
+		}
+	if (iInfo.iFree > iFree)
+		{
+		TTest::Printf(_L("Warning: %C: changed size -- was %ld now %ld\n"),
+					  iName[0], iFree, iInfo.iFree);
+		return r;
+		}
+	iFree = iInfo.iFree;
+	return r;
+	}
+
+TInt TSoakFill::Fill(TFileName& aName, TInt aNfiles)
+///
+/// Recursively create files and subdirectories at random.  If a subdirectory
+/// is created, the function recurses to fill it.  This generates a mixture of
+/// directories and files of varying lengths.
+/// @param aName   Directory in which to create other stuff.
+/// @param aNfiles Maximum number of files to create.
+/// @return Status of last thing created, KErrDiskFull when out of space.
+///
+	{
+	TInt oldlen = aName.Length();
+	if (oldlen + 10 > aName.MaxLength())
+		return KErrNone;
+	TInt r=iFs.Volume(iInfo, iDrive);
+	if (r != KErrNone)
+		return r;
+	TInt nfiles = I64LOW(iInfo.iFree/10000) + 5;
+	if (aNfiles > 0 && nfiles > aNfiles)
+		nfiles = aNfiles;
+	nfiles = Math::Rand(iSeed) % nfiles + 5;
+	TInt filesz = I64LOW(iInfo.iFree/nfiles/256) + 5;
+	// TTest::Printf(_L("nfiles = %d\n"), nfiles);
+	TInt i;
+	for (i = 0; i < nfiles; i++)
+		{
+		aName.SetLength(oldlen);
+		TBool dir = (Math::Rand(iSeed) % 5 == 0);
+		if (dir && oldlen < 80)
+			{
+			aName.Append(_L("d"));
+			aName.AppendNum(i);
+			aName.Append(_L("\\"));
+			r = iFs.MkDir(aName);
+			TInt busycount = 120;
+			while (busycount-- > 0)
+				{
+				r = iFs.MkDir(aName);
+				if (r == KErrDiskFull)
+					TTest::Printf(_L("Disk full creating %S\n"), &aName);
+				if (r != KErrInUse)
+					break;
+				TTest::Printf(_L("Warning: KErrInUse creating %S\n"), &aName);
+				User::After(1*KSecond);
+				}
+			// TTest::Printf(_L("mkdir %S = %d\n"), &aName, r);
+			if (r == KErrNone || r == KErrAlreadyExists)
+				{
+				r = Fill(aName, nfiles - i - 2);
+				}
+			if (r != KErrNone)
+				break;
+			}
+		else
+			{
+			aName.Append(_L("F"));
+			aName.AppendNum(i);
+			RFile f;
+			TInt busycount = 120;
+			while (busycount-- > 0)
+				{
+				r = f.Replace(iFs, aName, EFileStreamText | EFileWrite);
+				if (r != KErrInUse)
+					break;
+				TTest::Printf(_L("Warning: KErrInUse creating %S\n"), &aName);
+				User::After(1*KSecond);
+				}
+			if (r == KErrNone)
+				{
+				// iNames.AppendL(aName);
+				TInt num = Math::Rand(iSeed) % filesz + 10;
+				TBuf8<256> buf;
+				buf.Fill(TText(' '), 256);
+				TInt wr = 0;
+				while (num-- > 0 && r == KErrNone)
+					{
+					r = f.Write(buf);
+					if (r == KErrNone)
+						wr += 256;
+					else
+						break;
+					}
+				f.Close();
+				/*
+				if (r == KErrDiskFull)
+					TTest::Printf(_L("Disk full writing %S\n"), &aName);
+				*/
+				if (r != KErrNone)
+					break;
+				}
+			else
+				{
+				// TTest::Printf(_L("creat %S = %d\n"), &aName, r);
+				if (r == KErrDiskFull)
+					TTest::Printf(_L("Disk full creating %S\n"), &aName);
+				break;
+				}
+			}
+		}
+	aName.SetLength(oldlen);
+	return r;
+	}
+
+
+TSoakRemote::TSoakRemote(TInt aDriveCh)
+///
+/// Initialise testing for 'remote' drive (special filesystem).  Connects to
+/// file session, initialises the drive and timer, sets up the buffers and
+/// file name.
+/// @param aDriveCh Drive letter for the drive to test.
+///
+	{
+	TInt r = iFs.Connect();
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("iFs connect"));
+	iDrvCh = aDriveCh;
+	iSync  = EFalse;
+	iFs.CharToDrive(iDrvCh, iDrive);
+	iTimer.CreateLocal();
+	Setup();
+	}
+
+void TSoakRemote::Setup()
+/// Set up the buffers and the filename to be used.
+	{
+	for (TInt i = 0; i < KSoakNumBuf; i++)
+		{
+		iBuff[i].Fill('_', KSoakBufLen);
+		iStat[i] = KErrNone;
+		}
+	iName.Format(_L("%c:\\SOAKTEST.FILE"), iDrvCh);
+	}
+
+void TSoakRemote::Remount(TBool aSync)
+///
+/// Remount the test drive as (a)synchronous.
+/// @param aSync If true, the drive is set as synchronous access, otherwise
+///              as asynchronous.
+///
+	{
+	TFileName fsname;
+	iSync = aSync;
+	TInt r = iFs.FileSystemName(fsname, iDrive);
+	TEST(r == KErrNone || r == KErrNotFound);
+
+	if (fsname.Length() > 0)
+		{
+		r = iFs.ExtensionName(gPrimaryExtensions[iDrive].iName, iDrive, 0);
+		if (r == KErrNone)
+			gPrimaryExtensions[iDrive].iExists = ETrue;
+
+		r = iFs.DismountFileSystem(fsname, iDrive);
+		if(r != KErrNone)
+			{
+			TTest::Fail(HERE, r, _L("Dismounting file system %S"), &fsname);
+			}
+		}
+
+	TBufC<16> type = _L("asynchronous");
+	if (iSync)
+		type = _L("synchronous");
+	TTest::Printf(_L("Remount filesystem %c: %S as %S\n"), iDrvCh, &fsname, &type);
+
+#ifdef __CONCURRENT_FILE_ACCESS__
+	if (gPrimaryExtensions[iDrive].iExists == EFalse)
+		r=iFs.MountFileSystem(fsname,iDrive,iSync);
+	else
+		r=iFs.MountFileSystem(fsname,gPrimaryExtensions[iDrive].iName,iDrive,iSync);
+#else
+	if (gPrimaryExtensions[aDrive].iExists == EFalse)
+		r=iFs.MountFileSystem(fsname,iDrive);
+	else
+		r=iFs.MountFileSystem(fsname,gPrimaryExtensions[iDrive].iName,iDrive);
+#endif
+	TEST(r==KErrNone);
+	}
+
+TInt TSoakRemote::TestSubSession()
+///
+/// Test what happens when a file is closed in the middle of writing it.  Note
+/// that this assumes that the filesystem under test takes a second for each
+/// write operation (i.e. is the special test filesystem).
+///
+	{
+	TInt r = iFile.Replace(iFs, iName, EFileStreamText | EFileWrite);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("opening %S for writing"), iName.Ptr());
+	TInt i;
+	for (i = 0; i < KSoakNumBuf; i++)
+		{
+		iFile.Write(iBuff[i], iStat[i]);
+		}
+	// wait for a couple of writes to complete, then close the file before the
+	// others finish
+	User::After(KSessionWaitTime);
+	TTest::Printf(_L("Wait ended"));
+	iFile.Close();
+	TTest::Printf(_L("Close ended"));
+	// test what has happened
+	TBool bad = EFalse;
+	for (i = 0; i < KSoakNumBuf; i++)
+		{
+		User::WaitForRequest(iStat[i]);
+		r = iStat[i].Int();
+		switch (r)
+			{
+			case KErrNone:
+				if (i >= KSessionNumEnded)
+					{
+					TTest::Printf(_L("Write %d not cancelled"), i);
+					bad = ETrue;
+					}
+				break;
+			case KErrCancel:
+				if (i <= KSessionNumEnded)
+					{
+					TTest::Printf(_L("Write %d incorrectly cancelled"), i);
+					bad = ETrue;
+					}
+				TTest::Printf(_L("write %d cancelled\n"), i);
+				break;
+			default:
+				TTest::Fail(HERE, r, _L("incorrect status for write %d"), i);
+			}
+		}
+	iFs.Delete(iName);
+	TPtrC sbuf(iSync ? _L("sync") : _L("async"));
+	if (bad)
+		{
+		TTest::Printf(_L("TestSubSession %c: %S FAILED\n"), iName[0], &sbuf);
+		// Commented out for the moment as result is undefined
+		// TTest::Fail(HERE, _L("TestSubSession %c: %S FAILED\n"), iName[0], &sbuf);
+		}
+	TTest::Printf(_L("TestSubSession %c: %S OK\n"), iName[0], &sbuf);
+	return KErrNone;
+	}
+
+TInt TSoakRemote::TestSession()
+///
+/// Test what happens when a session is closed in the middle of writing a file.
+/// Note that this assumes that the filesystem under test takes a second for
+/// each write operation (i.e. is the special test filesystem).
+///
+	{
+	TInt r;
+	TInt i;
+	TPtrC sbuf(iSync ? _L("sync") : _L("async"));
+
+	r = iFile.Replace(iFs, iName, EFileStreamText | EFileWrite);
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("opening %S for writing"), iName.Ptr());
+	for (i = 0; i < KSoakNumBuf; i++)
+		{
+		iFile.Write(iBuff[i], iStat[i]);
+		}
+	// wait for a couple of them to complete, then close the session
+	User::After(KSessionWaitTime);
+	TTest::Printf(_L("Close FS %S"), &sbuf);
+	// iFs.SetDebugRegister(KFSYS | KFSERV | KTHRD);
+	iFs.Close();
+	TTest::Printf(_L("FS closed %S"), &sbuf);
+	// see what has happened to the writes (wait for at most another 10 seconds).
+	TRequestStatus tstat;
+	iTimer.After(tstat, 10*KSecond);
+	TBool busy = ETrue;
+	TInt  file = 0;
+	while (tstat == KRequestPending && busy)
+		{
+		User::WaitForAnyRequest();
+		busy = EFalse;
+		for (i = file; i < KSoakNumBuf; i++)
+			{
+			r = iStat[i].Int();
+			switch (r)
+				{
+				case KRequestPending:
+					busy = ETrue;
+					TTest::Printf(_L("write %d pending\n"), i);
+					break;
+				case KErrNone:
+					file = i + 1;
+					TTest::Printf(_L("write %d finished\n"), i);
+					break;
+				case KErrCancel:
+					file = i + 1;
+					TTest::Printf(_L("write %d cancelled\n"), i);
+					break;
+				default:
+					file = i + 1;
+					TTest::Fail(HERE, r, _L("incorrect status for write %d"), i);
+				}
+			}
+		}
+	TBool bad = EFalse;
+	if (busy)
+		{
+		for (i = 0; i < KSoakNumBuf; i++)
+			{
+			TBuf<64> buf;
+			r = iStat[i].Int();
+			if (r != KErrNone)
+				{
+				// We expect that the third and subsequent requests will either
+				// have failed with cancel or be still outstanding.  If either
+				// of the first two have failed or are outstanding, that's an
+				// error.
+				if (i < KSessionNumEnded || (r != KErrCancel && r != KRequestPending))
+					{
+					TTest::Fail(HERE, _L("write %d: %S\n"), i, &TTest::ErrStr(r, buf));
+					bad = ETrue;
+					}
+				}
+			}
+		}
+	iTimer.Cancel();
+	// if it's got this far, re-connect the file session
+	// User::After(100000);
+	// TTest::Printf(_L("FS closed (%S) yet?"), &sbuf);
+	r = iFs.Connect();
+	iFs.SetDebugRegister(0);
+	// TTest::Printf(_L("FS opened"));
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("iFs connect"));
+	r = iFs.Delete(iName);
+	for (TInt nr = 0; r == KErrInUse && nr < 100; nr++)
+		{
+		TTest::Printf(_L("Wait for previous session to close"));
+		User::After(10*KSecond);
+		r = iFs.Delete(iName);
+		}
+	if (r == KErrNotFound)
+		r = KErrNone;
+	if (r != KErrNone)
+		bad = ETrue;
+	TPtrC obuf(bad ? _L("FAIL") : _L("OK"));
+	TTest::Printf(_L("TestSession %c: %S %S\n"), iName[0], &sbuf, &obuf);
+	if (bad)
+		{
+		TTest::Printf(_L("Test session close %c: %S FAILED\n"), iName[0], &sbuf);
+		// Commented out for the moment as result is undefined
+		// TTest::Fail(HERE, _L("Test session close failed"));
+		}
+	return r;
+	}
+
+TInt TSoakRemote::TestMount()
+///
+/// Test dismounting with a file open (should fail)
+///
+	{
+	TFileName fsname;
+	TInt r = iFs.FileSystemName(fsname, iDrive);
+	TEST(r == KErrNone || r == KErrNotFound);
+
+	TPtrC sbuf(iSync ? _L("sync") : _L("async"));
+
+	r = iFile.Replace(iFs, iName, EFileStreamText | EFileWrite);
+	for (TInt nr = 0; r == KErrInUse && nr < 100; nr++)
+		{
+		TTest::Printf(_L("Warning: KErrInUse opening %S for writing\n"), &iName);
+		User::After(10*KSecond);
+		r = iFile.Replace(iFs, iName, EFileStreamText | EFileWrite);
+		}
+	if (r != KErrNone)
+		TTest::Fail(HERE, r, _L("opening %S for writing"), &iName);
+
+	if (fsname.Length() > 0)
+		{
+		r = iFs.DismountFileSystem(fsname, iDrive);
+		if (r == KErrNone)
+			{
+#ifdef __CONCURRENT_FILE_ACCESS__
+			r = iFs.MountFileSystem(fsname, iDrive, iSync);
+#else
+			r = iFs.MountFileSystem(fsname, iDrive);
+#endif
+			if (r != KErrNone)
+				TTest::Fail(HERE, r, _L("MountFileSystem(%S, %C:)"), &fsname, iDrvCh);
+			}
+		else if (r != KErrInUse)
+			{
+			TTest::Fail(HERE, r, _L("DismountFileSystem(%S, %C:)"), &fsname, iDrvCh);
+			}
+		}
+
+	iFile.Close();
+	iFs.Delete(iName);
+
+	TTest::Printf(_L("TestMount %c: %S OK\n"), iName[0], &sbuf);
+
+	return KErrNone;
+	}
+
+