// Copyright (c) 2001-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 "IpuTestUtils.h"
//
// Constants
_LIT(KTestPanic, "IpuTestHarness");
const TInt KFailedTestsGranularity = 10;
const TInt KMaxLogEntrySize = 256;
//
// CIpuTestHarness
//
CIpuTestHarness::CIpuTestHarness(const TDesC& aTitle)
: iTest(aTitle)
//
// Default c'tor
{
iTest.Title();
iCanStartTest = ETrue;
}
CIpuTestHarness::~CIpuTestHarness()
//
// D'tor
{
TTime endtime;
endtime.UniversalTime();
// Do resource handle leak test?
if (iDoResourceLeakTest)
ResourceLeakTest();
// End of tests - see if failed or ok
if (iFailedTests->Count())
{
TestHarnessFailed();
}
else
{
TestHarnessComplete();
}
iFailedTests->ResetAndDestroy();
delete iFailedTests;
// Log finish time
TDateTime t = endtime.DateTime();
LogIt(_L("Ended @ %d:%d:%d:%d"),t.Hour(),t.Minute(),t.Second(),t.MicroSecond());
TTime difftime(endtime.Int64() - iStartTime.Int64());
t = difftime.DateTime();
LogIt(_L("Execution time %d:%d:%d:%d"),t.Hour(),t.Minute(),t.Second(),t.MicroSecond());
// Close logs and test harness
iFlogger.CloseLog();
// iTest test harness performs UHEAP MARK/UNMARK check upon creation/destruction
// therefore, it must be destroyed last since it is created first in
// CIpuTestHarness
iTest.Close();
}
EXPORT_C CIpuTestHarness* CIpuTestHarness::NewLC(const TDesC& aTitle)
//
// Static factory c'tor
{
CIpuTestHarness* self = new (ELeave) CIpuTestHarness(aTitle);
CleanupStack::PushL(self);
self->ConstructL(aTitle);
return self;
}
EXPORT_C CIpuTestHarness* CIpuTestHarness::NewL(const TDesC& aTitle)
//
// Static factiry c'tor
{
CIpuTestHarness* self = CIpuTestHarness::NewLC(aTitle);
CleanupStack::Pop();
return self;
}
void CIpuTestHarness::ConstructL(const TDesC& aTitle)
//
// Non-trivial c'tor
{
// Create iFailedTests
iFailedTests = new (ELeave) CArrayPtrFlat<CTestInfo> (KFailedTestsGranularity);
// Start up logging server connection
TBuf<64> temp(aTitle);
DefaultLogFileName(temp);
CreateFlogger(temp, EFalse, EFalse);
iStartTime.UniversalTime();
TDateTime t = iStartTime.DateTime();
LogIt(_L("Started @ %d:%d:%d:%d"),t.Hour(),t.Minute(),t.Second(),t.MicroSecond());
// Find number of open resource handles
TInt processHandleCount=0;
RThread().HandleCount(processHandleCount,iStartHandleCount);
}
EXPORT_C void CIpuTestHarness::StartTestL(const TDesC& aName)
//
// Logs start of test aName
{
if (iCanStartTest)
{
// - increment test count
++iTestCount;
if (iTestMode == ETestModeNormal) // don't add this info when we are doing memory leak testing otherwise it
// would get leaked!
{
// Add this test to failed test list - set errorcode to zero
CTestInfo* temp = CTestInfo::NewLC(aName, iTestCount, 0);
iFailedTests->AppendL(temp);
CleanupStack::Pop(); // temp
// Stop new test being started until this one has ended
iTest.Start(aName);
iCanStartTest = EFalse;
}
TBuf<KMaxFileName + 4> buf;
buf.Format(KTestStartingWithDesc, iTestCount, &aName);
WriteComment(buf);
// Reset iStepNumber - start at 1
iStepNumber = 1;
}
else
{
// Panic client - bad usage - not allowed to nest tests
Panic(EBadStartTest);
}
}
EXPORT_C void CIpuTestHarness::NextStep(const TDesC& aStepName)
//
// Logs the next step in a test - for informative use.
{
if (!iCanStartTest)
{
TBuf<KMaxFileName + 4> buf;
buf.Format(KNextTestStepWithDesc, iTestCount, iStepNumber, &aStepName);
WriteComment(buf);
iTest.Next(aStepName);
++iStepNumber;
}
else
{
// Panic client - bad usage - test not started
Panic(EBadStartTest);
}
}
EXPORT_C void CIpuTestHarness::EndTest(TInt aErrorCode)
//
// Logs end of test
{
if (!iCanStartTest)
{
if (iTestMode == ETestModeNormal)
{
// Get ptr to this test's entry in failed list - will be the last entry
TBuf<KMaxFileName + 4> buf;
TInt index = iFailedTests->Count();
CTestInfo* ptr = iFailedTests->At(--index);
if (aErrorCode)
{
// Set the error code
ptr->SetErrorCode(aErrorCode);
buf.Format(KTestFailed, iTestCount, aErrorCode);
WriteComment(buf);
}
else
{
// Remove entry from list of failed tests
delete ptr;
iFailedTests->Delete(index);
}
}
// Allow new test to start
iTest.End();
iCanStartTest = ETrue;
}
else
{
if (iTestMode == ETestModeNormal)
// Panic client - bad usage - test not started
Panic(EBadEndTest);
// don't panic when we are memory leak testing as EndTestL will never get called to reset the test properly
}
}
EXPORT_C void CIpuTestHarness::LogIt(TRefByValue<const TDesC> aFmt, ...)
//
// Messages to the front end emulator and to the Inu log
{
VA_LIST list;
VA_START(list,aFmt);
TBuf<KMaxFileName + 4> buf;
buf.Append(KTestCommentPrepend);
buf.AppendFormatList(aFmt,list);
VA_END(list);
WriteComment(buf);
}
EXPORT_C void CIpuTestHarness::operator()(TInt aResult,TInt aLineNum)
//
// Overload operator ()
{
iTest(aResult, aLineNum);
}
EXPORT_C void CIpuTestHarness::operator()(TInt aResult)
//
// Overload operator ()
{
iTest(aResult);
}
EXPORT_C void CIpuTestHarness::PressAnyKey()
//
// Request a key press from user and wait - unless we are running a script
{
if (!iScriptRunning)
{
iTest.Printf(TRefByValue<const TDesC>_L("\nPress a key"));
iTest.Getch();
}
}
EXPORT_C void CIpuTestHarness::DumpData(HBufC8& aData, TBool logIt)
//
// Do a formatted dump of binary data, optionally logging it
{
// Iterate the supplied block of data in blocks of 16 bytes
TInt pos = 0;
TBuf<KMaxLogEntrySize> logLine;
TBuf<KMaxLogEntrySize> anEntry;
while (pos < aData.Length())
{
anEntry.Format(TRefByValue<const TDesC>_L("%04x : "), pos);
logLine.Append(anEntry);
// Hex output
TInt offset = 0;
for (offset = 0; offset < 16; offset++)
{
if (pos + offset < aData.Length())
{
TInt nextByte = aData[pos + offset];
anEntry.Format(TRefByValue<const TDesC>_L("%02x "), nextByte);
logLine.Append(anEntry);
}
else
{
anEntry.Format(TRefByValue<const TDesC>_L(" "));
logLine.Append(anEntry);
}
}
anEntry.Format(TRefByValue<const TDesC>_L(": "));
logLine.Append(anEntry);
// Char output
for (offset = 0; offset < 16; offset++)
{
if (pos + offset < aData.Length())
{
TInt nextByte = aData[pos + offset];
if ((nextByte >= 32) && (nextByte <= 127))
{
anEntry.Format(TRefByValue<const TDesC>_L("%c"), nextByte);
logLine.Append(anEntry);
}
else
{
anEntry.Format(TRefByValue<const TDesC>_L("."));
logLine.Append(anEntry);
}
}
else
{
anEntry.Format(TRefByValue<const TDesC>_L(" "));
logLine.Append(anEntry);
}
}
if (logIt)
{
LogIt(TRefByValue<const TDesC>_L("%S"), &logLine);
}
else
{
iTest.Printf(TRefByValue<const TDesC>_L("%S\n"), &logLine);
}
logLine.Zero();
// Advance to next 16 byte segment
pos += 16;
}
}
EXPORT_C void CIpuTestHarness::GetAnEntry(const TDesC& ourPrompt, TDes& currentstring)
//
// Get an input string from the user, displaying a supplied prompt and default string value
{
// If we're scripting, try reading from script first
TInt readScriptErr = KErrNotFound;
if (iScriptRunning)
{
readScriptErr = ReadLineFromScript(currentstring);
}
if (!readScriptErr)
return;
// Either not scripting, or hit end of script - continue with user input
TBuf16<KMaxUserEntrySize> ourLine;
TBuf<KMaxUserEntrySize> tempstring; //tempstring is a unicode descriptor
//create a temporary buffer where the
//unicode strings are stored in order to
//be displayed
ourLine.Zero ();
tempstring.Copy(currentstring); //Copy current string to Unicode buffer
TKeyCode key = EKeyNull; //current string buffer is 8 bits wide.
//Unicode string bufffer (tempstring) is 16 bits wide.
for (;;)
{
if (ourLine.Length () == 0)
{
iTest.Console()->SetPos (0, iTest.Console()->WhereY ());
iTest.Console()->Printf (_L ("%S"), &ourPrompt);
if (tempstring.Length () != 0) //get tempstring's number of items
iTest.Console()->Printf (_L (" = %S"), &tempstring); //if not zero print them to iTest.Console()
iTest.Console()->Printf (_L (" : "));
iTest.Console()->ClearToEndOfLine ();
}
key = iTest.Getch();
if (key == EKeyBackspace)
{
if (ourLine.Length() !=0)
{
ourLine.SetLength(ourLine.Length()-1);
iTest.Console()->Printf (_L ("%c"), key);
iTest.Console()->SetPos(iTest.Console()->WhereX(),iTest.Console()->WhereY());
iTest.Console()->ClearToEndOfLine();
} // end if (ourLine.Length() !=0)
} // end if (key == KeyBackSpace)
if (key == EKeyDelete)
{
ourLine.Zero();
iTest.Console()->SetPos (0, iTest.Console()->WhereY ());
iTest.Console()->ClearToEndOfLine ();
tempstring.Copy(ourLine);
break;
}
if (key == EKeyEnter)
break;
if (key < 32)
{
continue;
}
ourLine.Append (key);
iTest.Console()->Printf (_L ("%c"), key);
iTest.Console()->SetPos(iTest.Console()->WhereX(),iTest.Console()->WhereY());
iTest.Console()->ClearToEndOfLine();
if (ourLine.Length () == ourLine.MaxLength ())
break;
} // end of for statement
if ((key == EKeyEnter) && (ourLine.Length () == 0))
tempstring.Copy (currentstring); //copy contents of 8 bit "ourLine" descriptor
iTest.Console()->SetPos (0, iTest.Console()->WhereY ());
iTest.Console()->ClearToEndOfLine ();
iTest.Console()->Printf (_L ("%S"), &ourPrompt);
if ((key == EKeyEnter) && (ourLine.Length() !=0))
tempstring.Copy(ourLine);
if (tempstring.Length () != 0) //if temstring length is not zero
{
iTest.Console()->Printf (_L (" = %S\n"), &tempstring); //print the contents to iTest.Console()
LogIt(_L ("%S = %S\n"), &ourPrompt, &tempstring);
}
else
//iTest.Console()->Printf (_L (" is empty"));
iTest.Console()->Printf (_L ("\n"));
currentstring.Copy(tempstring); //copy 16 bit tempstring descriptor back
}
EXPORT_C TInt CIpuTestHarness::GetSelection(const TDesC& ourPrompt, const TDesC& validChoices)
//
// Present the user with a list of options, and get their selection
{
// If we're scripting, try reading from script first
TInt readScriptErr = KErrNotFound;
if (iScriptRunning)
{
TBuf<1> oneCharBuf;
readScriptErr = ReadLineFromScript(oneCharBuf);
if (!readScriptErr)
{
return validChoices.Locate((TChar)oneCharBuf[0]);
}
}
// Either not scripting, or hit end of script - continue with user input
TKeyCode key = EKeyNull;
iTest.Console()->SetPos (0, iTest.Console()->WhereY ());
iTest.Console()->Printf(_L("%S "), &ourPrompt);
iTest.Console()->Printf(_L("[%S] :"), &validChoices);
TInt retVal = KErrNotFound;
while (retVal == KErrNotFound)
{
key = iTest.Getch();
// Check that key is in the list of valid choices
retVal = validChoices.Locate((TChar)key);
}
iTest.Console()->Printf(_L("%c\n\n"), key);
return retVal;
}
EXPORT_C void CIpuTestHarness::SetScript(RFile& scriptFile)
//
// Sets the file to be used for a test script - ie. a file that contains commands used by
// GetEntry() and GetSelection()
{
iScriptFile = &scriptFile;
iScriptRunning = ETrue;
LogIt(_L("***SCRIPT STARTING***\n"));
}
TInt CIpuTestHarness::ReadLineFromScript(TDes& aBuffer)
//
// Reads the next line from the script file, and sets the passed-in descriptor with its contents.
// Returns KErrNone if reading succeeded; KErrNotFound if the EOF was reached. When EOF is reached,
// the file is closed.
{
// *********************************
// Assume script is 8-bit text file
// *********************************
TBool isAComment = ETrue;
TInt err = KErrNone;
TBuf<512> line;
while (isAComment && !err)
{
TFileText text;
text.Set(*iScriptFile);
line.SetLength(0);
for(;;)
{
TBuf8<2> c;
err = iScriptFile->Read(c,1);
if (err && err != KErrEof)
{
iTest.Printf(_L("Error reading file: %d\n"), err);
break;
}
if (c.Length() == 0)
{
err = KErrEof;
break;
}
else
{
if (c[0] == '\n') // break out if it is CR
break;
else if (c[0] != (TUint8)(0x0d)) // otherwise append the char, _unless_ it is a LF
line.Append(c[0]);
}
}
if (err == KErrNone && line.Locate('/') != 0) // comment (only works if it's the first character)
{
isAComment = EFalse;
}
}
// The line read is not a comment, or have hit end of file
if (!err)
{
// copy to passed in descriptor, but do not allow an overflow
aBuffer.Copy(line.Left(aBuffer.MaxLength()));
LogIt(_L("***SCRIPT : read command '%S' ***\n"), &aBuffer);
}
else
{
iScriptFile->Close();
err = KErrNotFound;
iScriptRunning = EFalse;
LogIt(_L("***SCRIPT ENDED***\n"));
}
return err;
}
void CIpuTestHarness::Panic(TInt aPanic)
//
// Panic the client program.
{
User::Panic(KTestPanic,aPanic);
}
void CIpuTestHarness::TestHarnessComplete()
//
// Test harness completed without failures
{
_LIT(KTestCompleteFormat, "Total Tests %d, Failed Tests %d");
TBuf<50> text;
text.AppendFormat(KTestCompleteFormat, iTestCount, iFailedTests->Count());
WriteComment(text);
WriteComment(KTestHarnessCompleted);
}
void CIpuTestHarness::TestHarnessFailed()
//
// Test harness has a failure - log information
{
TBuf<KMaxFileName + 4> buf;
buf.Format(KTestHarnessFailed, iFailedTests->Count());
WriteComment(buf);
// Log fialed tests' information
for (TInt ii=0; ii<iFailedTests->Count(); ++ii)
{
CTestInfo* failed = iFailedTests->At(ii);
TPtrC name = failed->Name();
LogIt(KTestFailInfo, failed->Number(), &name, failed->ErrorCode());
}
}
void CIpuTestHarness::ResourceLeakTest()
//
// Creates a new test that fails if any there are any leaked resource handles
{
// Start new test
_LIT(KResourceTestName, "Resource Handle Leak Test");
TRAPD(testError, StartTestL(KResourceTestName));
if(testError==KErrNone)
{
// Find number of opened handles
TInt processHandleCount=0;
TInt threadHandleCount=0;
RThread().HandleCount(processHandleCount,threadHandleCount);
TInt openHandleCount = iStartHandleCount-threadHandleCount;
TInt err = KErrNone;
if ( openHandleCount !=0 )
{
err = KErrGeneral;
LogIt(_L("Number leaked handles is %D"), openHandleCount);
}
EndTest(err);
}
else
{
_LIT(KTxtResourceTestRunError, "Unable to complete Resource Leak Test, error: %d");
LogIt(KTxtResourceTestRunError, testError);
EndTest(testError);
}
}
//
// CTestInfo
//
CIpuTestHarness::CTestInfo::CTestInfo()
//
// Default c'tor
{
}
CIpuTestHarness::CTestInfo::~CTestInfo()
//
// D'tor
{
delete iName;
}
CIpuTestHarness::CTestInfo* CIpuTestHarness::CTestInfo::NewLC(const TDesC& aName, TInt aNumber, TInt aErrorCode)
//
// Static factory c'tor
{
CTestInfo* self = new (ELeave) CTestInfo();
CleanupStack::PushL(self);
self->ConstructL(aName, aNumber, aErrorCode);
return self;
}
CIpuTestHarness::CTestInfo* CIpuTestHarness::CTestInfo::NewL(const TDesC& aName, TInt aNumber, TInt aErrorCode)
//
// Static factory c'tor
{
CTestInfo* self = NewLC(aName, aNumber, aErrorCode);
CleanupStack::Pop(); // self
return self;
}
void CIpuTestHarness::CTestInfo::ConstructL(const TDesC& aName, TInt aNumber, TInt aErrorCode)
//
// Non-trivial c'tor
{
iName = aName.AllocLC();
CleanupStack::Pop(); // iName
iNumber = aNumber;
iErrorCode = aErrorCode;
}
void CIpuTestHarness::CTestInfo::SetNameL(const TDesC& aName)
//
// Sets iName
{
HBufC* temp = aName.AllocLC();
CleanupStack::Pop(); // temp
delete iName;
iName = temp;
}
void CIpuTestHarness::CTestInfo::SetNumber(TInt aNumber)
//
// Sets iNumber
{
iNumber = aNumber;
}
void CIpuTestHarness::CTestInfo::SetErrorCode(TInt aErrorCode)
//
// Sets iErrorCode
{
iErrorCode = aErrorCode;
}