kerneltest/f32test/smassstorage/msdrive/t_msdrive.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/smassstorage/msdrive/t_msdrive.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,605 @@
+// Copyright (c) 2004-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:
+// Unit tests for the CMassStorageDrive and CDriveManager classes
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include <e32twin.h>
+#include <e32test.h>
+#include <e32property.h>
+
+#include "usbmsshared.h"
+#include "drivemanager.h"
+
+
+enum TestStep
+	{
+	EStepRead0,
+	EStepRead1,
+	EStepWrite0,
+	EStepWrite1,
+	EStepConnected,
+	EStepActive,
+	EStepMediaRemoved,
+	EStepLocked,
+	EStepDisconnecting,
+	EStepDisconnected,
+	EStepConnecting
+	};
+
+LOCAL_D RTest test(_L("MSDRIVE"));
+LOCAL_D const TDriveNumber gDrive1 = EDriveL; // used drive
+LOCAL_D const TDriveNumber gDrive2 = EDriveN; // unused drive
+LOCAL_D CDriveManager* gMgr = NULL;
+LOCAL_D TestStep gTestStep = EStepRead0;
+
+#define NEXT(x) test.Next(_L(#x)); gTestStep = x;
+
+
+/**
+These CProxyDrive functions are copies of code in sf_ext.cpp, 
+since we don't link to EFILE.
+*/
+CProxyDrive::CProxyDrive(CMountCB* aMount) : iMount(aMount) {}
+
+EXPORT_C TInt CProxyDrive::ControlIO(
+		const RMessagePtr2& /*aMessage*/,
+		TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
+	{
+	return(KErrNone);
+	}
+
+EXPORT_C TInt CProxyDrive::DeleteNotify(TInt64 /*aPos*/, TInt /*aLength*/)
+	{
+	return(KErrNone);
+	}
+
+EXPORT_C TInt CProxyDrive::GetInterface(TInt /*aInterfaceId*/, TAny*& /*aInterface*/, TAny* /*aInput*/)
+	{ return KErrNotSupported; }
+
+// Implemented the GetLastErrorInfo method here as this is usually 
+// exported by EFILE, but these unit tests don't link to it.
+EXPORT_C TInt CProxyDrive::GetLastErrorInfo(TDes8& /*anErrorInfo*/)
+	{ return KErrNotSupported; }
+
+CProxyDrive::~CProxyDrive()
+	{
+	}
+
+EXPORT_C TInt CProxyDrive::Read(TInt64 aPos, TInt aLength, const TAny* aTrg, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
+	{
+	return Read(aPos, aLength, *(TDes8*)aTrg);
+	}
+
+EXPORT_C TInt CProxyDrive::Write(TInt64 aPos, TInt /*aLength*/, const TAny* aSrc, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
+	{
+	return Write(aPos, *(TDesC8*)aSrc);
+	}
+
+/** 
+CMassStorageMountCB gets the CProxyDrive from the filesystem,
+but here we want to instantiate our own derived class for testing.
+This allows us to control the error code returned by each function.
+*/
+class CTestProxyDrive : public CProxyDrive
+	{
+public:
+	static TInt iRetval;
+	static TInt iRetval_caps;
+	static TLocalDriveCapsV4 iCaps;
+		 
+	CTestProxyDrive(CDriveManager& aDriveManager) : CProxyDrive(NULL)
+		{
+		iCaps.iType = ::EMediaUnknown;
+
+		iWriteTransferPublisher = CDriveWriteTransferPublisher::NewL(aDriveManager.iDrives);
+		iReadTransferPublisher = CDriveReadTransferPublisher::NewL(aDriveManager.iDrives);
+		}
+		
+	~CTestProxyDrive() 
+		{
+		delete iWriteTransferPublisher;
+		delete iReadTransferPublisher;
+		}
+	virtual TInt Initialise()
+		{
+		return iRetval;
+		}
+	virtual TInt Dismounted()
+		{
+		return iRetval;
+		}
+	virtual TInt Enlarge(TInt )
+		{
+		return iRetval;
+		}
+	virtual TInt ReduceSize(TInt , TInt )
+		{
+		return iRetval;
+		}
+	virtual TInt Read(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
+		{
+		iReadTransferPublisher->StartTimer();
+		return iRetval;
+		}
+	virtual TInt Read(TInt64 ,TInt len, TDes8& buf)
+		{
+		iReadTransferPublisher->StartTimer();
+		buf.SetLength(len);
+		return iRetval;
+		}
+	virtual TInt Write(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
+		{
+		iWriteTransferPublisher->StartTimer();
+		return iRetval;
+		}
+	virtual TInt Write(TInt64 ,const TDesC8& )
+		{
+		iWriteTransferPublisher->StartTimer();
+		return iRetval;
+		}
+	virtual TInt Caps(TDes8& aBuf)
+		{
+		((TLocalDriveCapsV4Buf&)aBuf) = iCaps;
+		
+		return iRetval_caps;
+		}
+	virtual TInt Format(TFormatInfo& )
+		{
+		return iRetval;
+		}
+	virtual TInt Format(TInt64 ,TInt )
+		{
+		return iRetval;
+		}
+	virtual TInt SetMountInfo(const TDesC8* ,TInt )
+		{
+		return iRetval;
+		}
+	virtual TInt ForceRemount(TUint )
+		{
+		return iRetval;
+		}
+	virtual TInt Unlock(TMediaPassword &, TBool )
+		{
+		return iRetval;
+		}
+	virtual TInt Lock(TMediaPassword &, TMediaPassword &, TBool )
+		{
+		return iRetval;
+		}
+	virtual TInt Clear(TMediaPassword &)
+		{
+		return iRetval;
+		}
+	virtual TInt ErasePassword()
+		{
+		return iRetval;
+		}
+
+private:
+	/**
+	Publish and subscribe properties for tracking data transfer volume
+	*/
+	CDriveWriteTransferPublisher* iWriteTransferPublisher;
+	CDriveReadTransferPublisher* iReadTransferPublisher;
+	};
+	
+TInt CTestProxyDrive::iRetval = KErrNone;
+TInt CTestProxyDrive::iRetval_caps = KErrNone;
+TLocalDriveCapsV4 CTestProxyDrive::iCaps;
+
+/**
+From USBMSAPP:
+A set of static objects that hold the latest properties published by Mass Storage,
+and a set of corresponding static functions that process the publish events. 
+The functions are passed by pointer to, and invoked by, CPropertyWatch instances.
+*/
+class PropertyHandlers
+	{
+public:
+	/** The prototype for all public property handler functions */
+	typedef void(*THandler)(RProperty&);
+
+public:
+	static void Read(RProperty& aProperty);
+	static void Written(RProperty& aProperty);
+	static void DriveStatus(RProperty& aProperty);
+
+public:
+	static TBuf8<16> iAllDrivesStatus;
+	static TUsbMsBytesTransferred iKBytesRead;
+	static TUsbMsBytesTransferred iKBytesWritten;
+	};
+
+/**
+From USBMSAPP:
+An active object that subscribes to a specified Mass Storage property and
+calls a provided handler each time the property is published.
+*/
+class CPropertyWatch : public CActive
+	{
+public:
+	static CPropertyWatch* NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler);
+private:
+	CPropertyWatch(PropertyHandlers::THandler aHandler);
+	void ConstructL(TUsbMsDriveState_Subkey aSubkey);
+	~CPropertyWatch();
+	void RunL();
+	void DoCancel();
+private:
+	RProperty iProperty;
+	PropertyHandlers::THandler iHandler;
+	};
+
+CPropertyWatch* CPropertyWatch::NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler)
+	{
+	CPropertyWatch* me=new(ELeave) CPropertyWatch(aHandler);
+	CleanupStack::PushL(me);
+	me->ConstructL(aSubkey);
+	return me;
+	}
+
+CPropertyWatch::CPropertyWatch(PropertyHandlers::THandler aHandler)
+	: CActive(0), iHandler(aHandler)
+	{}
+
+void CPropertyWatch::ConstructL(TUsbMsDriveState_Subkey aSubkey)
+	{
+	User::LeaveIfError(iProperty.Attach(KUsbMsDriveState_Category, aSubkey));
+	CActiveScheduler::Add(this);
+	// initial subscription and process current property value
+	RunL();
+	}
+
+CPropertyWatch::~CPropertyWatch()
+	{
+	Cancel();
+	iProperty.Close();
+	}
+
+void CPropertyWatch::DoCancel()
+	{
+	iProperty.Cancel();
+	}
+
+void CPropertyWatch::RunL()
+	{
+	// resubscribe before processing new value to prevent missing updates
+	iProperty.Subscribe(iStatus);
+
+	iHandler(iProperty);
+
+	SetActive();
+	}
+
+
+TBuf8<16> PropertyHandlers::iAllDrivesStatus;
+TUsbMsBytesTransferred PropertyHandlers::iKBytesRead;
+TUsbMsBytesTransferred PropertyHandlers::iKBytesWritten;
+
+
+/** 
+Handle a publish event for the Bytes Read property.
+*/
+void PropertyHandlers::Read(RProperty& aProperty)
+	{
+	const TUint KNoBytesToRead = 1000;
+	test(aProperty.Get(iKBytesRead)==KErrNone);
+	TInt kbytes = iKBytesRead[0];
+
+	TBuf8<KNoBytesToRead> buf;
+	buf.SetLength(KNoBytesToRead);
+	TInt err;
+
+	switch(gTestStep)
+		{
+		case EStepRead0:
+			// don't do anything until 1st KB reported in 1s interval
+			if(kbytes==0)
+				{
+				break;
+				}
+			test(kbytes==1);
+
+			test(KErrNone == gMgr->Drive(0,err)->Read(0,KNoBytesToRead,buf));
+			NEXT(EStepRead1);
+			break;
+
+		case EStepRead1:
+			test(kbytes==2);
+			
+			// trigger an update:
+			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
+			NEXT(EStepWrite0);
+			break;
+			
+		default:
+			break;
+		}
+	}
+
+/** 
+Handle a publish event for the Bytes Written property.
+*/
+void PropertyHandlers::Written(RProperty& aProperty)
+	{
+	const TUint KNoBytesToWrite = 1000;
+	test(aProperty.Get(iKBytesWritten)==KErrNone);
+	TInt kbytes = iKBytesWritten[0];
+
+	TBuf8<KNoBytesToWrite> buf;
+	buf.SetLength(KNoBytesToWrite);
+	TInt err;
+
+	switch(gTestStep)
+		{
+		case EStepWrite0:
+			test(kbytes==2);
+
+			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
+			NEXT(EStepWrite1);
+			break;
+
+		case EStepWrite1:
+			test(kbytes==3);
+			
+			// trigger transient change to Active state:
+			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
+			NEXT(EStepConnected);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+/** 
+Handle a publish event for the Drive Status property.
+*/
+void PropertyHandlers::DriveStatus(RProperty& aProperty)
+	{
+	RDebug::Print(_L(">> PropertyHandlers::DriveStatus")); 
+	TInt err = aProperty.Get(iAllDrivesStatus);
+	test(err == KErrNone);
+
+	// There should be status for 2 drives:
+	// (Note: there is a pair of bytes per drive,
+	// drive number and drive status.)
+	test(2 == iAllDrivesStatus.Length()/2);
+	test(iAllDrivesStatus[0] == gDrive1);
+	test(iAllDrivesStatus[2*1] == gDrive2);
+	TInt status = iAllDrivesStatus[1];
+
+	switch(gTestStep)
+		{
+		case EStepConnected:
+			test(status==EUsbMsDriveState_Connected);
+			
+			test(KErrNone==gMgr->Drive(0,err)->SetCritical(ETrue));	
+			NEXT(EStepActive);
+			break;
+			
+		case EStepActive:
+			test(status==EUsbMsDriveState_Active);
+					
+			CTestProxyDrive::iRetval_caps = KErrNotReady;
+			test(CMassStorageDrive::EMediaNotPresent
+					==gMgr->Drive(0,err)->CheckDriveState());	
+			CTestProxyDrive::iRetval_caps = KErrNone;
+			
+			NEXT(EStepMediaRemoved);
+			break;
+			
+		case EStepMediaRemoved:
+			{
+			test(status==EUsbMsDriveState_MediaNotPresent);
+			
+			gMgr->Drive(0,err)->CheckDriveState(); // clear old state
+			
+			CTestProxyDrive::iRetval = KErrLocked;
+			TBuf8<1> buf;
+			buf.SetLength(1);
+			test(KErrLocked==gMgr->Drive(0,err)->Write(0,buf));	
+			CTestProxyDrive::iRetval = KErrNone;
+			NEXT(EStepLocked);
+			}
+			break;
+			
+		case EStepLocked:
+			test(status==EUsbMsDriveState_Locked);
+			
+			test(KErrNone == gMgr->Disconnect(0));
+			test(KErrNone == gMgr->Disconnect(0)); // ensure it can be called more than once
+			NEXT(EStepDisconnecting);
+			break;
+		
+		case EStepDisconnecting:
+			test(status==EUsbMsDriveState_Disconnecting);
+			
+			test(KErrNone == gMgr->DeregisterDrive(0));
+			NEXT(EStepDisconnected);
+			break;
+		
+		case EStepDisconnected:
+			test(status==EUsbMsDriveState_Disconnected);
+		
+			test(KErrNone == gMgr->Connect(0));
+			test(KErrNone == gMgr->Connect(0)); // ensure it can be called more than once
+			NEXT(EStepConnecting);
+			break;
+		
+		case EStepConnecting:
+			test(status==EUsbMsDriveState_Connecting);
+			CActiveScheduler::Stop();
+			break;
+
+		default:
+			break;
+		}
+		
+	RDebug::Print(_L("<< PropertyHandlers::DriveStatus"));
+	}
+
+LOCAL_C void doTestL()
+	{
+	test.Start(_L("MSDrive1"));
+	
+	CActiveScheduler* sched = new(ELeave) CActiveScheduler;
+	CleanupStack::PushL(sched);
+	CActiveScheduler::Install(sched);
+
+	RArray<TInt> driveMap;
+	CleanupClosePushL(driveMap);
+	driveMap.AppendL(gDrive1);
+	driveMap.AppendL(gDrive2);
+	
+	gMgr = CDriveManager::NewL(driveMap);
+	CleanupStack::PushL(gMgr);
+	TInt err = KErrGeneral;
+	CMassStorageDrive* drive1 = gMgr->Drive(0,err);
+	test(err == KErrNone);
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Check initial state"));	
+	test(CMassStorageDrive::EDisconnected==drive1->MountState());
+	test(CMassStorageDrive::EErrDisMounted==drive1->DriveState());
+	test(0==drive1->KBytesRead());
+	test(0==drive1->KBytesWritten());
+	test(EFalse==drive1->IsMediaChanged());
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Ensure Read/Write/Caps don't work when disconnected"));	
+	
+	const TInt KNoBytes = 1000;
+	TBuf8<KNoBytes> buf;
+	TLocalDriveCapsV4 caps;
+	test(KErrDisconnected==drive1->Read(0,0,buf));
+	test(KErrDisconnected==drive1->Write(0,buf));
+	test(KErrDisconnected==drive1->Caps(caps));
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Test EConnecting state"));	
+
+	drive1->SetMountConnecting();
+	test(CMassStorageDrive::EConnecting==drive1->MountState());
+	test(KErrDisconnected==drive1->Read(0,0,buf));
+	test(KErrDisconnected==drive1->Write(0,buf));
+	test(KErrDisconnected==drive1->Caps(caps));
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Test EDisconnecting state"));	
+
+	drive1->SetMountDisconnecting();
+	test(CMassStorageDrive::EDisconnecting==drive1->MountState());
+	test(KErrDisconnected==drive1->Read(0,0,buf));
+	test(KErrDisconnected==drive1->Write(0,buf));
+	test(KErrDisconnected==drive1->Caps(caps));
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Test EConnected state"));	
+
+	CTestProxyDrive* proxyDrive = new(ELeave) CTestProxyDrive(*gMgr);
+	CleanupStack::PushL(proxyDrive);
+	
+	TBool mediaChanged = EFalse;
+	test(KErrNone==gMgr->RegisterDrive(*proxyDrive, mediaChanged, 0));
+	test(CMassStorageDrive::EConnected==drive1->MountState());
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Test SetCritical"));	
+	test(CMassStorageDrive::EIdle==drive1->DriveState());
+	test(KErrNone==drive1->SetCritical(ETrue));
+	test(CMassStorageDrive::EActive==drive1->DriveState());
+	test(KErrNone==drive1->SetCritical(EFalse));
+	test(CMassStorageDrive::EIdle==drive1->DriveState());
+
+	///////////////////////////////////////////////////////////////////////////	
+	test.Next(_L("Test that ProxyDrive is called"));	
+	
+	CTestProxyDrive::iRetval = KErrNone;
+
+	// Test that bytesRead is incremented correctly
+	// when the count increments from 999 to 1000:
+	test(KErrNone==drive1->Read(0,999,buf));
+	test(0==drive1->KBytesRead());
+	test(KErrNone==drive1->Read(0,1,buf));
+	test(1==drive1->KBytesRead());
+		
+	buf.SetLength(KNoBytes);
+	test(KErrNone==drive1->Write(0,buf));
+	test(KErrNone==drive1->Caps(caps));
+	// Write was called when EIdle, should restore state to EIdle
+	// after transient EActive state.
+	test(CMassStorageDrive::EIdle==drive1->DriveState());
+
+	CTestProxyDrive::iRetval = KErrDied;  // arbitrary test value
+	CTestProxyDrive::iRetval_caps = KErrDied;
+	test(KErrDied==drive1->Read(0,0,buf));
+	test(KErrDied==drive1->Write(0,buf));
+	test(KErrDied==drive1->Caps(caps));
+	CTestProxyDrive::iRetval = KErrNone;
+	CTestProxyDrive::iRetval_caps = KErrNone;
+
+	test.Next(_L("Test IsMediaChanged"));	
+	test(EFalse==drive1->IsMediaChanged(ETrue));
+	test(EFalse==drive1->IsMediaChanged(EFalse));
+
+	///////////////////////////////////////////////////////////////////////////	
+	
+	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesRead, PropertyHandlers::Read);
+	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesWritten, PropertyHandlers::Written);
+	CPropertyWatch::NewLC(EUsbMsDriveState_DriveStatus, PropertyHandlers::DriveStatus);
+
+	///////////////////////////////////////////////////////////////////////////	
+	
+	NEXT(EStepRead0);
+	CActiveScheduler::Start();
+	
+	///////////////////////////////////////////////////////////////////////////	
+
+	test.Printf(_L("\nCLEANING UP\n"));
+
+	CleanupStack::PopAndDestroy(3); //CPropertyWatch x 3
+	CleanupStack::PopAndDestroy(proxyDrive); 
+	CleanupStack::PopAndDestroy(gMgr); 
+	CleanupStack::PopAndDestroy(&driveMap); 
+	CleanupStack::PopAndDestroy(sched); 
+	
+	return;	
+	}
+
+GLDEF_C TInt E32Main()
+	{
+	__UHEAP_MARK;
+	CTrapCleanup* cleanup=CTrapCleanup::New();
+	
+	TRAPD(error,doTestL());
+	if (error)
+		test.Printf(_L("Leave occurred; code=%d\n"), error);
+	
+	test.End();	// output success/fail
+	test.Close();
+
+	delete cleanup;
+	__UHEAP_MARKEND;
+	return 0;
+	}