sl@0: // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Implementation of the CUnitTest base class. sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: sl@0: EXPORT_C CUnitTest::~CUnitTest() sl@0: { sl@0: Cancel(); sl@0: sl@0: // Delete any outstanding asynchronous transitions sl@0: if(iOutstandingTransitions) sl@0: { sl@0: iOutstandingTransitions->Reset(); sl@0: delete iOutstandingTransitions; sl@0: } sl@0: sl@0: if(iTransitions) sl@0: { sl@0: iTransitions->ResetAndDestroy(); sl@0: delete iTransitions; sl@0: } sl@0: sl@0: iLeaveErrorArray.Reset(); sl@0: sl@0: delete iFileMan; sl@0: iFs.Close(); sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::UnitTestConstructL() sl@0: { sl@0: User::LeaveIfError(iFs.Connect()); sl@0: iFileMan = CFileMan::NewL(iFs); sl@0: CTimer::ConstructL(); sl@0: iTransitions = new(ELeave) RPointerArray; sl@0: iOutstandingTransitions = new(ELeave) RPointerArray; sl@0: _LIT(KConstructingUnitTestMsg, "Constructed Unit Test named %S"); sl@0: iDataLogger.LogInformationWithParameters(KConstructingUnitTestMsg, &iUnitTestName); sl@0: } sl@0: sl@0: sl@0: CUnitTestInfo* CUnitTest::TransitionSetL() const sl@0: { sl@0: CUnitTestInfo* transitionSet = CUnitTestInfo::NewL(iUnitTestName); sl@0: return transitionSet; sl@0: } sl@0: sl@0: sl@0: EXPORT_C void CUnitTest::RunTest(TTimeIntervalMicroSeconds32 aTimeAfter /*= 0*/) sl@0: { sl@0: After(aTimeAfter); sl@0: _LIT(KTxtSeparator, "-----------------------------------------------------------------------------------"); sl@0: _LIT(KStartingUnitTest, "Beginning Unit Test named %S"); sl@0: iDataLogger.LogInformation(KTxtSeparator); sl@0: iDataLogger.LogInformationWithParameters(KStartingUnitTest, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KStartingUnitTest, &iUnitTestName); sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::RunL() sl@0: { sl@0: _LIT(KUnitTestRunLPanic, "CUnitTest::RunL"); sl@0: sl@0: TInt status = iStatus.Int(); sl@0: switch(status) sl@0: { sl@0: case (KTestBedRepeatTest): /* A stub has requested repeat of the last test */ sl@0: // Go back one so that we repeat the last test sl@0: --iNextTransitionIndex; sl@0: break; sl@0: sl@0: case (KTestBedTestLeft): /* The last transition's RunL left */ sl@0: case (KTestBedTestCancel): /* The last transition was cancelled */ sl@0: case (KTestBedLeakTestLoopDetected): /* A leak test detected an infinite loop */ sl@0: case (KTestBedFailedPreConditions): /* The last transition failed it's pre conditions */ sl@0: case (KTestBedFailedPostConditions): /* The last transition failed it's post conditions */ sl@0: // Go to the end of the test so that it finishes sl@0: iNextTransitionIndex = iTransitions->Count(); sl@0: break; sl@0: sl@0: case (KTestBedAsynchronousTransition): /* The last transition started an async request */ sl@0: // Remember that we have an outstanding request and then carry on sl@0: iOutstandingTransitions->Append((*iTransitions)[iNextTransitionIndex - 1]); sl@0: break; sl@0: sl@0: case (KErrNone): sl@0: break; sl@0: sl@0: default: sl@0: User::Panic(KUnitTestRunLPanic, KTestBedInvalidStatus); sl@0: } sl@0: sl@0: // If we still have more transitions to run sl@0: if(iNextTransitionIndex < iTransitions->Count()) sl@0: { sl@0: iStatus = KRequestPending; sl@0: SetActive(); sl@0: sl@0: // If the next transition is a blocking one then wait for all outstanding async sl@0: // requests to complete. Otherwise just run the next transition sl@0: if(((*iTransitions)[iNextTransitionIndex]->IsBlockingTransition()) && sl@0: (iOutstandingTransitions->Count() > 0)) sl@0: { sl@0: iWaitingForCompletion = ETrue; sl@0: } sl@0: else sl@0: { sl@0: (*iTransitions)[iNextTransitionIndex]->RunTransition(&iStatus); sl@0: ++iNextTransitionIndex; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // If we still have outstanding async requests then wait for these to complete sl@0: // otherwise we have finished this test sl@0: if(iOutstandingTransitions->Count() > 0) sl@0: { sl@0: iWaitingForCompletion = ETrue; sl@0: iStatus = KRequestPending; sl@0: SetActive(); sl@0: } sl@0: else sl@0: { sl@0: iUnitTestObserver.Complete(this); sl@0: sl@0: _LIT(KInfoPrintFailed, "Failed: Unit Test"); sl@0: _LIT(KTestLeft, "Failed: Unit Test %S left"); sl@0: _LIT(KTestLeftWithUnexpectedError, "Failed: Test %S left with unexpected error"); sl@0: _LIT(KTestFailed, "Failed: Unit Test %S failed a pre/post condition validation check"); sl@0: _LIT(KTestLeftWithExpectedError, "Test %S left with an anticipated error"); sl@0: _LIT(KTestCancelled, "Cancelled: Unit Test Transition %S was cancelled"); sl@0: _LIT(KTestEnteredInfiniteLoop, "Unit Test Transition %S aborted (infinitely looping)"); sl@0: _LIT(KEndingUnitTest, "Successfully completed Unit Test %S"); sl@0: // We use RTest if it is present in the framework to validate sl@0: // status codes for errors. Note: not all non KErrNone code mean sl@0: // there was an error and so we need to selective over which sl@0: // cases below we use RTest(). sl@0: switch(status) sl@0: { sl@0: case (KTestBedTestLeft): sl@0: { sl@0: TInt leaveCode = iCurrentlyExecutingTransition->GetErrorCode(); sl@0: //Check to see if the leave code is NOT on the list of known leaving codes sl@0: if(iLeaveErrorArray.Find(leaveCode) == KErrNotFound) sl@0: { sl@0: iDataLogger.LogInformationWithParameters(KTestLeft, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KTestLeft, &iUnitTestName); sl@0: if(iRTest) sl@0: { sl@0: (*iRTest)(status==KErrNone); sl@0: } sl@0: } sl@0: else //Leave code is on the list sl@0: { sl@0: TInt count = iTransitions->Count(); sl@0: //Check transition number and if it is the last transition then this is an expected error sl@0: CTransition* lastTransition = (*iTransitions)[count-1]; sl@0: if(iCurrentlyExecutingTransition == lastTransition) sl@0: { sl@0: iDataLogger.LogInformationWithParameters(KTestLeftWithExpectedError, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KEndingUnitTest, &iUnitTestName); sl@0: } sl@0: else //Otherwise, if not the last transition, the test failed with an unexpected error sl@0: { sl@0: User::InfoPrint(KInfoPrintFailed); sl@0: User::InfoPrint(iUnitTestName); sl@0: iDataLogger.LogInformationWithParameters(KTestLeftWithUnexpectedError, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KTestLeftWithUnexpectedError, &iUnitTestName); sl@0: if(iRTest) sl@0: { sl@0: (*iRTest)(status==KErrNone); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: break; sl@0: sl@0: case (KTestBedFailedPreConditions): sl@0: case (KTestBedFailedPostConditions): sl@0: { sl@0: User::InfoPrint(KInfoPrintFailed); sl@0: User::InfoPrint(iUnitTestName); sl@0: iDataLogger.LogInformationWithParameters(KTestFailed, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KTestFailed, &iUnitTestName); sl@0: if(iRTest) sl@0: { sl@0: (*iRTest)(status==KErrNone); sl@0: } sl@0: } sl@0: break; sl@0: sl@0: case (KTestBedTestCancel): sl@0: iDataLogger.LogInformationWithParameters(KTestCancelled, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KTestCancelled, &iUnitTestName); sl@0: if(iRTest) sl@0: { sl@0: (*iRTest)(status==KErrNone); sl@0: } sl@0: break; sl@0: sl@0: case (KTestBedLeakTestLoopDetected): sl@0: iDataLogger.LogInformationWithParameters(KTestEnteredInfiniteLoop, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KTestEnteredInfiniteLoop, &iUnitTestName); sl@0: if(iRTest) sl@0: { sl@0: (*iRTest)(status==KErrNone); sl@0: } sl@0: break; sl@0: sl@0: case (KErrNone): sl@0: iDataLogger.LogInformationWithParameters(KEndingUnitTest, &iUnitTestName); sl@0: iDataLogger.ReportInformationWithParameters(KEndingUnitTest, &iUnitTestName); sl@0: break; sl@0: sl@0: default: sl@0: User::Panic(KUnitTestRunLPanic, KTestBedInvalidStatus); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::AddTransitionL(CTransition* aTransition) sl@0: { sl@0: __ASSERT_DEBUG(aTransition, User::Panic(_L("CUnitTest"), KErrTestBedInvalidTransition)); sl@0: CleanupStack::PushL(aTransition); sl@0: User::LeaveIfError(iTransitions->Append(aTransition)); sl@0: CleanupStack::Pop(aTransition); sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::AddBlockingTransitionL(CTransition* aTransition) sl@0: { sl@0: __ASSERT_DEBUG(aTransition, User::Panic(_L("CUnitTest"), KErrTestBedInvalidTransition)); sl@0: aTransition->SetBlockingTransition(ETrue); sl@0: CleanupStack::PushL(aTransition); sl@0: User::LeaveIfError(iTransitions->Append(aTransition)); sl@0: CleanupStack::Pop(aTransition); sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::AddLeaveErrorCodeL(TInt aLeaveErrorCode) sl@0: { sl@0: User::LeaveIfError(iLeaveErrorArray.Append(aLeaveErrorCode)); sl@0: } sl@0: sl@0: EXPORT_C CTransition& CUnitTest::GetCurrentTransition() const sl@0: { sl@0: // Check fror a stray stub call sl@0: // We will always have a valid pointer here IF called sl@0: // from a stub in response to that transition's call sl@0: // on the stub's methods. sl@0: __ASSERT_DEBUG(iCurrentlyExecutingTransition, User::Invariant()); sl@0: return *iCurrentlyExecutingTransition; sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::SetCurrentTransition(CTransition& aTransition) sl@0: { sl@0: iCurrentlyExecutingTransition = &aTransition; sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::Complete(CTransition& aTransition, TInt aAsyncPostCheckError) sl@0: { sl@0: // Should never be NULL at this point sl@0: __ASSERT_DEBUG(iCurrentlyExecutingTransition, User::Invariant()); sl@0: if(iCurrentlyExecutingTransition == &aTransition) sl@0: iCurrentlyExecutingTransition = NULL; // Clear the current transition sl@0: sl@0: // Oops the code will crash if this is ever false sl@0: __ASSERT_DEBUG(iOutstandingTransitions, User::Invariant()); sl@0: sl@0: // Look-up the transition passed in... sl@0: TInt index = iOutstandingTransitions->Find(&aTransition); sl@0: if(index != KErrNotFound) sl@0: { sl@0: // ... and remove from the set of outstanding ones. sl@0: iOutstandingTransitions->Remove(index); sl@0: sl@0: // Did we fail a second-phase post-condition validation on an asynchronous transition? sl@0: // Or was it a normal transition completion? Regardless, we go for another iteration sl@0: // of the AO, passing through the error code. sl@0: TBool completeIt = (aAsyncPostCheckError != KErrNone); sl@0: if(iWaitingForCompletion && (iOutstandingTransitions->Count() == 0)) sl@0: completeIt = ETrue; sl@0: sl@0: if (completeIt) sl@0: { sl@0: TRequestStatus* status = &iStatus; sl@0: User::RequestComplete(status, aAsyncPostCheckError); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: __ASSERT_DEBUG(ETrue, sl@0: User::Panic(_L("CUnitTest"), KErrTestBedInvalidTransition)); sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::SetParametersL(TAny* /*aParams*/) sl@0: { sl@0: // Do nothing sl@0: } sl@0: sl@0: EXPORT_C void CUnitTest::DoCancel() sl@0: { sl@0: CTimer::DoCancel(); sl@0: sl@0: if(iCurrentlyExecutingTransition) sl@0: iCurrentlyExecutingTransition->Cancel(); sl@0: sl@0: // Cancel any outstanding asynchronous transitions sl@0: if(iOutstandingTransitions) sl@0: { sl@0: TInt count = iOutstandingTransitions->Count(); sl@0: for(TInt index = 0; index < count; ++index) sl@0: { sl@0: (*iOutstandingTransitions)[index]->Cancel(); sl@0: } sl@0: } sl@0: sl@0: iUnitTestObserver.Complete(this); sl@0: } sl@0: