kerneltest/f32test/server/t_dspace.cpp
changeset 0 a41df078684a
child 6 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/server/t_dspace.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1763 @@
+// Copyright (c) 1998-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_dspace.cpp
+//
+//
+
+#include <f32file.h>
+#include <e32test.h>
+#include "t_server.h"
+#include "t_chlffs.h"
+
+
+/* This tests disk space notification. Using RFs::NotifyDiskSpace a client can request
+to be notified if the free disk space crosses a specified threshold. This test requires
+a card to be present in d: */
+
+GLDEF_D RTest test(_L("T_DSPACE"));
+
+const TInt KMaxBufSize=512;
+#if defined(__WINS__)
+TInt KFileSize1=2048;
+TInt KFileSize2=4096;
+TInt KFileSize3=8192;
+#else
+TInt KFileSize1=512;
+TInt KFileSize2=1024;
+TInt KFileSize3=4096;
+#endif
+
+const TInt KHeapSize=0x2000;
+const TInt KStackSize=0x4000;
+
+TInt gMinFileSize;
+
+TBool LffsDrive = EFalse;
+
+TBuf8<KMaxBufSize> TheBuffer;
+TInt64 TheDiskSize;
+TInt RemovableDrive;
+TBuf<4> RemovableDriveBuf=_L("?:\\");
+
+_LIT(KTestFile1, "\\F32-TST\\testfile1");
+_LIT(KTestFile2, "\\F32-TST\\testFile2");
+_LIT(KTestDir1, "\\F32-TST\\testDir1\\");
+_LIT(KTestDir2, "\\F32-TST\\testDir2\\");
+_LIT(KFileFiller, "\\F32-TST\\fileFiller");
+
+// functions that may cause change in free disk space
+// not all of them of tested since some require knowledge of file system
+// to ensure change in free disk space
+enum TThreadTask
+	{
+	ETaskSetVolume,
+	ETaskMkDir,
+	ETaskRmDir,
+	ETaskDelete,		// test
+	ETaskRename,
+	ETaskReplace,		// test
+	ETaskFileCreate,
+	ETaskFileReplace,	// test
+	ETaskFileTemp,
+	ETaskFileWrite,		// test
+	ETaskFileWrite4KB,
+	ETaskFileWrite64KB,
+	ETaskFileSetSize,	// test
+	ETaskFileRename,
+	ETaskNoChange1,
+	ETaskNoChange2,
+	ETaskFileCreateLffs,// test
+	ETaskSpin
+	};
+
+
+LOCAL_C TBool IsWinsCDrive(TInt aDrive)
+//
+//
+//
+	{
+#if defined(__WINS__)
+	if(aDrive==KDefaultDrive)
+		return(gSessionPath[0]==(TText)'C');
+	else
+		return(aDrive==EDriveC);
+#else
+	return(EFalse);
+#endif
+	}
+
+LOCAL_C TInt64 FreeDiskSpace(TInt aDrive)
+//
+//
+//
+	{
+	TVolumeInfo v;
+	TInt r=TheFs.Volume(v,aDrive);
+	test(r==KErrNone);
+	return(v.iFree);
+	}
+
+LOCAL_C TInt64 DiskSize(TInt aDrive)
+//
+//
+//
+	{
+	TVolumeInfo v;
+	TInt r=TheFs.Volume(v,aDrive);
+	test(r==KErrNone);
+	return(v.iSize);
+	}
+
+// MinimumFileSize() -
+// Deduces the minimum space occupied by a file by creating a file of one byte
+// in length. This should equal the cluster size on FAT volumes.
+//
+LOCAL_C TInt MinimumFileSize(TInt aDrive)
+	{
+	TInt r = TheFs.Delete(KTestFile1);
+	test(r==KErrNone || r==KErrNotFound);
+
+	TInt64 freeSpace = FreeDiskSpace(aDrive);
+
+	RFile file;
+
+
+	r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+	test(r==KErrNone);
+
+	r = file.Write(TheBuffer,1);
+	test(r==KErrNone);
+	file.Close();
+	TInt64 newSpace = FreeDiskSpace(aDrive);
+
+	r = TheFs.Delete(KTestFile1);
+	test(r==KErrNone);
+
+
+	TInt64 minFileSize = freeSpace - newSpace;
+	test (minFileSize >= 0);
+	minFileSize = Max(minFileSize, 512);
+	test (minFileSize < KMaxTInt);
+
+	TInt minFileSizeLow = I64LOW(minFileSize);
+
+	RDebug::Print(_L("minFileSize %u"), minFileSizeLow);
+
+#if defined(__WINS__)
+	KFileSize1 = minFileSizeLow << 2;	// 512 * 2^2 = 512 * 4 = 2048;
+	KFileSize2 = minFileSizeLow << 3;	// 512 * 2^3 = 512 * 8 = 4096;
+	KFileSize3 = minFileSizeLow << 4;	// 512 * 2^4 = 512 * 16 = 8192;
+#else
+	KFileSize1 = minFileSizeLow;		// 512;
+	KFileSize2 = minFileSizeLow << 1;	// 512 * 2^1 = 512 * 2 = 1024;
+	KFileSize3 = minFileSizeLow << 3;	// 512 * 2^3 = 512 * 8 = 4096;
+#endif
+
+
+	return (TInt) minFileSizeLow;
+	}
+
+LOCAL_C void Initialise()
+//
+// do any initialisation before starting tests
+//
+	{
+	if(TheBuffer.Length()!=KMaxBufSize)
+		{
+		TheBuffer.SetLength(KMaxBufSize);
+		Mem::Fill((void*)TheBuffer.Ptr(),KMaxBufSize,0xab);
+		}
+	TheDiskSize=DiskSize(KDefaultDrive);
+#if defined(__WINS__)
+	RemovableDrive=EDriveX;
+#else
+	TDriveList drvList;
+	if(KErrNone == TheFs.DriveList(drvList))
+		{
+		TInt i;
+		//should be successful, otherwise it means a system w/o any drive!!!
+		for(i=0;i<KMaxDrives;i++)
+			{
+			TDriveInfo driveInfo;
+			if((drvList[i] != 0)
+				&& (KErrNone == TheFs.Drive(driveInfo, i))
+				&& (driveInfo.iType == EMediaHardDisk))
+				{
+				RemovableDrive = i;
+				test.Printf(_L("RemovableDrive = %d\n"),RemovableDrive);
+				break;
+				}
+			}
+		if(i == KMaxDrives)
+			{
+			test.Printf(_L("No Removable media found! Testing discontinued.\n"));
+			User::Exit(KErrNone);
+			}
+		}
+	else
+		{
+		test.Printf(_L("No Drive found! Testing discontinued.\n"));
+		User::Exit(KErrNone);
+		}
+#endif
+
+	test.Printf(_L("inside init++++++++++++++++++++++++++>\n\n\n"));
+	test.Printf(_L("RemovableDrive = %d\n"),RemovableDrive);
+	// initialise removable drive descriptor
+	TChar c;
+	TInt r=RFs::DriveToChar(RemovableDrive,c);
+	test(r==KErrNone);
+	RemovableDriveBuf[0]=(TText)c;
+
+	if( !LffsDrive )
+		{
+		// and create the default directory
+		TFileName d=gSessionPath;
+		d[0]=RemovableDriveBuf[0];
+		MakeDir(d);
+		}
+
+	// better format the default drive as long as not WINS c drive
+	TInt drive;
+	r= RFs::CharToDrive(gSessionPath[0],drive);
+	test(r==KErrNone);
+#if defined(__WINS__)
+	if(drive!=EDriveC)
+		Format(drive);
+#else
+	Format(drive);
+	// test not run on c: drive but does use it
+	Format(EDriveC);
+#endif
+	// and set the default directory
+	r=TheFs.MkDirAll(gSessionPath);
+	test(r==KErrNone || r==KErrAlreadyExists);
+
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone || r==KErrNotFound);
+	r=TheFs.Delete(KTestFile1);
+	test(r==KErrNone || r==KErrNotFound);
+	r=TheFs.Delete(KTestFile2);
+	test(r==KErrNone || r==KErrNotFound);
+	r=TheFs.RmDir(KTestDir1);
+	test(r==KErrNone || r==KErrNotFound);
+	r=TheFs.RmDir(KTestDir2);
+	test(r==KErrNone || r==KErrNotFound);
+
+	gMinFileSize = MinimumFileSize(drive);
+	}
+
+LOCAL_C TInt64 FillDisk(RFile& aFile,TInt64 aNewSpace,TInt aDrive)
+//
+// fill a file until free disk space equals aFreeSpace
+//
+	{
+	TInt64 space=FreeDiskSpace(aDrive);
+	test(space>aNewSpace);
+	while(space>aNewSpace)
+		{
+		TInt s=Min(KMaxBufSize, I64INT(space-aNewSpace));
+		TInt r=aFile.Write(TheBuffer,s);
+		if( !LffsDrive )
+			{
+			test(r==KErrNone);
+			}
+		else
+			{
+			//
+			// LFFS is less predictable than a normal drive because of the logging
+			// and metadata arrangement
+			//
+			test( (KErrNone==r) || (KErrDiskFull==r) );
+			if( KErrDiskFull == r )
+				{
+				// shrink the file back down again to give the requested free space
+				TInt fileSize;
+				r=aFile.Size( fileSize );
+				test( KErrNone == r );
+				test( TInt64(fileSize) > aNewSpace );
+				fileSize -= I64LOW(aNewSpace);
+				r=aFile.SetSize( fileSize );
+				test( KErrNone == r );
+
+				space=FreeDiskSpace(aDrive);
+				while( space < aNewSpace )
+					{
+					fileSize -= I64LOW(aNewSpace - space);
+					test( fileSize > 0 );
+					r=aFile.SetSize( fileSize );
+					test( KErrNone == r );
+					space=FreeDiskSpace(aDrive);
+					}
+				break;
+				}
+			}
+
+		space=FreeDiskSpace(aDrive);
+		}
+	return(space);
+	}
+
+LOCAL_C void WriteToFile(RFile& aFile,TInt aSize)
+//
+//
+//
+	{
+	while(aSize>0)
+		{
+		TInt s=Min(KMaxBufSize,aSize);
+		TInt r=aFile.Write(TheBuffer,s);
+		aSize-=s;
+
+		// Flush if write caching enabled to ensure we get disk space notifications
+		if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone))
+			r = aFile.Flush();
+
+		if( !LffsDrive )
+			{
+			test(r==KErrNone);
+			}
+		else
+			{
+			// we can't accurately predict the amount of data we can actually get
+			// on an LFFS drive, so it's posible we could exceed the available
+			// space even though we are writing less that the reported free space
+			test( (KErrNone==r) || (KErrDiskFull==r) );
+			if( KErrDiskFull == r )
+				{
+				break;	// just stop
+				}
+			}
+		}
+	}
+
+
+LOCAL_C void CleanupForThread(TInt aTask)
+//
+//
+//
+	{
+	TInt r;
+	switch(aTask)
+		{
+		case ETaskMkDir:
+			r=TheFs.RmDir(KTestDir1);
+			test(r==KErrNone);
+			break;
+		case ETaskRmDir: break;
+		case ETaskDelete: break;
+		case ETaskReplace:
+			r=TheFs.Delete(KTestFile2);
+			test(r==KErrNone);
+			break;
+		case ETaskFileReplace:
+			r=TheFs.Delete(KTestFile1);
+			test(r==KErrNone);
+			break;
+		case ETaskFileWrite:
+		case ETaskFileWrite4KB:
+		case ETaskFileWrite64KB:
+		case ETaskFileSetSize:
+		case ETaskFileCreateLffs:
+		case ETaskNoChange1:
+		case ETaskNoChange2:
+			r=TheFs.Delete(KTestFile1);
+			if(r!=KErrNone)
+				{
+				test.Printf(_L("r=%d"),r);
+				test(EFalse);
+				}
+			break;
+		case ETaskSpin:
+		default:break;
+		}
+	}
+
+LOCAL_C void InitialiseForThread(TInt aTask)
+//
+//
+//
+	{
+	TInt r;
+	RFile file,file2;
+	switch(aTask)
+		{
+		case ETaskMkDir:	break;
+		case ETaskRmDir:
+			r=TheFs.MkDir(KTestDir1);
+			test(r==KErrNone);
+			break;
+		case ETaskDelete:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			if( !LffsDrive )
+				{
+				r=file.SetSize(KFileSize1);
+				test(r==KErrNone);
+				}
+			else
+				{
+				// LFFS supports sparse files, so we have to write real data
+				// into the file to ensure that it uses up disk space
+				WriteToFile( file, KFileSize1 );
+				}
+			file.Close();
+			break;
+		case ETaskReplace:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			if( !LffsDrive )
+				{
+				r=file.SetSize(KFileSize1);
+				test(r==KErrNone);
+				}
+			else
+				{
+				WriteToFile( file, KFileSize2 );
+				}
+			file.Close();
+			r=file2.Create(TheFs,KTestFile2,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			if( !LffsDrive )
+				{
+				r=file2.SetSize(KFileSize3);
+				test(r==KErrNone);
+				}
+			else
+				{
+				WriteToFile( file2, gMinFileSize << 4);	// 512 * 16 = 8K
+				}
+			file2.Close();
+			break;
+		case ETaskFileReplace:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			if( !LffsDrive )
+				{
+				r=file.SetSize(KFileSize3*2);
+				}
+			else
+				{
+				WriteToFile( file, KFileSize3 );
+				}
+			test(r==KErrNone);
+			file.Close();
+			break;
+		case ETaskFileWrite:
+		case ETaskFileWrite4KB:
+		case ETaskFileWrite64KB:
+		case ETaskFileSetSize:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			file.Close();
+			break;
+		case ETaskNoChange1:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			if( !LffsDrive )
+				{
+				r=file.SetSize(KFileSize1);
+				test(r==KErrNone);
+				}
+			else
+				{
+				WriteToFile( file, KFileSize1 );
+				}
+			file.Close();
+			break;
+		case ETaskNoChange2:
+			r=file.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			file.Close();
+			break;
+		case ETaskFileCreateLffs:
+			r = TheFs.Delete(KTestFile1);
+			break;
+		case ETaskSpin:
+		default:break;
+		}
+	}
+
+LOCAL_C TInt ThreadFunction(TAny* aThreadTask)
+//
+//
+//
+	{
+	RTest test(_L("T_DSPACE_ThreadFunction"));
+	RFs fs;
+	TInt r=fs.Connect();
+	test(r==KErrNone);
+	r=fs.SetSessionPath(gSessionPath);
+	test(r==KErrNone);
+	TThreadTask task=*(TThreadTask*)&aThreadTask;
+	RFile file;
+	switch(task)
+		{
+		case ETaskMkDir:
+			r=fs.MkDir(KTestDir1);
+			test(r==KErrNone);
+			break;
+		case ETaskRmDir:
+			r=fs.RmDir(KTestDir1);
+			test(r==KErrNone);
+			break;
+		case ETaskDelete:
+			r=fs.Delete(KTestFile1);
+			test(r==KErrNone);
+			break;
+		case ETaskReplace:
+			r=fs.Replace(KTestFile1,KTestFile2);
+			test(r==KErrNone);
+			break;
+		case ETaskFileReplace:
+			r=file.Replace(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			file.Close();
+			break;
+		case ETaskFileWrite:
+			r=file.Open(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+#if defined(__WINS__)
+			WriteToFile( file, gMinFileSize << 4);	// 512 * 16 = 8K
+#else
+			WriteToFile( file, gMinFileSize << 1);	// 512 * 2 = 1K
+#endif
+			file.Close();
+			break;
+		case ETaskFileWrite4KB:
+			r=file.Open(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			WriteToFile(file,gMinFileSize << 3);	// 512 * 2^3 = 512 * 8 = 4K
+			file.Close();
+			break;
+		case ETaskFileWrite64KB:
+			r=file.Open(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			WriteToFile(file,gMinFileSize<<7);	// 512 * 2^7 = 512 * 128 = 64K
+			file.Close();
+			break;
+		case ETaskFileSetSize:
+			r=file.Open(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			r=file.SetSize(KFileSize3);
+			file.Close();
+			break;
+		case ETaskFileCreateLffs:
+			r=file.Create(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			file.Close();
+			break;
+		case ETaskNoChange1:
+			{
+			r=file.Open(fs,KTestFile1,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			TTime time;
+			time.HomeTime();
+			r=file.SetModified(time);
+			test(r==KErrNone);
+			file.Close();
+			break;
+			}
+		case ETaskNoChange2:
+			{
+			TEntry e;
+			r=fs.Entry(KTestFile1,e);
+			test(r==KErrNone);
+			break;
+			}
+		case ETaskSpin:
+			for(;;) {};
+
+		default:break;
+		}
+	fs.Close();
+	return(KErrNone);
+	}
+
+
+void TestCancellation()
+//
+// test error disk space notification requests can be cancelled
+//
+	{
+	test.Next(_L("test disk space cancellation"));
+	const TInt ThresholdSize=500;
+	// test a cancellation
+	// Up the priority of this thread so that we can cancel the request before the drive thread
+	// runs, to test whether cancelling still works.
+	RThread().SetPriority(EPriorityRealTime);
+	TRequestStatus stat1;
+	TheFs.NotifyDiskSpace(ThresholdSize,KDefaultDrive,stat1);
+	test(stat1==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrCancel);
+	RThread().SetPriority(EPriorityNormal);
+	// test no affect if already cancelled
+	stat1=KErrNone;
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrNone);
+	// set up two requests, cancel 1
+	TRequestStatus stat2;
+	TheFs.NotifyDiskSpace(ThresholdSize,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(ThresholdSize,KDefaultDrive,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(stat2);
+	test(stat1==KRequestPending && stat2==KErrCancel);
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrCancel);
+
+	if( !LffsDrive)
+		{
+		// now repeat with c: and removable drive
+		TheFs.NotifyDiskSpace(ThresholdSize,EDriveC,stat1);
+		TheFs.NotifyDiskSpace(ThresholdSize,RemovableDrive,stat2);
+		test(stat1==KRequestPending && stat2==KRequestPending);
+		TheFs.NotifyDiskSpaceCancel(stat1);
+		test(stat2==KRequestPending && stat1==KErrCancel);
+		TheFs.NotifyDiskSpaceCancel(stat2);
+		test(stat2==KErrCancel);
+		}
+	}
+
+void TestErrorConditions()
+//
+// test disk space notification requests return correct error value
+//
+	{
+	test.Next(_L("test error conditions"));
+	// attempt to set up disk space notification with a threshold of zero
+	TRequestStatus status;
+	TheFs.NotifyDiskSpace(0,KDefaultDrive,status);
+	test(status==KErrArgument);
+	// test on an empty drive
+	TheFs.NotifyDiskSpace(100,EDriveO,status);
+	test(status==KErrNotReady);
+	// test on a drive out of rance
+	TheFs.NotifyDiskSpace(100,27,status);
+	test(status==KErrBadName);
+	// new setup with threshold of one
+	TheFs.NotifyDiskSpace(1,KDefaultDrive,status);
+	test(status==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(status);
+	test(status==KErrCancel);
+	// and with a threshold > disk size
+	TheFs.NotifyDiskSpace(TheDiskSize+10,KDefaultDrive,status);
+	test(status==KErrArgument);
+	// now with a size of max size -1
+	TheFs.NotifyDiskSpace(TheDiskSize-1,KDefaultDrive,status);
+	test(status==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(status);
+	test(status==KErrCancel);
+	// set up mutiple requests and cancel one
+	TRequestStatus status2,status3;
+	TheFs.NotifyDiskSpace(TheDiskSize-10,KDefaultDrive,status);
+	TheFs.NotifyDiskSpace(TheDiskSize-10,KDefaultDrive,status2);
+	TheFs.NotifyDiskSpace(TheDiskSize-10,KDefaultDrive,status3);
+	test(status==KRequestPending&&status2==KRequestPending&&status3==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(status3);
+	test(status==KRequestPending&&status2==KRequestPending&&status3==KErrCancel);
+	// cancel the remaining ones
+	TheFs.NotifyDiskSpaceCancel();
+	test(status==KErrCancel&&status2==KErrCancel&&status3==KErrCancel);
+	}
+
+TInt GenerateMediaChange()
+	{
+	RLocalDrive d;
+	TBool flag=EFalse;
+	//Find the local drive number corresponding to removabledrive
+	TMediaSerialNumber serialNum;
+	TInt r = TheFs.GetMediaSerialNumber(serialNum, RemovableDrive);
+	if(r!= KErrNone)
+		return r;
+
+	TInt len = serialNum.Length();
+	test.Printf(_L("Serial number (len %d) :"), len);
+
+	TInt localDriveNum = -1;
+	for (TInt n=0; n<KMaxLocalDrives; n++)
+		{
+		r = d.Connect(n, flag);
+		if(r != KErrNone)
+			{
+			test.Printf(_L("drive %d: TBusLocalDrive::Connect() failed %d\n"), n, r);
+			continue;
+			}
+
+	    TLocalDriveCapsV5Buf capsBuf;
+	    TLocalDriveCapsV5& caps = capsBuf();
+		r = d.Caps(capsBuf);
+		if(r != KErrNone)
+			{
+			test.Printf(_L("drive %d: TBusLocalDrive::Caps() failed %d\n"), n, r);
+			continue;
+			}
+
+
+		TPtrC8 localSerialNum(caps.iSerialNum, caps.iSerialNumLength);
+		if (serialNum.Compare(localSerialNum) == 0)
+			{
+				localDriveNum = n;
+				d.Close();
+				break;
+			}
+
+		d.Close();
+		}
+	r =d.Connect(localDriveNum,flag);
+	if (r!=KErrNone)
+		return r;
+	d.ForceMediaChange();
+	d.Close();
+	return KErrNone;
+	}
+
+void TestDiskNotify()
+//
+// test functions that can result in disk change notification
+// format,scandrive, media change
+//
+	{
+	// make default directory
+	_LIT(defaultDir,"C:\\F32-TST\\");
+	TInt r=TheFs.MkDirAll(defaultDir);
+	test(r==KErrNone||r==KErrAlreadyExists);
+	// create the filler file
+	RFile file;
+	TFileName fileName=_L("C:");
+	fileName+=KFileFiller;
+	r=file.Create(TheFs,fileName,EFileShareAny|EFileWrite);
+	test(r==KErrNone);
+	TInt64 free=FreeDiskSpace(EDriveC);
+	// use up 16KB
+	FillDisk(file,free-16384,EDriveC);
+
+	// test formatting notifies clients on only specific drive
+	test.Next(_L("test formatting"));
+	TRequestStatus stat1, stat2;
+	TInt64 freeC=FreeDiskSpace(EDriveC);
+	TInt64 freeD=FreeDiskSpace(RemovableDrive);
+	TheFs.NotifyDiskSpace(freeC+1024,EDriveC,stat1);
+	TheFs.NotifyDiskSpace(freeD-1024,RemovableDrive,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+	RFormat f;
+	TInt count;
+	r=f.Open(TheFs,RemovableDriveBuf,EQuickFormat,count);
+	test(r==KErrNone);
+	while(count)
+		{
+		r=f.Next(count);
+		test(r==KErrNone);
+		}
+	f.Close();
+	User::After(1000000);
+	User::WaitForRequest(stat2);
+	test(stat1==KRequestPending && stat2==KErrNone);
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrCancel);
+
+	// and create the test directory for the removable drive
+	TFileName fName=_L("");
+	fName+=RemovableDriveBuf;
+	fName+=_L("F32-TST\\");
+	r=TheFs.MkDirAll(fName);
+	test(r==KErrNone);
+
+	// test that a media change notifies clients on all drives
+	test.Next(_L("media change"));
+	freeC=FreeDiskSpace(EDriveC);
+	freeD=FreeDiskSpace(RemovableDrive);
+	test.Printf(_L("free space on drive %d = 0x%x\n"),EDriveC,freeC);
+	test.Printf(_L("free space on drive %d = 0x%x\n"),RemovableDrive,freeD);
+	TheFs.NotifyDiskSpace(freeC+1024,EDriveC,stat1);
+	TheFs.NotifyDiskSpace(freeD-1024,RemovableDrive,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+//	UserSvr::ForceRemountMedia(ERemovableMedia0);
+	r = GenerateMediaChange();
+	if(r == KErrNone)
+		{
+		User::After(1000000);
+		User::WaitForRequest(stat2);
+		test(stat1==KRequestPending && stat2==KErrNone);
+		TheFs.NotifyDiskSpaceCancel(stat1);
+		test(stat1==KErrCancel);
+		}
+	else
+		{
+		test.Printf(_L("media change not supported, skipping this step\n"));
+		TheFs.NotifyDiskSpaceCancel(stat1);
+		test(stat1 == KErrCancel);
+		TheFs.NotifyDiskSpaceCancel(stat2);
+		test(stat2 == KErrCancel);
+		}
+
+	// test that scandrive notifies clients on only specific drives
+	test.Next(_L("scandrive"));
+	// first test that scandrive does not find any problems on the removable media
+	r=TheFs.ScanDrive(RemovableDriveBuf);
+	test(r==KErrNone);
+	// now set up disk space notification
+	freeC=FreeDiskSpace(EDriveC);
+	freeD=FreeDiskSpace(RemovableDrive);
+	test.Printf(_L("free space on drive %d = 0x%x\n"),EDriveC,freeC);
+	test.Printf(_L("free space on drive %d = 0x%x\n"),RemovableDrive,freeD);
+	TheFs.NotifyDiskSpace(freeC+8192,EDriveC,stat1);
+	TheFs.NotifyDiskSpace(freeD-8192,RemovableDrive,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+	r=TheFs.ScanDrive(RemovableDriveBuf);
+	test(r==KErrNone);
+	User::After(1000000);
+	User::WaitForRequest(stat2);
+	test(stat1==KRequestPending && stat2==KErrNone);
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrCancel);
+
+	file.Close();
+	r=TheFs.Delete(fileName);
+	test(r==KErrNone);
+	if(gSessionPath[0]!=(TText)'C')
+		{
+		r=TheFs.RmDir(defaultDir);
+		test(r==KErrNone||r==KErrInUse);
+		}
+
+	}
+
+void TestFunctions()
+//
+// test some of the functions that may result in a change in free disk space
+// not testing all functions that may result in free disk space change since
+// change is dependent on the file system
+//
+	{
+	test.Next(_L("test disk space functions"));
+	// create the filler file
+	RFile file;
+	TInt r=file.Create(TheFs,KFileFiller,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+	test(r==KErrNone);
+	TInt64 newSpace = FreeDiskSpace(KDefaultDrive)-8192;
+	FillDisk(file,newSpace,KDefaultDrive);
+
+	// check file write
+	test.Next(_L("check RFile:Write"));
+	TThreadTask task=ETaskFileWrite;
+	InitialiseForThread(task);
+	TInt64 free=FreeDiskSpace(KDefaultDrive);
+	TRequestStatus stat1;
+	TInt64 threshold=free-200;
+	TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+	test(stat1==KRequestPending);
+	RThread thread;
+	r=thread.Create(_L("thread1"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Resume();
+	User::WaitForRequest(stat1);
+	test(stat1==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold);
+	TRequestStatus deathStat;
+	thread.Logon( deathStat );
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// check file set size
+	// setting file size on LFFS only uses a small amount of disk space for metadata
+	// so we skip this test for an LFFS drive
+	if( !LffsDrive )
+		{
+		test.Next(_L("check RFile:SetSize"));
+		task=ETaskFileSetSize;
+		InitialiseForThread(task);
+		free=FreeDiskSpace(KDefaultDrive);
+		threshold=free-100;
+		TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+		test(stat1==KRequestPending);
+		r=thread.Create(_L("thread2"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+		test(r==KErrNone);
+		TRequestStatus deathStat;
+		thread.Logon( deathStat );
+		thread.Resume();
+		User::WaitForRequest(stat1);
+		test(stat1==KErrNone);
+		free=FreeDiskSpace(KDefaultDrive);
+		test(free<threshold);
+		User::WaitForRequest( deathStat );
+		thread.Close();
+		CleanupForThread(task);
+		}
+
+	// check disk space notification does not occur when threshold not crossed
+	TInt64 newFree;
+	test.Next(_L("check RFile:SetSize with wrong threshold"));
+
+	User::After(1000000);	//put in due to thread latency
+
+	task=ETaskFileSetSize;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	threshold=free+100;
+	TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+	test(stat1==KRequestPending);
+	r=thread.Create(_L("thread3"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.Resume();
+
+	User::After(1000000);
+
+	test(stat1==KRequestPending);
+	newFree=FreeDiskSpace(KDefaultDrive);
+/*
+	test.Printf(_L("threshold = %d and %d"),threshold.High(), threshold.Low());
+	test.Printf(_L("free = %d and %d"),free.High(), free.Low());
+	test.Printf(_L("newFree = %d and %d"),newFree.High(), newFree.Low());
+*/
+	if(!LffsDrive)
+		test(free<threshold && newFree<free);
+	else
+		test(free<threshold);		//changing file size on lffs does not do anything
+
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+	TheFs.NotifyDiskSpaceCancel(stat1);
+	test(stat1==KErrCancel);
+
+	// check for file delete
+	test.Next(_L("check RFs::Delete"));
+	task=ETaskDelete;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	threshold=free+300;
+	TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+	test(stat1==KRequestPending);
+	r=thread.Create(_L("thread4"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::WaitForRequest(stat1);
+	test(stat1==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free>threshold);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// check for replace with threshold too high
+	test.Next(_L("check RFs::Replace with threshold too high"));
+
+    if( LffsDrive )
+	{
+	    test.Printf( _L("Skipped.... test isn't consistent on LFFS drive\n") );
+	}
+    else
+	{
+        task=ETaskReplace;
+	    InitialiseForThread(task);
+	    free=FreeDiskSpace(KDefaultDrive);
+    #if defined(__WINS__)
+	    threshold=free + gMinFileSize * 16 + 2048;		// 512 * 16 + 2K = 10K
+    #else
+	    if(LffsDrive )
+		    threshold=free + (gMinFileSize << 4) + 2048;	// 512 * 16 + 2K = 10K
+	    else
+		    threshold=free + gMinFileSize * 9 + 392;	// 512 * 9 + 392 = 5000;
+    #endif
+
+	    TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+	    test(stat1==KRequestPending);
+
+	    r=thread.Create(_L("thread5"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	    test(r==KErrNone);
+	    thread.Logon( deathStat );
+	    thread.Resume();
+
+	    User::After(1000000);
+	    test(stat1==KRequestPending);
+	    newFree=FreeDiskSpace(KDefaultDrive);
+	    test(newFree<threshold && free<newFree);
+	    TheFs.NotifyDiskSpaceCancel();
+	    test(stat1==KErrCancel);
+	    User::WaitForRequest( deathStat );
+	    thread.Close();
+	    CleanupForThread(task);
+
+
+	    // check for replace
+	    test.Next(_L("check RFs:Replace"));
+	    task=ETaskReplace;
+	    InitialiseForThread(task);
+	    free=FreeDiskSpace(KDefaultDrive);
+	    threshold=free+200;
+	    TheFs.NotifyDiskSpace(threshold,KDefaultDrive,stat1);
+	    test(stat1==KRequestPending);
+	    r=thread.Create(_L("thread6"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	    test(r==KErrNone);
+	    thread.Logon( deathStat );
+	    thread.Resume();
+	    User::WaitForRequest(stat1);
+	    //User::After(1000000);
+	    test(stat1==KErrNone);
+	    User::WaitForRequest( deathStat );
+	    test(deathStat==KErrNone);
+	    thread.Close();
+	    CleanupForThread(task);
+	    free=FreeDiskSpace(KDefaultDrive);
+	    test(free>threshold);
+    }
+
+	// check that CSessionFS::iTheDrive is set in subsession calls
+	test.Next(_L("Check iTheDrive and subsessions"));
+	if( LffsDrive )
+		{
+		test.Printf( _L("Skipped.... test not done on LFFS drive\n") );
+		}
+	else
+		{
+		RFile f2;
+#if defined(__WINS__)
+		_LIT(someFile,"X:\\abcdef");
+#else
+		TBuf<10> someFile=_L("?:\\abcdef");
+		TChar c;
+		TInt r=RFs::DriveToChar(RemovableDrive,c);
+		test(r==KErrNone);
+		someFile[0]=(TText)c;
+#endif
+		_LIT(someDir,"C:\\1234\\");
+
+		r=f2.Create(TheFs,someFile,EFileShareAny|EFileWrite);
+		test(r==KErrNone);
+		r=TheFs.MkDir(someDir);
+		test(r==KErrNone);
+		TRequestStatus stat2;
+		TInt64 freeC=FreeDiskSpace(EDriveC);
+		TInt64 freeD=FreeDiskSpace(RemovableDrive);
+		TheFs.NotifyDiskSpace(freeC-4097,EDriveC,stat1);
+		TheFs.NotifyDiskSpace(freeD-4097,RemovableDrive,stat2);
+		test(stat1==KRequestPending && stat2==KRequestPending);
+		// before fix this would result in iTheDrive not being updated in next subsession call
+		// therefore this could would not result in a disk space notification
+		r=f2.SetSize(8192);
+		test(r==KErrNone);
+		User::After(1000000);
+		User::WaitForRequest(stat2);
+
+		if (stat2!=KErrNone)
+			test.Printf(_L("stat2=%d\n"),stat2.Int());
+		test(stat2==KErrNone);
+		if (stat1!=KRequestPending)
+			test.Printf(_L("stat1=%d\n"),stat1.Int());
+		test(stat1==KRequestPending);
+
+		f2.Close();
+		TheFs.NotifyDiskSpaceCancel();
+		test(stat1==KErrCancel);
+		r=TheFs.Delete(someFile);
+		test(r==KErrNone);
+		r=TheFs.RmDir(someDir);
+		test(r==KErrNone);
+		}
+
+	file.Close();
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone);
+	}
+
+
+
+void TestLffsFunctions()
+//
+// LFFS-specific tests for some functions which may cause a disk
+// space change
+//
+//
+	{
+	test.Next(_L("test LFFS disk space functions"));
+	// create the filler file
+	RFile file;
+	TInt r=file.Create(TheFs,KFileFiller,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+	test(r==KErrNone);
+	TInt64 newSpace = FreeDiskSpace(KDefaultDrive)-8192;
+	FillDisk(file,newSpace,KDefaultDrive);
+
+
+	// check file create
+	// Creating a file will always allocate space for the inode
+	test.Next(_L("check RFile:Create"));
+	TThreadTask task=ETaskFileCreateLffs;
+	InitialiseForThread(task);
+	TInt64 free=FreeDiskSpace(KDefaultDrive);
+	TInt64 threshold1=free-64;
+	TInt64 threshold2=free-KFileSize3;
+	TRequestStatus stat1, stat2;
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+	RThread thread;
+	r=thread.Create(_L("thread7"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	TRequestStatus deathStat;
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::WaitForRequest(stat1);
+	test(stat1==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold1);
+	test(stat2==KRequestPending);
+	test(free>threshold2);
+	TheFs.NotifyDiskSpaceCancel(stat2);
+	User::WaitForRequest(stat2);
+	test(stat2==KErrCancel);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	TInt64 threshold3;
+	TRequestStatus stat3;
+	RFile file2;
+	// don't test for wins urel since cant use RFs::ControlIo
+#if defined(_DEBUG)
+	// check background thread notification
+	test.Next(_L("check Background thread notification"));
+	task=ETaskSpin; // create thread to block background thread
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	threshold1=free-64;		// this should occur when we create test file
+	threshold2=free+9750;		// some impossible value
+	threshold3=free+10000;	// some other impossible value that will never happen
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	TheFs.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+	test(stat1==KRequestPending && stat2==KRequestPending && stat3==KRequestPending);
+	r=thread.Create(_L("thread8"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.SetPriority( EPriorityLess );
+	thread.Resume();	// start spinning, blocks background thread
+	// request background thread to notify a daft value
+    TPckgBuf<TInt64> cBuf;
+	cBuf() = threshold2;
+ 	// Hard code the value of ECioBackgroundNotifyDiskSize.
+ 	// Although the value is enumerated in f32\slffs\lffs_controlio.h this header file is being
+ 	// removed from the release codeline but retained in the base team development codeline.
+	#define ECioBackgroundNotifyDiskSize 10016
+	r = TheFs.ControlIo(GetDriveLFFS(), ECioBackgroundNotifyDiskSize, cBuf);
+	test( KErrNone==r );
+	// create a  file to force some roll-forward
+	r=file2.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+	test(r==KErrNone);
+	User::WaitForRequest(stat1);
+	test(stat1==KErrNone);
+	test(stat2==KRequestPending);
+	test(stat3==KRequestPending);
+	// kill the spinner thread to allow the background thread to execute
+	thread.Kill(KErrNone);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	// wait for the notifier
+	User::WaitForRequest(stat2);
+	test( stat2==KErrNone );
+	test( stat3==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(stat3);
+	User::WaitForRequest(stat3);
+	test(stat3==KErrCancel);
+	CleanupForThread(task);
+	file2.Close();
+	TheFs.Delete( KTestFile1 );
+#endif
+
+	// check background thread notification again, this time we won't request
+	// a special value - check that it notifies normally
+	test.Next(_L("check Background thread notification again"));
+	task=ETaskSpin; // create thread to block background thread
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	threshold1=free-64;		// this should occur when we create test file
+	threshold2=free+9750;		// some impossible value
+	threshold3=free+10000;	// some other impossible value that will never happen
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	TheFs.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+	test(stat1==KRequestPending && stat2==KRequestPending && stat3==KRequestPending);
+	r=thread.Create(_L("thread9"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.SetPriority( EPriorityLess );
+	thread.Resume();	// start spinning, blocks background thread
+	// create a  file to force some roll-forward
+	r=file2.Create(TheFs,KTestFile1,EFileShareAny|EFileWrite);
+	test(r==KErrNone);
+	User::WaitForRequest(stat1);
+	test(stat1==KErrNone);
+	test(stat2==KRequestPending);
+	test(stat3==KRequestPending);
+	// kill the spinner thread to allow the background thread to execute
+	thread.Kill(KErrNone);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	// wait for the notifier
+	test( stat2==KRequestPending );
+	test( stat3==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel(stat2);
+	User::WaitForRequest(stat2);
+	test(stat2==KErrCancel);
+	TheFs.NotifyDiskSpaceCancel(stat3);
+	User::WaitForRequest(stat3);
+	test(stat3==KErrCancel);
+	CleanupForThread(task);
+	file2.Close();
+	TheFs.Delete( KTestFile1 );
+
+
+	file.Close();
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone);
+	}
+
+
+void TestMultiple()
+//
+// test muliple requests and multiple sessions
+//
+	{
+	// create the filler file
+	RFile file;
+	TInt r=file.Create(TheFs,KFileFiller,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+	test(r==KErrNone);
+	TInt64 free=FreeDiskSpace(KDefaultDrive);
+	TInt64 freeSpaceLeft = gMinFileSize << 4;	// 512 * 2^4 = 512 * 16 = 8K
+	FillDisk(file,free-freeSpaceLeft,KDefaultDrive);
+	TInt size;
+	r=file.Size(size);
+	test(r==KErrNone);
+	test(size>1024);
+	test.Printf(_L("filler file size=0x%x\n"),size);
+
+	// test multiple requests
+	test.Next(_L("test multiple requests"));
+	TThreadTask task = ETaskFileWrite4KB;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	TInt64 threshold1=free-gMinFileSize;	// 512;
+	TInt64 threshold2=free - (gMinFileSize << 2);	// 512 * 4 = 2048;
+#if defined(__WINS__)
+	TInt64 threshold3=free-70000;	//NTFS over-allocates then reduces when file closed
+#else
+	TInt64 threshold3=free - (gMinFileSize << 5);	// 512 * 2^5 = 512 * 32 = 16K
+#endif
+	TRequestStatus stat1,stat2,stat3;
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	TheFs.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	RThread thread;
+	r=thread.Create(_L("thread1"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	TRequestStatus deathStat;
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::After(1000000);
+	User::WaitForRequest(stat1);
+	User::WaitForRequest(stat2);
+	test(stat1==KErrNone && stat2==KErrNone);
+	test(stat3==KRequestPending);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold1 && free<threshold2 && free>threshold3);
+	TheFs.NotifyDiskSpaceCancel(stat3);
+	test(stat3==KErrCancel);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// test multiple requests again
+	test.Next(_L("test multiple requests again"));
+	if( LffsDrive )
+		{
+		// SetSize doesn't use disk space on LFFS
+		test.Printf( _L("test skipped on LFFS drive\n") );
+		}
+	else
+		{
+		task=ETaskFileSetSize;
+		InitialiseForThread(task);	// this also does initialisation for task2
+		free=FreeDiskSpace(KDefaultDrive);
+		threshold1 = free + (gMinFileSize << 1);	// 512 * 2 = 1024
+		threshold2 = free + (gMinFileSize * 12);	// 512 * 12 = 6144
+		threshold3 = free - (gMinFileSize << 1);	// 512 * 2 = 1024;
+		TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+		TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+		TheFs.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+		test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+		r=thread.Create(_L("thread2"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+		test(r==KErrNone);
+		thread.Logon( deathStat );
+		thread.Resume();
+		User::After(10000);
+		User::WaitForRequest(stat3);
+		test(stat3==KErrNone && stat1==KRequestPending && stat2==KRequestPending);
+		free=FreeDiskSpace(KDefaultDrive);
+		test(free<threshold1);
+		User::WaitForRequest( deathStat );
+		thread.Close();
+		CleanupForThread(task);
+		if(!IsWinsCDrive(KDefaultDrive))
+			{
+			free=FreeDiskSpace(KDefaultDrive);
+			test(stat1==KRequestPending && stat2==KRequestPending);
+			file.SetSize(size - (gMinFileSize << 2));	// 512 * 4 = 2048
+			free=FreeDiskSpace(KDefaultDrive);
+			User::After(1000000);
+			User::WaitForRequest(stat1);
+			//User::WaitForRequest(stat2);
+			test(stat1==KErrNone && stat2==KRequestPending);
+			free=FreeDiskSpace(KDefaultDrive);
+			test(free>threshold1 && free<threshold2);
+			TheFs.NotifyDiskSpaceCancel();
+			test(stat2==KErrCancel);
+			}
+		else
+			{
+			TheFs.NotifyDiskSpaceCancel();
+			test(stat1==KErrCancel&&stat2==KErrCancel&&stat3==KErrNone);
+			}
+		}
+
+	// test multiple sessions all notified on disk space change
+	test.Next(_L("test multiple sessions on same drive"));
+	RFs ses2,ses3;
+	r=ses2.Connect();
+	test(r==KErrNone);
+	r=ses3.Connect();
+	test(r==KErrNone);
+	r=ses2.SetSessionPath(gSessionPath);
+	test(r==KErrNone);
+	r=ses3.SetSessionPath(gSessionPath);
+	test(r==KErrNone);
+	task=ETaskFileReplace;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	test.Printf(_L("free space on default drive = 0x%x\n"),free);
+	threshold1=free+gMinFileSize;			// 512
+	threshold2=free+(gMinFileSize << 1);	// 1024;
+	threshold3=free+(gMinFileSize << 2);	// 2048;
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	ses2.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	ses3.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	r=thread.Create(_L("thread3"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::After(1000000);
+	User::WaitForRequest(stat1);
+	User::WaitForRequest(stat2);
+	User::WaitForRequest(stat3);
+	test(stat1==KErrNone && stat2==KErrNone && stat3==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free>threshold1 && free>threshold2 && free>threshold3);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// test NotifyDiskSpaceCancel()
+	test.Next(_L("test RFs:NotifyDiskSpaceCancel"));
+	free=FreeDiskSpace(KDefaultDrive);
+	TheFs.NotifyDiskSpace(free-100,KDefaultDrive,stat1);
+	ses2.NotifyDiskSpace(free-100,KDefaultDrive,stat2);
+	ses3.NotifyDiskSpace(free-100,KDefaultDrive,stat3);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel();
+	test(stat1==KErrCancel&&stat2==KRequestPending&&stat3==KRequestPending);
+	ses2.NotifyDiskSpaceCancel(stat2);
+	test(stat2==KErrCancel&&stat3==KRequestPending);
+	ses3.NotifyDiskSpaceCancel();
+	test(stat3==KErrCancel);
+
+	if( !LffsDrive )
+		{
+		TInt sessionDrive;
+		r=RFs::CharToDrive(gSessionPath[0],sessionDrive);
+		test(r==KErrNone);
+		if(sessionDrive!=RemovableDrive)
+			{
+			// first create a file on the removable drive
+			RFile file2;
+			TFileName file2name=RemovableDriveBuf;
+			file2name+=_L("F32-TST\\testfile1");
+			TheFs.Delete(file2name);
+			r=file2.Create(TheFs,file2name,EFileShareAny|EFileWrite);
+			test(r==KErrNone);
+			r=file2.SetSize(KFileSize3);
+			test(r==KErrNone);
+			// test multiple sessions not notified on disk space change on wrong drive
+			test.Next(_L("test multiple sessions on different drives"));
+			task=ETaskFileReplace;
+			InitialiseForThread(task);
+			TInt64 freeDef=FreeDiskSpace(KDefaultDrive);
+			TInt64 freeRem=FreeDiskSpace(RemovableDrive);
+			threshold1=freeDef + (gMinFileSize << 1);	// 1024;
+			threshold2=freeRem + (gMinFileSize << 1);	// 1024;
+			TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+			ses2.NotifyDiskSpace(threshold2,RemovableDrive,stat2);
+			test(stat1==KRequestPending&&stat2==KRequestPending);
+			r=thread.Create(_L("thread4"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+			test(r==KErrNone);
+			thread.Logon( deathStat );
+			thread.Resume();
+			User::After(1000000);
+			User::WaitForRequest(stat1);
+			test(stat1==KErrNone && stat2==KRequestPending);
+			free=FreeDiskSpace(KDefaultDrive);
+			test(free>threshold1);
+			User::WaitForRequest( deathStat );
+			thread.Close();
+			CleanupForThread(task);
+			TheFs.NotifyDiskSpaceCancel(stat2);
+			test(stat2==KRequestPending);
+			ses2.NotifyDiskSpaceCancel(stat2);
+			test(stat2==KErrCancel);
+			file2.Close();
+			r=TheFs.Delete(file2name);
+			test(r==KErrNone);
+			}
+		}
+
+	ses2.Close();
+	ses3.Close();
+
+
+	file.Close();
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone);
+
+	}
+
+
+void TestLffsMultiple()
+//
+// test muliple requests and multiple sessions speicifcally for LFFS drive
+//
+	{
+	// create the filler file
+	RFile file;
+	TInt r=file.Create(TheFs,KFileFiller,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+	test(r==KErrNone);
+	TInt64 free=FreeDiskSpace(KDefaultDrive);
+	FillDisk(file,free-8192,KDefaultDrive);
+	TInt size;
+	r=file.Size(size);
+	test(r==KErrNone);
+	test.Printf(_L("filler file size=0x%x\n"),size);
+
+
+	// test multiple requests again
+	test.Next(_L("test multiple requests on LFFS") );
+
+	TThreadTask task=ETaskFileCreateLffs;
+	InitialiseForThread(task);	// this also does initialisation for task2
+	free=FreeDiskSpace(KDefaultDrive);
+	TInt64 threshold1=free+8192;
+	TInt64 threshold2=free+700; //increased threshold as LFFS, unpredicatably, can release drive space
+	TInt64 threshold3=free-64;
+	TRequestStatus stat1, stat2, stat3;
+//test.Printf(_L("set up notifiers"));
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	TheFs.NotifyDiskSpace(threshold3,KDefaultDrive,stat3);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	RThread thread;
+	r=thread.Create(_L("thread10"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	TRequestStatus deathStat;
+	thread.Logon( deathStat );
+//	test.Printf(_L("Resuming other thread"));
+	thread.Resume();
+	User::After(10000);
+	User::WaitForRequest(stat3);
+	test(stat3==KErrNone && stat1==KRequestPending && stat2==KRequestPending);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold1);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+//	test.Printf(_L("free =%d and %d"),free.Low(), free.High());
+//	test.Printf(_L("stat1=%d, stat2=%d"),stat1,stat2);
+	test(stat1==KRequestPending && stat2==KRequestPending);
+	file.SetSize(6144);
+	User::After(1000000);
+	User::WaitForRequest(stat2);
+	test(stat1==KRequestPending && stat2==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold1 && free>threshold2);
+	TheFs.NotifyDiskSpaceCancel();
+	User::WaitForRequest( stat1 );
+	test(stat1==KErrCancel);
+
+
+
+	TInt sessionDrive;
+	r=RFs::CharToDrive(gSessionPath[0],sessionDrive);
+	test(r==KErrNone);
+	if(sessionDrive!=EDriveC)
+		{
+		// test multiple sessions not notified on disk space change on wrong drive
+		test.Next(_L("test multiple sessions on different drives"));
+
+		RFs ses2,ses3;
+		r=ses2.Connect();
+		test(r==KErrNone);
+		r=ses3.Connect();
+		test(r==KErrNone);
+		r=ses2.SetSessionPath(gSessionPath);
+		test(r==KErrNone);
+		r=ses3.SetSessionPath(gSessionPath);
+		test(r==KErrNone);
+
+		// first create a file on the C:\ drive
+		RFile file2;
+		TFileName file2name=_L("C:\\");
+		file2name+=_L("F32-TST\\");
+		r=TheFs.MkDir(file2name);
+		test( KErrNone==r || KErrAlreadyExists==r );
+		file2name+=_L("testfile1");
+		TheFs.Delete(file2name);
+		r=file2.Create(TheFs,file2name,EFileShareAny|EFileWrite);
+		test(r==KErrNone);
+		WriteToFile( file2, KFileSize3 );
+
+		task=ETaskFileReplace;
+		InitialiseForThread(task);
+		TInt64 freeLffs=FreeDiskSpace(KDefaultDrive);
+		TInt64 freeD=FreeDiskSpace(EDriveC);
+		threshold1=freeLffs+1024;
+		threshold2=freeD+1024;
+		TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+		ses2.NotifyDiskSpace(threshold2,EDriveC,stat2);
+		test(stat1==KRequestPending&&stat2==KRequestPending);
+		r=thread.Create(_L("thread11"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+		test(r==KErrNone);
+		thread.Logon( deathStat );
+		thread.Resume();
+		User::After(1000000);
+		User::WaitForRequest(stat1);
+		test(stat1==KErrNone && stat2==KRequestPending);
+		free=FreeDiskSpace(KDefaultDrive);
+		test(free>threshold1);
+		User::WaitForRequest( deathStat );
+		thread.Close();
+		CleanupForThread(task);
+		TheFs.NotifyDiskSpaceCancel(stat2);
+		test(stat2==KRequestPending);
+		ses2.NotifyDiskSpaceCancel(stat2);
+		User::WaitForRequest( stat2 );
+		test(stat2==KErrCancel);
+		file2.Close();
+		r=TheFs.Delete(file2name);
+		test(r==KErrNone);
+		ses2.Close();
+		ses3.Close();
+		}
+
+
+
+	file.Close();
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone);
+
+	}
+
+
+void TestChangeNotification()
+//
+// test that disk space notification works with (extended) change notification
+//
+	{
+	// create a filler file
+	RFile file;
+	TInt r=file.Create(TheFs,KFileFiller,EFileShareAny|EFileWrite|EFileWriteDirectIO);
+	test(r==KErrNone);
+	TInt64 free=FreeDiskSpace(KDefaultDrive);
+	// use 8KB in filler file
+	FillDisk(file,free-8192,KDefaultDrive);
+
+	// test change notification when no disk space change
+	test.Next(_L("test change notification"));
+	TThreadTask task = ETaskNoChange1;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	TInt64 threshold1=free+gMinFileSize;
+	TInt64 threshold2=free-gMinFileSize;
+	TRequestStatus stat1,stat2,stat3;
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);		//more free space becoming available
+	TheFs.NotifyDiskSpace(threshold2,KDefaultDrive,stat2);
+	TheFs.NotifyChange(ENotifyAll,stat3);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	RThread thread;
+	r=thread.Create(_L("thread1"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	TRequestStatus deathStat;
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::After(1000000);
+	User::WaitForRequest(stat3);
+	if(!LffsDrive)
+		test(stat1==KRequestPending && stat2==KRequestPending && stat3==KErrNone);
+	else
+		test(stat2==KRequestPending && stat3==KErrNone);	//As below
+
+	TheFs.NotifyDiskSpaceCancel();
+	if(!LffsDrive)
+		test(stat1==KErrCancel && stat2==KErrCancel);
+	else
+		test(stat2==KErrCancel);	//is invalid for LFFS as can free up space un expectedly
+
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// Have three different sessions
+	// do an operation that will cause the change notification
+	// and disk change notification to be signalled
+	test.Next(_L(" test change notification and disk space notification"));
+	RFs session2,session3;
+	r=session2.Connect();
+	test(r==KErrNone);
+	r=session3.Connect();
+	test(r==KErrNone);
+	r=session2.SetSessionPath(gSessionPath);
+	test(r==KErrNone);
+	r=session3.SetSessionPath(gSessionPath);
+	test(r==KErrNone);
+	task=ETaskFileWrite;
+	InitialiseForThread(task);
+	free=FreeDiskSpace(KDefaultDrive);
+	threshold1=free-400;
+	TheFs.NotifyDiskSpace(threshold1,KDefaultDrive,stat1);
+	session2.NotifyChange(ENotifyAll,stat2);
+	session3.NotifyChange(ENotifyAll,stat3,KTestFile1);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	r=thread.Create(_L("thread2"),ThreadFunction,KStackSize,KHeapSize,KHeapSize,(TAny*)task);
+	test(r==KErrNone);
+	thread.Logon( deathStat );
+	thread.Resume();
+	User::After(1000000);
+	User::WaitForRequest(stat1);
+	User::WaitForRequest(stat2);
+	User::WaitForRequest(stat3);
+	test(stat1==KErrNone && stat2==KErrNone && stat3==KErrNone);
+	free=FreeDiskSpace(KDefaultDrive);
+	test(free<threshold1);
+	User::WaitForRequest( deathStat );
+	thread.Close();
+	CleanupForThread(task);
+
+	// check cancellation of change notification and disk space notification
+	// on same session
+	test.Next(_L("test cancellation of notifications"));
+	TheFs.NotifyDiskSpace(FreeDiskSpace(KDefaultDrive)-512,KDefaultDrive,stat1);
+	TheFs.NotifyChange(ENotifyAll,stat2);
+	TheFs.NotifyChange(ENotifyAll,stat3,KTestFile1);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	TheFs.NotifyChangeCancel();
+	test(stat1==KRequestPending&&stat2==KErrCancel&&stat3==KErrCancel);
+	TheFs.NotifyDiskSpaceCancel();
+	test(stat1==KErrCancel);
+	// request change notification again
+	TheFs.NotifyDiskSpace(FreeDiskSpace(KDefaultDrive)-512,KDefaultDrive,stat1);
+	TheFs.NotifyChange(ENotifyAll,stat2);
+	TheFs.NotifyChange(ENotifyAll,stat3,KTestFile1);
+	test(stat1==KRequestPending&&stat2==KRequestPending&&stat3==KRequestPending);
+	TheFs.NotifyDiskSpaceCancel();
+	test(stat1==KErrCancel&&stat2==KRequestPending&&stat3==KRequestPending);
+	TheFs.NotifyChangeCancel();
+	test(stat1==KErrCancel&&stat2==KErrCancel&&stat3==KErrCancel);
+
+
+	session2.Close();
+	session3.Close();
+
+	file.Close();
+	r=TheFs.Delete(KFileFiller);
+	test(r==KErrNone);
+
+	}
+
+GLDEF_C void CallTestsL()
+//
+// Do all tests
+//
+	{
+	TInt r = KErrNone;
+	TInt driveNumber;
+	if(IsTestingLFFS())
+		{
+		LffsDrive = ETrue;
+		r = TheFs.CharToDrive( gSessionPath[0], driveNumber );
+		test( KErrNone == r );
+		}
+
+	// at present internal ram drive not tested - the test should allow for fact
+	// that memory allocation will affect the free disk space on the internal ram drive
+
+	// pc file system c drive is also not tested - the test should allow for the fact
+	// that other windows processes may affect the free disk space
+#ifdef __WINS__
+	if(gSessionPath[0]==(TText)'C')
+#else
+	// check if gSessionPath drive is RAM drive
+	TDriveInfo driveInfo;
+	test(KErrNone == TheFs.CharToDrive(gSessionPath[0], driveNumber));
+	test(KErrNone == TheFs.Drive(driveInfo, driveNumber));
+	if(driveInfo.iType == EMediaRam)
+#endif
+		{
+#ifdef __WINS__
+		test.Printf( _L("C:\\ is not tested, test will exit") );
+#else
+		test.Printf( _L("%c:\\ is not tested (is RAM drive), test will exit"), gSessionPath[0]);
+#endif
+		return;
+		}
+	//Test uses C drive as secondary drive so test can't be tested on that drive
+	r = TheFs.CharToDrive(gSessionPath[0], driveNumber);
+	test(r == KErrNone);
+	if(driveNumber == EDriveC)
+		{
+		test.Printf(_L("Test uses C drive as secondary drive so test can't be test on C drive, test will exit"));
+		return;
+		}
+
+	Initialise();
+	TestErrorConditions();
+	TestCancellation();
+	TestFunctions();
+	TestMultiple();
+	if( !LffsDrive )
+		{
+		TestDiskNotify();
+		}
+
+	TestChangeNotification();
+
+	if( LffsDrive )
+		{
+
+		TestLffsFunctions();
+		TestLffsMultiple();
+		}
+	}
+