/*
* 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
* CTestThreadContainer class implementation. These functions are
* called from the context of the test execution thread.
*
*/
// INCLUDE FILES
#include <e32std.h>
#include <e32svr.h>
#include <e32uid.h>
#include <StifTestModule.h>
#include "ThreadLogging.h"
#include "TestEngineClient.h"
#include <stifinternal/TestServerClient.h>
#include "TestServer.h"
#include "TestThreadContainer.h"
#include "TestServerCommon.h"
#include "TestServerModuleIf.h"
#include "TestServerEvent.h"
#include "TestThreadContainerRunner.h"
#include <stifinternal/TestThreadContainerRunnerFactory.h>
// EXTERNAL DATA STRUCTURES
// EXTERNAL FUNCTION PROTOTYPES
// CONSTANTS
// MACROS
#ifdef THREADLOGGER
#undef THREADLOGGER
#endif
#define THREADLOGGER iThreadLogger
// LOCAL CONSTANTS AND MACROS
// MODULE DATA STRUCTURES
// LOCAL FUNCTION PROTOTYPES
// FORWARD DECLARATIONS
// ==================== LOCAL FUNCTIONS =======================================
// None
// ================= MEMBER FUNCTIONS =========================================
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: NewL
Description: Returns new CTestThreadContainer instance.
Parameters: None
Return Values: CTestThreadContainer* New instance
Errors/Exceptions: Function leaves if memory allocation fails or
CTestThreadContainer ConstructL leaves.
Status: Proposal
-------------------------------------------------------------------------------
*/
CTestThreadContainer* CTestThreadContainer::NewL(
CTestModuleContainer* aModuleContainer,
TThreadId aServerThreadId )
{
CTestThreadContainer* self =
new ( ELeave ) CTestThreadContainer( aModuleContainer );
CleanupStack::PushL( self );
self->ConstructL( aServerThreadId );
CleanupStack::Pop( self );
return self;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ConstructL
Description: Second level constructor.
Parameters: None
Return Values: CTestThreadContainer* New instance
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::ConstructL( TThreadId aServerThreadId )
{
User::LeaveIfError( iServerThread.Open( aServerThreadId ) );
iErrorPrintSem.SetHandle( ModuleContainer().ErrorPrintSemHandle() );
User::LeaveIfError( iErrorPrintSem.Duplicate( iServerThread ) );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: CTestThreadContainer
Description: Constructor.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
CTestThreadContainer::CTestThreadContainer(
CTestModuleContainer* aModuleContainer ):
iModuleContainer( aModuleContainer ),
iCheckResourceFlags( 0 )
{
ModuleContainer().SetThreadContainer( this );
StifMacroErrorInit(); // Initialization
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ~CTestThreadContainer
Description: Destructor
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
CTestThreadContainer::~CTestThreadContainer()
{
// Close mutexes
if ( iPrintMutex.Handle() != 0 ) iPrintMutex.Close();
if ( iEventMutex.Handle() != 0 ) iEventMutex.Close();
if ( iSndMutex.Handle() != 0 ) iSndMutex.Close();
if ( iRcvMutex.Handle() != 0 ) iRcvMutex.Close();
if ( iInterferenceMutex.Handle() != 0 ) iInterferenceMutex.Close();
if ( iMeasurementMutex.Handle() != 0 ) iMeasurementMutex.Close();
if ( iCommandMutex.Handle() != 0 ) iCommandMutex.Close();
// Mutex for testcomplete and cancel operations. Close duplicate mutex
if ( iTestThreadMutex.Handle() != 0 ) iTestThreadMutex.Close();
// Close semaphores
if ( iPrintSem.Handle() != 0 ) iPrintSem.Close();
if ( iErrorPrintSem.Handle() != 0 ) iErrorPrintSem.Close();
if ( iEventSem.Handle() != 0 ) iEventSem.Close();
if ( iSndSem.Handle() != 0 ) iSndSem.Close();
if ( iRcvSem.Handle() != 0 ) iRcvSem.Close();
if ( iInterferenceSem.Handle() != 0 ) iInterferenceSem.Close();
if ( iMeasurementSem.Handle() != 0 ) iMeasurementSem.Close();
//if ( iReceiverSem.Handle() != 0 ) iReceiverSem.Close();
if ( iCommandSem.Handle() != 0) iCommandSem.Close();
iServerThread.Close();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: InitializeModule
Description: Initialize test module.
Function obtains pointer to the first exported function of the test module,
and calls that function to obtain an instance of CTestModuleBase derived
object. After that the "Init()"-method is called. If some operation fails,
module will be deleted and error code is returned.
This function is a static member function, which is intented to be called
from the context of the test module thread.
Parameters: RLibrary& aModule :in: Module to be loaded
Return Values: TInt Error code from module
or memory allocation.
Errors/Exceptions: None.
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::InitializeModuleInThread ( RLibrary& aModule )
{
__TRACEI ( KInit, ( _L("Starting test module initialization") ) );
__TRACEI ( KInit, ( CStifLogger::EBold, _L("Module name \"%S\""),
&ModuleContainer().OperationName() ) );
ModuleContainer().OperationText() = _L("E32DLL");
TFileName moduleName;
TFileName tmpBuffer;
TInt r( KErrNone );
TFileName newNameBuffer;
TInt check = CheckModuleName( ModuleContainer().OperationName(), newNameBuffer );
if( check == KErrNone )
{
// Load the module(TestScripter)
r = aModule.Load( newNameBuffer );
}
else
{
// Load the module(Others)
RemoveOptionalIndex(ModuleContainer().OperationName(), newNameBuffer);
__TRACEI(KInit, (_L( "Valid module name is [%S] (extracted from [%S])"), &newNameBuffer, &ModuleContainer().OperationName()));
r = aModule.Load(newNameBuffer);
}
if ( r != KErrNone )
{
__TRACEI (KError, ( CStifLogger::EError, _L("Can't initialize test module code = %d"), r));
// Set error codes
ModuleContainer().OperationErrorResult() = r;
return r;
}
else
{
// Print module name
moduleName = aModule.FileName();
__TRACEI (KInit, ( _L("Loaded test module[%S]"), &moduleName ) );
}
// Verify the UID
TUid KUidTestModule = TUid::Uid ( 0x101FB3E7 );
TUidType requiredUID( KDynamicLibraryUid, KSharedLibraryUid, KUidTestModule );
TUidType moduleUID = aModule.Type();
if ( moduleUID != requiredUID )
{
// New instance can't be created
RDebug::Print( ( _L("STIF TF: Test module has invalid UID. Aborting loading!") ) );
__TRACEI (KError, ( CStifLogger::EError, _L("Test module has invalid UID. Aborting loading!")));
tmpBuffer.Format(_L("Module [%S] has invalid UID"), &moduleName);
ErrorPrint( 1, tmpBuffer );
ModuleContainer().OperationErrorResult() = KErrNotSupported;
return KErrNotSupported;
}
// Get pointer to first exported function
ModuleContainer().OperationText() = _L("1st EXPORTED function");
CTestInterfaceFactory libEntry;
libEntry = (CTestInterfaceFactory) aModule.Lookup(1);
if ( libEntry == NULL )
{
// New instance can't be created
__TRACEI (KError, ( CStifLogger::EError, _L("Can't initialize test module, NULL libEntry")));
// Set error codes
ModuleContainer().OperationErrorResult() = KErrNoMemory;
return KErrNoMemory;
}
else
{
__TRACEI ( KInit, ( _L("Pointer to 1st exported received")));
}
// initialize test module
__TRACEI ( KVerbose, (_L("Calling 1st exported at 0x%x"), (TUint32) libEntry ));
TRAPD ( err, iTestModule = (*libEntry)() );
// Handle leave from test module
if ( err != KErrNone )
{
__TRACEI (KError, ( CStifLogger::EError, _L("Leave when calling 1st exported function, code %d"), err));
tmpBuffer = _L("Leave from test module 1st EXPORTED function");
ErrorPrint( 1, tmpBuffer );
delete iTestModule;
iTestModule = NULL;
// Set error codes
ModuleContainer().OperationErrorResult() = err;
return err;
}
else if ( iTestModule == NULL ) // Handle NULL from test module init
{
__TRACEI (KError, ( CStifLogger::EError, _L("NULL pointer received when constructing test module")));
tmpBuffer = _L("Test module 1st EXPORTED function returned NULL");
ErrorPrint( 1, tmpBuffer );
delete iTestModule;
iTestModule = NULL;
// Set error codes
ModuleContainer().OperationErrorResult() = KErrNoMemory;
return KErrNoMemory;
}
else
{
__TRACEI (KInit, (_L("Entrypoint successfully called, test module instance at 0x%x"), (TUint32)iTestModule ) );
}
// Verify version number.
ModuleContainer().OperationText() = _L("Version");
TVersion moduleAPIVersion(0,0,0);
TVersion myOldAPIVersion( KOldTestModuleAPIMajor, KOldTestModuleAPIMinor, KOldTestModuleAPIBuild );
TVersion myAPIVersion( KTestModuleAPIMajor, KTestModuleAPIMinor, KTestModuleAPIBuild );
TRAP ( err, moduleAPIVersion = iTestModule->Version() );
if ( err != KErrNone || (( myOldAPIVersion.iMajor != moduleAPIVersion.iMajor ||
myOldAPIVersion.iMinor != moduleAPIVersion.iMinor )
&&
( myAPIVersion.iMajor != moduleAPIVersion.iMajor ||
myAPIVersion.iMinor != moduleAPIVersion.iMinor ))
)
{
tmpBuffer = moduleAPIVersion.Name();
__TRACEI (KError, ( CStifLogger::EError, _L("Incorrect test module version. Module version %S"), &tmpBuffer ) );
tmpBuffer = myOldAPIVersion.Name();
__TRACEI (KError, ( CStifLogger::EError, _L("Required version %S"), &tmpBuffer ) );
tmpBuffer.Format(_L("Invalid version in [%S]"), &moduleName );
ErrorPrint( 1, tmpBuffer );
// Set error codes
ModuleContainer().OperationErrorResult() = KErrNotSupported;
return KErrNotSupported;
}
ModuleContainer().OperationText() = _L("InitL");
// Initialize test module
TInt initResult = KErrNone;
TRAP ( err,
CTestModuleIf::NewL( NULL, iTestModule );
TFileName tmp = ModuleContainer().TestModuleIniFile();
initResult = iTestModule->InitL( tmp, ModuleContainer().OperationIntBuffer() );
);
// Handle leave from test module
if ( err != KErrNone )
{
__TRACEI (KError, ( CStifLogger::EError, _L("Leave when initializing test module code %d"), err));
tmpBuffer = _L("Leave from test module InitL");
ErrorPrint( 1, tmpBuffer );
ModuleContainer().OperationText() = _L("DESTRUCTOR");
delete iTestModule;
iTestModule = NULL;
ModuleContainer().OperationText() = _L("");
// Set error codes
ModuleContainer().OperationErrorResult() = err;
return err;
}
else if ( initResult != KErrNone )
{ // Handle failed initialisation of test module
__TRACEI (KError, ( CStifLogger::EError, _L("Can't initialize test module, code %d"), initResult));
ModuleContainer().OperationText() = _L("DESTRUCTOR");
delete iTestModule;
iTestModule = NULL;
ModuleContainer().OperationText() = _L("");
// Set error code
ModuleContainer().ModuleResult() = initResult;
return initResult;
}
ModuleContainer().OperationText() = _L("");
__TRACEI (KInit, ( CStifLogger::EBold, _L("Test module initialization done")));
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: EnumerateInThread
Description: Enumerate test cases. Function calls GetTestCases method
from the test module.
This function is a static member function, which is intented to be called
from the context of the test module thread.
Parameters: None
Return Values: TInt Error code.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::EnumerateInThread()
{
TInt err = KErrNone;
__TRACEI ( KInit, ( CStifLogger::EBold, _L("Calling GetTestCasesL") ) );
if ( iCases == NULL )
{
iCases = new RPointerArray<TTestCaseInfo>;
if ( iCases == NULL )
{
ModuleContainer().OperationErrorResult() = KErrNoMemory;
__TRACEI ( KError, ( _L("Can't create pointer array for cases") ) );
return ModuleContainer().OperationErrorResult();
}
}
// Thread ID logging(For error situations) !!!!! ----------
/*
RThread t;
RDebug::Print(_L("XXXXXXXXXXXXXXXXXXXXXX CurrentThread=[%d]"), t.Id() );
t.Open( t.Id() );
RDebug::Print(_L("XXXXXXXXXXXXXXXXXXXXXX Real id=[%d]"), t.Id() );
t.Close();
*/
// --------------------------------------------------------
ModuleContainer().OperationText() = _L("GetTestCasesL");
TRAPD (r, err = iTestModule->GetTestCasesL(
ModuleContainer().OperationName(),
*iCases ) );
ModuleContainer().OperationText() = _L("");
// Leave
if ( r != KErrNone )
{
__TRACEI ( KError, ( CStifLogger::ERed, _L("GetTestCasesL leave code %d"), r ) );
TName tmpBuffer = _L("Leave from test module GetTestCasesL");
ErrorPrint( 1, tmpBuffer );
FreeEnumerationDataInThread();
ModuleContainer().OperationErrorResult() = r;
return r;
}
// Error originating from test module
if ( err != KErrNone )
{
__TRACEI ( KError, ( CStifLogger::ERed, _L("GetTestCasesL returned error %d"), err ) );
FreeEnumerationDataInThread();
ModuleContainer().ModuleResult() = err;
return err;
}
__TRACEI ( KInit, ( _L("GetTestCasesL successfully called") ) );
// All ok.
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: FreeEnumerationDataInThread
Description: Frees the enumeration data. This function is called, when
the enumeration data is read from execution thread heap to server thread
heap. If cases have not been enumerated function does nothing.
Function is intented to be called from the context of the test module thread.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::FreeEnumerationDataInThread()
{
__TRACEI ( KInit, ( _L("Freeing test case array") ) );
if ( iCases )
{
iCases->ResetAndDestroy();
delete iCases;
iCases = NULL;
}
__TRACEI ( KInit, ( _L("Freeing test case array done") ) );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ExecuteTestCaseInThread
Description: Execute test case. This function calls either RunTestCase or
ExecuteOOMTestCase to execute and report the results.
This function is a static member function, which is intented to be called
from the context of the test module thread.
Parameters: None
Return Values: TInt: Error code
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::ExecuteTestCaseInThread()
{
TVersion moduleAPIVersion;
moduleAPIVersion = iTestModule->Version();
__TRACEI ( KInit,
( CStifLogger::EBold, _L("Executing test case file=[%S] case=%d"),
&ModuleContainer().OperationName(),
ModuleContainer().OperationIntBuffer() ) );
TInt r = KErrNone;
// Thread handle
RThread thisRt;
r = DuplicateMutexHandles( thisRt );
// Result from RunTestCase
TTestResult caseResult;
// Execution result from RunTestCase
TInt err = KErrNone;
// Fill in initial values
TestExecution().FullResult().iCaseExecutionResultType =
TFullTestResult::ECaseExecuted;
TestExecution().FullResult().iCaseExecutionResultCode = KErrNone;
TestExecution().FullResult().iStartTime.HomeTime();
TestExecution().TestThreadFailure() = CTestExecution::ETestThreadOk;
// Set handle to test execution
TRAP( r, CTestModuleIf::NewL( this,
iTestModule ) );
ModuleContainer().OperationText() =_L("RunTestCaseL");
// Do resource checks before starting test case
iCheckResourceFlags = 0;
TInt tmp;
TInt threadHandleCountBeforeTest;
// Request count check
TInt requestCountBeforeTest = thisRt.RequestCount();
// Handle count check, not checking process handles
thisRt.HandleCount( tmp, threadHandleCountBeforeTest );
// If handle ok, then execute test
if( r == KErrNone )
{
TInt testCaseNumber = ModuleContainer().OperationIntBuffer();
// Do the test
__TRACEI ( KInit, ( _L("About to call RunTestCaseL. If nothing in log \
after line \"Calling RunTestCaseL\", check testserver log file.") ) );
TInt firstMemFailure = 0;
TInt lastMemFailure = 0;
// Store the OOM test type
CTestModuleBase::TOOMFailureType failureType;
// Check if the current test case is supposed to be run using OOM
if( iTestModule->OOMTestQueryL( ModuleContainer().OperationName(),
testCaseNumber,
failureType,
firstMemFailure,
lastMemFailure ) )
{
// Run the test case in OOM conditions
r = ExecuteOOMTestCase( testCaseNumber,
firstMemFailure,
lastMemFailure,
err,
caseResult );
}
else
{
// Run the test case the old way, without OOM testing
__TRACEI ( KInit, ( _L("Calling RunTestCaseL - \
OOM condition is not set") ) );
TRAP( r, err = iTestModule->RunTestCaseL(
testCaseNumber,
ModuleContainer().OperationName(),
caseResult ) );
}
}
// Do resource checks after test case execution
// Handle count check
TInt threadHandleCountAfterTest;
thisRt.HandleCount( tmp, threadHandleCountAfterTest );
// Request count check
TInt requestCountAfterTest = thisRt.RequestCount();
ModuleContainer().OperationText() =_L("");
// Store end time
TestExecution().FullResult().iEndTime.HomeTime();
// Remove handle to testexecution
TRAPD( rr, CTestModuleIf::NewL( NULL, iTestModule ) );
if ( rr != KErrNone )
{
__TRACEI ( KError, ( _L("Memory low in executionthread.") ) );
// Do not actually handle error
}
// Report test result. Parts of this will be overwritten if error
// is detected
TestExecution().FullResult().iTestResult = caseResult;
// Get target exit reasons
CTestModuleIf::TExitReason allowedExitReason;
TInt allowedExitCode = KErrNone;
ExitReason( allowedExitReason, allowedExitCode );
TBool returnLeakCheckFail = EFalse;
// Check are STIF macros used
if( iTestMacroInfo.iIndication )
{
// STIF macros are used. Set description info, test case to
// ECaseExecuted state and case execution result code to KErrNone
// to get test case to failed category.
TName tmpResultDes;
__TRACEI ( KError, ( CStifLogger::ERed, _L("Leave from RunTestCaseL(STIF TF's macro is used)" ) ) );
// Set result description
tmpResultDes.Copy( _L( "FILE[") );
tmpResultDes.Append( iTestMacroInfo.iFileDes );
tmpResultDes.Append( _L( "] FUNCTION[" ) );
tmpResultDes.Append( iTestMacroInfo.iFunctionDes );
tmpResultDes.Append( _L( "] LINE[" ) );
tmpResultDes.AppendNum( iTestMacroInfo.iLine );
tmpResultDes.Append( _L( "]" ) );
// Other result information
TestExecution().FullResult().iTestResult.iResult =
iTestMacroInfo.iReceivedError;
TestExecution().FullResult().iTestResult.iResultDes = tmpResultDes;
TestExecution().FullResult().iCaseExecutionResultType =
TFullTestResult::ECaseExecuted;
// Set category to failed cases
TestExecution().FullResult().iCaseExecutionResultCode = KErrNone;
StifMacroErrorInit(); // Initialization back to default
}
else if( r != KErrNone )
{ // Case has left, overwrite normal result description string
__TRACEI ( KError, ( CStifLogger::ERed, _L("Leave from RunTestCaseL, code %d"), r ) );
// Set result description
TName tmpResultDes = _L("Leave during case:");
// Check if there was already some description passed to result object
if(caseResult.iResultDes.Length() > 0)
{
tmpResultDes.Format(_L("Leave during case [%S]:"), &caseResult.iResultDes);
if(tmpResultDes.Length() > KStifMaxResultDes)
{
tmpResultDes.SetLength(KStifMaxResultDes);
}
}
// Other result information
TestExecution().FullResult().iTestResult.iResult = KErrGeneral;
TestExecution().FullResult().iTestResult.iResultDes = tmpResultDes;
TestExecution().FullResult().iCaseExecutionResultType =
TFullTestResult::ECaseLeave;
TestExecution().FullResult().iCaseExecutionResultCode = r;
}
else if ( err != KErrNone )
{
// Case has returned error (e.g. case not found )
__TRACEI ( KError, ( CStifLogger::ERed, _L("RunTestCaseL returned error %d"), err ) );
TestExecution().FullResult().iCaseExecutionResultType =
TFullTestResult::ECaseErrorFromModule;
TestExecution().FullResult().iCaseExecutionResultCode = err;
}
else if ( allowedExitReason != CTestModuleIf::ENormal )
{
// Test is failed, because it should end to panic or exception.
__TRACEI ( KInit, ( _L("Case ended normally even if it should end to panic/exception") ) );
TestExecution().FullResult().iTestResult.iResult = KErrGeneral;
TestExecution().FullResult().iTestResult.iResultDes =
_L("Case did not ended to panic/exception");
}
// If test case is passed, check memory leak, handles etc...
else if( caseResult.iResult == KErrNone )
{
returnLeakCheckFail = ETrue;
}
// Test case leak checks
// In EKA2 heap size cannot be measured because THeapWalk is no longer supported
LeakChecksForTestCase( returnLeakCheckFail,
threadHandleCountBeforeTest,
threadHandleCountAfterTest,
requestCountBeforeTest,
requestCountAfterTest );
// Close execution specific handles
iPrintMutex.Close();
iEventMutex.Close();
iSndMutex.Close();
iRcvMutex.Close();
iInterferenceMutex.Close();
iMeasurementMutex.Close();
iCommandMutex.Close();
// The Wait operation is performed to let the message from TestServer
// to TestEngine achieve TestEngine or TestCombiner.
iCommandSem.Wait();
// Note: iTestThreadMutex.iClose() mutex will be used later, close in destructor.
iPrintSem.Close();
iEventSem.Close();
iSndSem.Close();
iRcvSem.Close();
iInterferenceSem.Close();
iMeasurementSem.Close();
iCommandSem.Close();
// Close thread handle
thisRt.Close();
__TRACEI ( KVerbose, ( _L("ExecuteTestCase out") ) );
// continues from CTestModuleContainer::RunL
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DuplicateMutexHandles
Description: Duplicates mutex handles
Parameters: None
Return Values: TInt
Errors/Exceptions: Panic if duplication fails
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::DuplicateMutexHandles( RThread& aThread )
{
// For duplicating mutexes
iPrintMutex.SetHandle( TestExecution().PrintMutexHandle() );
iEventMutex.SetHandle( TestExecution().EventMutexHandle() );
iSndMutex.SetHandle( TestExecution().SndMutexHandle() );
iRcvMutex.SetHandle( TestExecution().RcvMutexHandle() );
iInterferenceMutex.SetHandle( TestExecution().InterferenceMutexHandle() );
iMeasurementMutex.SetHandle( TestExecution().MeasurementMutexHandle() );
iCommandMutex.SetHandle(TestExecution().CommandMutexHandle());
// Mutex for testcomplete and cancel operations. For duplicating mutex
iTestThreadMutex.SetHandle( TestExecution().TestThreadMutexHandle() );
// For duplicating semaphores
iPrintSem.SetHandle( TestExecution().PrintSemHandle() );
iEventSem.SetHandle( TestExecution().EventSemHandle() );
iSndSem.SetHandle( TestExecution().SndSemHandle() );
iRcvSem.SetHandle( TestExecution().RcvSemHandle() );
iInterferenceSem.SetHandle( TestExecution().InterferenceSemHandle() );
iMeasurementSem.SetHandle( TestExecution().MeasurementSemHandle() );
iCommandSem.SetHandle(TestExecution().CommandSemHandle());
// Store thread id for later use
TestExecution().SetTestThread( aThread.Id() );
// Duplicate handles from server thread
TRAPD( r,
User::LeaveIfError( iPrintMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iEventMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iSndMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iRcvMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iInterferenceMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iMeasurementMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iCommandMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iTestThreadMutex.Duplicate( iServerThread ) );
User::LeaveIfError( iPrintSem.Duplicate( iServerThread ) );
User::LeaveIfError( iEventSem.Duplicate( iServerThread ) );
User::LeaveIfError( iSndSem.Duplicate( iServerThread ) );
User::LeaveIfError( iRcvSem.Duplicate( iServerThread ) );
User::LeaveIfError( iInterferenceSem.Duplicate( iServerThread ) );
User::LeaveIfError( iMeasurementSem.Duplicate( iServerThread ) );
User::LeaveIfError( iCommandSem.Duplicate( iServerThread ) );
);
// Raise panic if duplications failed
if( r != KErrNone )
{
Panic( EDuplicateFail );
}
// Return the result, no error occurred
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ExecuteOOMTestCase
Description: Executes OOM test case
Parameters: None
Return Values: TInt
Errors/Exceptions: Panic if EOOMDisableLeakChecks is not set and test case
leaks memory.
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::ExecuteOOMTestCase( TInt aTestCaseNumber,
TInt aFirst,
TInt aLast,
TInt& aResult,
TTestResult& caseResult )
{
TBool OOMwarning = EFalse;
__TRACEI ( KInit, ( _L("CTestThreadContainer::ExecuteOOMTestCase") ) );
__TRACEI ( KInit, ( _L("Executing test case #%d using OOM"), aTestCaseNumber ) );
// OOM test environment initialization
TRAPD( r, iTestModule->OOMTestInitializeL(
ModuleContainer().OperationName(),
aTestCaseNumber ); );
for( TInt i=aFirst; i<aLast; i++ )
{
// Fail the i:nth heap allocation
User::__DbgSetAllocFail( RHeap::EUser, RHeap::EFailNext, i );
//__TRACEI ( KInit, ( _L("Setting %d:nth heap allocation to fail"), i ) );
// Intersection of iCheckResourceFlags and
// EDisableMemoryLeakChecksInOOM to check if memory leak checks are to
// be used with OOM testing.
if( !( iCheckResourceFlags & CTestModuleIf::EOOMDisableLeakChecks ) )
{
User::__DbgMarkStart( RHeap::EUser );
}
TRAP( r, aResult = iTestModule->RunTestCaseL(
aTestCaseNumber,
ModuleContainer().OperationName(),
caseResult ) );
// Raise panic if test case leaks memory and EOOMDisableLeakChecks is not
// set
if( !( iCheckResourceFlags & CTestModuleIf::EOOMDisableLeakChecks ) )
{
User::__DbgMarkEnd( RHeap::EUser, 0 );
}
// If no error occurred, fake a memory error to make sure that this is
// the last test. If this last allocation goes wrong, it proves that
// either the FAILNEXT() macro has reached its limit or that somewhere
// in the code some object TRAPped the OOM exception and did not leave.
if( ( r != KErrNoMemory ) && ( aResult != KErrNoMemory )
&& ( caseResult.iResult != KErrNoMemory ) )
{
TInt* dummy = new TInt;
OOMwarning = ( dummy != NULL );
delete dummy;
}
// Cancel the simulated heap allocation failure
User::__DbgSetAllocFail( RHeap::EUser, RHeap::ENone, 1 );
if( ( r != KErrNoMemory ) && !OOMwarning && ( aResult != KErrNoMemory )
&& ( caseResult.iResult != KErrNoMemory ) )
{
// If we get here test was executed properly (= no memory error
// and no warning)
break;
}
if( OOMwarning )
{
// It is possible that during testing some components TRAP the OOM
// exception and continue to run (do not leave) or they return an
// error other than KErrNoMemory. These situations are making the
// OOM testing really difficult, so they should be detected and
// make the tester aware.
// Since each test case might have a specific oppinion on handling
// this situation, it is left up to the tester to handle it by
// implementing the OOMHandleWarningL method. STIF will log a
// warning and call OOMHandleWarningL method.
// Print the OOM error message
__TRACEI ( KInit, ( _L("Possible trapped or non-leaving allocation in test case #%d"), i ) );
iTestModule->OOMHandleWarningL( ModuleContainer().OperationName(), aTestCaseNumber, i );
// Clear the warning flag
OOMwarning = EFalse;
}
}
// OOM test environment finalization
__TRACEI ( KInit, ( _L("Calling OOMTestFinalizeL") ) );
TRAPD( fres, iTestModule->OOMTestFinalizeL(
ModuleContainer().OperationName(),
aTestCaseNumber ); );
// Check the result
if( fres != KErrNone )
{
__TRACEI ( KInit, ( _L("OOMTestFinalizeL execution failed with error %d"), fres ) );
}
return r;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: LeakChecksForTestCase
Description: Checks test case for memory, handle and request leaks
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::LeakChecksForTestCase( TBool aReturnLeakCheckFail,
TInt aThreadHandleCountBeforeTest,
TInt aThreadHandleCountAfterTest,
TInt aRequestCountBeforeTest,
TInt aRequestCountAfterTest )
{
__TRACEI ( KInit, ( _L("CTestThreadContainer::LeakChecksForTestCase") ) );
// Note: Request leaks detection is disabled in UI components testing
if( !( iCheckResourceFlags & CTestModuleIf::ETestLeaksRequests ) &&
( aRequestCountBeforeTest != aRequestCountAfterTest ) &&
( !iModuleContainer->GetTestModule()->GetTestServer()->UiTesting()))
{
// Test is failed, because it should end to panic or exception.
__TRACEI ( KError, ( CStifLogger::ERed,
_L("Asynchronous request leak from test module. Request count before:[%d] and after:[%d] test."),
aRequestCountBeforeTest, aRequestCountAfterTest ) );
// Set failure status
TestExecution().TestThreadFailure() |= CTestExecution::ETestRequestLeak;
if( aReturnLeakCheckFail )
{
aReturnLeakCheckFail = EFalse; // return first fail
#ifndef STIF_DISABLE_LEAK_CHECK
// Testcase set to failed when request leak occurred
TestExecution().FullResult().iTestResult.iResult = KErrGeneral;
#endif
TestExecution().FullResult().iTestResult.iResultDes =
_L("Asynchronous request leak from testmodule");
TestExecution().FullResult().iTestResult.iResultDes.
AppendNum( aRequestCountAfterTest );
}
}
// Note: Handle leaks detection is disabled in UI components testing
if( !( iCheckResourceFlags & CTestModuleIf::ETestLeaksHandles ) &&
( aThreadHandleCountBeforeTest != aThreadHandleCountAfterTest ) &&
( !iModuleContainer->GetTestModule()->GetTestServer()->UiTesting()) )
{
// Test is failed, because it should end to panic or exception.
__TRACEI ( KError, ( CStifLogger::ERed,
_L("Thread handle leak from test module. Handle count before:[%d] and after:[%d] test."),
aThreadHandleCountBeforeTest, aThreadHandleCountAfterTest ) );
// Set failure status
TestExecution().TestThreadFailure() |= CTestExecution::ETestHandleLeak;
if( aReturnLeakCheckFail )
{
aReturnLeakCheckFail = EFalse; // return first fail
#ifndef STIF_DISABLE_LEAK_CHECK
// Testcase is set to failed yet when handle leak occurred
TestExecution().FullResult().iTestResult.iResult = KErrGeneral;
#endif
TestExecution().FullResult().iTestResult.iResultDes =
_L("Thread handle leak from testmodule");
TestExecution().FullResult().iTestResult.iResultDes.
AppendNum( aRequestCountAfterTest );
}
}
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DeleteTestModule
Description: Deletes a test module
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::DeleteTestModule()
{
__TRACEI ( KInit, ( _L("Deleting test module instance at 0x%x"), iTestModule ) );
// Delete the test module
ModuleContainer().OperationText() = _L("DESTRUCTOR");
TRAPD( r, delete iTestModule );
ModuleContainer().OperationText() = _L("");
iTestModule = NULL;
if ( r )
{
__TRACEI ( KError, ( _L("Leave when deleting test module, code %d"), r ) );
}
__TRACEI ( KInit, ( _L("Test module instance deleted") ) );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: TestCases
Description: Returns constant pointer to test case array
Parameters: None
Return Values: const RPointerArray<TTestCaseInfo>* Test cases
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
const RPointerArray<TTestCaseInfo>* CTestThreadContainer::TestCases() const
{
return iCases;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ErrorPrint
Description: Prints error
Parameters: const TInt aPriority :in: Priority
TPtrC aError: in: Error
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::ErrorPrint( const TInt aPriority,
TPtrC aError )
{
// Get access to print stuff
iErrorPrintSem.Wait();
// Get status variable from server
TRequestStatus* status =
ModuleContainer().GetRequest( CTestModuleContainer::ERqErrorPrint );
if( status == NULL )
{
Panic( ENullRequest );
return;
}
// Fill in progress
TErrorNotification& progress = ModuleContainer().ErrorNotification();
progress.iPriority = aPriority;
progress.iText = aError;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DoNotifyPrint
Description: If print notification available, notification is copied to
client memory space and request is completed.
Else new print queue item is created and appended to print
queue. If queue is full or memory can't be allocated,
then message will be discarded.
Parameters: const TInt aPriority : :in: Priority
const TStifInfoName& aDes :in: Description
const TName& aBuffer :in: Value
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::DoNotifyPrint( const TInt aPriority,
const TStifInfoName& aDes,
const TName& aBuffer )
{
// Get access to print stuff
iPrintSem.Wait();
iPrintMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iPrintSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqPrint );
if( status == NULL )
{
iPrintMutex.Signal();
Panic( ENullRequest );
return;
}
if( *status != KRequestPending )
{
// CPrintHandler::DoCancel called before getting here, just return
iPrintMutex.Signal();
return;
}
// Fill in progress
TTestProgress& progress = TestExecution().TestProgress();
progress.iPosition = aPriority;
progress.iDescription = aDes;
progress.iText = aBuffer;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
iPrintMutex.Signal();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DoNotifyEvent
Description: Forward event request.
Parameters: const TEventIf: in: Event definition
TRequestStatus* aStatus: in: TRequestStatus to complete
Return Values: None
Errors/Exceptions: Panics if event array can't be created
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::DoNotifyEvent( TEventIf& aEvent,
TRequestStatus* aStatus )
{
TInt ret = KErrNone;
// Send event req
SetEventReq( TEventDef::EEventCmd, aEvent, aStatus );
if( aStatus == NULL )
{
// Synchronous Event command used ->
// Block until completed with ECmdComplete from NotifyEvent
// Cannot be done before ERelEvent,
// because Unset may be blocking the server
User::WaitForRequest( iReqStatus );
User::After( 1 );// workaround found for STIF 347
// Return result from engine
ret = iReqStatus.Int();
}
return ret;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: CancelEvent
Description: Cancels pending event request.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::CancelEvent( TEventIf& aEvent,
TRequestStatus* aStatus )
{
__TRACEI( KMessage, ( _L( "CTestThreadContainer::CancelEvent(%d): %S [%p]" ),
aEvent.Type(), &aEvent.Name(), aStatus ) );
// Send event req
SetEventReq( TEventDef::EEventCmdCancel, aEvent, aStatus );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetExitReason
Description: Set exit reason
Parameters: const TExitReason aExitReason in: Exit reason
const TInt aExitCode in: Exit code
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::SetExitReason( const CTestModuleIf::TExitReason aExitReason,
const TInt aExitCode )
{
TInt exitCode = aExitCode;
if( ( aExitReason == CTestModuleIf::ENormal ) &&
( aExitCode != KErrNone ) )
{
__TRACEI( KError,
( _L( "SetExitReason: Exit type normal uses always exit code 0 (given %d is not used)" ),
exitCode ) );
exitCode = KErrNone;
}
ModuleContainer().AllowedExitReason() = aExitReason;
ModuleContainer().AllowedExitCode() = exitCode;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetBehavior
Description: Set test behaviour.
Parameters: const CTestModuleIf::TTestBehavior aType: in: behaviour type
TAny* aPtr: in: data
Return Values: Symbian OS error code.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::SetBehavior( const CTestModuleIf::TTestBehavior aType,
TAny* /*aPtr*/ )
{
if( aType & CTestModuleIf::ETestLeaksMem )
{
iCheckResourceFlags |= CTestModuleIf::ETestLeaksMem;
}
if( aType & CTestModuleIf::ETestLeaksRequests )
{
iCheckResourceFlags |= CTestModuleIf::ETestLeaksRequests;
}
if( aType & CTestModuleIf::ETestLeaksHandles )
{
iCheckResourceFlags |= CTestModuleIf::ETestLeaksHandles;
}
// For OOM testing
if( aType & CTestModuleIf::EOOMDisableLeakChecks )
{
iCheckResourceFlags |= CTestModuleIf::EOOMDisableLeakChecks;
}
if( !( aType & iCheckResourceFlags ) )
{
return KErrNotFound;
}
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ExitReason
Description: Gets exit reason
Parameters: TExitReason& aExitReason out: Exit reason
TInt& aExitCode out: Exit code
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::ExitReason( CTestModuleIf::TExitReason& aExitReason,
TInt& aExitCode )
{
aExitReason = ModuleContainer().AllowedExitReason();
aExitCode = ModuleContainer().AllowedExitCode();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetEventReq
Description: Sets asynchronous event request.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::SetEventReq( TEventDef::TEventCmdType aType,
TEventIf& aEvent,
TRequestStatus* aStatus )
{
// Get access to event stuff
iEventSem.Wait();
iEventMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iPrintSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqEvent );
if( status == NULL )
{
iEventMutex.Signal();
Panic( ENullRequest );
return;
}
if( *status != KRequestPending )
{
// CEventHandler::DoCancel called before getting here, just return
iEventMutex.Signal();
return;
}
// Fill in event on server thread
TEventDef& event = TestExecution().EventDef();
event.iType = aType;
event.iEvent.Copy( aEvent );
if( aStatus )
{
// Store TRequestStatus which is completed when next EEnable comes in
event.iStatus = aStatus;
}
else
{
iReqStatus = KRequestPending;
event.iStatus = &iReqStatus;
}
__TRACEI( KMessage ,(_L("SetReq Stat %d, %x"), this,
aStatus ));
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
iEventMutex.Signal();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DoRemoteReceive
Description: Enable remote receive and send.
Parameters:
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::DoRemoteReceive( TStifCommand aRemoteCommand,
TParams aParams,
TInt aLen,
TRequestStatus& aStatus )
{
switch( aRemoteCommand )
{
case EStifCmdSend: // "Send"
case EStifCmdReboot: // "Send"
case EStifCmdStoreState: // "Send"
case EStifCmdGetStoredState: // "Receive, this must be done with two phase"
case EStifCmdMeasurement: // "Receive"
{
__TRACEI( KMessage, ( _L( "CTestThreadContainer::DoRemoteReceive Wait SndSem" ) ) );
// Get access to sender
// (for receive, used for securing access to shared memory)
iSndSem.Wait();
iSndMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iPrintSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqSnd );
if( status == NULL )
{
iSndMutex.Signal();
Panic( ENullRequest );
return;
}
if( *status != KRequestPending )
{
// CSndHandler::DoCancel called before getting here, just return
iSndMutex.Signal();
return;
}
// Fill in information
TCmdDef& aDef = TestExecution().SndInfo();
aDef.iCommand = aRemoteCommand;
aDef.iParam = aParams;
aDef.iLen = aLen;
aDef.iStatus = &aStatus;
__TRACEI( KMessage ,
(_L("CTestThreadContainer::DoRemoteReceive Complete request 0x%x"),
status ));
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
iSndMutex.Signal();
}
break;
case EStifCmdReceive: // "Receive"
{
__TRACEI( KMessage, ( _L( "CTestThreadContainer::DoRemoteReceive Wait RcvSem" ) ) );
// Get access to receive handler
iRcvSem.Wait();
iRcvMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iPrintSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqRcv );
if( status == NULL )
{
iRcvMutex.Signal();
Panic( ENullRequest );
return;
}
if( *status != KRequestPending )
{
// CRcvHandler::DoCancel called before getting here, just return
iRcvMutex.Signal();
return;
}
// Fill in information
TCmdDef& aDef = TestExecution().RcvInfo();
aDef.iCommand = aRemoteCommand;
aDef.iParam = aParams;
aDef.iLen = aLen;
aDef.iStatus = &aStatus;
__TRACEI( KMessage ,
(_L("CTestThreadContainer::DoRemoteReceive Complete request 0x%x"),
status ));
__TRACEI( KMessage, ( _L( "CTestThreadContainer::DoRemoteReceive signal RcvSem" ) ) );
//iReceiverSem.Signal();
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
iRcvMutex.Signal();
}
break;
default:
TRequestStatus* rs = &aStatus;
User::RequestComplete( rs, KErrNotSupported );
break;
}
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: DoRemoteReceiveCancel
Description: Cancel DoRemoteReceive
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::DoRemoteReceiveCancel()
{
// Get access to receive handler
iRcvSem.Wait();
iRcvMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iPrintSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqRcv );
if( status == NULL )
{
iRcvMutex.Signal();
return KErrNotFound;
}
if( *status != KRequestPending )
{
// CRcvHandler::DoCancel called before getting here, just return
iRcvMutex.Signal();
Panic( ENullRequest );
return KErrNone;
}
// Fill in information
TCmdDef& aDef = TestExecution().RcvInfo();
aDef.iCommand = EStifCmdReceiveCancel;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
iRcvMutex.Signal();
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: TestComplete
Description: Complete test operation: Get test case, run test case,
complete test case, etc.
Parameters: TInt aCompletionCode: in: completion code.
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::TestComplete( TInt aCompletionCode )
{
// Get status variable from server
TRequestStatus* status =
ModuleContainer().GetRequest( CTestModuleContainer::ERqTestCase );
if( status == NULL )
{
Panic( ENullRequest );
return;
}
// Complete action to server
if( iTestThreadMutex.Handle() == 0 )
{
// Actual test case is not started yet. Inititialization phase is ongoing.
// Before the completion check if the status was not already completed
// from other thread in CTestModuleContainer::DoCancel().
// For details see Jira STIF-564
if(*status == KRequestPending)
iServerThread.RequestComplete( status, aCompletionCode );
}
else
{
// Test case execution is started. Test is ongoing.
// Before the completion check if the status was not already completed
// from other thread in CTestModuleContainer::DoCancel().
// For details see Jira STIF-564
if(*status == KRequestPending)
{
iTestThreadMutex.Wait(); // Block that complete and cancel do not
// executed at the same time.
iServerThread.RequestComplete( status, aCompletionCode );
iTestThreadMutex.Signal();
}
}
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: UIExecutionThread
Description: This is the test module execution thread "main" function".
All test module function calls are executed in context of this execution
thread.
When the thread is resumed first time, function goes to wait a semaphore.
Operations are initiated by setting operation and signaling the semaphore.
If operation is synchronous, then end of operation is signaled by using
OperationCompleted -Semaphore. When operation is done, function (and thread)
are going to wait OperationSemaphore.
Function exist either when operation does fatal error, or operation type
is "Exit". The thread function exist from it's main loop and the thread
will be terminated.
Parameters: TAny* aParams: :in: Pointer to CTestModuleContainer
Return Values: TInt KErrNone
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::UIExecutionThread( TAny* aParams )
{
CTestModuleContainer* moduleContainer =
(CTestModuleContainer*) aParams;
CTestModule* module = moduleContainer->GetTestModule();
CTestServer* testServer = module->GetTestServer();
CTestThreadContainerRunnerFactory* factory = testServer->GetTestThreadContainerRunnerFactory();
RThread server;
// Duplicate handles from server thread
TInt ret = server.Open( moduleContainer->ServerThreadId() );
if( ret != KErrNone )
{
Panic( EThreadHandleOpenFail );
}
RSemaphore OperationStartSemaphore;
OperationStartSemaphore.SetHandle(
moduleContainer->OperationStartSemHandle() );
if( OperationStartSemaphore.Duplicate( server ) != KErrNone )
{
Panic( EDuplicateFail );
}
RSemaphore OperationChangeSemaphore;
OperationChangeSemaphore.SetHandle(
moduleContainer->OperationChangeSemHandle() );
if( OperationChangeSemaphore.Duplicate( server ) != KErrNone )
{
Panic( EDuplicateFail );
}
server.Close();
CTestThreadContainerRunner* runner = factory->CreateL();
runner->Setup( moduleContainer );
while ( runner->IsReusable() )
{
// Thread is going to suspend
runner->CheckSignalFromSuspend();
// Wait next operation
OperationStartSemaphore.Wait();
// Get operation semaphore
OperationChangeSemaphore.Wait();
// Run and wait active object
runner->RunOneIteration();
OperationChangeSemaphore.Signal();
}
OperationStartSemaphore.Close();
OperationChangeSemaphore.Close();
runner->TeareDown();
runner->Deque();
factory->DeleteL( runner );
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ExecutionThread
Description: This is the test module execution thread "main" function".
All test module function calls are executed in context of this execution
thread.
When the thread is resumed first time, function goes to wait a semaphore.
Operations are initiated by setting operation and signaling the semaphore.
If operation is synchronous, then end of operation is signaled by using
OperationCompleted -Semaphore. When operation is done, function (and thread)
are going to wait OperationSemaphore.
Function exist either when operation does fatal error, or operation type
is "Exit". The thread function exist from it's main loop and the thread
will be terminated.
Parameters: TAny* aParams: :in: Pointer to CTestModuleContainer
Return Values: TInt KErrNone
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::ExecutionThread( TAny* aParams )
{
TInt error( KErrNone );
const TUint32 KAll = 0xFFFFFFFF;
#ifndef __HIDE_IPC_V1__ // e.g. 7.0s, 8.0a
RThread currentThread;
currentThread.SetExceptionHandler( ExceptionHandler, KAll );
#else // PlatSec used. Thread exception management is part of the User class.
User::SetExceptionHandler( ExceptionHandler, KAll );
#endif // __HIDE_IPC_V1__
// Check parameters
__ASSERT_ALWAYS( aParams, Panic( EInvalidCTestThreadContainer ) );
CTestModuleContainer* moduleContainer =
(CTestModuleContainer*) aParams;
// Create cleanup stack
CTrapCleanup* tc = CTrapCleanup::New();
__ASSERT_ALWAYS( tc, Panic( ECreateTrapCleanup ) );
CTestThreadContainer* exec = NULL;
TRAPD( err,
exec = CTestThreadContainer::NewL( moduleContainer,
moduleContainer->ServerThreadId() );
);
if( err != KErrNone )
{
Panic( ENullTestThreadContainer );
}
// Construct the logger
TName path = _L("C:\\logs\\testframework\\testserver\\");
TFileName name = _L("testserver_thread_");
name.Append( moduleContainer->TestModuleName() );
// Create logger, in Wins use HTML in HW default logger
TLoggerSettings loggerSettings;
// Directory must create by hand if test server log wanted
loggerSettings.iCreateLogDirectories = EFalse;
loggerSettings.iOverwrite = ETrue;
loggerSettings.iTimeStamp = ETrue;
loggerSettings.iLineBreak = ETrue;
loggerSettings.iEventRanking = EFalse;
loggerSettings.iThreadId = EFalse;
loggerSettings.iHardwareFormat = CStifLogger::ETxt;
#ifndef FORCE_STIF_INTERNAL_LOGGING_TO_RDEBUG
loggerSettings.iEmulatorFormat = CStifLogger::EHtml;
loggerSettings.iHardwareOutput = CStifLogger::EFile;
loggerSettings.iEmulatorOutput = CStifLogger::EFile;
#else
RDebug::Print( _L( "STIF Test Server's thread logging forced to RDebug" ) );
loggerSettings.iEmulatorFormat = CStifLogger::ETxt;
loggerSettings.iHardwareOutput = CStifLogger::ERDebug;
loggerSettings.iEmulatorOutput = CStifLogger::ERDebug;
#endif
loggerSettings.iUnicode = EFalse;
loggerSettings.iAddTestCaseTitle = EFalse;
TRAP ( error, exec->iThreadLogger = CStifLogger::NewL( path, name,
loggerSettings ) );
RLibrary module; // Handle to test module library
TBool reusable = ETrue; // Is test module reusable?
TBool initialized = EFalse; // Is module initialized?
TBool signalFromSuspend = EFalse; // Send signal from suspend state?
RThread server;
// Duplicate handles from server thread
TInt ret = server.Open( moduleContainer->ServerThreadId() );
if( ret != KErrNone )
{
Panic( EThreadHandleOpenFail );
}
RSemaphore OperationStartSemaphore;
OperationStartSemaphore.SetHandle(
exec->ModuleContainer().OperationStartSemHandle() );
if( OperationStartSemaphore.Duplicate( server ) != KErrNone )
{
Panic( EDuplicateFail );
}
RSemaphore OperationChangeSemaphore;
OperationChangeSemaphore.SetHandle(
exec->ModuleContainer().OperationChangeSemHandle() );
if( OperationChangeSemaphore.Duplicate( server ) != KErrNone )
{
Panic( EDuplicateFail );
}
server.Close();
ret = KErrNone;
// The test module thread will stay in this loop until it either
// dies or is exited nicely.
while ( reusable )
{
// Thread is going to suspend
if ( signalFromSuspend )
{
signalFromSuspend = EFalse;
exec->TestComplete( ret );
}
ret = KErrNone;
// Wait next operation
OperationStartSemaphore.Wait();
// Get operation semaphore
OperationChangeSemaphore.Wait();
switch ( moduleContainer->OperationType() )
{
// Test module initialisation
case CTestModuleContainer::EInitializeModule:
{
__ASSERT_ALWAYS ( !initialized,
Panic( EReInitializingTestModule ) );
// Initialize module
if ( exec->InitializeModuleInThread( module ) == KErrNone )
{
initialized = ETrue;
}
signalFromSuspend = ETrue;
break;
}
// Test case enumeration
case CTestModuleContainer::EEnumerateInThread:
{
__ASSERT_ALWAYS ( initialized,
Panic( ETestModuleNotInitialized ) );
ret = exec->EnumerateInThread();
signalFromSuspend = ETrue;
break;
}
// Free test case enumeration data
case CTestModuleContainer::EFreeEnumerationData:
{
__ASSERT_ALWAYS ( initialized,
Panic( ETestModuleNotInitialized ) );
exec->FreeEnumerationDataInThread ();
signalFromSuspend = ETrue;
break;
}
// Execute test case
case CTestModuleContainer::EExecuteTestInThread:
{
__ASSERT_ALWAYS ( initialized,
Panic( ETestModuleNotInitialized ) );
ret = exec->ExecuteTestCaseInThread ();
signalFromSuspend = ETrue;
break;
}
// Exiting (i.e test server is unloading)
case CTestModuleContainer::EExit:
{
reusable = EFalse;
break;
}
// Illegal state
default:
{
Panic( EInvalidTestModuleOperation );
}
}
OperationChangeSemaphore.Signal();
}
OperationStartSemaphore.Close();
OperationChangeSemaphore.Close();
exec->DeleteTestModule();
// Close handle to module. No function calls to test
// module are possible after this line.
module.Close();
// Delete logger
delete exec->iThreadLogger;
exec->iThreadLogger = NULL;
// Delete clean-up stack.
delete tc;
tc = NULL;
// Operation completed ( = Exit completed )
exec->TestComplete( KErrNone );
delete exec;
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: Panic
Description: Panicing function for test thread.
Parameters: TPanicReason aReason: in: Reason code
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::Panic( TPanicReason aReason )
{
RDebug::Print( _L("CTestThreadContainer::Panic %d"), aReason );
User::Panic( _L("CTestThreadContainer::Panic"), aReason );
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ServerAlive
Description: Check that server is alive.
Parameters: None
Return Values: None
Errors/Exceptions: Panics thread if server has died.
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::IsServerAlive()
{
if( iServerThread.ExitType() != EExitPending )
{
// Server thread has died
__RDEBUG( ( _L( "Server died" ) ) );
Panic( EServerDied );
}
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: TestExecution
Description: Return CTestExecution handle to "parent" i.e. server.
Parameters: None
Return Values: CTestExecution&
Errors/Exceptions: Panics thread if server has died.
Status: Proposal
-------------------------------------------------------------------------------
*/
CTestExecution& CTestThreadContainer::TestExecution()
{
IsServerAlive();
CTestExecution* execution = iModuleContainer->TestExecution();
if( execution == NULL )
{
Panic( ENullExecution );
}
return *execution;
};
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: TestExecution
Description: Return CTestExecution handle to "parent" i.e. server.
Parameters: None
Return Values: CTestExecution&
Errors/Exceptions: Panics thread if server has died.
Status: Proposal
-------------------------------------------------------------------------------
*/
CTestModuleContainer& CTestThreadContainer::ModuleContainer()
{
IsServerAlive();
return *iModuleContainer;
};
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: ExceptionHandler
Description: Test execution thread exception handler
Just kill thread. Undertaker handles rest.
Parameters: TExcType: in: Exception type
Return Values: None
Errors/Exceptions: This function kills the thread where it is executed in.
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::ExceptionHandler ( TExcType aType )
{
// Kill the current thread, undertaker handles rest
RThread current;
current.Kill( aType );
// This line is never executed, because thread has been killed.
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: StifMacroErrorInit
Description: STIF TF's macro. Initialized TTestMacro.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::StifMacroErrorInit()
{
iTestMacroInfo.iIndication = EFalse;
iTestMacroInfo.iFileDes = KNullDesC;
iTestMacroInfo.iFunctionDes = KNullDesC;
iTestMacroInfo.iLine = 0;
iTestMacroInfo.iReceivedError = 0;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: StifMacroError
Description: STIF TF's macros. Saves information for later use.
Parameters: TInt aMacroType: in: Macro type(0:TL, 1:T1L, 2:T2L, etc.)
TDesC& aFile: in: Modified file information.
TDesC& aFunction: in: Modified function information.
TInt aLine: in: Line information.
TInt aResult: in: Received result.
TInt aExpected1: in: Expected result from user.
TInt aExpected2: in: Expected result from user.
TInt aExpected3: in: Expected result from user.
TInt aExpected4: in: Expected result from user.
TInt aExpected5: in: Expected result from user.
Return Values: Symbian OS error code
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::StifMacroError( TInt aMacroType,
const TText8* aFile,
const char* aFunction,
TInt aLine,
TInt aResult,
TInt aExpected1,
TInt aExpected2,
TInt aExpected3,
TInt aExpected4,
TInt aExpected5 )
{
TStifMacroDes file;
TStifMacroDes function;
// Modifies aFile and aFunction lengths if nesessarily.
// File and function maximun length is KStifMacroMax.
SetMacroInformation( KStifMacroMax, KStifMacroMax,
aFile, aFunction, file, function );
// Log more information to file and rdebug
switch( aMacroType )
{
case 0: // TL macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. FILE[%S], FUNCTION[%S], LINE[%d]" ),
&file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. FILE[%S], FUNCTION[%S], LINE[%d]" ),
&file, &function, aLine );
break;
}
case 1: // T1L macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, &file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, &file, &function, aLine );
break;
}
case 2: // T2L macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, &file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, &file, &function, aLine );
break;
}
case 3: // T3L macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, &file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, &file, &function, aLine );
break;
}
case 4: // T4L macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, aExpected4, &file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, aExpected4, &file, &function, aLine );
break;
}
case 5: // T5L macro
{
__TRACEI( KError, ( CStifLogger::ERed,
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, aExpected4, aExpected5, &file, &function, aLine ) );
RDebug::Print(
_L( "FAIL: STIF TF's macro. RECEIVED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], EXPECTED[%d], FILE[%S], FUNCTION[%S], LINE[%d]" ),
aResult, aExpected1, aExpected2, aExpected3, aExpected4, aExpected5, &file, &function, aLine );
break;
}
default: // default, faulty handling
{
__TRACEI( KError, ( CStifLogger::EError,
_L( "CTestThreadContainer::StifMacroError(): Macro faulty handling(Macro type is incorrect)" ) ) );
RDebug::Print(
_L( "ERROR: CTestThreadContainer::StifMacroError(): Macro faulty handling(Macro type is incorrect)" ) );
return KErrArgument; // Test case goes to crashed category
}
}
// Modifies aFile and aFunction lengths if nesessarily.
// File maximun length is KStifMacroMaxFile.
// Function maximun length is KStifMacroMaxFunction.
SetMacroInformation( KStifMacroMaxFile, KStifMacroMaxFunction,
aFile, aFunction, file, function );
// Set information for later use(this information is
// limited and can be seen in UI)
iTestMacroInfo.iIndication = ETrue;
iTestMacroInfo.iFileDes = file;
iTestMacroInfo.iFunctionDes = function;
iTestMacroInfo.iLine = aLine;
if( aResult == KErrNone )
{
// aResult is KErrNone. TL macro is used or expected result(s) are/is
// negative value(s). Received error code is mapped to KErrArgument
// because this is erronous case.
iTestMacroInfo.iReceivedError = KErrArgument;
}
else
{
iTestMacroInfo.iReceivedError = aResult;
}
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetMacroInformation
Description: Modifies aRecFile and aRecFunction lengths if nesessarily.
Parameters: TInt aMaxLength: in: Maximum length of file information.
TInt aMaxLength: in: Maximum length of function information.
const TText8* aRecFile: in: Received file information.
char* aRecFunction: in: Received function information.
TDes& aFile: inout: Modified file.
TDes& aFunction: inout: Modified function.
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::SetMacroInformation( TInt aFileMaxLength,
TInt aFuntionMaxLength,
const TText8* aRecFile,
const char* aRecFunction,
TDes& aFile,
TDes& aFunction )
{
// Create 8 to 16
TPtrC8 buf_file;
buf_file.Set( aRecFile );
// File description length is limited. Extracts the rightmost part of the
// data.
aFile.Copy( buf_file.Right( aFileMaxLength ) );
aFile.LowerCase();
if( aRecFunction )
{
// Create 8 to 16
TPtrC8 buf_func;
buf_func.Set( (const unsigned char*)aRecFunction );
// Function description length is limited. Extracts the leftmost part
// of the data.
aFunction.Copy( buf_func.Left( aFuntionMaxLength ) );
aFunction.LowerCase();
}
else
{
// Function is not given(WINS)
aFunction.Copy( _L( "-" ) );
}
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: AddInterferenceThread
Description: With this can be store information about test interference
thread to client space.
Parameters: RThread aSTIFTestInterference: in: Thread information to store
Return Values: TInt: Symbian OS error code.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::AddInterferenceThread(
RThread aSTIFTestInterference )
{
// Get access to test interference stuff
iInterferenceSem.Wait();
iInterferenceMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section
// and this verifies that iInterferenceSem and
// RequestComplete is done successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqInterference );
if( status == NULL )
{
iInterferenceMutex.Signal();
Panic( ENullRequest );
return KErrNone;
}
if( *status != KRequestPending )
{
// CInterferenceHandler::DoCancel called before getting here,
// just return
iInterferenceMutex.Signal();
return KErrNone;
}
// Add thread to Array. Via array can handle test interference thread's
// kill in panic etc. cases
TTestInterference& testInterface = TestExecution().TestInterference();
testInterface.iThreadId = aSTIFTestInterference.Id();
testInterface.iOperation = TTestInterference::EAppend;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
// Goes to CInterferenceHandler::RunL()
iInterferenceMutex.Signal();
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: RemoveInterferenceThread
Description: With this can be remove information about test interference
thread from client space.
Parameters: RThread aSTIFTestInterference: in: Thread information to store
Return Values: TInt: Symbian OS error code.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::RemoveInterferenceThread(
RThread aSTIFTestInterference )
{
// Get access to test interference stuff
iInterferenceSem.Wait();
iInterferenceMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section
// and this verifies that iInterferenceSem and
// RequestComplete is done successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqInterference );
if( status == NULL )
{
iInterferenceMutex.Signal();
Panic( ENullRequest );
return KErrNone;
}
if( *status != KRequestPending )
{
// CInterferenceHandler::DoCancel called before getting here, just return
iInterferenceMutex.Signal();
return KErrNone;
}
// Add thread to Array. Via array can handle test interference thread's
// kill in panic etc. cases
TTestInterference& testInterface = TestExecution().TestInterference();
testInterface.iThreadId = aSTIFTestInterference.Id();
testInterface.iOperation = TTestInterference::ERemove;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
// Goes to CInterferenceHandler::RunL()
iInterferenceMutex.Signal();
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: HandleMeasurementProcess
Description: With this can be stored information about test measurement
to TestServer space.
Parameters: CSTIFTestMeasurement::TMeasurement aSTIFMeasurementInfo: in:
Struct for measurement information.
Return Values: TInt: Symbian OS error code.
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestThreadContainer::HandleMeasurementProcess(
CSTIFTestMeasurement::TStifMeasurementStruct aSTIFMeasurementInfo )
{
// Get access to test measurement stuff
// This is syncronous operation and other request cannot executed at the
// same time. In this case iMeasurementSem is not signaled in StarL().
// So iMeasurementSem.Wait(); is not needed in this case.
iMeasurementMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section
// and this verifies that iMeasurementSem and
// RequestComplete is done successfully.
// Get status variable from server
TRequestStatus* status =
TestExecution().GetRq( CTestExecution::ERqMeasurement );
if( status == NULL )
{
iMeasurementMutex.Signal();
Panic( ENullRequest );
return KErrNone;
}
if( *status != KRequestPending )
{
// CMeasurementHandler::DoCancel called before getting here,
// just return
iMeasurementMutex.Signal();
return KErrNone;
}
TTestMeasurement& testmeasurement = TestExecution().TestMeasurement();
testmeasurement.iMeasurementStruct = aSTIFMeasurementInfo;
// Complete action to server
iServerThread.RequestComplete( status, KErrNone );
// Goes to CMeasurementHandler::RunL()
// Make this synchronous and block until needed operations are done.
iMeasurementSem.Wait();
// This continue here when iMeasurementSem.Signal is said in
// CMeasurementHandler::RunL(). So when measurement operations are done.
// Error code from measurement related operations
TInt ret( testmeasurement.iMeasurementStruct.iOperationResult );
iMeasurementMutex.Signal();
return ret;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetEventReq
Description: Sets asynchronous event request.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::DoNotifyCommand(TCommand aCommand, const TDesC8& aParamsPckg)
{
// Get access to command stuff
iCommandSem.Wait();
iCommandMutex.Wait(); // Take mutex to get access to server thread.
// Between Wait and Signal is critical section and this
// verifies that iCommandSem and RequestComplete is done
// successfully.
// Get status variable from server
TRequestStatus* status = TestExecution().GetRq(CTestExecution::ERqCommand);
if(status == NULL)
{
iCommandMutex.Signal();
Panic(ENullRequest);
return;
}
if(*status != KRequestPending)
{
iCommandMutex.Signal();
return;
}
// Fill in information
CCommandDef& aDef = TestExecution().CommandDef();
aDef.iCommand = aCommand;
aDef.iParamsPckg.Copy(aParamsPckg);
// Complete action to server
iServerThread.RequestComplete(status, KErrNone);
iCommandMutex.Signal();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: GetTestCaseTitleL
Description: Gets title of currently running test.
Parameters: aTestCaseTitle: OUT: test case title descriptor
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::GetTestCaseTitleL(TDes& aTestCaseTitle)
{
ModuleContainer().GetTestCaseTitleL(aTestCaseTitle);
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetThreadLogger
Description: Sets thread logger.
Parameters: CStifLogger* aThreadLogger: in: Pointer to thread logger.
Return Values: None
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
void CTestThreadContainer::SetThreadLogger( CStifLogger* aThreadLogger )
{
iThreadLogger = aThreadLogger;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: SetThreadLogger
Description: Gets thread logger.
Parameters: None
Return Values: Pointer to CStifLogger.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
CStifLogger* CTestThreadContainer::GetThreadLogger()
{
return iThreadLogger;
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: UITesting
Description: Gets information if testserver supports UI testing.
Parameters: None
Return Values: True if testserver supports UI testing, False if testserver
doesn't support UI testing.
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
EXPORT_C TBool CTestThreadContainer::UITesting()
{
return iModuleContainer->GetTestModule()->GetTestServer()->UiTesting();
}
/*
-------------------------------------------------------------------------------
Class: CTestThreadContainer
Method: GetUiEnvProxy
Description: Gets UIEnvProxy.
Parameters: None
Return Values: Pointer to UIEnvProxy
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
EXPORT_C CUiEnvProxy* CTestThreadContainer::GetUiEnvProxy()
{
return iModuleContainer->GetTestModule()->GetTestServer()->GetUiEnvProxy();
}
// End of File