testexecfw/symbianunittestfw/sutfw/sutfwcore/sutfwframework/src/symbianunittest.cpp
author Johnson Ma <johnson.ma@nokia.com>
Mon, 29 Mar 2010 14:46:27 +0800
changeset 1 bbd31066657e
parent 0 3e07fef1e154
permissions -rw-r--r--
publish symbianunittest v1.1.0

/*
* 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:  
*
*/

#include "symbianunittestresult.h"
#include "symbianunittestobserver.h"
#include "sutlogger.h"
#include <symbianunittest.h>
#include <e32math.h>
#include <utf.h>
#include <e32debug.h>

// Assertion failure message formats:
_LIT8( KIntsNotEqualFormat, "Asserted: expected=%d, actual=%d" );
_LIT8( KDesCsNotEqualFormat, "Asserted: expected='%S', actual='%S'" );
_LIT8( KAssertLeaveFormat1, "'%S' expected to leave: expected=%d, actual=%d" );
_LIT8( KAssertLeaveFormat2, "'%S' expected to leave but did not leave" );

const TInt KMaxSizeOfTwoIntsAsText = 80;
const TInt KErrSymbianUnitTestAssertionFailed = -99999999;
_LIT( KTestThreadName, "SymbianUnitTestThread" );
const TInt KTestThreadMaxHeapSize = 0x400000; // 4 MB
_LIT8( KDoubleColon8, "::" );
_LIT( KDoubleColon16, "::" );

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::ConstructL( const TDesC8& aName )
    {    
    TInt doubleColonPos( aName.FindF( KDoubleColon8 ) );
    TPtrC8 classNamePtr( aName );
    if ( doubleColonPos > 0 )
        {
        classNamePtr.Set( aName.Left( doubleColonPos ) );
        }
    
    iName = CnvUtfConverter::ConvertToUnicodeFromUtf8L( classNamePtr ); 
    
    SUT_LOG_FORMAT(_L("enter CSymbianUnitTest::ConstructL -- aName : [%S]"), iName );
    
    // unify the same name for armv5 & winscw. 
    // on winscw, the function name is abc, 
    // on armv5,  the function name is void abc. 
    // unify to abc. 
    TChar chSpace( ' ' );
    TChar chTab( '\t' );
    TChar chAsterisk( '*' );
    TChar chAnd( '&' );
    TBuf<100> strTmp( *iName ); 
    strTmp.Trim();
    TInt pos;
    
    // "char * & abc" => abc
    pos = strTmp.LocateReverse( chAnd );
    if( KErrNotFound != pos ) 
    	{
    	TPtrC ptr = strTmp.Right( strTmp.Length() - pos - 1 );
    	strTmp = ptr;
    	strTmp.Trim();
    	}
    // "void * abc" => abc
    pos = strTmp.LocateReverse( chAsterisk );
    if( KErrNotFound != pos ) 
    	{
    	TPtrC ptr = strTmp.Right( strTmp.Length() - pos - 1 );
    	strTmp = ptr;
    	strTmp.Trim();
    	}
    // "void abc"
    pos = strTmp.LocateReverse( chSpace );
    if( KErrNotFound != pos ) 
    	{
    	TPtrC ptr = strTmp.Right( strTmp.Length() - pos - 1 );
    	strTmp = ptr;
    	strTmp.Trim();
    	}
    // "void 	abc"
    pos = strTmp.LocateReverse( chTab );
    if( KErrNotFound != pos ) 
    	{
    	TPtrC ptr = strTmp.Right( strTmp.Length() - pos - 1 );
    	strTmp = ptr;
    	strTmp.Trim();
    	}
    	
    *iName = strTmp;
    
    SUT_LOG_FORMAT(_L("exit CSymbianUnitTest::ConstructL -- aName : [%S]"), iName );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C CSymbianUnitTest::CSymbianUnitTest()
 : iAllocFailureType( RHeap::ENone ),
   iAllocFailureRate( 0 )
    {
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C CSymbianUnitTest::~CSymbianUnitTest() 
    { 
    delete iName;
    iTestCases.ResetAndDestroy();
    }

// -----------------------------------------------------------------------------
// From MSymbianUnitTestInterface
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::ExecuteL(
    MSymbianUnitTestObserver& aObserver,
    CSymbianUnitTestResult& aResult,
    MSymbianUnitTestInterface::TFailureSimulation aFailureSimulation,
    const CDesCArray& aTestCaseNames,
    TInt aTimeout )
    {
    if ( aFailureSimulation == EMemAllocFailureSimulation )
        {
        iAllocFailureType = RHeap::EDeterministic;
        }
    else
        {
        iAllocFailureType = RHeap::ENone;
        }      
    SUT_LOG_FORMAT(_L("start testing, total test cases[%d]"), iTestCases.Count());
    //print the test cases name in the log
    for ( TInt i = iTestCases.Count() -1; i >=0 ; i-- )
        {
        CSymbianUnitTestCase& testCase = *( iTestCases[ i ] );
	//check the specified test case list if any
	if ( aTestCaseNames.Count() > 0 )
	    {
	    TInt index=0;
	    TInt ret = aTestCaseNames.Find(testCase.Name(), index );
	    if (ret != 0)
	        {
		//the case isn't in the specified test case names, skip it
                SUT_LOG_FORMAT(_L("skip test case[%S]"), &testCase.Name());
		delete iTestCases[i];
                iTestCases.Remove(i);
                continue;
	        }
	    }
	SUT_LOG_FORMAT(_L("TestCase[%S]"), &testCase.Name());
        }

    for ( TInt i = 0; i < iTestCases.Count(); i++ )
        {
        CSymbianUnitTestCase& testCase = *( iTestCases[ i ] );

        aResult.StartTestL( testCase.Name() );
        ExecuteTestCaseInThreadL( testCase, aResult, aTimeout );
        aResult.EndTestL();
        aObserver.IncrementExecutedTestsCount();        
        }
    SUT_LOG_FORMAT(_L("testing finished, total passed test cases[%d]"), aResult.PassedTestCount());
    }

// -----------------------------------------------------------------------------
// From MSymbianUnitTestInterface
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CSymbianUnitTest::TestCaseCount()
    {
    return iTestCases.Count();
    }

// -----------------------------------------------------------------------------
// From MSymbianUnitTestInterface
// -----------------------------------------------------------------------------
//
EXPORT_C const TDesC& CSymbianUnitTest::Name() const
    {
    if ( iName )
        {
        return *iName;
        }
    return KNullDesC;
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::SetupL()
    {
    // The default implementation is no operation
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::Teardown()
    {
    // The default implementation is no operation
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CSymbianUnitTest::IsMemoryAllocationFailureSimulationUsed() const
    {
    return ( iAllocFailureType == RHeap::EDeterministic );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AddTestCaseL( 
    const TDesC& aName,
    FunctionPtr aSetupFunction,
    FunctionPtr aTestFunction,
    FunctionPtr aTeardownFunction )
    {
    const TInt KTestCaseNameLength = 
        Name().Length() + KDoubleColon16().Length() + aName.Length(); 
    HBufC* name = HBufC::NewLC( KTestCaseNameLength );
    name->Des().Append( Name() );
    name->Des().Append( KDoubleColon16 );
    name->Des().Append( aName ); 
    CSymbianUnitTestCase* testCase = 
        CSymbianUnitTestCase::NewL( 
            *name, aSetupFunction, aTestFunction, aTeardownFunction );
    CleanupStack::PopAndDestroy( name );
    CleanupStack::PushL( testCase );
    iTestCases.AppendL( testCase );
    CleanupStack::Pop( testCase );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    TInt aExpectedValue, 
    TInt aActualValue, 
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    if ( aExpectedValue != aActualValue )
        {
        StopAllocFailureSimulation();
        HBufC8* msg = HBufC8::NewLC( 
            KIntsNotEqualFormat().Size() + KMaxSizeOfTwoIntsAsText );
        msg->Des().Format( KIntsNotEqualFormat, aExpectedValue, aActualValue );
        AssertionFailedL( *msg, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    const TDesC8& aExpectedValue,
    const TDesC8& aActualValue,
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    if ( aExpectedValue.Compare( aActualValue ) != 0 )
        {
        StopAllocFailureSimulation();
        TInt size = 
            KDesCsNotEqualFormat().Size() + 
            aExpectedValue.Size() + 
            aActualValue.Size();
        HBufC8 *msg = HBufC8::NewLC( size );
        msg->Des().Format( KDesCsNotEqualFormat, 
                           &aExpectedValue, 
                           &aActualValue );
        AssertionFailedL( *msg, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    const TDesC16& aExpectedValue,
    const TDesC16& aActualValue,
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    if ( aExpectedValue.Compare( aActualValue ) != 0 )
        {
        StopAllocFailureSimulation();
        HBufC8* msg = NotEqualsMessageLC( aExpectedValue, aActualValue );
        AssertionFailedL( *msg, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertLeaveL(
    const TDesC8& aStatement,
    TInt aActualLeaveCode,
    TInt aExpectedLeaveCode,
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    if ( aActualLeaveCode == KErrNoMemory &&
         aExpectedLeaveCode != KErrNoMemory &&
         iAllocFailureType == RHeap::EDeterministic )
        {
        User::Leave( KErrNoMemory );
        }   
    if ( aActualLeaveCode != aExpectedLeaveCode )
        {
        StopAllocFailureSimulation();
        HBufC8* msg = HBufC8::NewLC( 
            KAssertLeaveFormat1().Size() + 
            aStatement.Size() + 
            KMaxSizeOfTwoIntsAsText );
        msg->Des().Format( KAssertLeaveFormat2, &aStatement, 
                           aExpectedLeaveCode, aActualLeaveCode );        
        AssertionFailedL( *msg, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::RecordNoLeaveFromStatementL(
    const TDesC8& aStatement,
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    StopAllocFailureSimulation();
    HBufC8* msg = 
        HBufC8::NewLC( KAssertLeaveFormat1().Size() + aStatement.Size() );
    msg->Des().Format( KAssertLeaveFormat1, &aStatement );        
    AssertionFailedL( *msg, aLineNumber, aFileName );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertionFailedL(
    const TDesC8& aFailureMessage,
    TInt aLineNumber,
    const TDesC8& aFileName )
    {
    TInt dummy( 0 );
    TInt heapCellsBeforeAddingTheFailure( User::Heap().AllocSize( dummy ) );
    
    if ( iTestResult )
        {
        User::LeaveIfError( iTestResult->AddAssertFailure( 
            aFailureMessage, aLineNumber, aFileName ) );
        }
        
    TInt heapCellsAfterAddingTheFailure( User::Heap().AllocSize( dummy ) );
    iHeapCellsReservedByAssertFailure = 
        heapCellsAfterAddingTheFailure - heapCellsBeforeAddingTheFailure;
    User::Leave( KErrSymbianUnitTestAssertionFailed );
    }


// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    TInt aExpectedValue, 
    TInt aActualValue, 
    TInt aLineNumber,
    const TDesC8& aFileName,
    const TDesC8& aFailureMessage )
    {
    if ( aExpectedValue != aActualValue )
        {
        StopAllocFailureSimulation();
        AssertionFailedL( aFailureMessage, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    const TDesC8& aExpectedValue,
    const TDesC8& aActualValue,
    TInt aLineNumber,
    const TDesC8& aFileName,
    const TDesC8& aFailureMessage )
    {
    if ( aExpectedValue.Compare( aActualValue ) != 0 )
        {
        StopAllocFailureSimulation();
        AssertionFailedL( aFailureMessage, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CSymbianUnitTest::AssertEqualsL(
    const TDesC16& aExpectedValue,
    const TDesC16& aActualValue,
    TInt aLineNumber,
    const TDesC8& aFileName,
    const TDesC8& aFailureMessage )
    {
    if ( aExpectedValue.Compare( aActualValue ) != 0 )
        {
        StopAllocFailureSimulation();
        AssertionFailedL( aFailureMessage, aLineNumber, aFileName );
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
CSymbianUnitTest::CSymbianUnitTestCase* 
CSymbianUnitTest::CSymbianUnitTestCase::NewL( 
    const TDesC& aName,
    FunctionPtr aSetupFunction,
    FunctionPtr aTestFunction,
    FunctionPtr aTeardownFunction )
    {
    CSymbianUnitTestCase* self = 
        new( ELeave )CSymbianUnitTestCase( 
            aSetupFunction, aTestFunction, aTeardownFunction );
    CleanupStack::PushL( self );
    self->ConstructL( aName );
    CleanupStack::Pop( self );
    return self;    
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
CSymbianUnitTest::CSymbianUnitTestCase::CSymbianUnitTestCase( 
    FunctionPtr aSetupFunction,
    FunctionPtr aTestFunction,
    FunctionPtr aTeardownFunction ) :
    iSetupFunction( aSetupFunction ),
    iTestFunction( aTestFunction ),
    iTeardownFunction( aTeardownFunction )
    {
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::CSymbianUnitTestCase::ConstructL( const TDesC& aName )
    {
    iName = aName.AllocL();
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
CSymbianUnitTest::CSymbianUnitTestCase::~CSymbianUnitTestCase()
    {
    delete iName;
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
const TDesC& CSymbianUnitTest::CSymbianUnitTestCase::Name() const
    {
    return *iName;
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
HBufC8* CSymbianUnitTest::NotEqualsMessageLC(
    const TDesC16& aExpectedValue,
    const TDesC16& aActualValue )
    {
    TInt length = 
        KDesCsNotEqualFormat().Length() + 
        aExpectedValue.Length() + 
        aActualValue.Length();
    HBufC8* msg = HBufC8::NewLC( length );

    HBufC8* expected = 
        CnvUtfConverter::ConvertFromUnicodeToUtf8L( aExpectedValue );
    CleanupStack::PushL( expected );

    HBufC8* actual = CnvUtfConverter::ConvertFromUnicodeToUtf8L( aActualValue );
    CleanupStack::PushL( actual );

    msg->Des().Format( KDesCsNotEqualFormat, expected, actual );

    CleanupStack::PopAndDestroy( actual );
    CleanupStack::PopAndDestroy( expected );
    
    return msg;
    }
    
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::ExecuteTestCaseInThreadL(
    CSymbianUnitTestCase& aTestCase,
    CSymbianUnitTestResult& aResult,
    TInt aTimeout )
    {
    iCurrentTestCase = &aTestCase;
    iTestResult = &aResult;

    //create exec thread
    TName threadName( KTestThreadName );
    // Append a random number to make the name unique
    const TInt KThreadIdWidth = 10;
    threadName.AppendNumFixedWidthUC( Math::Random(), EHex, KThreadIdWidth );
    RThread execThread;
    TInt err = execThread.Create( threadName,
                              TestThreadEntryFunction,
                              KDefaultStackSize,
                              KMinHeapSize,
                              KTestThreadMaxHeapSize,
                              this );
    User::LeaveIfError( err );
    CleanupClosePushL( execThread );
    

    //start exec thread
    TRequestStatus status;
    execThread.Logon( status );
    execThread.Resume();
    
    TBool timedOut = EFalse;
    if (aTimeout > 0 )
        {
	SUT_LOG_DEBUGF(_L("run test case with timeout %d"), aTimeout);
	//run test case with timeout control
        TRequestStatus waitStatus = KRequestPending;
        RTimer timer;
        User::LeaveIfError(timer.CreateLocal());
        CleanupClosePushL(timer);
        timer.After(waitStatus, aTimeout*1000000);
        User::WaitForRequest( status, waitStatus );
        if (waitStatus.Int() == KRequestPending)
            {
            timer.Cancel();
            }

        if (status.Int() == KRequestPending)
            {
	    //test case did not complete in time
	    //terminate the exec thread
	    SUT_LOG_DEBUG(" test case timed out, kill the exec thread");
	    timedOut = ETrue;
            execThread.Kill(KErrTimedOut);
	    aResult.AddTimeOutErrorL( aTimeout );
            } 
        CleanupStack::PopAndDestroy( &timer );
	}
    else 
        {
	SUT_LOG_DEBUG(" run test case without timeout");
	//exec test case without timeout control
        User::WaitForRequest(status);
        }
    if (status.Int() != KErrNone && !timedOut)
        {
        SUT_LOG_DEBUG("testcase exec thread panic");
        aResult.AddPanicInfoL( 
        execThread.ExitCategory(), 
        execThread.ExitReason(), 
        iAllocFailureRate );
        }
    CleanupStack::PopAndDestroy( &execThread );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
TInt CSymbianUnitTest::TestThreadEntryFunction( TAny* aPtr )
    {
    CSymbianUnitTest* self = reinterpret_cast< CSymbianUnitTest* >( aPtr );
    TInt err = KErrNoMemory;
    CTrapCleanup* cleanupStack = CTrapCleanup::New();
    if ( cleanupStack )
        {
        // Operator new used without ELeave on purpose to avoid using TRAP.
        CActiveScheduler* scheduler = new CActiveScheduler;
        if ( scheduler )
            {
            CActiveScheduler::Install( scheduler );
            TRAP( err, self->ExecuteTestCaseL() )
            delete scheduler;
            }
        }
    delete cleanupStack;
    return err;
    }    

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::ExecuteTestCaseL()
    {    
    __ASSERT_ALWAYS( iTestResult, User::Leave( KErrNotFound ) );
    __ASSERT_ALWAYS( iCurrentTestCase, User::Leave( KErrNotFound ) );
    iAllocFailureRate = 0;
    iLeakedMemory = 0;
    TInt leaveCodeFromTest( KErrNoMemory );
    if ( iAllocFailureType == RHeap::EDeterministic )
        {
        TUint counter( 1 );
        while ( leaveCodeFromTest == KErrNoMemory )
            {
            iAllocFailureRate = counter;
            DoExecuteTestCaseL( leaveCodeFromTest );
            counter++;
            }
        }
    else
        {
        DoExecuteTestCaseL( leaveCodeFromTest );
        }
    // Add the possible failure or memory leak to the results
    if ( leaveCodeFromTest == KErrNone )
        {
        if ( iLeakedMemory > 0 )
            {
            iTestResult->AddMemoryLeakInfoL( iLeakedMemory, iAllocFailureRate );
            }     
        }
    else if ( leaveCodeFromTest != KErrSymbianUnitTestAssertionFailed )
        {
        iTestResult->AddLeaveFromTestL( leaveCodeFromTest, iAllocFailureRate );
        }
    else
        {
        // No operation here. 
        // Assertion failure has happened and it has been added to the results.
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::DoExecuteTestCaseL( TInt& aLeaveCodeFromTest )
    {
    __ASSERT_ALWAYS( iTestResult, User::Leave( KErrNotFound ) );
    __ASSERT_ALWAYS( iCurrentTestCase, User::Leave( KErrNotFound ) );
    
    aLeaveCodeFromTest = KErrNone;
    iHeapCellsReservedByAssertFailure = 0;
    TInt memoryBeforeTest( 0 ); 
    TInt heapCellsBeforeTest( User::Heap().AllocSize( memoryBeforeTest ) );
    
    TRAPD( err, ( this->*iCurrentTestCase->iSetupFunction )() )
    if ( err != KErrNone )
        {       
        ( this->*iCurrentTestCase->iTeardownFunction )();
        iTestResult->AddSetupErrorL( err );
        return;
        }
    
    StartAllocFailureSimulation();
    TRAP( aLeaveCodeFromTest, ( this->*iCurrentTestCase->iTestFunction )() );
    StopAllocFailureSimulation();
       
    ( this->*iCurrentTestCase->iTeardownFunction )();

    TInt memoryAfterTest( 0 );
    TInt heapCellsAfterTest( User::Heap().AllocSize( memoryAfterTest ) );
    TInt leakedHeapCells = 
        heapCellsAfterTest - 
            ( heapCellsBeforeTest + iHeapCellsReservedByAssertFailure );
    if ( leakedHeapCells > 0 )
        {
        iLeakedMemory = memoryAfterTest - memoryBeforeTest;
        }
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::StartAllocFailureSimulation()
    {
    __UHEAP_SETFAIL( iAllocFailureType, iAllocFailureRate );
    }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
//
void CSymbianUnitTest::StopAllocFailureSimulation()
    {
    __UHEAP_RESET;
    }