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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // Unit tests for the CMassStorageDrive and CDriveManager classes
24 #include <e32std_private.h>
27 #include <e32property.h>
29 #include "usbmsshared.h"
30 #include "drivemanager.h"
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;
54 #define NEXT(x) test.Next(_L(#x)); gTestStep = x;
58 These CProxyDrive functions are copies of code in sf_ext.cpp,
59 since we don't link to EFILE.
61 CProxyDrive::CProxyDrive(CMountCB* aMount) : iMount(aMount) {}
63 EXPORT_C TInt CProxyDrive::ControlIO(
64 const RMessagePtr2& /*aMessage*/,
65 TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
70 EXPORT_C TInt CProxyDrive::DeleteNotify(TInt64 /*aPos*/, TInt /*aLength*/)
75 EXPORT_C TInt CProxyDrive::GetInterface(TInt /*aInterfaceId*/, TAny*& /*aInterface*/, TAny* /*aInput*/)
76 { return KErrNotSupported; }
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; }
83 CProxyDrive::~CProxyDrive()
87 EXPORT_C TInt CProxyDrive::Read(TInt64 aPos, TInt aLength, const TAny* aTrg, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
89 return Read(aPos, aLength, *(TDes8*)aTrg);
92 EXPORT_C TInt CProxyDrive::Write(TInt64 aPos, TInt /*aLength*/, const TAny* aSrc, TInt /*aThreadHandle*/, TInt /*aOffset*/, TInt /*aFlags*/)
94 return Write(aPos, *(TDesC8*)aSrc);
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.
102 class CTestProxyDrive : public CProxyDrive
106 static TInt iRetval_caps;
107 static TLocalDriveCapsV4 iCaps;
109 CTestProxyDrive(CDriveManager& aDriveManager) : CProxyDrive(NULL)
111 iCaps.iType = ::EMediaUnknown;
113 iWriteTransferPublisher = CDriveWriteTransferPublisher::NewL(aDriveManager.iDrives);
114 iReadTransferPublisher = CDriveReadTransferPublisher::NewL(aDriveManager.iDrives);
119 delete iWriteTransferPublisher;
120 delete iReadTransferPublisher;
122 virtual TInt Initialise()
126 virtual TInt Dismounted()
130 virtual TInt Enlarge(TInt )
134 virtual TInt ReduceSize(TInt , TInt )
138 virtual TInt Read(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
140 iReadTransferPublisher->StartTimer();
143 virtual TInt Read(TInt64 ,TInt len, TDes8& buf)
145 iReadTransferPublisher->StartTimer();
149 virtual TInt Write(TInt64 ,TInt ,const TAny* ,TInt ,TInt )
151 iWriteTransferPublisher->StartTimer();
154 virtual TInt Write(TInt64 ,const TDesC8& )
156 iWriteTransferPublisher->StartTimer();
159 virtual TInt Caps(TDes8& aBuf)
161 ((TLocalDriveCapsV4Buf&)aBuf) = iCaps;
165 virtual TInt Format(TFormatInfo& )
169 virtual TInt Format(TInt64 ,TInt )
173 virtual TInt SetMountInfo(const TDesC8* ,TInt )
177 virtual TInt ForceRemount(TUint )
181 virtual TInt Unlock(TMediaPassword &, TBool )
185 virtual TInt Lock(TMediaPassword &, TMediaPassword &, TBool )
189 virtual TInt Clear(TMediaPassword &)
193 virtual TInt ErasePassword()
200 Publish and subscribe properties for tracking data transfer volume
202 CDriveWriteTransferPublisher* iWriteTransferPublisher;
203 CDriveReadTransferPublisher* iReadTransferPublisher;
206 TInt CTestProxyDrive::iRetval = KErrNone;
207 TInt CTestProxyDrive::iRetval_caps = KErrNone;
208 TLocalDriveCapsV4 CTestProxyDrive::iCaps;
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.
216 class PropertyHandlers
219 /** The prototype for all public property handler functions */
220 typedef void(*THandler)(RProperty&);
223 static void Read(RProperty& aProperty);
224 static void Written(RProperty& aProperty);
225 static void DriveStatus(RProperty& aProperty);
228 static TBuf8<16> iAllDrivesStatus;
229 static TUsbMsBytesTransferred iKBytesRead;
230 static TUsbMsBytesTransferred iKBytesWritten;
235 An active object that subscribes to a specified Mass Storage property and
236 calls a provided handler each time the property is published.
238 class CPropertyWatch : public CActive
241 static CPropertyWatch* NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler);
243 CPropertyWatch(PropertyHandlers::THandler aHandler);
244 void ConstructL(TUsbMsDriveState_Subkey aSubkey);
250 PropertyHandlers::THandler iHandler;
253 CPropertyWatch* CPropertyWatch::NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler)
255 CPropertyWatch* me=new(ELeave) CPropertyWatch(aHandler);
256 CleanupStack::PushL(me);
257 me->ConstructL(aSubkey);
261 CPropertyWatch::CPropertyWatch(PropertyHandlers::THandler aHandler)
262 : CActive(0), iHandler(aHandler)
265 void CPropertyWatch::ConstructL(TUsbMsDriveState_Subkey aSubkey)
267 User::LeaveIfError(iProperty.Attach(KUsbMsDriveState_Category, aSubkey));
268 CActiveScheduler::Add(this);
269 // initial subscription and process current property value
273 CPropertyWatch::~CPropertyWatch()
279 void CPropertyWatch::DoCancel()
284 void CPropertyWatch::RunL()
286 // resubscribe before processing new value to prevent missing updates
287 iProperty.Subscribe(iStatus);
295 TBuf8<16> PropertyHandlers::iAllDrivesStatus;
296 TUsbMsBytesTransferred PropertyHandlers::iKBytesRead;
297 TUsbMsBytesTransferred PropertyHandlers::iKBytesWritten;
301 Handle a publish event for the Bytes Read property.
303 void PropertyHandlers::Read(RProperty& aProperty)
305 const TUint KNoBytesToRead = 1000;
306 test(aProperty.Get(iKBytesRead)==KErrNone);
307 TInt kbytes = iKBytesRead[0];
309 TBuf8<KNoBytesToRead> buf;
310 buf.SetLength(KNoBytesToRead);
316 // don't do anything until 1st KB reported in 1s interval
323 test(KErrNone == gMgr->Drive(0,err)->Read(0,KNoBytesToRead,buf));
330 // trigger an update:
331 test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
341 Handle a publish event for the Bytes Written property.
343 void PropertyHandlers::Written(RProperty& aProperty)
345 const TUint KNoBytesToWrite = 1000;
346 test(aProperty.Get(iKBytesWritten)==KErrNone);
347 TInt kbytes = iKBytesWritten[0];
349 TBuf8<KNoBytesToWrite> buf;
350 buf.SetLength(KNoBytesToWrite);
358 test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
365 // trigger transient change to Active state:
366 test(KErrNone == gMgr->Drive(0,err)->Write(0,buf));
367 NEXT(EStepConnected);
376 Handle a publish event for the Drive Status property.
378 void PropertyHandlers::DriveStatus(RProperty& aProperty)
380 RDebug::Print(_L(">> PropertyHandlers::DriveStatus"));
381 TInt err = aProperty.Get(iAllDrivesStatus);
382 test(err == KErrNone);
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];
395 test(status==EUsbMsDriveState_Connected);
397 test(KErrNone==gMgr->Drive(0,err)->SetCritical(ETrue));
402 test(status==EUsbMsDriveState_Active);
404 CTestProxyDrive::iRetval_caps = KErrNotReady;
405 test(CMassStorageDrive::EMediaNotPresent
406 ==gMgr->Drive(0,err)->CheckDriveState());
407 CTestProxyDrive::iRetval_caps = KErrNone;
409 NEXT(EStepMediaRemoved);
412 case EStepMediaRemoved:
414 test(status==EUsbMsDriveState_MediaNotPresent);
416 gMgr->Drive(0,err)->CheckDriveState(); // clear old state
418 CTestProxyDrive::iRetval = KErrLocked;
421 test(KErrLocked==gMgr->Drive(0,err)->Write(0,buf));
422 CTestProxyDrive::iRetval = KErrNone;
428 test(status==EUsbMsDriveState_Locked);
430 test(KErrNone == gMgr->Disconnect(0));
431 test(KErrNone == gMgr->Disconnect(0)); // ensure it can be called more than once
432 NEXT(EStepDisconnecting);
435 case EStepDisconnecting:
436 test(status==EUsbMsDriveState_Disconnecting);
438 test(KErrNone == gMgr->DeregisterDrive(0));
439 NEXT(EStepDisconnected);
442 case EStepDisconnected:
443 test(status==EUsbMsDriveState_Disconnected);
445 test(KErrNone == gMgr->Connect(0));
446 test(KErrNone == gMgr->Connect(0)); // ensure it can be called more than once
447 NEXT(EStepConnecting);
450 case EStepConnecting:
451 test(status==EUsbMsDriveState_Connecting);
452 CActiveScheduler::Stop();
459 RDebug::Print(_L("<< PropertyHandlers::DriveStatus"));
462 LOCAL_C void doTestL()
464 test.Start(_L("MSDrive1"));
466 CActiveScheduler* sched = new(ELeave) CActiveScheduler;
467 CleanupStack::PushL(sched);
468 CActiveScheduler::Install(sched);
470 RArray<TInt> driveMap;
471 CleanupClosePushL(driveMap);
472 driveMap.AppendL(gDrive1);
473 driveMap.AppendL(gDrive2);
475 gMgr = CDriveManager::NewL(driveMap);
476 CleanupStack::PushL(gMgr);
477 TInt err = KErrGeneral;
478 CMassStorageDrive* drive1 = gMgr->Drive(0,err);
479 test(err == KErrNone);
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());
489 ///////////////////////////////////////////////////////////////////////////
490 test.Next(_L("Ensure Read/Write/Caps don't work when disconnected"));
492 const TInt KNoBytes = 1000;
494 TLocalDriveCapsV4 caps;
495 test(KErrDisconnected==drive1->Read(0,0,buf));
496 test(KErrDisconnected==drive1->Write(0,buf));
497 test(KErrDisconnected==drive1->Caps(caps));
499 ///////////////////////////////////////////////////////////////////////////
500 test.Next(_L("Test EConnecting state"));
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));
508 ///////////////////////////////////////////////////////////////////////////
509 test.Next(_L("Test EDisconnecting state"));
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));
517 ///////////////////////////////////////////////////////////////////////////
518 test.Next(_L("Test EConnected state"));
520 CTestProxyDrive* proxyDrive = new(ELeave) CTestProxyDrive(*gMgr);
521 CleanupStack::PushL(proxyDrive);
523 TBool mediaChanged = EFalse;
524 test(KErrNone==gMgr->RegisterDrive(*proxyDrive, mediaChanged, 0));
525 test(CMassStorageDrive::EConnected==drive1->MountState());
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());
535 ///////////////////////////////////////////////////////////////////////////
536 test.Next(_L("Test that ProxyDrive is called"));
538 CTestProxyDrive::iRetval = KErrNone;
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());
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());
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;
562 test.Next(_L("Test IsMediaChanged"));
563 test(EFalse==drive1->IsMediaChanged(ETrue));
564 test(EFalse==drive1->IsMediaChanged(EFalse));
566 ///////////////////////////////////////////////////////////////////////////
568 CPropertyWatch::NewLC(EUsbMsDriveState_KBytesRead, PropertyHandlers::Read);
569 CPropertyWatch::NewLC(EUsbMsDriveState_KBytesWritten, PropertyHandlers::Written);
570 CPropertyWatch::NewLC(EUsbMsDriveState_DriveStatus, PropertyHandlers::DriveStatus);
572 ///////////////////////////////////////////////////////////////////////////
575 CActiveScheduler::Start();
577 ///////////////////////////////////////////////////////////////////////////
579 test.Printf(_L("\nCLEANING UP\n"));
581 CleanupStack::PopAndDestroy(3); //CPropertyWatch x 3
582 CleanupStack::PopAndDestroy(proxyDrive);
583 CleanupStack::PopAndDestroy(gMgr);
584 CleanupStack::PopAndDestroy(&driveMap);
585 CleanupStack::PopAndDestroy(sched);
590 GLDEF_C TInt E32Main()
593 CTrapCleanup* cleanup=CTrapCleanup::New();
595 TRAPD(error,doTestL());
597 test.Printf(_L("Leave occurred; code=%d\n"), error);
599 test.End(); // output success/fail