kerneltest/f32test/server/t_dspace.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
child 42 a179b74831c9
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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);
		}
#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
	TheDiskSize=DiskSize(KDefaultDrive);
	// 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();
		}
	}