os/kernelhwsrv/kerneltest/f32test/smassstorage/msdrive/t_msdrive.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // Unit tests for the CMassStorageDrive and CDriveManager classes
    15 // 
    16 //
    17 
    18 /**
    19  @file
    20  @internalTechnology
    21 */
    22 
    23 #include <e32std.h>
    24 #include <e32std_private.h>
    25 #include <e32twin.h>
    26 #include <e32test.h>
    27 #include <e32property.h>
    28 
    29 #include "usbmsshared.h"
    30 #include "drivemanager.h"
    31 
    32 
    33 enum TestStep
    34 	{
    35 	EStepRead0,
    36 	EStepRead1,
    37 	EStepWrite0,
    38 	EStepWrite1,
    39 	EStepConnected,
    40 	EStepActive,
    41 	EStepMediaRemoved,
    42 	EStepLocked,
    43 	EStepDisconnecting,
    44 	EStepDisconnected,
    45 	EStepConnecting
    46 	};
    47 
    48 LOCAL_D RTest test(_L("MSDRIVE"));
    49 LOCAL_D const TDriveNumber gDrive1 = EDriveL; // used drive
    50 LOCAL_D const TDriveNumber gDrive2 = EDriveN; // unused drive
    51 LOCAL_D CDriveManager* gMgr = NULL;
    52 LOCAL_D TestStep gTestStep = EStepRead0;
    53 
    54 #define NEXT(x) test.Next(_L(#x)); gTestStep = x;
    55 
    56 
    57 /**
    58 These CProxyDrive functions are copies of code in sf_ext.cpp, 
    59 since we don't link to EFILE.
    60 */
    61 CProxyDrive::CProxyDrive(CMountCB* aMount) : iMount(aMount) {}
    62 
    63 EXPORT_C TInt CProxyDrive::ControlIO(
    64 		const RMessagePtr2& /*aMessage*/,
    65 		TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
    66 	{
    67 	return(KErrNone);
    68 	}
    69 
    70 EXPORT_C TInt CProxyDrive::DeleteNotify(TInt64 /*aPos*/, TInt /*aLength*/)
    71 	{
    72 	return(KErrNone);
    73 	}
    74 
    75 EXPORT_C TInt CProxyDrive::GetInterface(TInt /*aInterfaceId*/, TAny*& /*aInterface*/, TAny* /*aInput*/)
    76 	{ return KErrNotSupported; }
    77 
    78 // Implemented the GetLastErrorInfo method here as this is usually 
    79 // exported by EFILE, but these unit tests don't link to it.
    80 EXPORT_C TInt CProxyDrive::GetLastErrorInfo(TDes8& /*anErrorInfo*/)
    81 	{ return KErrNotSupported; }
    82 
    83 CProxyDrive::~CProxyDrive()
    84 	{
    85 	}
    86 
    87 EXPORT_C TInt CProxyDrive::Read(TInt64 aPos, TInt aLength, const TAny* aTrg, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
    88 	{
    89 	return Read(aPos, aLength, *(TDes8*)aTrg);
    90 	}
    91 
    92 EXPORT_C TInt CProxyDrive::Write(TInt64 aPos, TInt /*aLength*/, const TAny* aSrc, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
    93 	{
    94 	return Write(aPos, *(TDesC8*)aSrc);
    95 	}
    96 
    97 /** 
    98 CMassStorageMountCB gets the CProxyDrive from the filesystem,
    99 but here we want to instantiate our own derived class for testing.
   100 This allows us to control the error code returned by each function.
   101 */
   102 class CTestProxyDrive : public CProxyDrive
   103 	{
   104 public:
   105 	static TInt iRetval;
   106 	static TInt iRetval_caps;
   107 	static TLocalDriveCapsV4 iCaps;
   108 		 
   109 	CTestProxyDrive(CDriveManager& aDriveManager) : CProxyDrive(NULL)
   110 		{
   111 		iCaps.iType = ::EMediaUnknown;
   112 
   113 		iWriteTransferPublisher = CDriveWriteTransferPublisher::NewL(aDriveManager.iDrives);
   114 		iReadTransferPublisher = CDriveReadTransferPublisher::NewL(aDriveManager.iDrives);
   115 		}
   116 		
   117 	~CTestProxyDrive() 
   118 		{
   119 		delete iWriteTransferPublisher;
   120 		delete iReadTransferPublisher;
   121 		}
   122 	virtual TInt Initialise()
   123 		{
   124 		return iRetval;
   125 		}
   126 	virtual TInt Dismounted()
   127 		{
   128 		return iRetval;
   129 		}
   130 	virtual TInt Enlarge(TInt )
   131 		{
   132 		return iRetval;
   133 		}
   134 	virtual TInt ReduceSize(TInt , TInt )
   135 		{
   136 		return iRetval;
   137 		}
   138 	virtual TInt Read(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
   139 		{
   140 		iReadTransferPublisher->StartTimer();
   141 		return iRetval;
   142 		}
   143 	virtual TInt Read(TInt64 ,TInt len, TDes8& buf)
   144 		{
   145 		iReadTransferPublisher->StartTimer();
   146 		buf.SetLength(len);
   147 		return iRetval;
   148 		}
   149 	virtual TInt Write(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
   150 		{
   151 		iWriteTransferPublisher->StartTimer();
   152 		return iRetval;
   153 		}
   154 	virtual TInt Write(TInt64 ,const TDesC8& )
   155 		{
   156 		iWriteTransferPublisher->StartTimer();
   157 		return iRetval;
   158 		}
   159 	virtual TInt Caps(TDes8& aBuf)
   160 		{
   161 		((TLocalDriveCapsV4Buf&)aBuf) = iCaps;
   162 		
   163 		return iRetval_caps;
   164 		}
   165 	virtual TInt Format(TFormatInfo& )
   166 		{
   167 		return iRetval;
   168 		}
   169 	virtual TInt Format(TInt64 ,TInt )
   170 		{
   171 		return iRetval;
   172 		}
   173 	virtual TInt SetMountInfo(const TDesC8* ,TInt )
   174 		{
   175 		return iRetval;
   176 		}
   177 	virtual TInt ForceRemount(TUint )
   178 		{
   179 		return iRetval;
   180 		}
   181 	virtual TInt Unlock(TMediaPassword &, TBool )
   182 		{
   183 		return iRetval;
   184 		}
   185 	virtual TInt Lock(TMediaPassword &, TMediaPassword &, TBool )
   186 		{
   187 		return iRetval;
   188 		}
   189 	virtual TInt Clear(TMediaPassword &)
   190 		{
   191 		return iRetval;
   192 		}
   193 	virtual TInt ErasePassword()
   194 		{
   195 		return iRetval;
   196 		}
   197 
   198 private:
   199 	/**
   200 	Publish and subscribe properties for tracking data transfer volume
   201 	*/
   202 	CDriveWriteTransferPublisher* iWriteTransferPublisher;
   203 	CDriveReadTransferPublisher* iReadTransferPublisher;
   204 	};
   205 	
   206 TInt CTestProxyDrive::iRetval = KErrNone;
   207 TInt CTestProxyDrive::iRetval_caps = KErrNone;
   208 TLocalDriveCapsV4 CTestProxyDrive::iCaps;
   209 
   210 /**
   211 From USBMSAPP:
   212 A set of static objects that hold the latest properties published by Mass Storage,
   213 and a set of corresponding static functions that process the publish events. 
   214 The functions are passed by pointer to, and invoked by, CPropertyWatch instances.
   215 */
   216 class PropertyHandlers
   217 	{
   218 public:
   219 	/** The prototype for all public property handler functions */
   220 	typedef void(*THandler)(RProperty&);
   221 
   222 public:
   223 	static void Read(RProperty& aProperty);
   224 	static void Written(RProperty& aProperty);
   225 	static void DriveStatus(RProperty& aProperty);
   226 
   227 public:
   228 	static TBuf8<16> iAllDrivesStatus;
   229 	static TUsbMsBytesTransferred iKBytesRead;
   230 	static TUsbMsBytesTransferred iKBytesWritten;
   231 	};
   232 
   233 /**
   234 From USBMSAPP:
   235 An active object that subscribes to a specified Mass Storage property and
   236 calls a provided handler each time the property is published.
   237 */
   238 class CPropertyWatch : public CActive
   239 	{
   240 public:
   241 	static CPropertyWatch* NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler);
   242 private:
   243 	CPropertyWatch(PropertyHandlers::THandler aHandler);
   244 	void ConstructL(TUsbMsDriveState_Subkey aSubkey);
   245 	~CPropertyWatch();
   246 	void RunL();
   247 	void DoCancel();
   248 private:
   249 	RProperty iProperty;
   250 	PropertyHandlers::THandler iHandler;
   251 	};
   252 
   253 CPropertyWatch* CPropertyWatch::NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler)
   254 	{
   255 	CPropertyWatch* me=new(ELeave) CPropertyWatch(aHandler);
   256 	CleanupStack::PushL(me);
   257 	me->ConstructL(aSubkey);
   258 	return me;
   259 	}
   260 
   261 CPropertyWatch::CPropertyWatch(PropertyHandlers::THandler aHandler)
   262 	: CActive(0), iHandler(aHandler)
   263 	{}
   264 
   265 void CPropertyWatch::ConstructL(TUsbMsDriveState_Subkey aSubkey)
   266 	{
   267 	User::LeaveIfError(iProperty.Attach(KUsbMsDriveState_Category, aSubkey));
   268 	CActiveScheduler::Add(this);
   269 	// initial subscription and process current property value
   270 	RunL();
   271 	}
   272 
   273 CPropertyWatch::~CPropertyWatch()
   274 	{
   275 	Cancel();
   276 	iProperty.Close();
   277 	}
   278 
   279 void CPropertyWatch::DoCancel()
   280 	{
   281 	iProperty.Cancel();
   282 	}
   283 
   284 void CPropertyWatch::RunL()
   285 	{
   286 	// resubscribe before processing new value to prevent missing updates
   287 	iProperty.Subscribe(iStatus);
   288 
   289 	iHandler(iProperty);
   290 
   291 	SetActive();
   292 	}
   293 
   294 
   295 TBuf8<16> PropertyHandlers::iAllDrivesStatus;
   296 TUsbMsBytesTransferred PropertyHandlers::iKBytesRead;
   297 TUsbMsBytesTransferred PropertyHandlers::iKBytesWritten;
   298 
   299 
   300 /** 
   301 Handle a publish event for the Bytes Read property.
   302 */
   303 void PropertyHandlers::Read(RProperty& aProperty)
   304 	{
   305 	const TUint KNoBytesToRead = 1000;
   306 	test(aProperty.Get(iKBytesRead)==KErrNone);
   307 	TInt kbytes = iKBytesRead[0];
   308 
   309 	TBuf8<KNoBytesToRead> buf;
   310 	buf.SetLength(KNoBytesToRead);
   311 	TInt err;
   312 
   313 	switch(gTestStep)
   314 		{
   315 		case EStepRead0:
   316 			// don't do anything until 1st KB reported in 1s interval
   317 			if(kbytes==0)
   318 				{
   319 				break;
   320 				}
   321 			test(kbytes==1);
   322 
   323 			test(KErrNone == gMgr->Drive(0,err)->Read(0,KNoBytesToRead,buf));
   324 			NEXT(EStepRead1);
   325 			break;
   326 
   327 		case EStepRead1:
   328 			test(kbytes==2);
   329 			
   330 			// trigger an update:
   331 			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
   332 			NEXT(EStepWrite0);
   333 			break;
   334 			
   335 		default:
   336 			break;
   337 		}
   338 	}
   339 
   340 /** 
   341 Handle a publish event for the Bytes Written property.
   342 */
   343 void PropertyHandlers::Written(RProperty& aProperty)
   344 	{
   345 	const TUint KNoBytesToWrite = 1000;
   346 	test(aProperty.Get(iKBytesWritten)==KErrNone);
   347 	TInt kbytes = iKBytesWritten[0];
   348 
   349 	TBuf8<KNoBytesToWrite> buf;
   350 	buf.SetLength(KNoBytesToWrite);
   351 	TInt err;
   352 
   353 	switch(gTestStep)
   354 		{
   355 		case EStepWrite0:
   356 			test(kbytes==2);
   357 
   358 			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
   359 			NEXT(EStepWrite1);
   360 			break;
   361 
   362 		case EStepWrite1:
   363 			test(kbytes==3);
   364 			
   365 			// trigger transient change to Active state:
   366 			test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
   367 			NEXT(EStepConnected);
   368 			break;
   369 
   370 		default:
   371 			break;
   372 		}
   373 	}
   374 
   375 /** 
   376 Handle a publish event for the Drive Status property.
   377 */
   378 void PropertyHandlers::DriveStatus(RProperty& aProperty)
   379 	{
   380 	RDebug::Print(_L(">> PropertyHandlers::DriveStatus")); 
   381 	TInt err = aProperty.Get(iAllDrivesStatus);
   382 	test(err == KErrNone);
   383 
   384 	// There should be status for 2 drives:
   385 	// (Note: there is a pair of bytes per drive,
   386 	// drive number and drive status.)
   387 	test(2 == iAllDrivesStatus.Length()/2);
   388 	test(iAllDrivesStatus[0] == gDrive1);
   389 	test(iAllDrivesStatus[2*1] == gDrive2);
   390 	TInt status = iAllDrivesStatus[1];
   391 
   392 	switch(gTestStep)
   393 		{
   394 		case EStepConnected:
   395 			test(status==EUsbMsDriveState_Connected);
   396 			
   397 			test(KErrNone==gMgr->Drive(0,err)->SetCritical(ETrue));	
   398 			NEXT(EStepActive);
   399 			break;
   400 			
   401 		case EStepActive:
   402 			test(status==EUsbMsDriveState_Active);
   403 					
   404 			CTestProxyDrive::iRetval_caps = KErrNotReady;
   405 			test(CMassStorageDrive::EMediaNotPresent
   406 					==gMgr->Drive(0,err)->CheckDriveState());	
   407 			CTestProxyDrive::iRetval_caps = KErrNone;
   408 			
   409 			NEXT(EStepMediaRemoved);
   410 			break;
   411 			
   412 		case EStepMediaRemoved:
   413 			{
   414 			test(status==EUsbMsDriveState_MediaNotPresent);
   415 			
   416 			gMgr->Drive(0,err)->CheckDriveState(); // clear old state
   417 			
   418 			CTestProxyDrive::iRetval = KErrLocked;
   419 			TBuf8<1> buf;
   420 			buf.SetLength(1);
   421 			test(KErrLocked==gMgr->Drive(0,err)->Write(0,buf));	
   422 			CTestProxyDrive::iRetval = KErrNone;
   423 			NEXT(EStepLocked);
   424 			}
   425 			break;
   426 			
   427 		case EStepLocked:
   428 			test(status==EUsbMsDriveState_Locked);
   429 			
   430 			test(KErrNone == gMgr->Disconnect(0));
   431 			test(KErrNone == gMgr->Disconnect(0)); // ensure it can be called more than once
   432 			NEXT(EStepDisconnecting);
   433 			break;
   434 		
   435 		case EStepDisconnecting:
   436 			test(status==EUsbMsDriveState_Disconnecting);
   437 			
   438 			test(KErrNone == gMgr->DeregisterDrive(0));
   439 			NEXT(EStepDisconnected);
   440 			break;
   441 		
   442 		case EStepDisconnected:
   443 			test(status==EUsbMsDriveState_Disconnected);
   444 		
   445 			test(KErrNone == gMgr->Connect(0));
   446 			test(KErrNone == gMgr->Connect(0)); // ensure it can be called more than once
   447 			NEXT(EStepConnecting);
   448 			break;
   449 		
   450 		case EStepConnecting:
   451 			test(status==EUsbMsDriveState_Connecting);
   452 			CActiveScheduler::Stop();
   453 			break;
   454 
   455 		default:
   456 			break;
   457 		}
   458 		
   459 	RDebug::Print(_L("<< PropertyHandlers::DriveStatus"));
   460 	}
   461 
   462 LOCAL_C void doTestL()
   463 	{
   464 	test.Start(_L("MSDrive1"));
   465 	
   466 	CActiveScheduler* sched = new(ELeave) CActiveScheduler;
   467 	CleanupStack::PushL(sched);
   468 	CActiveScheduler::Install(sched);
   469 
   470 	RArray<TInt> driveMap;
   471 	CleanupClosePushL(driveMap);
   472 	driveMap.AppendL(gDrive1);
   473 	driveMap.AppendL(gDrive2);
   474 	
   475 	gMgr = CDriveManager::NewL(driveMap);
   476 	CleanupStack::PushL(gMgr);
   477 	TInt err = KErrGeneral;
   478 	CMassStorageDrive* drive1 = gMgr->Drive(0,err);
   479 	test(err == KErrNone);
   480 
   481 	///////////////////////////////////////////////////////////////////////////	
   482 	test.Next(_L("Check initial state"));	
   483 	test(CMassStorageDrive::EDisconnected==drive1->MountState());
   484 	test(CMassStorageDrive::EErrDisMounted==drive1->DriveState());
   485 	test(0==drive1->KBytesRead());
   486 	test(0==drive1->KBytesWritten());
   487 	test(EFalse==drive1->IsMediaChanged());
   488 
   489 	///////////////////////////////////////////////////////////////////////////	
   490 	test.Next(_L("Ensure Read/Write/Caps don't work when disconnected"));	
   491 	
   492 	const TInt KNoBytes = 1000;
   493 	TBuf8<KNoBytes> buf;
   494 	TLocalDriveCapsV4 caps;
   495 	test(KErrDisconnected==drive1->Read(0,0,buf));
   496 	test(KErrDisconnected==drive1->Write(0,buf));
   497 	test(KErrDisconnected==drive1->Caps(caps));
   498 
   499 	///////////////////////////////////////////////////////////////////////////	
   500 	test.Next(_L("Test EConnecting state"));	
   501 
   502 	drive1->SetMountConnecting();
   503 	test(CMassStorageDrive::EConnecting==drive1->MountState());
   504 	test(KErrDisconnected==drive1->Read(0,0,buf));
   505 	test(KErrDisconnected==drive1->Write(0,buf));
   506 	test(KErrDisconnected==drive1->Caps(caps));
   507 
   508 	///////////////////////////////////////////////////////////////////////////	
   509 	test.Next(_L("Test EDisconnecting state"));	
   510 
   511 	drive1->SetMountDisconnecting();
   512 	test(CMassStorageDrive::EDisconnecting==drive1->MountState());
   513 	test(KErrDisconnected==drive1->Read(0,0,buf));
   514 	test(KErrDisconnected==drive1->Write(0,buf));
   515 	test(KErrDisconnected==drive1->Caps(caps));
   516 
   517 	///////////////////////////////////////////////////////////////////////////	
   518 	test.Next(_L("Test EConnected state"));	
   519 
   520 	CTestProxyDrive* proxyDrive = new(ELeave) CTestProxyDrive(*gMgr);
   521 	CleanupStack::PushL(proxyDrive);
   522 	
   523 	TBool mediaChanged = EFalse;
   524 	test(KErrNone==gMgr->RegisterDrive(*proxyDrive, mediaChanged, 0));
   525 	test(CMassStorageDrive::EConnected==drive1->MountState());
   526 
   527 	///////////////////////////////////////////////////////////////////////////	
   528 	test.Next(_L("Test SetCritical"));	
   529 	test(CMassStorageDrive::EIdle==drive1->DriveState());
   530 	test(KErrNone==drive1->SetCritical(ETrue));
   531 	test(CMassStorageDrive::EActive==drive1->DriveState());
   532 	test(KErrNone==drive1->SetCritical(EFalse));
   533 	test(CMassStorageDrive::EIdle==drive1->DriveState());
   534 
   535 	///////////////////////////////////////////////////////////////////////////	
   536 	test.Next(_L("Test that ProxyDrive is called"));	
   537 	
   538 	CTestProxyDrive::iRetval = KErrNone;
   539 
   540 	// Test that bytesRead is incremented correctly
   541 	// when the count increments from 999 to 1000:
   542 	test(KErrNone==drive1->Read(0,999,buf));
   543 	test(0==drive1->KBytesRead());
   544 	test(KErrNone==drive1->Read(0,1,buf));
   545 	test(1==drive1->KBytesRead());
   546 		
   547 	buf.SetLength(KNoBytes);
   548 	test(KErrNone==drive1->Write(0,buf));
   549 	test(KErrNone==drive1->Caps(caps));
   550 	// Write was called when EIdle, should restore state to EIdle
   551 	// after transient EActive state.
   552 	test(CMassStorageDrive::EIdle==drive1->DriveState());
   553 
   554 	CTestProxyDrive::iRetval = KErrDied;  // arbitrary test value
   555 	CTestProxyDrive::iRetval_caps = KErrDied;
   556 	test(KErrDied==drive1->Read(0,0,buf));
   557 	test(KErrDied==drive1->Write(0,buf));
   558 	test(KErrDied==drive1->Caps(caps));
   559 	CTestProxyDrive::iRetval = KErrNone;
   560 	CTestProxyDrive::iRetval_caps = KErrNone;
   561 
   562 	test.Next(_L("Test IsMediaChanged"));	
   563 	test(EFalse==drive1->IsMediaChanged(ETrue));
   564 	test(EFalse==drive1->IsMediaChanged(EFalse));
   565 
   566 	///////////////////////////////////////////////////////////////////////////	
   567 	
   568 	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesRead, PropertyHandlers::Read);
   569 	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesWritten, PropertyHandlers::Written);
   570 	CPropertyWatch::NewLC(EUsbMsDriveState_DriveStatus, PropertyHandlers::DriveStatus);
   571 
   572 	///////////////////////////////////////////////////////////////////////////	
   573 	
   574 	NEXT(EStepRead0);
   575 	CActiveScheduler::Start();
   576 	
   577 	///////////////////////////////////////////////////////////////////////////	
   578 
   579 	test.Printf(_L("\nCLEANING UP\n"));
   580 
   581 	CleanupStack::PopAndDestroy(3); //CPropertyWatch x 3
   582 	CleanupStack::PopAndDestroy(proxyDrive); 
   583 	CleanupStack::PopAndDestroy(gMgr); 
   584 	CleanupStack::PopAndDestroy(&driveMap); 
   585 	CleanupStack::PopAndDestroy(sched); 
   586 	
   587 	return;	
   588 	}
   589 
   590 GLDEF_C TInt E32Main()
   591 	{
   592 	__UHEAP_MARK;
   593 	CTrapCleanup* cleanup=CTrapCleanup::New();
   594 	
   595 	TRAPD(error,doTestL());
   596 	if (error)
   597 		test.Printf(_L("Leave occurred; code=%d\n"), error);
   598 	
   599 	test.End();	// output success/fail
   600 	test.Close();
   601 
   602 	delete cleanup;
   603 	__UHEAP_MARKEND;
   604 	return 0;
   605 	}