// Copyright (c) 1997-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\manager\t_notifydismount.cpp
// 
//

#include <f32file.h>
#include <e32test.h>
#include <e32hal.h>
#include <e32math.h>
#include <f32dbg.h>
#include <hal.h>
#include "t_server.h"

LOCAL_D TFullName fsname;
LOCAL_D	RFile file; 
LOCAL_D TRequestStatus stat;
LOCAL_D	TBuf8<0x10> buf; 
LOCAL_D	TFileName fn;
GLDEF_D	RTest test(_L("t_notifydismount"));

#if defined(_DEBUG)
const TInt KControlIoRuggedOn=2;
const TInt KControlIoRuggedOff=3;
const TInt KControlIoIsRugged=4;
#endif

#if defined(_DEBUG)
LOCAL_C void TestFileHandleClosure(TInt aDrvNum)
//
// check that open file handles may be closed following a NotifyDismount in
// forced dismount mode
//	
{
	test.Next( _L("Test File Handle Closure"));

	TInt r = file.Replace(TheFs, fn, EFileWrite); 
	test(r == KErrNone); 
	r = TheFs.FileSystemName(fsname,aDrvNum);
	test(r == KErrNone); 
	buf = _L8("handle test23456");
	r = file.Write(buf); 
	test(r == KErrNone); 
	TheFs.NotifyDismount(aDrvNum, stat, EFsDismountForceDismount); 
	User::WaitForRequest(stat);         
	test(stat.Int() == KErrNone);

	// PDEF137626 Connectivity: Phone reboots automatically when connecting to PC via USB after pl 
	// Check that writing data to a file when the drive is dismounted doesn't upset the file server
	r = file.Write(buf); 
	test(r == KErrNotReady || r == KErrDisMounted); 

	// PDEF091956 was a file server fault EFsDriveThreadError when the file 
	// handle was closed
	file.Close(); 
	r = TheFs.MountFileSystem(fsname,aDrvNum);
	test(r == KErrNone); 
}


LOCAL_C void TestRequestCancelling(TInt aDrvNum)
//
// check that Cancelling all drive thread requests allows File server object to be closed down gracefully
// PDEF101895- Device crash in efile.exe when plugging/unplugging USB cable using fast file ... 
//
	{
	test.Next( _L("Test Request Cancelling") );

	TInt r = TheFs.FileSystemName(fsname,aDrvNum);
	test(r == KErrNone); 
	
	//***************************************
	// first test with an open file handle
	//***************************************
	r = file.Replace(TheFs, fn, EFileWrite); 
	test(r == KErrNone); 

	// up the priority of this thread so that we can queue 2 requests onto the drive thread - 
	// i.e. a TFsNotifyDismount and a TFsCloseObject
	RThread				thisThread;
	thisThread.SetPriority(EPriorityRealTime);

	// Post a TFsNotifyDismount do drive thread - this will cancel all requests when it runs
	// including the subsequent TFsCloseObject...
	TheFs.NotifyDismount(aDrvNum, stat, EFsDismountForceDismount); 

	
	// Post a TFsCloseObject do drive thread - this should be cancelled before it is processed
	// by the earlier TFsNotifyDismount
	file.Close();

	User::WaitForRequest(stat);         
	test(stat.Int() == KErrNone);
	
	thisThread.SetPriority(EPriorityNormal);

	r = TheFs.MountFileSystem(fsname,aDrvNum);
	test(r == KErrNone); 


	//***************************************
	// now test with an open directory handle
	//***************************************

	RDir dir;
	TFileName sessionPath;
	r=TheFs.SessionPath(sessionPath);
	test(r==KErrNone);
	TFileName path=_L("?:\\*");
	path[0]=sessionPath[0];
	r=dir.Open(TheFs,path,KEntryAttMaskSupported);
	test(r==KErrNone);

	thisThread.SetPriority(EPriorityRealTime);
	TheFs.NotifyDismount(aDrvNum, stat, EFsDismountForceDismount); 
	dir.Close();

	User::WaitForRequest(stat);         
	test(stat.Int() == KErrNone);
	
	thisThread.SetPriority(EPriorityNormal);

	r = TheFs.MountFileSystem(fsname,aDrvNum);
	test(r == KErrNone); 
	}


