diff -r b183ec05bd8c -r 19bba8228ff0 devicediagnosticsfw/diagframework/src/diagpluginexecplanimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devicediagnosticsfw/diagframework/src/diagpluginexecplanimpl.cpp Wed Sep 01 12:27:42 2010 +0100 @@ -0,0 +1,1577 @@ +/* +* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "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: Class definition of CDiagPluginExecPlanImpl +* +*/ + + +// CLASS DECLARATION +#include "diagpluginexecplanimpl.h" + +// SYSTEM INCLUDE FILES +#include +#include // CStack +#include // MDiagPlugin +#include // MDiagSuitePlugin +#include // MDiagTestPlugin +#include // CDiagPluginPool +#include // RDiagResultsDatabaseRecord +#include // CDiagResultsDbItemBuilder +#include // MDiagResultDetail +#include // For debug log +#include // CDiagResultsDbRecordEngineParam + +// USER INCLUDE FILES +#include "diagframework.pan" // panic codes +#include "diagexecplanentryimpl.h" // CDiagExecPlanEntryImpl +#include "diagexecplanentryimpltest.h" // CDiagExecPlanEntryImplTest +#include "diagexecplanentryimplsuite.h" // CDiagExecPlanEntryImplSuite +#include "diagcleanupresetanddestroy.h" // CleanupRPointerArrayPushL +#include "diagengineconfig.h" // TDiagEngineConfig + + +// DATA + +// MACROS +// Uncomment the line below to enable more plan creation log. +// #define _DEBUG_EXEC_PLAN + + +// LOCAL DATA TYPES +/** +* Used for keeping track of suite level stack. +* It is used in InsertSuiteTransitionsL() function. ( STEP_5 ) +*/ +struct TTransitionStackEntry + { + /** + * iLeavelUid - Uid of the current level. + */ + TUid iLevelUid; + + /** + * iPrepareIndex - Index in the plan where ETypeSuitePrepare entry is. + * This information is used to update the suite iAsDependent field. + */ + TInt iPrepareIndex; + }; + + +// ======== LOCAL FUNCTIONS ======== + + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::NewL +// --------------------------------------------------------------------------- +// +CDiagPluginExecPlanImpl* CDiagPluginExecPlanImpl::NewL( + MDiagEngineCommon& aEngine, + const TDiagEngineConfig& aEngineConfig, + MDiagExecPlanEntryImplObserver& aEntryObserver ) + { + CDiagPluginExecPlanImpl* self = new ( ELeave ) CDiagPluginExecPlanImpl( + aEngine, + aEngineConfig, + aEntryObserver ); + + return self; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::CDiagPluginExecPlanImpl +// --------------------------------------------------------------------------- +// +CDiagPluginExecPlanImpl::CDiagPluginExecPlanImpl( + MDiagEngineCommon& aEngine, + const TDiagEngineConfig& aEngineConfig, + MDiagExecPlanEntryImplObserver& aEntryObserver ) + : CActive( EPriorityLow ), + iEngine( aEngine ), + iEngineConfig( aEngineConfig ), + iPlanEntryObserver( aEntryObserver ) + { + CActiveScheduler::Add( this ); + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::~CDiagPluginExecPlanImpl +// --------------------------------------------------------------------------- +// +CDiagPluginExecPlanImpl::~CDiagPluginExecPlanImpl() + { + Cancel(); + iPlan.ResetAndDestroy(); + iPlan.Close(); + iExecutedEntries.ResetAndDestroy(); + iExecutedEntries.Close(); + } + + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::InitaliazeL +// Creating a fresh session. +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::InitializeL( TRequestStatus& aStatus, + const RPointerArray< MDiagPlugin >& aBatch ) + { + LOGSTRING( "CDiagPluginExecPlanImpl::InitializeL: new session") + + __ASSERT_ALWAYS( iState == EStateIdle, Panic( EDiagFrameworkInternal ) ); + + aStatus = KRequestPending; + iClientStatus = &aStatus; + + iExecutedEntries.ResetAndDestroy(); + iPlan.ResetAndDestroy(); + iResumeIndex = 0; + + // pre-step execution step. copy the argument to a local plan + TInt i; + for ( i = 0; i < aBatch.Count(); i++ ) + { + __ASSERT_ALWAYS( aBatch[i] != NULL, Panic( EDiagFrameworkBadArgument ) ); + + CDiagExecPlanEntryImpl* newEntry = CreateDefaultPlanEntryLC( + *(aBatch[i]), + EFalse ); // aAsDependency + + iPlan.AppendL( newEntry ); // ownership transferred. + CleanupStack::Pop( newEntry ); + newEntry = NULL; + } + + LOGSTRING( "CDiagPluginExecPlanImpl::InitializeL: Initial Batch" ) + LogPlanL(); + + ChangeState( EStateExpandDependencyAndSuites ); + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::InitializeL +// Continuing from incomplete session. +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::InitializeL( TRequestStatus& aStatus ) + { + LOGSTRING( "CDiagPluginExecPlanImpl::InitaliazeL: continue" ) + + // validate input. + __ASSERT_ALWAYS( iState == EStateIdle, Panic( EDiagFrameworkInternal ) ); + + // if we are resuming incomplete session, it must incomplete. + TBool isRecordCompleted = EFalse; + User::LeaveIfError( iEngine.DbRecord().IsTestCompleted( isRecordCompleted ) ); + __ASSERT_ALWAYS( !isRecordCompleted, Panic( EDiagFrameworkBadArgument ) ); + + aStatus = KRequestPending; + iClientStatus = &aStatus; + iResumeIndex = 0; + + iExecutedEntries.ResetAndDestroy(); + iPlan.ResetAndDestroy(); + + // Retrieve records from database and use it as base for creating + // a new plan. + RPointerArray< CDiagResultsDatabaseItem > previousResults; + DiagFwInternal::CleanupRPointerArrayPushL< CDiagResultsDatabaseItem >( &previousResults ); + + User::LeaveIfError( + iEngine.DbRecord().GetTestResults( previousResults ) ); + + TInt resultCount = previousResults.Count(); + for( TInt i = 0; i < resultCount; i++ ) + { + // Get the test plug-in. + // Note that FindPlugin does not transfer ownership and hence testPlugin + // and does not need to be deallocated. + MDiagPlugin& testPlugin = + iEngine.PluginPool().FindPluginL( previousResults[i]->TestUid() ); + + // This should have been a test plug-in + __ASSERT_DEBUG( testPlugin.Type() == MDiagPlugin::ETypeTestPlugin, + Panic( EDiagFrameworkInternal ) ); + + CDiagExecPlanEntryImplTest* testEntry = + CDiagExecPlanEntryImplTest::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagTestPlugin& >( testPlugin ), + previousResults[i]->WasDependency(), + previousResults[i]->TestResult() ); + + if ( testEntry->Result() == CDiagResultsDatabaseItem::EQueuedToRun ) + { + // it was queued to be executed. + iPlan.AppendL( testEntry ); // ownership transferred. + } + else + { + // it was already executed. + iExecutedEntries.AppendL( testEntry ); // ownership transferred. + } + CleanupStack::Pop( testEntry ); + testEntry = NULL; + } + + CleanupStack::PopAndDestroy( &previousResults ); + + iResumeIndex = iExecutedEntries.Count(); + + if ( iResumeIndex != 0 ) + { + LOGSTRING( "CDiagPluginExecPlanImpl::InitializeL: Queued Items" ) + LogPlanL(); + + ChangeState( EStateExpandDependencyAndSuites ); + } + else + { + LOGSTRING( "CDiagPluginExecPlanImpl::InitializeL: " + L"Items in DB may be invalid. Fully recreate plan" ) + + // If iResumeIndex is 0, it means that all items in the db are + // marked as EQueuedToRun. Because of the async nature of db, + // it could also indicate that not previous plan was not fully + // written to the database. In this case, read the engine parameter + // and try to recreate the plan from scratch. + // + // If it was already fully written but hadn't had a chance to really + // execute anything, this will not cause any ill effects since + // plan created should be identical and simply reset + // all results in db to EQueuedToRun. + iExecutedEntries.ResetAndDestroy(); + iPlan.ResetAndDestroy(); + + RPointerArray< MDiagPlugin > batch; + CleanupClosePushL( batch ); // items are not owned, so no need for "Destroy" + + CDiagResultsDbRecordEngineParam* engineParam = NULL; + User::LeaveIfError( + iEngine.DbRecord().GetEngineParam( engineParam ) ); + + CleanupStack::PushL( engineParam ); + + const RArray< TUid >& batchUids = engineParam->ExecutionsUidArray(); + + // Read original Uids from previous engine param. + for( TInt i = 0; i < batchUids.Count(); i++ ) + { + MDiagPlugin* testPlugin = NULL; + User::LeaveIfError( + iEngine.PluginPool().FindPlugin( batchUids[i], testPlugin ) ); + + __ASSERT_DEBUG( testPlugin != NULL, Panic( EDiagFrameworkInternal ) ); + if ( testPlugin ) //lint !e774 This will be evaluated on non-debug build. + { + batch.AppendL( testPlugin ); + } + } + + CleanupStack::PopAndDestroy( engineParam ); + engineParam = NULL; + + // Call the normal InitializeL() method as if it is a new session. + InitializeL( aStatus, batch ); + + CleanupStack::PopAndDestroy( &batch ); // calls Close() + } + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::operator[] +// --------------------------------------------------------------------------- +// +CDiagExecPlanEntryImpl& CDiagPluginExecPlanImpl::operator[]( TInt aIndex ) + { + return *iPlan[aIndex]; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::CurrentExecutionItem +// --------------------------------------------------------------------------- +// +CDiagExecPlanEntryImpl& CDiagPluginExecPlanImpl::CurrentExecutionItem() + { + return *iPlan[iExecutionCursor]; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::ResetExecutionCursor +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::ResetExecutionCursor() + { + iExecutionCursor = 0; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::MoveCursorToNext +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::MoveCursorToNext() + { + TBool value = EFalse; + + if ( iExecutionCursor < iPlan.Count() - 1 ) + { + iExecutionCursor++; + value = ETrue; + } + else + { + // cannot move beyond the last item. + } + + return value; + } + +// ======== From CActive ========= +// --------------------------------------------------------------------------- +// From CActive +// CDiagPluginExecPlanImpl::RunL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::RunL() + { + /*---------------------------------------------------------------------- + Create execution plan: + Overview: + + STEP_1. Expand Dependency + STEP_2. Expand Suites. + STEP_3. Repeat STEP_1 & STEP_2 until no changes are made to the plan + STEP_4. Check and remove empty suites + STEP_5. Add Prepare/Finalize suite execution. + STEP_6. Store execution plan to results db. + STEP_7. Append items that were executed in the previous test + to the beginning of the test. + + Details: + STEP_1. Expand Dependency + In this step, each item is checked for dependency and + dependent items will be inserted into plan. + Note that if dependent item also depends on something else, those + will be added immediately. + + All items inserted during this phase is inserted as dependent. + + STEP_2. Expand suites + Suites are expandeded to individual tests or suites. Suite + item will be expanded between parent suite's Prepare and + Finalize items. + + iAsDependent value will be inherited from the parent suite + that it expanded from. E.g. if suite was dependent, then so will + all the items that included from that suite. + + Note that it does not remove the original suite entry in the plan. + This is to make sure that dependencies inserted will be grouped + within the same group as the plug-ins that depended on it. + If they are removed, and re-inserted later in STEP_5, it will + cause the dependent items to be outside of the original grouping. + e.g. S1 = {P1} + S2 = {P2, P3} + + and P2 depends on P1 + + When dependency and suites are expanded with original + suite removed, after STEP_3, the plan will look like this: + (* indicates iAsDependent == ETrue) + + 0 1 2 + *P1 P2 P3 + + When it tries to add suite transition back in to the plan, + it will look like this: + (* indicates iAsDependent == ETrue) + (< indicates suite prepare execution.) + (> indicates suite finalize execution.) + + 0 1 2 3 4 5 6 + + + which is somewhat incorrect since *P1 is added due to + dependency. + + Instead, if original suite grouping is kept, after STEP_3 + + 0 1 2 3 4 + + + After STEP_5 + + 0 1 2 3 4 5 6 + P2 P3 S2> + + which is more correct. + + Note that this will be done in breadth first style. If there is a + nested suites, it will not be expanded until next cycle. + This is to allow dependencies of the suite to be expanded before + being removed. + + STEP_3. Repeat STEP_1 & STEP_2 until no changes are made. + This step ensures that suites are expanded, and all dependent + items are added to the plan. + + STEP_4. Remove Empty suites + Because dependent items can be moved, it is possible that plan ends + up with empty suites. This section will check for + suite prepare/finalize that has nothing inside, and remove them. + + STEP_5. Add Prepare/Finalize steps for suites. + As a final step, it will navigate all the entries in the plan, and + insert proper prepare/finalize steps. This is based on stack, + where every suite prepare is pushed into a stack, and poped when + suite finalize is called. e.g. consider following plug-ins: + + S1 = { P1, P2 } + S2 = { P3, P4 } + + And P3 depends on P2. + + And resulting plan after STEP_4 looked like this: + + 0 1 2 3 4 + + + After STEP_5, it should look like this: + + 0 1 2 3 4 5 6 + P3 P4 S2> + + STEP_6. Store execution plan to results db. + This allows continuation of the incomplete results. + Only test plug-ins are written to the database. + + STEP_7. Append items that were executed in the previous test + to the beginning of the test. + + Other Notes: + + NOTE_1) This logic currently does not check for circular dependencies. + If circular dependency exists, the program will go into an + infinite loop in ExpandDependenciesL(). + + NOTE_2) Inserting duplicate items. + Due to dependencies, duplicate entries may exist in the plan. + + When items are inserted during STEP_1 - STEP_2, it will be checked + for duplicates. + + Duplicate removal policy is as follows: + RULE_A) If an item with "iAsDependent == ETrue" is being inserted, + AND if an item already exist in the plan before current index, + then it will not be inserted. + + RULE_B) Any items inserted with "iAsDependent == EFalse" will + always be inserted at the requested index. + This is because they are considered to be user input, and + plan will not try to re-order the execution order that + client or user has specified. + + RULE_C) The first instance of an item has higher priority than later + instances. Later instace of duplicate will be removed, unless + it violates RULE_B. (e.g. item has "iAsDependent == EFalse".) + + This has a side effect: item may execute twice in certain cases. + E.g. original plan has two plug-ins: P1, P2, P3 + P1 depends on P2, and P3 also depends ond P2 + + Original Input: 0 1 2 + P1 P2 P3 + + After STEP_3, it should look like this. + (* indicates iAsDependent == ETrue) + + 0 1 2 3 4 + *P2 P1 P2 *P2 P3 + + During STEP_4, duplicates are removed, and it should look like this: + (* indicates iAsDependent == ETrue) + + 0 1 2 4 + *P2 P1 P2 P3 + + Note that there are two P2s in the plan; + at 0 with iAsDependent == ETrue and at 2 with iAsDependent == EFalse + + There may be some discussions on whether this is how it should be. + However, at this point, it seems that it is more important that + execution appears to the user in the order specified. + + To make sure that user sees that P2 is executed only once, plug-in + developer may need to make sure that if a plug-in provides services, + it should also handle cases where it is executed twice. + + In reality, such cases can be avoided by grouping the plug-ins + in the correct order, hence it should not be an issue. + + ----------------------------------------------------------------------*/ + + // First, error handling. + LOGSTRING3( "CDiagPluginExecPlanImpl::RunL: State = %d, Err = %d", + iState, iStatus.Int() ) + + User::LeaveIfError( iStatus.Int() ); + + switch ( iState ) + { + case EStateExpandDependencyAndSuites: + { + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_1") + // STEP_1. Expand dependency. + if ( !iEngineConfig.IsDependencyDisabled() ) + { + ExpandDependenciesL(); + } + + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_2") + // STEP_2. Expand suites. + if ( ExpandSuitesL() ) + { + // STEP_3. Repeat STEP_1, STEP_2 until no changes are made. + // Do a self transition to repeat STEP_1 and STEP_2 + ChangeState( EStateExpandDependencyAndSuites ); + } + else + { + // no changes are made to the plan. Move to STEP_4 + ChangeState( EStateRemoveEmptySuites ); + } + } + break; + + case EStateRemoveEmptySuites: + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_4") + // STEP_4. Remove empty suites. This can happen because of duplicate + // dependencies moving later dependent test to earlier position. + RemoveEmptySuites(); + + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING( "CDiagPluginExecPlanImpl:CreatePlanL: " + L"Before Inserting Suite Transtions" ) + LogPlanL(); + #endif // _DEBUG_EXEC_PLAN + + // Continue to STEP_5 + ChangeState( EStateInsertSuiteTransitions ); + break; + + case EStateInsertSuiteTransitions: + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_5 - Insert Suite Transtions") + // STEP_5. Add pre/post suite execution + InsertSuiteTransitionsL(); + + // STEP_6. Store plan to db. + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_6 - Store to db") + // this time, manually update state, since storing + // to database needs to use iStatus + iState = EStateStoreToDb; + + // Move cursor to one before so that it can start checking from + // the beginning. StoreNextTestPluginToDbL() will move the cursor + // as soon as it enters. + iExecutionCursor = -1; + StoreNextTestPluginToDbL(); + + break; + + case EStateStoreToDb: + LOGSTRING2( "CDiagPluginExecPlanImpl::RunL: STEP_6 - " + L"item 0x%08x stored.", + iPlan[iExecutionCursor]->Plugin().Uid().iUid ) + + StoreNextTestPluginToDbL(); + break; + + case EStatePlanCreated: + // STEP_7. Prepend the items that were executed in last session + // to the beginning of the execution plan. + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: STEP_7 - prepend executed items" ) + PrependExecutedItemsL(); + + LOGSTRING( "CDiagPluginExecPlanImpl::RunL: Final Plan" ) + LogPlanL(); + + LOGSTRING2( "CDiagPluginExecPlanImpl::RunL: Resume at index %d", iResumeIndex ) + + LogPlanInRecordL(); + + ResetExecutionCursor(); + + ReportResult( KErrNone ); + break; + + case EStateIdle: + default: + __ASSERT_DEBUG( 0, Panic( EDiagFrameworkInternal ) ); + break; + } + } + +// --------------------------------------------------------------------------- +// From CActive +// CDiagPluginExecPlanImpl::DoCancel +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::DoCancel() + { + iPlan.ResetAndDestroy(); + iExecutedEntries.ResetAndDestroy(); + iState = EStateIdle; + + ReportResult( KErrCancel ); + } + +// --------------------------------------------------------------------------- +// From CActive +// CDiagPluginExecPlanImpl::RunError +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::RunError( TInt aError ) + { + iPlan.ResetAndDestroy(); + iExecutedEntries.ResetAndDestroy(); + iState = EStateIdle; + + ReportResult( aError ); + + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::CurrentIndex +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::CurrentIndex() const + { + return iExecutionCursor; + } + + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::CurrentTestIndex +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::CurrentTestIndex( TBool aIncludeDependency ) const + { + TInt count = 0; + + for ( TInt i = 0; i < iPlan.Count() && i < iExecutionCursor; i++ ) + { + if ( iPlan[i]->Plugin().Type() == MDiagPlugin::ETypeTestPlugin ) + { + // if caller wants to include both explicit and dependent + // or if plug-in is not depentent test, count. + if ( aIncludeDependency || ( !iPlan[i]->AsDependency() ) ) + { + count++; + } + } + } + + // subtract 1 if we only went past the last test. + if ( count >= TestCount( aIncludeDependency ) ) + { + count--; + } + return count; + } + + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::TestCount +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::TestCount( TBool aIncludeDependency ) const + { + TInt count = 0; + + for ( TInt i = 0; i < iPlan.Count(); i++ ) + { + if ( iPlan[i]->Plugin().Type() == MDiagPlugin::ETypeTestPlugin ) + { + // if caller wants to include both explicit and dependent + // or if plug-in is not depentent test, count. + if ( aIncludeDependency || ( !iPlan[i]->AsDependency() ) ) + count++; + } + } + return count; + } + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::operator[] () const +// --------------------------------------------------------------------------- +// +const MDiagExecPlanEntry& CDiagPluginExecPlanImpl::operator[] ( TInt aIndex ) const + { + __ASSERT_ALWAYS( aIndex >= 0 && aIndex < iPlan.Count(), + Panic( EDiagFrameworkArrayBounds ) ); + return *( static_cast< MDiagExecPlanEntry* >( iPlan[aIndex] ) ); + } + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::CurrentExecutionItem const +// --------------------------------------------------------------------------- +// +const MDiagExecPlanEntry& CDiagPluginExecPlanImpl::CurrentExecutionItem() const + { + __ASSERT_ALWAYS( iExecutionCursor >= 0 && iExecutionCursor < iPlan.Count(), + Panic( EDiagFrameworkArrayBounds ) ); + return *( static_cast< MDiagExecPlanEntry* >( iPlan[iExecutionCursor] ) ); + } + + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::IsLastTest +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::IsLastTest() const + { + if ( TestCount( ETrue ) == 0 ) + { + // there was no test in the plan.. + // In this case, it is always ETrue. + return ETrue; + } + + return ( CurrentTestIndex( ETrue ) == TestCount( ETrue ) - 1 ); + } + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::IsLastPlugin +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::IsLastPlugin() const + { + if ( iPlan.Count() == 0 ) + { + // if plan was empty, always consider it to be the last. + return ETrue; + } + + return iExecutionCursor == ( iPlan.Count() -1 ); + } + + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::Count +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::Count() const + { + return iPlan.Count(); + } + +// --------------------------------------------------------------------------- +// From MDiagPluginExecPlan +// CDiagPluginExecPlanImpl::ResumeIndex +// --------------------------------------------------------------------------- +// +TInt CDiagPluginExecPlanImpl::ResumeIndex() const + { + return iResumeIndex; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::ChangeState +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::ChangeState( TState aState ) + { + LOGSTRING3( "CDiagPluginExecPlanImpl::ChangeState: state change %d -> %d", + iState, aState ) + + iState = aState; + + TRequestStatus* stat = &iStatus; + User::RequestComplete( stat, KErrNone ); + SetActive(); + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::ExpandDependenciesL +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::ExpandDependenciesL() + { + // STEP_1. Expand dependency. + // Before modifying this function, please see STEP_1 comments in CreatePlanL() + TInt planIdx = 0; + TBool planChanged( EFalse ); + while ( planIdx < iPlan.Count() ) + { + TBool itemAdded = EFalse; + + // we are only interested in type ETypeTestExec or ETypeSuiteUnexpanded. + // If it is ETypeSuitePrepare or ETypeSuiteFinalize, that means + // that it is a suite and it has been already been expanded to + // prepare/finalize. Since dependencies are resolved before suites + // are expanded, we are not interested in re-evaluating expanded tests. + if ( iPlan[planIdx]->Type() == CDiagExecPlanEntryImpl::ETypeTestExec || + iPlan[planIdx]->Type() == CDiagExecPlanEntryImpl::ETypeSuiteUnexpanded ) + { + // dependencies should be rare. So granuality 1 should be fine. + CPtrCArray* depList = new( ELeave )CPtrCArray( 1 ); + CleanupStack::PushL( depList ); + iPlan[planIdx]->Plugin().GetLogicalDependenciesL( *depList ); + + // iterate through the dependiencies BACKWARDS to make sure + // that dependencies are inserted in the same order + // specified. If it is not added backwards, developers may + // be surprised that dependencies are executed + // in reverse order of what is specified in the XML. + for ( TInt depIdx = depList->Count() - 1; depIdx >= 0; depIdx-- ) + { + // If dependency is specified, but the dependent plug-in is + // not found in plug-in pool, this probably means + // plug-in database is corrupted. + MDiagPlugin& plugin = iEngine.PluginPool().FindPluginL( ( *depList )[ depIdx ] ); + + if ( InsertPluginL( plugin, + ETrue, + planIdx ) ) + { + itemAdded = ETrue; + } + } + CleanupStack::PopAndDestroy( depList ); + depList = NULL; + } + + // Advance to next item in plan only if no new item is added. + // This ensures that items just added are re-evaluated for + // dependency, and expand if it is an unexpanded suite. + if ( !itemAdded ) + { + planIdx++; + } + else + { + planChanged = ETrue; + } + } + + return planChanged; + } + + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::ExpandSuitesL +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::ExpandSuitesL() + { + // STEP_2. Expand suites. + // Before modifying this function, please see STEP_1 comments in CreatePlanL() + TBool planChanged = EFalse; + TInt i = 0; + while ( i < iPlan.Count() ) + { + if ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuiteUnexpanded ) + { + // If it is unexpanded, it must be a suite. + __ASSERT_DEBUG( iPlan[i]->Plugin().Type() == MDiagPlugin::ETypeSuitePlugin, + Panic( EDiagFrameworkInternal ) ); + planChanged = ETrue; + + // Change type from ETestExec to ETypeSuitePrepare. This indicates + // that the suite item in the plan has been examined and + // expanded. + iPlan[i]->SetType( CDiagExecPlanEntryImpl::ETypeSuitePrepare ); + + // dependency is inherited + TBool asDependency = iPlan[i]->AsDependency(); + + // Get Children from the plug-in + MDiagSuitePlugin& suite = static_cast( iPlan[i]->Plugin() ); + RPointerArray children; + CleanupClosePushL( children ); + + suite.GetChildrenL( children, MDiagSuitePlugin::ESortByPosition ); + + i++; // insert rest after current item. + + TInt childIdx = 0; + while ( childIdx < children.Count() ) + { + if ( InsertPluginL( *( children[childIdx] ), + asDependency, + i ) ) + { + // new item is added. Move to next + i++; + } + childIdx++; + } + + children.Reset(); // children pointers are not owned + CleanupStack::PopAndDestroy( &children ); + + // insert suite finalize entry into plan + CDiagExecPlanEntryImplSuite* finalizeEntry = + CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + suite, + asDependency, + CDiagExecPlanEntryImpl::ETypeSuiteFinalize ); + + iPlan.InsertL( static_cast< CDiagExecPlanEntryImpl* >( finalizeEntry ), i ); + CleanupStack::Pop( finalizeEntry ); // owership transferred + finalizeEntry = NULL; + i++; + } + else + { + // this one is not suite, so examine the next one. + i++; + } + } + + return planChanged; + } + +// Looking for STEP_3? It is in RunL() .. + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::RemoveEmptySuites +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::RemoveEmptySuites() + { + // STEP_4. Remove empty suites + // Before modifying this function, please see STEP_1 comments in CreatePlanL() + + // Checking for duplicate is done by checking if suite prepare/finalize + // is in the plan next to each other. + // + // NOTE_4 + // After a empty suite is removed, step back one index. + // so that the previous item will be re-evaluated, in case removing them cased + // the parent suite to be empty as well. + // e.g + // i == 1 + // index: 0 [1] 2 3 + // A> + // after removal, now prepare/finalize A becomes next to each other + // + // i == 1 + // index: 0 [1] + // + // + // in order to make sure that above case is handled, decrement i and + // re-evaluate from index 0 + // + TInt i = 0; + while ( i < iPlan.Count() - 2 ) // no need to go beyond the last item + { + if ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuitePrepare && + iPlan[i+1]->Type() == CDiagExecPlanEntryImpl::ETypeSuiteFinalize ) + { + // if these two do not match, then there may have been + // a problem during plan creation. + __ASSERT_DEBUG( iPlan[i]->Plugin().Uid() == iPlan[i+1]->Plugin().Uid(), + Panic( EDiagFrameworkCorruptPlugin ) ); + + + CDiagExecPlanEntryImpl* entry = iPlan[ i + 1 ]; + iPlan.Remove( i+1 ); + delete entry; + entry = NULL; + + entry = iPlan[i]; + iPlan.Remove( i ); + delete entry; + entry = NULL; + + // Wondering why step back by one? See NOTE_4 + if ( i > 0 ) + { + i--; + } + } + else + { + // it's not empty. Examine next item. + i++; + } + } + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::InsertSuiteTransitionsL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::InsertSuiteTransitionsL() + { + // STEP_5. Add pre/post suite execution based on test parent change. + // Before modifying this function, please see STEP_1 comments in CreatePlanL() + + // if plan is empty, nothing to do. + if ( iPlan.Count() == 0 ) + { + return; + } + + // Create a new CStack. Second template parameter ETrue means that + // CStack owns the stack entries. + CStack* stack = + new( ELeave )CStack; + + CleanupStack::PushL( stack ); + + // create a root entry + AddRootSuiteIfMissingL(); + + // In this loop, iPlan.Count() cannot be cached because new item may be + // added within the loop + TInt i = 0; + while ( i < iPlan.Count() ) + { + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING2( "CDiagPluginExecPlanImpl::InsertSuiteTransitionsL: ------ %d", i ) + LogPlanL(); + #endif // _DEBUG_EXEC_PLAN + + // Case 1 + // If current item is a suite prepare, push a new level into stack. + if ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuitePrepare ) + { + // we are entering a new suite. Push current plan item into stack. + PushNewLevelL( *stack, i ); + i++; + continue; //lint !e960 continue OK. examine next item. + } + + // Case 2 + // If current item is a suite finalize, pop a level from stack. + if ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuiteFinalize && + stack->Head() != NULL && + stack->Head()->iLevelUid == iPlan[i]->Plugin().Uid() ) + { + // we are leaving a suite. Pop top item from suite. + PopLevel( *stack ); + i++; + continue; //lint !e960 : continue OK. examine next item. + } + + // it was neither prepare or finalize. Examine item as current level. + const TTransitionStackEntry& level = *(stack->Head()); // Peek + + TUid newLevelUid = iPlan[i]->Plugin().ParentUid(); + + // Case 3 + if ( newLevelUid == level.iLevelUid ) + { + // still in the same suite level. No need to add new items. + + // check if current item is non-dependent. + // If so, update the iAsDependent to reflect that it includes + // a non-dependent item. + if ( !(iPlan[i]->AsDependency()) ) + { + // update the prepare item, indicating that it has + // an non-dependent item + iPlan[level.iPrepareIndex]->SetAsDependency( EFalse ); + } + i++; + continue; //lint !e960 : continue OK. examine next item. + } + + // If parent UID is differnt from current level uid, + // it can mean one of the following: + // A ) We are entering a new suite. + // B ) We leaving a suite. + // Case B ) can be deteced by checking whether the new parent is + // already in the stack somewhere (stack will be popped until we are + // in the same level), or if there is no more item of the same + // level is in the plan, which means it is not needed. + // If case B ) fails, assume case A. + if ( IsLevelInStack( *stack, newLevelUid ) || + !IsThisSuiteNeededAfterThisIndex( stack->Head()->iLevelUid, i ) ) + { + // Case B) Insert a new Finalize item. + AddSuiteFinalizeL( level.iPrepareIndex, i ); + } + else + { + // Case A) Insert a new prepare item. + AddSuitePrepareL( newLevelUid, i ); + } + } + + // When all done, stack must be empty. + __ASSERT_DEBUG( stack->IsEmpty(), Panic( EDiagFrameworkInternal ) ); + + CleanupStack::PopAndDestroy( stack ); + stack = NULL; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::AddRootSuiteIfMissingL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::AddRootSuiteIfMissingL() + { + // Check to see if root element is already in the plan. + if ( iPlan[0]->Plugin().Uid() != KDiagRootSuiteUid ) + { + // root( uid 0 ) does not exist in the plan. + // Add prepare and finalize root into the plan. + MDiagPlugin& rootPlugin = iEngine.PluginPool().FindPluginL( KDiagRootSuiteUid ); + + CDiagExecPlanEntryImplSuite* rootEntry = + CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagSuitePlugin& >( rootPlugin ), + ETrue, // iAsDependent == ETrue. Updated as later. + CDiagExecPlanEntryImpl::ETypeSuitePrepare ); + + iPlan.InsertL( static_cast< CDiagExecPlanEntryImpl* >( rootEntry ), 0 ); + CleanupStack::Pop( rootEntry ); // ownership transferred above. + rootEntry = NULL; + + rootEntry = CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagSuitePlugin& >( rootPlugin ), + ETrue, // iAsDependent == ETrue. Updated as later. + CDiagExecPlanEntryImpl::ETypeSuiteFinalize ); + + // insert same entry with finalize as type + iPlan.AppendL( static_cast< CDiagExecPlanEntryImpl* >( rootEntry ) ); + CleanupStack::Pop( rootEntry ); // ownership transferred above + rootEntry = NULL; + } + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::PushNewLevelL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::PushNewLevelL( + CStack& aStack, + TInt aHeadIndex ) + { + TTransitionStackEntry* level = new( ELeave )TTransitionStackEntry; + + __ASSERT_ALWAYS( aHeadIndex >= 0 && aHeadIndex < iPlan.Count(), + Panic( EDiagFrameworkInternal ) ); + + level->iLevelUid = iPlan[aHeadIndex]->Plugin().Uid(); + level->iPrepareIndex = aHeadIndex; + + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING2( "CDiagPluginExecPlanImpl:: Push 0x%08x", level->iLevelUid.iUid ) + #endif // _DEBUG_EXEC_PLAN + + aStack.PushL( level ); // owership transferred. + level = NULL; //lint !e423 Ownership transferred. No leak here. + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::PopLevel +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::PopLevel( CStack& aStack ) + { + TTransitionStackEntry* level = aStack.Pop(); + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING2( "CDiagPluginExecPlanImpl:: Pop 0x%08x", level->iLevelUid.iUid ) + #endif // _DEBUG_EXEC_PLAN + delete level; + level = NULL; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::IsLevelInStack +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::IsLevelInStack( + CStack& aStack, + TUid aLevelUid ) const + { + for ( TInt i = 0; i < aStack.Count(); i++ ) + { + const TTransitionStackEntry& currEntry = *aStack[i]; + if ( currEntry.iLevelUid == aLevelUid ) + { + return ETrue; + } + } + + return EFalse; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::IsThisSuiteNeededAfterThisIndex +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::IsThisSuiteNeededAfterThisIndex( + TUid aSuiteUid, + TInt aIndex ) const + { + for ( TInt i = aIndex; i < iPlan.Count(); i++ ) + { + if ( iPlan[i]->Plugin().Uid() == aSuiteUid || // finalize already exists + iPlan[i]->Plugin().ParentUid() == aSuiteUid ) + { + return ETrue; + } + } + + return EFalse; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::AddSuitePrepareL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::AddSuitePrepareL( TUid aLevelUid, TInt aAt ) + { + MDiagPlugin& suitePlugin = iEngine.PluginPool().FindPluginL( aLevelUid ); + + CDiagExecPlanEntryImplSuite* prepareEntry = + CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagSuitePlugin& >( suitePlugin ), + ETrue, // iAsDependent. Updated as later if not true. + CDiagExecPlanEntryImpl::ETypeSuitePrepare ); + + iPlan.InsertL( static_cast< CDiagExecPlanEntryImpl* >( prepareEntry ), aAt ); + CleanupStack::Pop( prepareEntry ); // ownership transfer + prepareEntry = NULL; + + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING2( "CDiagPluginExecPlanImpl:: InsertPrepare 0x%08x", aLevelUid ) + #endif // _DEBUG_EXEC_PLAN + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::AddSuiteFinalizeL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::AddSuiteFinalizeL( TInt aPrepareIndex, TInt aAt ) + { + // It is identical to the prepare entry, except that type is ETypeSuiteFinalize + __ASSERT_ALWAYS( aPrepareIndex >= 0 && aPrepareIndex < iPlan.Count(), + Panic( EDiagFrameworkInternal ) ); + + CDiagExecPlanEntryImplSuite& prepareEntry = + static_cast< CDiagExecPlanEntryImplSuite& > ( *(iPlan[aPrepareIndex]) ); + + CDiagExecPlanEntryImplSuite* finalizeEntry = + CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + prepareEntry.SuitePlugin(), + prepareEntry.AsDependency(), + CDiagExecPlanEntryImpl::ETypeSuiteFinalize ); + + iPlan.InsertL( static_cast< CDiagExecPlanEntryImpl* >( finalizeEntry ), aAt ); + CleanupStack::Pop( finalizeEntry ); // ownership transmitted above + finalizeEntry = NULL; + + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING2( "CDiagPluginExecPlanImpl:: InsertFinalize 0x%08x", prepareEntry.Plugin().Uid().iUid ); + #endif // _DEBUG_EXEC_PLAN + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::StoreNextTestPluginToDbL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::StoreNextTestPluginToDbL() + { + // STEP_6. Store plan to DB. + // In this step, all test entries must be stored in db with EQueuedToRun + // status. Since DbRecord().LogTestResult() is an async call, only one + // item can be written at a time. + // + // To do this, this function will search for the next "TEST" plug-in that + // needs to be logged to DB. Once it is found, it will call async function + // LogTestResult() and wait for RunL() to execute again. + // In RunL(), StoreNextTestPluginToDbL() is called again, and it will look for the + // next test plugin to store until it loops through all items in plan. + while ( MoveCursorToNext() ) + { + if ( CurrentExecutionItem().Plugin().Type() == MDiagPlugin::ETypeTestPlugin ) + { + LOGSTRING2( "CDiagPluginExecPlanImpl::RunL: STEP_6 - storing item 0x%08x", + CurrentExecutionItem().Plugin().Uid().iUid ) + + CDiagResultsDatabaseItem* resultItem = + CDiagResultsDbItemBuilder::CreateSimpleDbItemL( + CurrentExecutionItem().Plugin().Uid(), + CurrentExecutionItem().AsDependency(), + CDiagResultsDatabaseItem::EQueuedToRun ); + + // Record initial test result to db. + // StoreNextTestPluginToDbL() will be called again later from RunL() + iEngine.DbRecord().LogTestResult( iStatus, *resultItem ); + SetActive(); + + delete resultItem; + resultItem = NULL; + + // Exit here since we must wait for LogTestResult() to complete + // before moving on to the next item. + return; + } + } + + // All items are stored now. + ChangeState( EStatePlanCreated ); + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::PrependExecutedItemsL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::PrependExecutedItemsL() + { + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING( "CDiagExecPlanEntryImpl::PrependExecutedItemsL(). " + L" Before prepending executed entries" ); + LogPlanL(); + #endif // _DEBUG_EXEC_PLAN + + for ( TInt lastIndex = iExecutedEntries.Count() - 1; + lastIndex >= 0 ; + lastIndex-- ) + { + // Insert last entry from the executed list to the beginning. + iPlan.InsertL( iExecutedEntries[ lastIndex ], 0 ); + iExecutedEntries.Remove( lastIndex ); + } + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::ReportResult +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::ReportResult( TInt aError ) + { + User::RequestComplete( iClientStatus, aError ); + iClientStatus = NULL; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::InsertPluginL +// --------------------------------------------------------------------------- +// +TBool CDiagPluginExecPlanImpl::InsertPluginL( MDiagPlugin& aPlugin, + TBool aAsDependency, + TInt aAt ) + { + #ifdef _DEBUG_EXEC_PLAN + LOGSTRING4( "CDiagPluginExecPlanImpl::InsertPluginL:" + L"Id = 0x%x, Type = %s, At %d", + aPlugin.Uid().iUid, + ( aPlugin.Type() == MDiagPlugin::ETypeTestPlugin ? L"Test" : L"Suite" ), + aAt ) + #endif // _DEBUG_EXEC_PLAN + + __ASSERT_ALWAYS( aAt >= 0 && aAt <= iPlan.Count(), Panic( EDiagFrameworkArrayBounds ) ); + + // First, examine items before given index. + // If new item has iAsDependent == ETrue, and also found before the given index, + // --> it is okay to not insert the item, since it will just be duplicates. + // if new item has iAsDependent == EFalse, + // --> no need to check for items prior to current position, since + // explicit + // where it was requested. + TInt idx = 0; + if ( aAsDependency ) + { + // check if it was already executed in the previous session. + for ( idx = 0; idx < iExecutedEntries.Count(); idx++ ) + { + if ( iExecutedEntries[idx]->Plugin().Uid() == aPlugin.Uid() ) + { + // matching item found. + // No need to instert a new item. + return EFalse; + } + } + + // check for items in current plan. + for ( idx = 0; idx < aAt && idx < iPlan.Count(); idx++ ) + { + if ( iPlan[idx]->Plugin().Uid() == aPlugin.Uid() ) + { + // Matching item found. + // No change to plan made. + return EFalse; + } + } + } + + // Duplicate item not found before given index. + // An item will be inserted at the given index. Now, we search for + // duplicates after current index, and see if we can remove them. + idx = aAt; + while ( idx < iPlan.Count() ) + { + if ( iPlan[idx]->Plugin().Uid() == aPlugin.Uid() && iPlan[idx]->AsDependency() ) + { + // Item is found, and it was for dependency. + // We can move this item to current position. + // For now, we just need to remove it, since it will be added at + // current position when for loop is done. + CDiagExecPlanEntryImpl* entry = iPlan[idx]; + iPlan.Remove( idx ); + delete entry; + entry = NULL; + + // no need to increment idx, since current item is removed. + // idx should already be at the next item. + } + else + { + idx++; // check next item. + } + } + + // Either plug-in is not found, or we found one that we can move. + // Add to current position. + CDiagExecPlanEntryImpl* newEntry = CreateDefaultPlanEntryLC( + aPlugin, + aAsDependency ); + + iPlan.InsertL( newEntry, aAt ); + CleanupStack::Pop( newEntry ); + newEntry = NULL; + + return ETrue; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::CreateDefaultPlanEntryL +// +// --------------------------------------------------------------------------- +// +CDiagExecPlanEntryImpl* CDiagPluginExecPlanImpl::CreateDefaultPlanEntryLC( + MDiagPlugin& aPlugin, + TBool aAsDependency ) const + { + CDiagExecPlanEntryImpl* newEntry = NULL; + + if ( aPlugin.Type() == MDiagPlugin::ETypeTestPlugin ) + { + newEntry = static_cast< CDiagExecPlanEntryImpl* >( + CDiagExecPlanEntryImplTest::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagTestPlugin& >( aPlugin ), + aAsDependency, + CDiagResultsDatabaseItem::EQueuedToRun ) ); + } + else + { + newEntry = static_cast< CDiagExecPlanEntryImpl* >( + CDiagExecPlanEntryImplSuite::NewLC( + iEngine, + iEngineConfig, + iPlanEntryObserver, + static_cast< MDiagSuitePlugin& >( aPlugin ), + aAsDependency, + CDiagExecPlanEntryImpl::ETypeSuiteUnexpanded ) ); + } + + return newEntry; + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::LogPlan +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::LogPlanL() const + { + #ifdef _DEBUG + LOGSTRING( "CDiagPluginExecPlanImpl::LogPlan(): Plan Dump." ) + for ( TInt i = 0; i < iPlan.Count(); i++ ) + { + HBufC* pluginName = iPlan[i]->Plugin().GetPluginNameL( + MDiagPlugin::ENameLayoutListSingle ); + + LOGSTRING5( "Plan Entry: Id = 0x%08x, %s, Dep=[%s], Name = %S", + iPlan[i]->Plugin().Uid().iUid, + ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuitePrepare ? L"<" : + ( iPlan[i]->Type() == CDiagExecPlanEntryImpl::ETypeSuiteFinalize ? L">" : L" " ) ), + ( iPlan[i]->AsDependency() ? L"*" : L" " ), + pluginName ) + + delete pluginName; + } + #endif // _DEBUG + } + +// --------------------------------------------------------------------------- +// CDiagPluginExecPlanImpl::LogPlanInRecordL +// --------------------------------------------------------------------------- +// +void CDiagPluginExecPlanImpl::LogPlanInRecordL() const + { + #ifdef _DEBUG + + LOGSTRING( "CDiagPluginExecPlanImpl::LogPlanInRecord()" ) + + RPointerArray< CDiagResultsDatabaseItem > results; + DiagFwInternal::CleanupRPointerArrayPushL< CDiagResultsDatabaseItem >( &results ); + User::LeaveIfError( + iEngine.DbRecord().GetTestResults( results ) ); + + TInt resultCount = results.Count(); + + LOGSTRING2( " Result Count = %d", resultCount ) + + for ( TInt i = 0; i < resultCount; i++ ) + { + LOGSTRING4( " Test UID = 0x%08x, Result = %d, Dep = %d", + results[i]->TestUid().iUid, + results[i]->TestResult(), + results[i]->WasDependency() ) + } + + CleanupStack::PopAndDestroy( &results ); + + #endif // _DEBUG + } + +// End of File +