/*
* 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 <stifinternal/TestThreadContainerRunnerFactory.h>
// EXTERNAL DATA STRUCTURES
// EXTERNAL FUNCTION PROTOTYPES
// CONSTANTS
// MACROS
// LOCAL CONSTANTS AND MACROS
// MODULE DATA STRUCTURES
// Struct to pass parameters to server thread
struct TThreadStartTestServer
{
TFileName iName; // Server name
RThread iServerThread; // The server thread
RSemaphore iStarted; // Startup syncronisation semaphore
TBool iInNewThread; // Is thread running in new process?
TInt iStartupResult;// Start-up result
TBool iUiTesting; // Is it testserver for UI testing
CTestThreadContainerRunnerFactory* iTestThreadContainerRunnerFactory; // Pointer to CTestThreadContainerRunner. Defined when
// iUiTesting is true
};
// LOCAL FUNCTION PROTOTYPES
// FORWARD DECLARATIONS
// ==================== LOCAL FUNCTIONS =======================================
// None
// ================= MEMBER FUNCTIONS =========================================
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: PanicServer
Description: Panics the server.
Parameters: const TTestServerPanic aPanic: in: Panic code
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestServer::PanicServer( const TTestServerPanic aPanic )
{
#ifdef USE_LOGGER
// Check if logger is available, if so, use it.
CStifLogger* log = (CStifLogger*) Dll::Tls();
if ( log )
{
log->Log( CStifLogger::ERed, _L("TestServer.DLL Panic %d"), aPanic);
}
#endif
RDebug::Print( _L( "CTestServer::PanicServer" ) );
_LIT( KTxtTestServer,"CTestServer" );
User::Panic( KTxtTestServer,aPanic );
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: NewL
Description: Returns new CTestServer object
Parameters: const TFileName& aName: in: Server name
Return Values: None
Errors/Exceptions: Leaves if memory allocation, ConstructL or StartL leaves.
Status: Approved
-------------------------------------------------------------------------------
*/
CTestServer* CTestServer::NewL( const TFileName& aName )
{
CTestServer* self = new( ELeave ) CTestServer();
CleanupStack::PushL( self );
// Construct the server
self->ConstructL( aName );
// Start the server
self->StartL( aName );
CleanupStack::Pop( self );
return self;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: ConstructL
Description: Second level constructor. Obtains pointer to library
entrypoint.
Parameters: const TFileName& aName: in: Server name
Return Values: None
Errors/Exceptions: Leaves if entrypoint can't be obtained.
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestServer::ConstructL( const TFileName& aName )
{
// Construct heap buffer for configuration file
iModuleNameBuffer = HBufC::NewL( aName.Length() );
iModuleName.Set ( iModuleNameBuffer->Des() );
iModuleName.Copy ( aName );
iContainerIndex = CObjectConIx::NewL();
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: CTestServer
Description: Constructor.
Initialises non-zero member variables and base class with correct
priority.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CTestServer::CTestServer() : CServer2( CTestServer::ETestServerPriority ),
iModuleName(0, 0),
iSessionCount( 0 )
{
iFirstTime = ETrue;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: ~CTestServer
Description: Destructor
Frees memory.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CTestServer::~CTestServer()
{
delete iModuleNameBuffer;
iModuleNameBuffer = NULL;
delete iContainerIndex;
iContainerIndex = NULL;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: NewContainerL
Description: Returns new container. Used to store subsessions
Parameters: None
Return Values: CObjectCon* New object container
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CObjectCon* CTestServer::NewContainerL()
{
CObjectCon* container = iContainerIndex->CreateL();
iSessionCount++;
return container;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: DeleteContainer
Description: Deletes a container.
Parameters: CObjectCon* aContainer: in: Container to be removed
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestServer::DeleteContainer( CObjectCon* aContainer )
{
iContainerIndex->Remove( aContainer );
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: SessionClosed
Description: Inform Server that session is closed.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestServer::SessionClosed()
{
// Decrease session count
iSessionCount--;
// Check if last session is closed
if ( iSessionCount <= 0 )
{
// Stop the active scheduler
// Execution will continue in ThreadFunction()
CActiveScheduler::Stop();
}
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: NewSessionL
Description: Returns new session.
Parameters: const TVersion &aVersion: in: Version required
Return Values: CSharableSession* New session
Errors/Exceptions: Leaves if invalid version or CTestModule construction
leaves
Status: Approved
-------------------------------------------------------------------------------
*/
CSession2* CTestServer::NewSessionL( const TVersion& aVersion,
const RMessage2& /*aMessage*/ ) const
{
// check version is ok
TVersion v( KTestServerMajorVersionNumber,
KTestServerMinorVersionNumber,
KTestServerBuildVersionNumber
);
if( !User::QueryVersionSupported( v,aVersion ) )
{
User::Leave( KErrNotSupported );
}
return CTestModule::NewL( ( CTestServer* ) this );
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: ModuleName
Description: Returns module name
Parameters: None
Return Values: const TDesC&* Module name
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
const TDesC& CTestServer::ModuleName() const
{
return iModuleName;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: FirstTime
Description: Is module already once initialised.
Parameters: None
Return Values: TBool Has module initialized?
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
//@spe const TBool CTestServer::FirstTime() const
TBool CTestServer::FirstTime() const
{
return iFirstTime;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: ClearFirstTime
Description: Clear module first time flag.
Parameters: None
Return Values: None
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
void CTestServer::ClearFirstTime()
{
iFirstTime = EFalse;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: ThreadFunction
Description: The thread function, where Test Server lives in
Parameters: TAny* aStarted: in: Start-up information
Return Values: TInt Result from test module
Errors/Exceptions: Clean-up stack can't be created because cannot
leave, error checks are done locally.
Panics if:
invalid start-up information
Test Server can't be started
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestServer::ThreadFunction( TAny* aStarted )
{
__UHEAP_MARK;
TInt error( KErrNone );
// Get start-up information
TThreadStartTestServer* startInfo = ( TThreadStartTestServer* ) aStarted;
__ASSERT_ALWAYS( startInfo,PanicServer( ENoStartupInformation ) );
// Create clean-up stack
CTrapCleanup* tc = CTrapCleanup::New();
__ASSERT_ALWAYS( tc, PanicServer(ECreateTrapCleanup));
// Construct the logger
TName path = _L("C:\\logs\\testframework\\testserver\\");
TFileName name = _L("testserver_");
name.Append ( startInfo->iName );
// 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 logging forced to RDebug" ) );
loggerSettings.iEmulatorFormat = CStifLogger::ETxt;
loggerSettings.iHardwareOutput = CStifLogger::ERDebug;
loggerSettings.iEmulatorOutput = CStifLogger::ERDebug;
#endif
loggerSettings.iUnicode = EFalse;
loggerSettings.iAddTestCaseTitle = EFalse;
CStifLogger* logger = NULL;
TRAP ( error, logger = CStifLogger::NewL( path, name, loggerSettings ) );
// Thread Local Storage is used for get pointer to logger.
Dll::SetTls ( logger );
__TRACE( KInit,( _L( "TestServer.DLL server starting" ) ) );
__TRACE( KInit,( CStifLogger::EBold, _L( "Loading module: %S"), &startInfo->iName ) );
RLibrary module;
TInt ret = KErrNone;
TFileName newNameBuffer;
TInt check = CheckModuleName( startInfo->iName, newNameBuffer );
if( check == KErrNone )
{
// Load the module(TestScripter)
ret = module.Load( newNameBuffer );
}
else
{
RemoveOptionalIndex(startInfo->iName, newNameBuffer);
__TRACE(KInit, (CStifLogger::EBold, _L( "Valid module name is [%S] (extracted from [%S])"), &newNameBuffer, &startInfo->iName));
// Load the module(Others)
ret = module.Load(newNameBuffer);
}
// If test module loading fails, do not start server
if( ret != KErrNone )
{
__TRACE( KError,( CStifLogger::ERed, _L( "Test module loading failed, code = %d" ), ret ) );
__TRACE( KError,( _L( "Check that module is compiled properly and stored to correct directory and all DLLs that it requires are available" ) ) );
// Error will be handled in StartNewServer
startInfo->iStartupResult = ret;
startInfo->iStarted.Signal();
if ( !startInfo->iInNewThread )
{
startInfo->iStarted.Close();
}
module.Close();
Dll::FreeTls();
// Delete logger
delete logger;
logger = NULL;
// Delete clean-up stack
delete tc;
tc = NULL;
__UHEAP_MARKEND;
return ret;
}
else
{
__TRACE( KInit,( _L( "Test module loaded correctly" ) ) );
}
// Verify that there is function
CTestInterfaceFactory libEntry = ( CTestInterfaceFactory ) module.Lookup( 1 );
if( libEntry == NULL )
{
// Error will be handled in StartNewServer
__TRACE( KError,( CStifLogger::ERed, _L( "Can't find entrypoint from test module" ) ) );
startInfo->iStartupResult = KErrNotFound;
startInfo->iStarted.Signal();
if ( !startInfo->iInNewThread )
{
startInfo->iStarted.Close();
}
module.Close();
Dll::FreeTls();
// Delete logger
delete logger;
logger = NULL;
// Delete clean-up stack
delete tc;
tc = NULL;
__UHEAP_MARKEND;
return KErrNotFound;
}
module.Close();
// Construct and install active scheduler
CActiveScheduler* scheduler = new CActiveScheduler;
__ASSERT_ALWAYS( scheduler, PanicServer( EMainSchedulerError ) );
CActiveScheduler::Install( scheduler );
// Construct server
CTestServer* server = NULL;
TRAPD( err, server = CTestServer::NewL( startInfo->iName ) );
__ASSERT_ALWAYS( !err, PanicServer( ESvrCreateServer ) );
server->iUiTesting = startInfo->iUiTesting;
server->iTestThreadContainerRunnerFactory = startInfo->iTestThreadContainerRunnerFactory;
// Inform that we are up and running
startInfo->iStartupResult = KErrNone;
startInfo->iStarted.Signal();
if ( !startInfo->iInNewThread )
{
startInfo->iStarted.Close();
}
// Start handling requests
CActiveScheduler::Start();
// Execution continues from here after CActiveScheduler::Stop
__TRACE( KVerbose,( _L( "TestServer.DLL active scheduler stopped" ) ) );
// Delete the server
delete server;
server = NULL;
__TRACE( KVerbose,( _L( "TestServer.DLL server object deleted" ) ) );
delete scheduler;
scheduler = NULL;
__TRACE( KVerbose,( _L( "Active scheduler deleted" ) ) );
__TRACE ( KInit, (_L("TestServer.DLL ThreadFunction exiting, server closing")) );
Dll::FreeTls();
// Delete logger
delete logger;
logger = NULL;
// Delete clean-up stack
delete tc;
tc = NULL;
__UHEAP_MARKEND;
return KErrNone;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: GetServerThreadId
Description: Returns server thread id
Parameters: None
Return Values: TInt : thread id
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
TInt CTestServer::GetServerThreadId()
{
RThread thread;
return thread.Id();
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: GetTestThreadContainerRunnerFactory
Description: Returns server thread id
Parameters: None
Return Values: TInt : thread id
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CTestThreadContainerRunnerFactory* CTestServer::GetTestThreadContainerRunnerFactory()
{
return iTestThreadContainerRunnerFactory;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: UiTesting
Description: Gets information if testserver supports UI testing
Parameters: None
Return Values: True if testserver supports UI testing, False if not.
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
TBool CTestServer::UiTesting()
{
return iUiTesting;
}
/*
-------------------------------------------------------------------------------
Class: CTestServer
Method: GetUiEnvProxy
Description: Gets UIEnvProxy
Parameters: None
Return Values: Pointer to UIEnvProxy.
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
CUiEnvProxy* CTestServer::GetUiEnvProxy()
{
return iTestThreadContainerRunnerFactory->GetUiEnvProxy();
}
// ================= OTHER EXPORTED FUNCTIONS =================================
/*
-------------------------------------------------------------------------------
Class: -
Method: StartNewServer
Description: Starts a new server. Server will be running its own
thread and this functions returns when server is up and running or
server start-up fails.
Parameters: const TFileName& aModuleFileName: in: Module name
TFileName& aServerName: in: Server name
const TBool aInNewThread: in: Is new thread
RSemaphore aSynchronisation: in: For synchronisation
TBool aUiTestingServer: in: Indicates if testserver should support UI testing
CTestThreadContainerRunnerFactory* aTestThreadContainerRunnerFactory: in: Pointer to runner factory
Return Values: TInt: Symbian error code
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
EXPORT_C TInt StartNewServer( const TFileName& aModuleFileName,
TFileName& aServerName,
const TBool aInNewThread,
RSemaphore aSynchronisation,
TBool aUiTestingServer,
CTestThreadContainerRunnerFactory* aTestThreadContainerRunnerFactory
)
{
__UHEAP_MARK;
//Check server not already started
TFindServer findServer( aModuleFileName );
TFullName name;
if( findServer.Next( name ) == KErrNone )
{
// Server already started, nothing to do
aServerName = aModuleFileName;
__UHEAP_MARKEND;
return KErrAlreadyExists;
}
// Construct start-up information object
TThreadStartTestServer* startInfo = new TThreadStartTestServer();
if( startInfo == NULL )
{
__UHEAP_MARKEND;
return KErrNoMemory;
}
// Fill the start-up information
startInfo->iName = aModuleFileName;
startInfo->iStartupResult = KErrNone;
startInfo->iStarted = aSynchronisation;
startInfo->iInNewThread = aInNewThread;
startInfo->iUiTesting = aUiTestingServer;
startInfo->iTestThreadContainerRunnerFactory = aTestThreadContainerRunnerFactory;
// EKA1
if ( aInNewThread )
{
// Create thread
TInt res = startInfo->iServerThread.Create(
startInfo->iName , // Name of thread
CTestServer::ThreadFunction, // Thread function
KDefaultStackSize, // Stack size
KDefaultHeapSize, // Heap initial size
KMaxHeapSize, // Heap start max size
startInfo // Parameter to thread function
);
// If thread creation failed
if( res != KErrNone )
{
startInfo->iStarted.Close(); // Close semaphore
delete startInfo;
startInfo = NULL;
__UHEAP_MARKEND;
return res;
}
// Now start thread
startInfo->iServerThread.SetPriority( EPriorityMuchMore );
startInfo->iServerThread.Resume();
// Wait until the thread is started
startInfo->iStarted.Wait();
// Server is started( or it has returned error )
}
// EKA2 and EKA1's HW
else
{
// Call directly thread function, this starts server and
// blocks this thread.
// Priority is default, not known reason why should be greater
// than default priority (Work over a year).
CTestServer::ThreadFunction( startInfo );
}
// Set server name
aServerName = aModuleFileName;
// Free memory
TInt r = startInfo->iStartupResult;
startInfo->iServerThread.Close();
startInfo->iStarted.Close();
delete startInfo;
startInfo = NULL;
__UHEAP_MARKEND;
// Return start-up result.
return r;
}
/*
-------------------------------------------------------------------------------
Class: -
Method: StartNewServer
Description: Starts a new server. Server will be running its own
thread and this functions returns when server is up and running or
server start-up fails.
Parameters: const TFileName& aModuleFileName: in: Module name
TFileName& aServerName: in: Server name
const TBool aInNewThread: in: Is new thread
RSemaphore aSynchronisation: in: For synchronisation
Return Values: TInt: Symbian error code
Errors/Exceptions: None
Status: Approved
-------------------------------------------------------------------------------
*/
EXPORT_C TInt StartNewServer( const TFileName& aModuleFileName,
TFileName& aServerName,
const TBool aInNewThread,
RSemaphore aSynchronisation
)
{
return StartNewServer( aModuleFileName, aServerName, aInNewThread, aSynchronisation, false, NULL );
}
/*
-------------------------------------------------------------------------------
Class: -
Method: StartNewServer
Description: Starts a new server. Server will be running its own
thread and this functions returns when server is up and running or
server start-up fails.
Parameters: const TFileName& aName: in: Module name to be used
TFileName& aServerName: out: The name of the server
Return Values: TInt Error code / KErrNone
Errors/Exceptions: None
Status: Proposal
-------------------------------------------------------------------------------
*/
EXPORT_C TInt StartNewServer( const TFileName& aModuleFileName,
TFileName& aServerName
)
{
__UHEAP_MARK;
RMutex startupMutex;
TInt ret( KErrNone );
// Global mutex already created(see CTestEngineServer::ThreadFunction).
// Open global mutex.
ret = startupMutex.OpenGlobal( KStifTestServerStartupMutex );
if( ret != KErrNone )
{
// Not able to open mutex
return ret;
}
startupMutex.Wait();
//Check server not already started
TFindServer findServer( aModuleFileName );
TFullName name;
if( findServer.Next( name ) == KErrNone )
{
// Server already started, nothing to do
aServerName = aModuleFileName;
// release startupmutex
startupMutex.Signal();
startupMutex.Close();
__UHEAP_MARKEND;
return KErrAlreadyExists;
}
RSemaphore startupSemaphore;
startupSemaphore.CreateLocal( 0 );
// Start server in new thread
TInt r = StartNewServer ( aModuleFileName, aServerName, ETrue, startupSemaphore );
// startupSemaphore will be closed inside StartNewServer when start-up is done.
// release startupmutex
startupMutex.Signal();
startupMutex.Close();
__UHEAP_MARKEND;
// Return start-up result.
return r;
}
// End of File