diff -r 000000000000 -r 83f4b4db085c toolsandutils/autotest/src/autotest.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolsandutils/autotest/src/autotest.cpp Tue Feb 02 01:39:43 2010 +0200 @@ -0,0 +1,850 @@ +// Copyright (c) 2000-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: +// Framework for Automated Test code. +// +// + +/** + @file autotest.cpp +*/ + +#include "autotest.h" + +#include // for RFileLogger + + +_LIT(KTestKeywordSuccess, "SUCCESS"); +_LIT(KTestKeywordFailed, "FAILED"); +_LIT(KTestKeywordNotSupported, "NOT SUPPORTED"); + +// +// implementation of CAutoTest +// + +EXPORT_C CAutoTest* CAutoTest::NewL(const struct TAutoTestCase* aTestcases, TInt aNumberOfTestcases, const TDesC& aName, RTest& aTest) +/** + * 2 phase constructor + * + * @param aTestcases Pointer to the table with test cases + * @param aNumberOfTestcases Number of automated test cases + * @param aName Name of the test report file + * @param aTest The RTest object owned by the client + * @return CAutoTest* A newly created CAutoTest object + */ + { + CAutoTest* p = new (ELeave) CAutoTest(aTestcases, aNumberOfTestcases, aName, aTest); + CleanupStack::PushL(p); + p->ConstructL(); + CleanupStack::Pop(); + return p; + } + + +CAutoTest::~CAutoTest() +/** + * D'tor + */ + { + // close file handles + iTestReport.Close(); + iFileServer.Close(); + + delete iCommandLine; + delete iTestResults; + delete iSkipList; + iTestCases = NULL; + } + + +EXPORT_C TInt CAutoTest::ExecuteL() +/** + * Execute the test suite. + * + * @return test result, KErrNone if all tests passed + */ + { + (void)User::LeaveIfError(iTestReport.Write(_L8("--- test report start---\r\n"))); + + // + // machine info + // + MachineInfoL(); + + // + // date & time info + // + (void)User::LeaveIfError(iTestReport.Write(_L8("Time started: "))); + LogTimeL(); + + // + // component under test info (optional) + // + if(iCompInfo.Length()) + { + (void)User::LeaveIfError(iTestReport.Write(_L8("Component info: "))); + (void)User::LeaveIfError(iTestReport.Write(iCompInfo)); + (void)User::LeaveIfError(iTestReport.Write(_L8("\r\n"))); + } + + + // decide to run the menu or the complete test + if(iKeepGoing) + RunAllTestsL(); + else + StartMenuL(); + + // date & time info + (void)User::LeaveIfError(iTestReport.Write(_L8("Time finished: "))); + LogTimeL(); + + (void)User::LeaveIfError(iTestReport.Write(_L8("--- test report end---\r\n"))); + + return iTestSuiteFailed ? 84 : KErrNone; + + } + +EXPORT_C void CAutoTest::TestCheckPointCompareL(TInt aVal,TInt aExpectedVal, const TDesC& aText, char* aFile,TInt aLine) + { + if(aVal == aExpectedVal) + return; + + TBuf<200> temp; + temp.Copy(TPtrC8((TText8*)aFile)); + + iRTest.Printf(_L("FAILED test %d : Val = %d Exp Val = %d file %S, line %d\n") + , iTestCase+1, aVal, aExpectedVal, &temp, aLine); + + // + // Log failure to log file, even if iKeepGoing is EFalse. + // The code used to only log the failure to the file if iKeepGoing was ETrue. + // If a filaure occurs it needs to be written to log regardless of iKeepGoing. + TBuf8<256> stringBuf; + stringBuf.AppendFormat(_L8("FAILED test %d : Val = %d Exp Val = %d file %s, line %d : "),iTestCase+1,aVal,aExpectedVal, aFile, aLine); + stringBuf.Append(aText); + stringBuf.AppendFormat(_L8("\r\n")); + (void)User::LeaveIfError(iTestReport.Write(stringBuf)); + iTestSuiteFailed = ETrue; + + User::Leave(aVal); + } + + +EXPORT_C void CAutoTest::TestCheckPointCodeL(TInt aCase, char* aFile, TInt aLine) +/** + * hidden by the macro GLOBAL_CHECKPOINT_CODE. Used to validate return values etc. + * If the 'keepgoing' command line option is specified, the test program * will not stop, but just print a warning in the test report, including + * which file and line where the test failed. Then it will leave the test + * case and do the next one. + * + * @param aCase 0 no error or the error code + * @param aFile name of the file involved here + * @param aLine the line of the file involved here + */ + { + if(!aCase) + return; + + // Convert to unicode for log to console + TBuf<200> temp; + temp.Copy(TPtrC8((TText8*)aFile)); + + iRTest.Printf(_L("FAILED test %d : Code = %d file %S, line %d\n"), iTestCase+1, aCase, &temp, aLine); + + // + // Log failure to log file, even if iKeepGoing is EFalse. + // The code used to only log the failure to the file if iKeepGoing was ETrue. + // If a filaure occurs it needs to be written to log regardless of iKeepGoing. + TBuf8<256> stringBuf; + stringBuf.AppendFormat(_L8("FAILED test %d : Code = %d file %s, line %d\r\n"),iTestCase+1, aCase, aFile, aLine); + (void)User::LeaveIfError(iTestReport.Write(stringBuf)); + iTestSuiteFailed = ETrue; + + User::Leave(aCase); + } + +EXPORT_C void CAutoTest::TestCheckPointL(TBool aCase, char* aFile, TInt aLine) +/** + * hidden by the macro GLOBAL_CHECKPOINT. Used to validate return values etc. + * If the 'keepgoing' option is specified, the test program + * will not stop, but just print a warning in the test report, including + * which file and line where the test failed. Then it will leave the test + * case and do the next one. + * + * @param aCase if true success, false means failed + * @param aFile name of the file involved here + * @param aLine the line of the file involved here + */ + { + if(aCase) + return; + + // Convert to unicode for log to console + TBuf<200> temp; + temp.Copy(TPtrC8((TText8*)aFile)); + + iRTest.Printf(_L("FAILED test %d : file %S, line %d\n"),iTestCase+1, &temp, aLine); + + // Log failure to log file, even if iKeepGoing is EFalse. + // The code used to only log the failure to the file if iKeepGoing was ETrue. + // If a filaure occurs it needs to be written to log regardless of iKeepGoing. + TBuf8<256> stringBuf; + stringBuf.AppendFormat(_L8("FAILED test %d : file %s, line %d\r\n"),iTestCase+1, aFile, aLine); + (void)User::LeaveIfError(iTestReport.Write(stringBuf)); + iTestSuiteFailed = ETrue; + User::Leave(84); + } + +EXPORT_C void CAutoTest::SetCompInfo(const TDesC8& aCompInfo) +/** + * store information about the Component under test in the autotest object + * + * @param aCompInfo descriptor containin the component info + */ + { + iCompInfo = aCompInfo; + } + +// +// protected +// + +CAutoTest::CAutoTest(const struct TAutoTestCase* aTestcases, TInt aNumberOfTestcases, const TDesC& aName, RTest& aTest) +/** + * C'tor + * + * @param aTestcases Pointer to the table with test cases + * @param aNumberOfTestcases Number of automated test cases + * @param aName Name of the test report file + * @param aTest The RTest object owned by the client + */ + :iTestCases(aTestcases) + ,iNumberOfTestcases(aNumberOfTestcases) + ,iTestReportName(aName) + ,iKeepGoing(EFalse) + ,iTestSuiteFailed(ETrue) + ,iRTest(aTest) + ,iSkipList(NULL) + ,iCommandLine(NULL) + { + } + + +void CAutoTest::ConstructL() +/** + * 2nd phase of 2 phased constructor. + * + * create a new table for test results storing, depending on the + * number of test cases. Connect to the file server and open + * and replace the test report file. + */ + { + // No serial port logging + iRTest.SetLogged(EFalse); + // Array of test result codes + iTestResults = new (ELeave) TInt[iNumberOfTestcases]; + // Array of bools to determine whether a test should be skipped + // Set to ETrue if a test is not supported + iSkipList = new (ELeave) TBool[iNumberOfTestcases]; + Mem::FillZ(iSkipList,iNumberOfTestcases * sizeof(TBool)); + (void)User::LeaveIfError(iFileServer.Connect()); + + // Read the command line or contents of config file specified on the command line, + // into private storage + GetCommandLineL(); + // Get the name of the output log file + SetLogFileL(); + // Set up the test skip options + GetSkipOptions(); + // Set the keepgoing flag + GetKeepGoingOption(); + } + + +// +// private +// + +void CAutoTest::PrintMenu() const +/** + * print a menu to the console + */ + { + + iRTest.Printf(_L("menu:\n") ); + iRTest.Printf(_L(" ! - Run all tests\n") ); + iRTest.Printf(_L(" ^C - Quit\n") ); + + for(TInt i=0;i aFmt,...) +/** + * print to console and logfile + */ + { + VA_LIST list; + VA_START(list, aFmt); + TBuf<0x100> aBuf; + aBuf.AppendFormatList(aFmt, list); + + // console + iRTest.Printf(aBuf); + + // logfile + LogExtra((TText8*)__FILE__, __LINE__, aFmt); + } + + +void CAutoTest::StartMenuL() +/** + * run the menu forever, until press Ctrl-C + * + * @leave this function may leave + */ + { + TInt ret; + + + FOREVER + { + TBuf<256> inputBuf; + + PrintMenu(); + + TInt key = iRTest.Getch(); + iRTest.Printf(_L("%c"), key); + + switch(key) + { + case '!': + // for the time, that there is a request in the menu to run all the + // test cases, the iKeepGoing flag shoulg should be set and deleted + // afterwards; because test cases behave different in automated and + // manual mode; and here we are definitely in the automated mode + iKeepGoing = ETrue; + RunAllTestsL(); + iKeepGoing = EFalse; + break; + + case 0x03: // Ctrl-C + return; + + default: + + inputBuf.Append(key); + GetString(*iRTest.Console(), inputBuf); + TLex lexer(inputBuf); + TInt value; + ret = lexer.Val(value); + value-=1; + if(ret == KErrNone) + { + iRTest.Printf(_L("Testcase: %d\n"), value+1); + if((value >= 0) && (value < (TInt)iNumberOfTestcases)) + { + iTestCase = value; + ret = RunATestL(); + } + else + break; + + // + // Display the success or failure to the console + if(ret == KErrNone) + iRTest.Printf(_L("SUCCESS! \n")); + else if(ret == KErrNotSupported) + iRTest.Printf(_L("NOT SUPPORTED\n"), ret); + else + iRTest.Printf(_L("FAILED! (ret=%d)\n"), ret); + + // + // Log our success/failure in the log file + TBuf8<256> stringBuf8; + stringBuf8.AppendFormat(_L8("test %d: "), value +1 ); + + if(ret == KErrNone) + stringBuf8.Append(KTestKeywordSuccess); + else if(ret == KErrNotSupported) + stringBuf8.Append(KTestKeywordNotSupported); + else + stringBuf8.Append(KTestKeywordFailed); + + stringBuf8.AppendFormat(_L8(" (return=%d) "), ret ); + TBuf<256> buf; + buf.AppendFormat(_L("[%s]\r\n"), iTestCases[value].iText); + stringBuf8.Append(buf); + (void)User::LeaveIfError(iTestReport.Write(stringBuf8)); + } + + break; + + } // end, switch(key) + + } // end, FOREVER + + } + +void CAutoTest::RunAllTestsL() +/** + * run all the tests for this test program + */ + { + TInt ret; + iRTest.Printf(_L("Now running all the testcases...\n") ); + + for(TInt i=0;i asterisk=_L("*"); + TName search=asterisk; + + TFindThread findHb; + findHb.Find(search); + TFullName name; + + while (findHb.Next(name)==KErrNone) + { + TBuf8<300> stringBuf8(_L8("\tThread Name = ")); + stringBuf8.Append(name); + stringBuf8.Append(_L8("\r\n")); + iTestReport.Write(stringBuf8); + } + } + + +TInt CAutoTest::RunATestL()// const +/** + * run a test and return the error code. + * the testcase is gived in iTestCase + * + * @return error code for that test case + */ + { + TInt ret; + + if(iSkipList[iTestCase]) + return KErrNotSupported; + + // check the index first + if(iTestCase < 0 || iTestCase >= (TInt)iNumberOfTestcases) + User::Leave(KErrNotFound); + + iRTest.Printf(_L("%d %s \n"), iTestCase+1, iTestCases[iTestCase].iText ); + + // get number of outstanding requetsts on thread before we run the test + TInt reqsAtStart = RThread().RequestCount(); + if(reqsAtStart!=0) + { + TBuf8<256> stringBuf8; + stringBuf8.AppendFormat(_L8("Warning: thread had %d oustanding requests before starting test %d!\r\n"),reqsAtStart,iTestCase+1); + iTestReport.Write(stringBuf8); + } + + // get number of Handles *before* we start the program + TInt processHandleCountBefore; + TInt threadHandleCountBefore; + RThread().HandleCount(processHandleCountBefore, threadHandleCountBefore); + + // ------------------------------------------ + void (*fn)(void) = iTestCases[iTestCase].iFunc; + TRAP(ret, fn() ); + // ------------------------------------------ + + // + // if the test case failed then we dont bother + // with checking the outstanding requests etc. + // + if(ret == KErrNone) + { + // get number of Handles *after* the program is finished + TInt processHandleCountAfter; + TInt threadHandleCountAfter; + RThread().HandleCount(processHandleCountAfter, threadHandleCountAfter); + + // check that we are closing all the handles + if(threadHandleCountBefore stringBuf8; + stringBuf8.AppendFormat(_L8("%d handles left open after test %d returned!\r\n"),threadHandleCountAfter-threadHandleCountBefore,iTestCase+1); + iTestReport.Write(stringBuf8); + + PrintThreadsToLogFile(); + + return KErrGeneral; + } + + // check that number of oustanding requests on thread has not increased + const TInt reqsNow=RThread().RequestCount(); + if(reqsNow>reqsAtStart) + { + TBuf8<256> stringBuf8; + stringBuf8.AppendFormat(_L8("%d requests were left outstanding after test %d returned!\r\n"),reqsNow,iTestCase+1); + iTestReport.Write(stringBuf8); + return KErrGeneral; + } + } + return ret; + } + +void CAutoTest::SetLogFileL() +/** + * Parse the command line buffer to see if a logfile has been specified + * If a logfile is specified create it + */ + { + TPtr ptr = iCommandLine->Des(); + TLex lex(ptr); + + // Parse the command line for the "-log" + while(!lex.Eos()) + { + TPtrC ptr = lex.NextToken(); + _LIT(KLogCommand,"-log"); + if(ptr == KLogCommand) + break; + } + // Read the log filename + if(!lex.Eos()) + { + TPtrC file = lex.NextToken(); + iTestReportName.Copy(file); + } + // Replace if it exists and if not, create the folder then create the file + if(iTestReport.Replace(iFileServer, iTestReportName, EFileWrite) != KErrNone) + { + if(iTestReport.Create(iFileServer, iTestReportName, EFileWrite) == KErrPathNotFound) + (void)User::LeaveIfError(iFileServer.MkDirAll(iTestReportName)); + (void)User::LeaveIfError(iTestReport.Create(iFileServer, iTestReportName, EFileWrite)); + } + } + +void CAutoTest::GetCommandLineL() +/** + * Parse the command line to see if a config file has been specified: "-config afile.txt" + * If it has, then read the contents of the file into the private command line buffer. Otherwise + * copy the real command line into the private command line buffer + */ + { + TBuf cmdLine; + TBuf fileName; + + #ifndef EKA2 + RProcess().CommandLine(cmdLine); + #else + User::CommandLine(cmdLine); + #endif + + TLex lex(cmdLine); + // Parse the real command line for the -config option + while(!lex.Eos()) + { + TPtrC ptr = lex.NextToken(); + _LIT(KConfigCommand,"-config"); + if(ptr == KConfigCommand) + break; + } + // Read the config filename if it's there + if(!lex.Eos()) + { + TPtrC file = lex.NextToken(); + fileName.Copy(file); + } + // If the filename has not been specified, set up the default + // This may not exist either, by the way. + if(!fileName.Length()) + { + _LIT(KDefaultConfigFile,"c:\\autotestconfig.txt"); + fileName = KDefaultConfigFile; + } + RFile file; + CleanupClosePushL(file); + // Try to open the config file + if(file.Open(iFileServer,fileName, EFileRead) == KErrNone) + // Config file exists + // read it into a local 8 bit buffer + { + TInt size; + // Get the size of the file + (void)User::LeaveIfError(file.Size(size)); + HBufC8* buf8 = HBufC8::NewL(size); + CleanupStack::PushL(buf8); + TPtr8 ptr8 = buf8->Des(); + // Read the file contents into the heap buffer + (void)User::LeaveIfError(file.Read(ptr8,size)); + // Copy to private class unicode buffer + iCommandLine = HBufC::NewL(size); + TPtr ptr16 = iCommandLine->Des(); + ptr16.Copy(ptr8); + CleanupStack::PopAndDestroy(buf8); + } + else + // Config file does not exist so copy the real command line buffer to the private class copy + { + iCommandLine = HBufC::NewL(cmdLine.Length()); + TPtr ptr16 = iCommandLine->Des(); + ptr16.Copy(cmdLine); + } + CleanupStack::PopAndDestroy(&file); + } + +void CAutoTest::GetKeepGoingOption() +/** + * Read the keepgoing flag from the command line bufffer + */ + { + _LIT(KKeepGoingString, "keepgoing"); + TInt ret = iCommandLine->Find(KKeepGoingString); + if(ret >= KErrNone) + iKeepGoing = ETrue; + } + +void CAutoTest::GetSkipOptions() +/** + * Read the test skip options, if any, from the command line buffer + * Usage: "-skip 1 4 5" to skip tests 1, 4 & 5 and log as NOT SUPPORTED + */ + { + TPtr ptr = iCommandLine->Des(); + TLex lex(ptr); + // Parse for -skip + while(!lex.Eos()) + { + TPtrC token = lex.NextToken(); + _LIT(KSkipCommand,"-skip"); + if(token == KSkipCommand) + break; + } + // Loop through reading the test numbers to skip + while(!lex.Eos()) + { + TInt testNum; + TPtrC ptr = lex.NextToken(); + TLex lex(ptr); + // Read the test number as an integer + if(lex.Val(testNum) != KErrNone) + // Exit at the first token that's not a valid number + break; + if(testNum <= 0 || testNum > iNumberOfTestcases) + // Out of range + continue; + // Skip this test + iSkipList[testNum-1] = ETrue; + } + } + + +void CAutoTest::PrintTestReportL(TInt aTestIndex) +/** + * print a test report to file and console + */ + { + // print status + TBuf8<256> stringBuf8; + stringBuf8.AppendFormat(_L8("test %d: "), aTestIndex+1 ); + if(iTestResults[aTestIndex] == KErrNone) + stringBuf8.Append(KTestKeywordSuccess); + else if(iTestResults[aTestIndex] == KErrNotSupported) + stringBuf8.Append(KTestKeywordNotSupported); + else + stringBuf8.Append(KTestKeywordFailed); + stringBuf8.AppendFormat(_L8(" (return=%d) "), iTestResults[aTestIndex] ); + TBuf<256> buf; + buf.AppendFormat(_L("[%s]\r\n"), iTestCases[aTestIndex].iText); + stringBuf8.Append(buf); + (void)User::LeaveIfError(iTestReport.Write(stringBuf8)); + TBuf<256> stringBuf; + stringBuf.Copy(stringBuf8); + iRTest.Printf(stringBuf); + } + + +EXPORT_C void CAutoTest::GetString(CConsoleBase& aConsole, TDes& aString) const +/** + * This function re-invents the wheel (once again...) + * It reads a string from the keyboard. Uses Getch() to get keys and does not + * return until ENTER is pressed. + * + * @param aConsole const reference to the console + * @param aString reference to the string to be read into + */ + { + TKeyCode code; + TBuf<1> kjar; + + FOREVER + { + code = aConsole.Getch(); + kjar.SetLength(0); + kjar.Append(code); + + aConsole.Printf(_L("%S"),&kjar); + + // If finish editing string + if (code == '\r') + break; + + // if remove last character + if ((code == 0x08) && (aString.Length() != 0)) + aString.SetLength((aString.Length()-1)); + else + aString.Append(code); + } + aConsole.Printf(_L("\n")); + } + + +void CAutoTest::LogTimeL() +/* + * write date&time info to log file + */ + { + TBuf8<64> buf8; + TTime now; + now.HomeTime(); + TDateTime dateTime; + dateTime = now.DateTime(); + buf8.Format(_L8("%02d/%02d/%04d %02d:%02d.%02d \r\n"), dateTime.Day()+1, dateTime.Month()+1, dateTime.Year(), + dateTime.Hour(), dateTime.Minute(), dateTime.Second() ); + (void)User::LeaveIfError(iTestReport.Write(buf8)); + } + +EXPORT_C TBool CAutoTest::KeepGoing(void) +// For external visibility +// Handy if someone wants to use defaults instead of taking keyboard input + { + return iKeepGoing; + } + + +EXPORT_C void CAutoTest::LogExtra(const TText8* aFile, TInt aLine, TRefByValue aFmt,...) +/** + * Write a free formatted string to a second log file and to the console. + * The second log files name is the same as the main log file plus '.EXTRA'. + */ + { + VA_LIST list; + VA_START(list,aFmt); + // + // Assemble file name for second log file + _LIT(KExtra,".EXTRA"); + TBuf logFileName; // +6 to hold the text ".EXTRA" + logFileName.Copy(iTestReportName); + logFileName.Append(KExtra); + + // + // Assemble string to be written to second log + TBuf<256> buf; + { // Braces used to scope lifetime of TBuf objects + TPtrC8 fileName8(aFile); + TBuf<128> fileName; + fileName.Copy(fileName8); + buf.Format(_L("\t%S\t%d\t"), &fileName, aLine); + + buf.AppendFormatList(aFmt,list); + } + + // + // (1) Open/create log file. + // (2) Write text to log file (RFileLogger adds a time stamp for us) + // (3) Close log + _LIT(KLogFolder,"autotest"); + RFileLogger log; + if(log.Connect()==KErrNone) + { + log.CreateLog(KLogFolder,logFileName,EFileLoggingModeAppend); + log.Write(buf); + log.CloseLog(); + log.Close(); + } + + // + // Also write the text to the console (with a new line after) + buf.Zero(); + buf.AppendFormatList(aFmt,list); + iRTest.Printf(_L("%S\n"),&buf); + } + + +void CAutoTest::MachineInfoL() +/* + * write machine info to log file + */ + { + TBuf8<256> buf8; + buf8.AppendFormat(_L8("Machine info: \r\n")); + // + // platform + // +#if defined(__WINS__) + _LIT8(KPlatform, "WINS"); +#elif defined(__MARM_ARMI__) + _LIT8(KPlatform, "ARMI"); +#elif defined(__MARM_ARM4__) + _LIT8(KPlatform, "ARM4"); +#elif defined(__MARM_THUMB__) + _LIT8(KPlatform, "THUMB"); +#else + _LIT8(KPlatform, "unknown"); +#endif + +#if defined (_DEBUG) + _LIT8(KDebugRelease, "Debug"); +#else + _LIT8(KDebugRelease, "Release"); +#endif + + buf8.AppendFormat(_L8(" Platform: %S %S\r\n"), &KPlatform, &KDebugRelease ); + + // + // machine info from Hal9000 + // + TMachineInfoV1Buf machine; + UserHal::MachineInfo(machine); + + TVersion romver = machine().iRomVersion; + TInt64 uid = machine().iMachineUniqueId; + TInt khz = machine().iProcessorClockInKHz; + + buf8.AppendFormat(_L8(" Rom version: %d.%d.%d\r\n"), romver.iMajor, romver.iMinor, romver.iBuild ); + buf8.AppendFormat(_L8(" Unique ID: 0x%016x\r\n"), uid ); + buf8.AppendFormat(_L8(" CPU Speed: %d kHz\r\n"), khz ); + + User::LeaveIfError(iTestReport.Write(buf8)); + } + +#ifndef EKA2 +GLDEF_C TInt E32Dll(TDllReason /*aReason*/) +// +// DLL entry point +// + { + return(KErrNone); + } +#endif +// EOF - AUTOTEST.CPP