stif/TestServer/src/Testserversession.cpp
branchRCL_3
changeset 59 8ad140f3dd41
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stif/TestServer/src/Testserversession.cpp	Wed Oct 13 16:17:58 2010 +0300
@@ -0,0 +1,1673 @@
+/*
+* Copyright (c) 2009 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: This module contains implementation of CTestServer 
+* class member functions.
+*
+*/
+
+// INCLUDE FILES
+#include <e32std.h>
+#include <e32svr.h>
+#include "TestEngineClient.h"
+#include <StifTestModule.h>
+#include <stifinternal/TestServerClient.h>
+#include "TestServer.h"
+#include "TestServerCommon.h"
+#include "TestServerModuleIf.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES  
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// MODULE DATA STRUCTURES
+// Struct to pass parameters to server thread
+struct TThreadStartTestServerSession
+    {
+    RThread    iServerThread; // The server thread
+    RSemaphore iStarted;      // Startup syncronisation semaphore   
+    TInt       iStartupResult;// Start-up result
+    };
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ==================== LOCAL FUNCTIONS =======================================
+
+// None
+
+// ================= MEMBER FUNCTIONS =========================================
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: NewL
+
+    Description: Create new test cases list    
+
+    Parameters: const TDesC& aConfigFileName :in:  Config file name
+
+    Return Values: CTestCasesList* Pointer to new test cases list
+
+    Errors/Exceptions: Leaves if memory allocation fails or ConstructL leaves.
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+CTestCasesList* CTestCasesList::NewL( const TDesC& aConfigFileName )
+    {    
+    CTestCasesList* self = new(ELeave)CTestCasesList;
+    CleanupStack::PushL( self );
+    self->ConstructL( aConfigFileName );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: ~CTestCasesList
+
+    Description: Destructor    
+
+    Parameters: 
+
+    Return Values: 
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+CTestCasesList::~CTestCasesList()
+    {
+    delete iConfigFileName;
+    iConfigFileName = NULL;
+    iTestCases.ResetAndDestroy();
+    iTestCases.Close();
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: AppendTestCaseL
+
+    Description: Appends test case.
+
+    Parameters: const TDesC& aTestCaseTitle  in: Test case title
+
+    Return Values: 
+
+    Errors/Exceptions: Leaves if memory allocation fails
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestCasesList::AppendTestCaseL( const TDesC& aTestCaseTitle )
+    {
+    HBufC* testCaseTitle = aTestCaseTitle.AllocL();
+    CleanupStack::PushL( testCaseTitle );
+    iTestCases.AppendL( testCaseTitle );
+    CleanupStack::Pop( testCaseTitle );
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: GetTestCaseTitleL
+
+    Description: Returns specified test case title  
+
+    Parameters: TInt aIndex: in: Requested test case index. 
+
+    Return Values: Test case title.
+
+    Errors/Exceptions: Leaves if test case index is invalid
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+const TDesC& CTestCasesList::GetTestCaseTitleL( TInt aIndex ) const
+    {    
+    if ( ( aIndex < 0 ) || ( aIndex >= iTestCases.Count() ) )
+        {
+        User::Leave( KErrNotFound );
+        }
+    return *iTestCases[ aIndex ];
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: GetConfigFileName
+
+    Description: Returns config file name  
+
+    Parameters: 
+
+    Return Values: Config file name.
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+const TDesC& CTestCasesList::GetConfigFileName() const
+    {    
+    return *iConfigFileName;
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: Count
+
+    Description: Returns count of test cases.    
+
+    Parameters: 
+
+    Return Values: Test cases count.
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestCasesList::Count() const
+    {
+    return iTestCases.Count();
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: ResetAndDestroy
+
+    Description: Resets list of test cases.    
+
+    Parameters: 
+
+    Return Values: 
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestCasesList::ResetAndDestroy()
+    {    
+    iTestCases.ResetAndDestroy();
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: ~CTestCasesList
+
+    Description: Destructor    
+
+    Parameters: 
+
+    Return Values: 
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+CTestCasesList::CTestCasesList()
+    {
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestCasesList
+
+    Method: ~CTestCasesList
+
+    Description: Destructor    
+
+    Parameters: 
+
+    Return Values: 
+
+    Errors/Exceptions: 
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestCasesList::ConstructL( const TDesC& aConfigFileName )
+    {
+    iConfigFileName = aConfigFileName.AllocL();
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: NewL
+
+    Description: Create new Test Module    
+
+    Parameters: RThread& aClient              :in:  Handle to client
+                CTestServer* aServer          :in:  Pointer to server
+
+    Return Values: CTestModule*                     Pointer to new test module
+
+    Errors/Exceptions: Leaves if memory allocation fails or ConstructL leaves.
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+CTestModule* CTestModule::NewL( CTestServer* aServer )
+    {
+
+    CTestModule* self=new( ELeave ) CTestModule();
+    CleanupStack::PushL( self );
+    self->ConstructL( aServer );
+    CleanupStack::Pop();
+    return self;
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CTestModule
+
+    Description: Constructor.
+    Initialise base class.
+
+    Parameters: RThread& aClient              :in:  Handle to client
+
+    Return Values: None
+
+    Errors/Exceptions: None
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+CTestModule::CTestModule() :
+    CSession2(),
+    iIni(0, 0)
+    {
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: ~CTestModule
+
+    Description: Destructor.
+    Deallocate memory and close handles.
+
+    Parameters: None
+
+    Return Values: None
+
+    Errors/Exceptions: None.
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+CTestModule::~CTestModule()
+    {
+
+    __TRACE( KVerbose, ( _L( "Closing test module" ) ) );
+        
+    // Free test case related data. No error checking here, because if freeing
+    // fails, then nothing can be done in destructor.
+    FreeCaseData();
+
+    // Delete all test module instances.
+    // Cleanup all RTestExecution has been opened by client but not properly
+    // closed for example if timeout occurs and client died. This almost do
+    // same than CloseSession() in "if( iTestExecutionHandle )" branch.
+    if( iTestExecutionHandle )
+        {
+        TInt handle = 0;
+        CObject* theObj = NULL;
+        TInt count = iTestExecutionHandle->Count();
+
+        for( TInt i = 0 ; i < count; i++ )
+            {
+            // Get pointer to CTestExecution
+            theObj=iTestExecutionHandle->operator[]( i );
+            if( theObj )
+                {
+                handle=iTestExecutionHandle->At( theObj );
+
+                CTestExecution* testcase = (CTestExecution*) theObj;
+                // Cancels test(s) execution(timeout, exit, etc)
+                // For example TestCombiner is timeouted => TestCombiner's
+                // TestServer+TestModule(s) should cancel also.
+                testcase->CancelTestExecution();
+                // CTestExecution will panic if test case is ongoing!!! This
+                // should be stopped, client should handless this.
+                iTestExecutionHandle->Remove( handle );
+                }
+            }
+
+        delete iTestExecutionHandle;
+        iTestExecutionHandle = NULL;
+
+        }
+
+    // Delete all FREE Test Module instances.
+    if( iTestModuleInstances )
+        {
+        iTestModuleInstances->ResetAndDestroy();
+        delete iTestModuleInstances;
+        iTestModuleInstances = NULL;
+        }
+
+    // Delete ini file heap buffer
+    delete iIniBuffer;
+    iIniBuffer = NULL;
+
+    // Delete array of test case titles
+    iTestCaseTitlesMap.ResetAndDestroy();
+    iTestCaseTitlesMap.Close();
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: ConstructL
+
+    Description: Second level constructor.
+    
+    Parameters: CTestServer* aServer          :in:  Server
+
+    Return Values: None
+
+    Errors/Exceptions: Leaves if:
+                       base class CreateL leaves
+                       Object index creation fails
+                       Object container creation fails
+                       Pointer array construction fails                       
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::ConstructL( CTestServer* aServer )
+    {
+
+	__TRACE( KVerbose, ( _L( "CTestModule::ConstructL - constructing server session" ) ) );
+
+    // second-phase construct base class
+    //CSession2::CreateL( /* *aServer */ );
+    iTestServer = aServer;
+
+    // create new object index
+    iTestExecutionHandle = CObjectIx::NewL();
+
+    // Initialize the object container using the object
+    // container index in the server.
+    iContainer = iTestServer->NewContainerL();
+
+    iTestModuleInstances = new( ELeave ) RPointerArray<CTestModuleContainer>;
+
+	__TRACE( KError, ( _L( "CTestModule::ConstructL - constructing server session done" ) ) );
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CountResources
+
+    Description: Resource counting
+
+    Parameters: None
+
+    Return Values: TInt                             Resource count
+
+    Errors/Exceptions: None
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::CountResources()
+    {
+
+    return iResourceCount;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: NumResources
+
+    Description: Get resources, writes to Message()
+
+    Parameters: None
+
+    Return Values: None
+
+    Errors/Exceptions: Panic client if result can't be written to descriptor.
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::NumResources( const RMessage2& aMessage )
+    {
+
+    TPckgBuf<TInt> countPckg( iResourceCount );
+
+    TRAPD( r, aMessage.WriteL( 0, countPckg ) );
+    if( r !=KErrNone )
+        {
+        PanicClient( EBadDescriptor, aMessage );
+        }
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: PanicClient
+
+    Description: Panic clients.
+
+    Parameters: TInt aPanic                   :in:  Panic code
+
+    Return Values: None
+
+    Errors/Exceptions: None
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::PanicClient( const TInt aPanic, const RMessage2& aMessage ) const
+    {
+
+    __TRACE( KError,( _L( "CTestModule::PanicClient code = %d" ), aPanic ) );
+
+    _LIT( KTxtTestModule,"CTestModule" );
+
+    aMessage.Panic( KTxtTestModule, aPanic );
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CloseSession
+
+    Description: Close session
+
+    Parameters: None
+
+    Return Values: TInt: Symbian error code.
+
+    Errors/Exceptions: None
+
+    Status: Approved
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::CloseSession( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::CloseSession in" ) ) );
+
+    // Delete all unclosed subsession handle before closing session.
+    // Remove iTestExecutionHandle contents. iTestExecutionHandle countains
+    // CTestExecution that handles test case have been created.
+    if( iTestExecutionHandle )
+        {
+        TInt handle = 0;
+        CObject* theObj = NULL;
+        TInt count = iTestExecutionHandle->Count();        
+        for ( TInt i = count - 1; i >= 0; i-- )
+            {
+            theObj=iTestExecutionHandle->operator[]( i );
+            if( theObj )
+                {
+                handle=iTestExecutionHandle->At( theObj );
+                // CTestExecution will panic if test case is ongoing!!! This
+                // should be stopped, client should handle this.
+                iTestExecutionHandle->Remove( handle );
+                }
+            }
+
+        delete iTestExecutionHandle;
+        iTestExecutionHandle = NULL;
+        }
+
+    // Deletion must be done here, because the "CloseSession" message is 
+    // completed before execution continues from CActiveScheduler::Start 
+    // location, and the main thread can continue execution 
+    // and therefore shutdown itself and all threads in that process.
+    
+    // Delete the object container
+    iTestServer->DeleteContainer( iContainer );
+
+    // Free test case related data
+    TInt r = FreeCaseData();
+
+    // Delete all FREE Test Module instances
+    iTestModuleInstances->ResetAndDestroy();
+    delete iTestModuleInstances;
+    iTestModuleInstances = NULL;
+
+     // Inform server that session is closed
+    iTestServer->SessionClosed();
+
+	// Check for avoiding multiple complete, see CTestModule::ServiceL()
+    if( r != KErrNone )
+        {
+		// In error cases do not complete, just return
+        __TRACE( KMessage,( _L( "CTestModule::CloseSession out (1)" ) ) );
+        return r;
+        }
+
+    aMessage.Complete( KErrNone );
+
+    // After active scheduler shutdown is done, execution continues from
+    // CTestServer::ThreadFunction()
+
+    __TRACE( KMessage,( _L( "CTestModule::CloseSession out (2)" ) ) );
+    return r;
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: ServiceL
+
+    Description: Trap harness for dispatcher
+
+    Parameters: const RMessage& aMessage  :inout:   Message
+
+    Return Values: None
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::ServiceL( const RMessage2& aMessage )
+    {
+
+    // NOTE! HW testing slows down dramatically if adds commants here
+
+    //__TRACE( KMessage,( _L( "CTestModule::ServiceL in" ) ) );
+
+    TInt r = KErrNone;
+    TRAPD( ret, r = DispatchMessageL( aMessage ) );
+
+    if ( ret != KErrNone )
+        {
+        // Complete message on leaving cases with leave code.
+        __TRACE( KError, ( CStifLogger::ERed, _L( "CTestModule::DispatchMessageL leaved" ) ) );
+        aMessage.Complete( ret );
+        }
+
+    // Complete message with error code originating from message handling
+    // function.
+    if ( r != KErrNone )
+        {
+        __TRACE( KError, ( CStifLogger::ERed, _L( "CTestModule::DispatchMessageL returned error" ) ) );
+        aMessage.Complete( r );
+        }
+
+     // __TRACE( KMessage,( _L( "CTestModule::ServiceL out" ) ) );
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: DispatchMessageL
+
+    Description: Dispatch message, calls corresponding function to do it.
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Error code
+
+    Errors/Exceptions: Leaves if operation handling function leaves
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::DispatchMessageL( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::DispatchMessageL %d" ),
+        aMessage.Function()  ) );
+    switch( aMessage.Function() )
+    {
+    // Session specific
+    case ETestServerCloseSession:                // Close whole session
+        {
+		__TRACE( KError ,( _L( "Closing test module session" ) ) );
+        return CloseSession( aMessage );
+        }
+    case ETestServerGetServerThreadId:           // Get Server ThreadId
+        {
+		__TRACE( KError ,( _L( "Return server thread id" ) ) );
+        return GetServerThreadIdL( aMessage );
+        }
+
+    case ETestModuleCreateSubSession :           // Create new test module subsession
+        {
+		__TRACE( KError,( _L( "Creating test module session" ) ) );
+        return CreateModuleSessionL( aMessage );
+        }
+
+    case ETestModuleCloseSubSession:             // Close module subsession
+        {
+        // Nothing to do.
+        aMessage.Complete( KErrNone );
+        return KErrNone;
+        }
+
+    case ETestExecutionCreateSubSession:         // Create new test execution subsession
+        {
+        return NewTestExecutionL( aMessage );
+        }
+ 
+    case ETestModuleEnumerateTestCases:          // Enumerate test cases
+        {
+		__TRACE( KError,( _L( "Enumerating test cases" ) ) );
+        return EnumerateTestCasesL( aMessage );
+        }
+        
+    case ETestModuleGetTestCases:                // Get test cases
+        {
+        return GetTestCasesL( aMessage );
+        }
+
+    case ETestModuleErrorNotification:           // Request error notification
+        {
+        return HandleErrorNotificationL( aMessage );
+        }
+
+    case ETestModuleCancelAsyncRequest:
+        {
+        return CancelAsyncRequestL( aMessage );
+        }
+  
+    // Subsession specific
+    case ETestExecutionRunTestCase:              // Run test case
+        {
+		__TRACE( KInit,( _L( "Running test case" ) ) );
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->RunTestCase( aMessage );
+        }
+        
+    case ETestExecutionNotifyProgress:           // Test case prints
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->NotifyPrint( aMessage );
+        }
+
+    case ETestExecutionNotifyEvent:              // Event notifications
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->NotifyEvent( aMessage );
+        }
+        
+    case ETestExecutionNotifyRemoteCmd:          // RemoteCmd notifications
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->NotifyRemoteCmd( aMessage );
+        }
+    case ETestExecutionReadRemoteCmdInfo:          // RemoteCmd reading
+        {
+        //CTestExecution* testCase=CaseFromHandle( aMessage.Int3() );
+        //return testCase->NotifyRemoteCmd( aMessage );
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->ReadRemoteCmdInfo( aMessage );
+        }
+
+
+    case ETestExecutionResume:                   // Resume case execution
+        {
+		__TRACE( KVerbose,( _L( "Resuming test execution" ) ) );
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->Resume( aMessage );
+        }
+        
+    case ETestExecutionPause:                    // Pause case execution
+        {
+		__TRACE( KVerbose,( _L( "Pausing test execution" ) ) );
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->Pause( aMessage );
+        }
+            
+    case ETestExecutionCloseSubSession:          // Close execution subsession
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->CloseTestExecution( aMessage );
+        }
+
+    case ETestExecutionCancelAsyncRequest:       // Cancel async request
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->CancelRequestL( aMessage );
+        }
+    case ETestExecutionNotifyCommand:            // Command notifications
+        {
+        CTestExecution* testCase=CaseFromHandle( aMessage.Int3(), aMessage );
+        return testCase->NotifyCommand( aMessage );
+        }
+
+    default:                                     // Invalid request
+        {
+        PanicClient( EBadRequest, aMessage );
+        return KErrNotSupported;
+        }
+    }
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: EnumerateTestCases
+
+    Description: Enumerates test cases and returns test case count by writing
+    result to client side package.
+
+    Function obtains a TestModule and calls EnumerateTestCases from it.
+    Test module will be released when calling GetTestCases function.
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::EnumerateTestCasesL( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::EnumerateTestCasesL in" ) ) );
+ 
+    // Get data from message
+    TFileName config;
+    TRAPD( res, aMessage.ReadL( 0, config ) );
+    if(res == KErrDied)
+        {
+        RDebug::Print(_L("CTestModule::EnumerateTestCasesL() Reading from RMessage ended with KErrDied. Client is not alive anymore and this request will be ignored"));
+        return res;
+        }
+    else if( res != KErrNone )
+        {
+        RDebug::Print(_L("CTestModule::EnumerateTestCasesL() #1 Panic client with [%d], res=[%d]"), EBadDescriptor, res);
+        PanicClient( EBadDescriptor, aMessage );
+        return res;
+        }
+
+    // Free old case data
+    User::LeaveIfError( FreeCaseData() );
+
+    // Get test cases from the test module
+    User::LeaveIfError( GetTestModule( iEnumerateModule, config ) );
+
+    // Enumerate test cases
+    iEnumerateModule->EnumerateTestCases( config );
+
+    // Something fatal?
+    TInt r = iEnumerateModule->OperationErrorResult();
+    if(  r != KErrNone )
+        {
+        // Enumeration module have crashed.
+        delete iEnumerateModule;
+        iEnumerateModule = NULL;
+
+        return r;
+        }
+
+    // Error from module?
+    if( iEnumerateModule->ModuleResult() != KErrNone )
+        {
+        return iEnumerateModule->ModuleResult();
+        }
+
+    // Write count to Ptr1()
+    const RPointerArray<TTestCaseInfo>* testCases = iEnumerateModule->TestCases();
+    
+    if( testCases == NULL )
+        {
+        User::Leave( KErrGeneral );
+        }
+    
+    CTestCasesList* testCasesList = NULL;
+    for ( TInt i = 0; i < iTestCaseTitlesMap.Count(); i++ )
+        {
+        if ( iTestCaseTitlesMap[ i ]->GetConfigFileName() == config )
+            {
+            testCasesList = iTestCaseTitlesMap[ i ];
+            break;
+            }
+        }
+    if ( testCasesList == NULL )
+        {
+        testCasesList = CTestCasesList::NewL( config );
+        CleanupStack::PushL( testCasesList );
+        iTestCaseTitlesMap.AppendL( testCasesList );
+        CleanupStack::Pop( testCasesList );
+        }
+    
+    // Store titles (for further use, i.e. when asked for title from the interface via CTestModuleIf->CTestThreadContainer->CTestModuleContainer)
+    testCasesList->ResetAndDestroy();
+    TInt i;
+    for(i = 0; i < testCases->Count(); i++)
+        {
+        //Handle situation when test cases are enumerated not as 0-based (testscripter, ...)
+        if(i == 0 && (*testCases)[i]->iCaseNumber > 0)
+            {
+            testCasesList->AppendTestCaseL( KNullDesC );
+            }
+        testCasesList->AppendTestCaseL( (*testCases)[i]->iTitle );
+        }
+    
+    TPckgBuf<TInt> countPckg( testCases->Count() );
+    TRAP( res, aMessage.WriteL( 1, countPckg ) );
+    if(res == KErrDied)
+        {
+        RDebug::Print(_L("CTestModule::EnumerateTestCasesL() Writing to RMessage ended with KErrDied. Client is not alive anymore and this request will be ignored"));
+        return res;
+        }
+    else if( res != KErrNone )
+        {
+        RDebug::Print(_L("CTestModule::EnumerateTestCasesL() #2 Panic client with [%d], res=[%d], config=[%S]"), EBadDescriptor, res, &config);
+        PanicClient( EBadDescriptor, aMessage );
+        return res;
+        }
+
+    // All ok, complete message
+    aMessage.Complete( KErrNone );
+
+    __TRACE( KMessage,( _L( "CTestModule::EnumerateTestCasesL out" ) ) );
+    
+    return KErrNone;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: GetTestCasesL
+
+    Description: Get test cases. Enumerate test cases must be called before
+    calling this function.
+
+    Function releases the test module reserved by EnumerateTestCase().
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: Leaves if cases have not been enumerated.
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::GetTestCasesL( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::GetTestCasesL in" ) ) );
+
+    // Leave if cases have not been enumerated.
+    //User::LeaveIfNull(( TAny* ) iEnumerateModule->TestCases() );
+    if( ( TAny* ) iEnumerateModule->TestCases() == NULL )
+        {
+        User::Leave( KErrGeneral );
+        }
+
+    const TInt len = sizeof( TTestCaseInfo );
+    
+    // Get cases
+    const RPointerArray<TTestCaseInfo>& cases = *iEnumerateModule->TestCases();
+
+    // Get number of cases
+    const TInt Kcount = cases.Count();
+
+    // Loop through case and copy then to client's descriptor.
+    for( TInt i = 0; i < Kcount; i++ )
+        {
+
+        // Construct package for source data
+        TTestCaseInfoPckg tmpPackage( *cases[i] );
+
+        // Write to correct location
+        aMessage.WriteL( 0, tmpPackage, i *len );
+
+        }
+
+    // Free case data and the test module
+    User::LeaveIfError( FreeCaseData() );
+
+    // Finished
+    aMessage.Complete( KErrNone );
+
+    __TRACE( KMessage,( _L( "CTestModule::GetTestCasesL out" ) ) );
+
+    return KErrNone;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: HandleErrorNotification
+
+    Description: Request error notification.
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::HandleErrorNotificationL( const RMessage2& aMessage )
+    {
+    
+    iErrorMessage = aMessage;
+    iErrorMessageAvailable = ETrue;
+
+    return KErrNone;
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: GetServerThreadId
+
+    Description: Request server state notification.
+
+    Parameters: const RMessage& aMessage :inout: Message to be handled
+
+    Return Values: TInt Operation result
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::GetServerThreadIdL( const RMessage2& aMessage )
+    {
+      
+    TInt id( iTestServer->GetServerThreadId() );
+   
+    TPckg<TThreadId> threadIdPckg( id );
+   
+    TRAPD( res, aMessage.WriteL( 0, threadIdPckg ) );
+    
+      // Finished
+    aMessage.Complete( res );
+    
+    return KErrNone;
+
+    }      
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CancelAsyncRequest
+
+    Description: Cancels asynchronous request
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::CancelAsyncRequestL( const RMessage2& aMessage )
+    {
+    
+    switch ( aMessage.Int0() )
+        {
+        case ETestModuleErrorNotification:
+            {
+            if ( iErrorMessageAvailable )
+                {
+                iErrorMessage.Complete ( KErrCancel );
+                iErrorMessageAvailable = EFalse;
+                }
+            aMessage.Complete ( KErrNone );
+            break;
+            }
+
+        default:
+            {
+            PanicClient( EInvalidRequestCancel, aMessage );
+            break;
+            }
+        }
+
+    return KErrNone;
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: ErrorPrint
+
+    Description: Prints error
+
+    Parameters: const TInt aPriority :in: Priority
+                TPtrC aError: in: Error
+
+    Return Values: None
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::ErrorPrint( const TInt aPriority, 
+                              TPtrC aError )
+    {
+
+    if ( iErrorMessageAvailable )
+        {        
+        TErrorNotification error;
+        TErrorNotificationPckg errorPckg ( error );
+
+        error.iModule = _L("TestServer");
+        error.iPriority = aPriority;
+        error.iText = aError;
+
+        TRAPD( r, iErrorMessage.WriteL( 0, errorPckg ) );
+        
+        // Do not handle errors
+        iErrorMessageAvailable = EFalse;
+        iErrorMessage.Complete( r );
+
+        }
+    else
+        {
+        RDebug::Print (_L("Error message lost %d [%S]"), aPriority, &aError );
+        }
+    
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: FreeCaseData
+
+    Description: Frees the test case data and test module that is used in
+    enumeration.
+
+    Parameters: None
+
+    Return Values: TInt                             Error code
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::FreeCaseData()
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::FreeCaseData in" ) ) );
+ 
+    TInt r = KErrNone;
+
+    if( iEnumerateModule )
+        {
+        
+        // Deallocate testcase memory
+        iEnumerateModule->FreeEnumerationData();
+
+        // Free the module
+        r = FreeTestModule( iEnumerateModule );
+
+        iEnumerateModule = NULL;
+
+        }
+
+    __TRACE( KMessage,( _L( "CTestModule::FreeCaseData out" ) ) );
+
+    return r;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CreateModuleSessionL
+
+    Description: Creates a new module session.
+    Just take the initialisation file name from message.
+
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: Leaves if memory allocation fails
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::CreateModuleSessionL( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::CreateModuleSession in" ) ) );
+
+    // Take parameters
+    TFileName ini;
+    TRAPD( res, aMessage.ReadL( 0, ini ) );
+    if( res != KErrNone )
+        {
+        PanicClient( EBadDescriptor, aMessage );
+        return res;
+        }
+
+    // Construct heap buffer for initialization file name
+    iIniBuffer = HBufC::NewL( ini.Length() );
+    iIni.Set ( iIniBuffer->Des() );
+    iIni.Copy ( ini );
+
+    aMessage.Complete( KErrNone );
+
+    __TRACE( KMessage,( _L( "CTestModule::CreateModuleSession out" ) ) );
+
+    return KErrNone;
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: NewTestExecutionL
+
+    Description: Create new test execution subsession
+    
+    Parameters: const RMessage& aMessage  :inout:   Message to be handled
+    
+    Return Values: TInt                             Operation result
+
+    Errors/Exceptions: Function leaves if object can't be created or
+                       it can't be added to container.
+                       Function panics client if message contains invalid
+                       descriptor.
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::NewTestExecutionL( const RMessage2& aMessage )
+    {
+
+    __TRACE( KMessage,( _L( "CTestModule::NewTestExecutionL in" ) ) );
+
+    // Get data from message
+    TInt caseNumber = aMessage.Int0();
+    TFileName config;
+    
+    TRAPD( res, aMessage.ReadL( 1, config ) );
+    if( res != KErrNone )
+        {
+        PanicClient( EBadDescriptor, aMessage );
+        return res;
+        }
+
+    // Make new object
+    CTestExecution* execution=CTestExecution::NewL( this, caseNumber, config );
+
+    // add object to object container to generate unique id
+    iContainer->AddL( execution );
+
+    // add object to object index; this returns a unique handle so we can get it again
+    TInt handle=iTestExecutionHandle->AddL( execution );
+
+    // write the handle to client
+    TPckg<TInt> handlePckg( handle );
+    TRAP( res, aMessage.WriteL( 3, handlePckg ) );
+    if( res != KErrNone )
+        {
+        iTestExecutionHandle->Remove( handle );
+        PanicClient( EBadDescriptor, aMessage );
+        return res;
+        }
+
+    // notch up another resource
+    iResourceCount++;
+
+    // Complete message
+    aMessage.Complete( KErrNone );
+
+    __TRACE( KMessage,( _L( "CTestModule::NewTestExecutionL out" ) ) );
+
+    return KErrNone;
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: DeleteTestExecution
+
+    Description: Deletes Test Execution by handle
+
+    Parameters: const TUint aHandle           :in:  Handle
+
+    Return Values: None
+
+    Errors/Exceptions: Panics client if invalid handle
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+void CTestModule::DeleteTestExecution( const TUint aHandle, const RMessage2& aMessage )
+    {
+
+    // Verify that handle is valid
+    CaseFromHandle( aHandle, aMessage );
+
+    // Remove object
+    iTestExecutionHandle->Remove( aHandle );
+
+    // Decrement resource count.
+    iResourceCount--;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: CaseFromHandle
+
+    Description: Return subsession from handle
+
+    Parameters: const TUint aHandle           :in:  Handle
+
+    Return Values: CTestExecution*                  Test Execution object
+
+    Errors/Exceptions: Function panics client if invalid handle.
+
+    Status: Approved
+    
+-------------------------------------------------------------------------------
+*/
+CTestExecution* CTestModule::CaseFromHandle( const TUint aHandle, const RMessage2& aMessage ) const
+    {
+
+    CTestExecution* testcase =( CTestExecution* ) iTestExecutionHandle->At( aHandle );
+
+    if( testcase == NULL )
+        {
+        PanicClient( EBadSubsessionHandle, aMessage );
+        }
+
+    return testcase;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: GetTestModule
+
+    Description: Gets a CTestModuleBase*. If there is a free entry
+    in the free list, then it is returned. Otherwise a new one is created.
+
+    Parameters: CTestModuleContainer*& aContainer: out: container pointer.
+                const TDesC& aConfig: in: Test case (config) file name.
+
+    Return Values: Symbian OS error code
+    
+    Errors/Exceptions: None
+
+    Status: Proposal
+
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::GetTestModule( CTestModuleContainer*& aContainer,
+                                    const TDesC& aConfig )
+    {
+    TInt ret = KErrNone;
+
+    if( iTestModuleInstances->Count() )
+        {
+        // Return an existing one        
+        aContainer =( *iTestModuleInstances )[0];
+        iTestModuleInstances->Remove( 0 );
+		__TRACE( KInit,( _L( "Reusing old test module container instance at 0x%x" ), (TUint32) aContainer ) );
+        }
+    else
+        {
+        // Create a new one    
+        __TRACE( KInit,( _L( "Creating new test module instance" ) ) );
+        TRAPD( err, aContainer = CTestModuleContainer::NewL( 
+                                                iTestServer->ModuleName(),
+                                                this,
+                                                aConfig ) );
+
+        // If module can't be created, then return NULL.
+        if( err )
+            {
+            __TRACE( KError,( _L( "Can't create new test module container instance" ) ) );
+            aContainer = NULL;
+            ret = err;
+            }
+		else
+			{
+			__TRACE( KInit,( _L( "Test module instance container created" ) ) );
+			}
+
+        if( aContainer )
+            {
+
+            // Initialise module
+            aContainer->Initialize( iTestServer->ModuleName(), iTestServer->FirstTime() );
+
+            if( aContainer->OperationErrorResult() != KErrNone )
+                {
+                // Can't initialise module, delete it
+                __TRACE( KError,( CStifLogger::ERed, _L( "Operation error, can't initialize test module container instance" ) ) );
+                ret = aContainer->OperationErrorResult();                    
+                delete aContainer;
+                aContainer = NULL;
+                }
+            else if( aContainer->ModuleResult() != KErrNone )
+                {
+                // Can't initialise module, delete it
+                __TRACE( KError,( CStifLogger::ERed, _L( "Module error, can't initialize test module container instance" ) ) );
+                ret = aContainer->ModuleResult();
+                delete aContainer;
+                aContainer = NULL;
+                }
+            else
+                {
+                // Module initialised properly, clear the first time flag.
+                iTestServer->ClearFirstTime();
+				__TRACE( KInit,( _L( "Test module container initialized at 0x%x" ), (TUint32) aContainer ) );
+                }
+            }
+        }
+
+    return ret;
+
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: FreeTestModule
+
+    Description:Frees a CTestModuleContainer. This function can be called
+    from the context of the test execution thread.
+    
+    Parameters: CTestModuleContainer* aModule :in:  Module to be freed
+    
+    Return Values: TInt                             Error code
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+TInt CTestModule::FreeTestModule( CTestModuleContainer* aModule )
+    {
+  
+    // Free the module
+    TInt r = iTestModuleInstances->Append( aModule );
+    if( r != KErrNone )
+        {
+        delete aModule;
+        aModule = NULL;
+        }
+
+    return r;
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: IniName
+
+    Returns the initialisation file name
+    
+    Parameters: None
+    
+    Return Values: const TDesC&                     Initialisation file name
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+
+const TDesC& CTestModule::IniName() const
+    {
+
+    return iIni;
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: Name
+
+    Returns the module name
+    
+    Parameters: None
+    
+    Return Values: const TDesC&                     Initialisation file name
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+const TDesC& CTestModule::Name() const
+    {
+
+    return iTestServer->ModuleName();
+
+    }
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: GetTestCaseTitleL
+
+    Gets title of currently running test case
+    
+    Parameters: None
+    
+    Return Values: TInt aTestCaseNumber: in: index of currently running test case
+                   TDes& aTestCaseTitle: out: test case title
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+void CTestModule::GetTestCaseTitleL(TInt aTestCaseNumber, const TDesC& aConfigFile,TDes& aTestCaseTitle)
+    {
+    CTestCasesList* testCasesList = NULL;
+    for ( TInt i = 0; i < iTestCaseTitlesMap.Count(); i++ )
+        {
+        if ( iTestCaseTitlesMap[ i ]->GetConfigFileName() == aConfigFile )
+            {
+            testCasesList = iTestCaseTitlesMap[ i ];
+            break;
+            }
+        }
+    if ( testCasesList == NULL )
+        {
+        User::Leave( KErrNotFound );
+        }
+    
+    RDebug::Print(_L("Trying to get test case title from module. Index=%d, count=%d"), aTestCaseNumber, testCasesList->Count() );
+    
+    aTestCaseTitle.Copy( testCasesList->GetTestCaseTitleL( aTestCaseNumber ) );
+    }
+
+
+/*
+-------------------------------------------------------------------------------
+
+    Class: CTestModule
+
+    Method: GetTestServer
+
+    Gets pointer to TestServer
+    
+    Parameters: None
+    
+    Return Values: CTestServer : pointer to TestServer
+
+    Errors/Exceptions: None
+
+    Status: Proposal
+    
+-------------------------------------------------------------------------------
+*/
+CTestServer* CTestModule::GetTestServer()
+    {
+    return iTestServer;
+    }
+
+// End of File