LOCAL_C void TestFileSizeFlushing(TInt aDrvNum)
//
// check that new file sizes are flushed during a NotifyDismount in forced
// dismount mode
//	 
	{	
	test.Next( _L("Test File Size Flushing with EFsDismountForceDismount") );

	TInt size = 0;
	TInt r = file.Replace(TheFs, fn, EFileWrite); 
	test(r == KErrNone); 
	r = TheFs.FileSystemName(fsname,aDrvNum);
	test(r == KErrNone); 
	buf = _L8("size test9123456"); 
	r = file.Write(buf); 
	test(r == KErrNone); 
	r = file.Flush(); 
	test(r == KErrNone); 
	r = file.Write(buf); 
	test(r == KErrNone); 
	TheFs.NotifyDismount(aDrvNum, stat, EFsDismountForceDismount); 
	User::WaitForRequest(stat);         
	test(stat.Int() == KErrNone);
	file.Close();
	r = TheFs.MountFileSystem(fsname,aDrvNum);
	test(r == KErrNone); 
	file.Open(TheFs, fn, EFileWrite);
	r = file.Size(size); 
	test(r == KErrNone); 
	// PDEF091956 was, for example, a file size of 16 rather than 32. new file sizes were
	// not flushed for the forced dismount. this was only a problem with rugged fat off.
	test(size == 32);
	file.Close();

	test.Next( _L("Test File Size Flushing with EFsDismountNotifyClients") );
	size = 0;
	r = file.Replace(TheFs, fn, EFileWrite); 
	test(r == KErrNone); 

	r = file.Write(buf); 
	test(r == KErrNone); 

	r = file.Write(buf); 
	test(r == KErrNone); 

	TheFs.NotifyDismount(aDrvNum, stat, EFsDismountNotifyClients); 
	User::WaitForRequest(stat);

	test(stat.Int() == KErrNone);
	file.Close();

	r = TheFs.MountFileSystem(fsname,aDrvNum);
	test(r == KErrNone); 
	file.Open(TheFs, fn, EFileWrite);
	r = file.Size(size); 
	test(r == KErrNone); 
	test(size == 32);
	file.Close();
	}
#endif

LOCAL_C void TestNotifyCancel(TInt aDrvNum)
//
//	
	{
	test.Next( _L("Test Cancelling a notifier"));

	TRequestStatus status;
	TheFs.NotifyDismount( aDrvNum, status, EFsDismountRegisterClient );
	TheFs.NotifyDismountCancel(status);
	User::WaitForRequest( status );
	test(status.Int() == KErrCancel);

	// up the priority of this thread so that we can queue 2 requests onto the drive thread - 
	// to test CNotifyInfo objects are cleaned up correctly even if the drive thread doesn't run
	RThread	thisThread;
	thisThread.SetPriority(EPriorityRealTime);
	TheFs.NotifyDismount( aDrvNum, status, EFsDismountRegisterClient );
	TheFs.NotifyDismountCancel(status);
	User::WaitForRequest( status );
	test(status.Int() == KErrCancel);
	}

GLDEF_C void CallTestsL()
//
// Call tests that may leave
//
	{
	test.Title();
	TInt drvNum, r;

	r=TheFs.CharToDrive(gDriveToTest,drvNum);
	test(r==KErrNone);


	// dismounting with file system extension present doesn't seem to work
	// so skip the test for now.
	TFullName extName;
	r = TheFs.ExtensionName(extName,drvNum, 0);
	if (r == KErrNone)
		{
		test.Printf(_L("File system extension present (%S). Skipping test.\n"), &extName);
		return;
		}

	
	fn.Format(_L("%c:\\notifydismount.tst"), TUint(gDriveToTest));

	test.Start( _L("Test Notify Dismount") );

#if defined(_DEBUG)
	// the EFsDriveThreadError file server fault (PDEF091956) was only detected 
	// in debug mode
 	TestFileHandleClosure(drvNum);
	TestRequestCancelling(drvNum);
#else
	test.Printf(_L("CallTestsL: Skip TestFileHandleClosure - urel mode.\n"));
#endif
	
	TestNotifyCancel(drvNum);

#if defined(_DEBUG)
	// failure to observe flushing of file size (PDEF091956) only observed 
	// in debug mode
	// debug mode required to determine rugged or non rugged FAT and to switch between these 
	// modes 
	if (IsFileSystemFAT(TheFs,drvNum) || IsFileSystemFAT32(TheFs,drvNum))
		{
		// next test requires rugged fat off
		TUint8 isRugged;
		TPtr8 pRugged(&isRugged,1,1);
		r=TheFs.ControlIo(drvNum,KControlIoIsRugged,pRugged);
		test(r==KErrNone);
		if(isRugged)
			{
			r=TheFs.ControlIo(drvNum,KControlIoRuggedOff);
			test(r==KErrNone);
			}

		TestFileSizeFlushing(drvNum);
	
		// if originally rugged set system back to rugged
		if(isRugged)
			{
			r=TheFs.ControlIo(drvNum,KControlIoRuggedOn);
			test(r==KErrNone);
			}	
		}
	else
		{
		test.Printf(_L("CallTestsL: Skip TestFileSizeFlushing - not a FAT filesystem.\n"));
		}
#else 
		test.Printf(_L("CallTestsL: Skip TestFileSizeFlushing - urel mode.\n"));
#endif

	test.End();
 
	}
