diff options
author | Ed Baak <ed.baak@nokia.com> | 2010-07-29 09:04:01 +1000 |
---|---|---|
committer | Ed Baak <ed.baak@nokia.com> | 2010-07-29 09:04:01 +1000 |
commit | ab5fbe577829fedf5416d5d0835b67dbaeb88688 (patch) | |
tree | cd96ef8d26ef431462a4b7c250aeb8e090d3fd18 | |
parent | 533711dbd5534efa85d22a1519f233bb9e1ceefd (diff) |
Experiment with using old test results clases
-rw-r--r-- | libqsystemtest/qkeystring.cpp | 405 | ||||
-rw-r--r-- | libqsystemtest/qkeystring.h | 46 | ||||
-rw-r--r-- | libqsystemtest/qtest.cpp | 14 | ||||
-rw-r--r-- | libqsystemtest/qtest.h | 115 | ||||
-rw-r--r-- | libqsystemtest/qtestlog.cpp | 596 | ||||
-rw-r--r-- | libqsystemtest/qtestlog.h | 154 | ||||
-rw-r--r-- | libqsystemtest/qtestresult.cpp | 2570 | ||||
-rw-r--r-- | libqsystemtest/qtestresult.h | 131 | ||||
-rw-r--r-- | qtuitest-host.pri | 6 |
9 files changed, 4037 insertions, 0 deletions
diff --git a/libqsystemtest/qkeystring.cpp b/libqsystemtest/qkeystring.cpp new file mode 100644 index 0000000..36cf409 --- /dev/null +++ b/libqsystemtest/qkeystring.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** + ** + ** Declaration of QKeyString class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "qtest.h" +#include "qkeystring.h" + +/*! + \class QKeyString qkeystring.h + \brief The QKeyString class provides lightweight xml style formatted strings. + + Use this class if you want to exchange a set of values using a single string: + the keystring. + You can add more data to the keystring by specifying a keyname and a value + and can then use the findKey functions to retrieve this data again from the string. + A typical use is: + \code + int myFirstValue = 1; + QString mySecondValue = "This is my data"; + + QKeyString myString; + myString.addKey("KEY1",myFirstValue); + myString.addKey("KEY2",mySecondValue); + \endcode + After this myString looks like: + \code + <KEY1>1</KEY1><KEY2>This is my data</KEY2> + \endcode + + Now you can e.g. save the string in a QSetting, save it to a file, send it to a + receiver using a socket connection or whatever you like. + The receiving side then can use the findKey functions to retrieve the data. +*/ + +/*! + Creates an empty keystring. +*/ + +QKeyString::QKeyString() +{ +} + +/*! + Creates a keystring and fills it with \a s. +*/ + +QKeyString::QKeyString( const QString &s ) : QString(s) +{ +} + +/*! + The destructor. +*/ + +QKeyString::~QKeyString() +{ +} + +/*! + Adds a new \a key /\a value pair to the keystring instance. + The format being used by addKey is \code<key>value</key>.\endcode + If the key already exists it is replaced by the new value. +*/ + +void QKeyString::addKey( const QString &key, const QString &value ) +{ + setKey( key, value ); +} + +/*! + \overload + The int \a value is first converted to a string and then inserted into the + instance. +*/ + +void QKeyString::addKey( const QString &key, int value ) +{ + QString tmp; + tmp.sprintf("%d",value); + setKey( key, tmp ); +} + +/*! + \internal + Converts \a value to an XML type string. +*/ +void QKeyString::rawToXml( QString &value ) +{ +// if (value.find( '\n' ) >= 0) +// qDebug( "rawToXml: " + value ); + int pos; + + // convert & into & + pos = 0; + do { + pos = value.indexOf( "&", pos ); + if (pos >= 0) { + value.remove(pos,1); + value.insert(pos,"&"); + pos++; + } + } while (pos >= 0); + + // convert < into < + pos = 0; + do { + pos = value.indexOf( "<", pos ); + if (pos >= 0) { + value.remove(pos,1); + value.insert(pos,"<"); + pos++; + } + } while (pos >= 0); + + // convert > into > + pos = 0; + do { + pos = value.indexOf( ">", pos ); + if (pos >= 0) { + value.remove(pos,1); + value.insert(pos,">"); + pos++; + } + } while (pos >= 0); + + // convert \n into &br; + pos = 0; + do { + pos = value.indexOf( '\n', pos ); + if (pos >= 0) { + value.remove(pos,1); + value.insert(pos,"&br;"); +// qDebug( "found a crlf" ); + pos++; + } + } while (pos >= 0); +} + +/*! + \internal + Converts \a value from XML to a 'raw' type string. +*/ +void QKeyString::xmlToRaw( QString &value ) +{ + int pos; + + // convert &br; into \n + pos = 0; + do { + pos = value.indexOf( "&br;", pos ); + if (pos >= 0) { + value.remove(pos,4); + value.insert(pos,'\n'); + pos++; + } + } while (pos >= 0); + + // convert > into > + pos = 0; + do { + pos = value.indexOf( ">", pos ); + if (pos >= 0) { + value.remove(pos,4); + value.insert(pos,">"); + pos++; + } + } while (pos >= 0); + + // convert < into < + pos = 0; + do { + pos = value.indexOf( "<", pos ); + if (pos >= 0) { + value.remove(pos,4); + value.insert(pos,"<"); + pos++; + } + } while (pos >= 0); + + // convert & into & + pos = 0; + do { + pos = value.indexOf( "&", pos ); + if (pos >= 0) { + value.remove(pos,5); + value.insert(pos,"&"); + pos++; + } + } while (pos >= 0); +} + +/*! + Sets an existing \a key to the specified \a value. + If the key does not exist yet it is appended. + The format being used by setKey is \code<key>value</key>\endcode +*/ + +void QKeyString::setKey( const QString &key, const QString &value ) +{ + if (key == "") { + qWarning( "QKeyString::setKey()... using an empty key is not allowed" ); + return; + } + + QString val( value ); + rawToXml( val ); + + int pos1, pos2; + pos1 = mid(0).indexOf( "<"+key+">" ); + if (pos1>=0) { + pos2 = mid(0).indexOf( "</"+key+">",pos1); + if (pos2 > pos1) { + pos1+= key.length()+2; + remove( pos1, pos2-pos1 ); + insert( pos1, val ); + return; + } + } + + QString S; + S = "<" + key + ">" + val + "</" + key + ">"; + append( S ); +} + +/*! + \overload + The int \a value is first converted to a string and then inserted into the + instance. +*/ + +void QKeyString::setKey( const QString &key, int value ) +{ + QString tmp; + tmp.sprintf("%d",value); + setKey( key, tmp ); +} + +/*! + Searches for \a key in the keystring instance and returns the associated + value into \a keyValue if found. + This functions returns TRUE if the specified key is found and returns FALSE in + all other cases. + The bool \a resetIfNotFound can be used to specify what needs to happen if the + key is not found. If \a resetIfNotFound == TRUE (the default) and the specified + \a key is not found the returned \a keyValue will be "". + If \a resetIfNotFound == FALSE and the specified \a key is not found the returned + \a keyValue will be undefined, e.g. it will have the same value as before the + function was called. +*/ + +// NOTE: see the bottom of the file for optimization issues. +bool QKeyString::findKey( const QString &key, QString &keyValue, bool resetIfNotFound ) const +{ + int pos1, pos2; + pos1 = mid(0).indexOf( "<"+key+">" ); + if (pos1>=0) { + pos2 = mid(0).indexOf( "</"+key+">",pos1 ); + if (pos2 > pos1) { + pos1+= key.length()+2; + keyValue = mid(pos1,pos2-pos1); + xmlToRaw( keyValue ); + return TRUE; + } + } + + if (resetIfNotFound) + keyValue = ""; + + return FALSE; +} + +/*! + \overload + If the specified \a key is found an attempt is made to convert the associated + keyValue to an int. + The function returns TRUE if both the key is found and the conversion is + successfull. + If the key is not found and \a resetIfNotFound is set to TRUE (the default), + \a value will be set to 0 (zero) else if \a resetIfNotFound is FALSE it will + be left to the value with which the function was called. +*/ + +bool QKeyString::findKey( const QString &key, int &value, bool resetIfNotFound ) const +{ + QString tmp; + if (findKey(key,tmp,FALSE)) { + bool ok; + value = tmp.toInt(&ok); + if (!ok && resetIfNotFound) + value = 0; + return ok; + } else { + if (resetIfNotFound) + value = 0; + return FALSE; + } +} + +/*! + Removes the specified \a key + associated text from the string. + If the key is not found the returned string will be an exact copy + of the current contents of the keystring instance. + Example: + \code + QKeyString s; + ... + // s == "<FOO>This is a foo text</FOO><BAR>This is a bar text</BAR>" + s.removeKey("FOO"); + // s == "<BAR>This is a bar text</BAR>" + \endcode +*/ + +QString QKeyString::removeKey( const QString &key ) +{ + QString retValue; + int pos1, pos2; + pos1 = mid(0).indexOf( "<"+key+">"); + if (pos1 >= 0) { + pos2 = mid(0).indexOf( "</"+key+">",pos1); + if (pos2 > pos1) { + int len = pos2 - pos1 + key.length()+3; + retValue = remove(pos1,len); + return retValue; + } + } + + return left(length()); +} + +/* +This was an attempt to do some performance optimizations for findKey. +With short (typical) strings the gain was ~40% e.g. 3.1 seconds with the existing code and +1.9 seconds with the experimental code for 50000 searches!. +But with longer strings the existing code required 3.3 seconds and the experimental code +required 20!!!! + +So no real optimization. +Also 3.3 seconds to do 50000 searches is not really bad. + +int locateKey( const QString &src, const QString &key, uint startPos = 0 ) +{ + for (uint s=startPos; s<src.length(); s++) { + if (src.at(s) == '<') { + if (src.at(s+key.length()+1) == '>') { + if (src.mid(s,key.length()) == key) + return s; + } + } + } + return -1; +} + +void QKeyString::xmlToRaw2( QString &value ) +{ + int pos; + + // convert <br> into \n + do { + pos = locateKey( value, "br" ); + if (pos > 0) { + value.remove(pos,4); + value.insert(pos,'\n'); + } + } while (pos >= 0); + + // convert <&br> into <br> + do { + pos = locateKey( value, "&br" ); + if (pos >= 0) { + value.remove(pos,5); + value.insert(pos,"<br>"); + } + } while (pos >= 0); +} + + bool QKeyString::findKey2( const QString &key, QString &keyValue, bool resetIfNotFound ) const +{ + int pos1, pos2; + pos1 = locateKey( *this, key ); + if (pos1>=0) { + pos2 = locateKey( *this, "/" + key, pos1 ); + if (pos2 > pos1) { + pos1+= key.length()+2; + keyValue = mid( pos1, pos2-pos1 ); + xmlToRaw2( keyValue ); + return TRUE; + } + } + + if (resetIfNotFound) + keyValue = ""; + + return FALSE; +} +*/ diff --git a/libqsystemtest/qkeystring.h b/libqsystemtest/qkeystring.h new file mode 100644 index 0000000..771260d --- /dev/null +++ b/libqsystemtest/qkeystring.h @@ -0,0 +1,46 @@ +/**************************************************************************** + ** + ** Definition of QKeyString class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef QKEYSTRING_H +#define QKEYSTRING_H + +#include "qtest.h" +#include <QString> + +class QKeyString : public QString +{ +public: + QKeyString(); + QKeyString( const QString &s ); + virtual ~QKeyString(); + + void addKey( const QString &key, const QString &value ); + void setKey( const QString &key, const QString &value ); + bool findKey( const QString &key, QString &keyValue, bool resetIfNotFound = TRUE ) const; + + void addKey( const QString &key, int value ); + void setKey( const QString &key, int value ); + bool findKey( const QString &key, int &value, bool resetIfNotFound = TRUE ) const; + + QString removeKey( const QString &key ); + +//private: + static void rawToXml( QString &value ); + static void xmlToRaw( QString &value ); +}; + +#endif + + + diff --git a/libqsystemtest/qtest.cpp b/libqsystemtest/qtest.cpp new file mode 100644 index 0000000..67aa8ac --- /dev/null +++ b/libqsystemtest/qtest.cpp @@ -0,0 +1,14 @@ +/**************************************************************************** + ** + ** Declaration of QTest class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + diff --git a/libqsystemtest/qtest.h b/libqsystemtest/qtest.h new file mode 100644 index 0000000..559f7ae --- /dev/null +++ b/libqsystemtest/qtest.h @@ -0,0 +1,115 @@ +/**************************************************************************** + ** + ** Definition of QTest class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef QTEST_H +#define QTEST_H + +#define QTEST_VERSION "1.2.0" + +class QTestSocket; +class QMouseEvent; +class QTimer; +class QTabWidget; +class QLabel; + +#ifdef Q_OS_TEMP +// remove the 'nonstandard extension used: '<funcName>' +// uses SEH and '<obj>' has destructor' +#pragma warning(disable: 4509) +#endif + +# define EV_PROCESS_TIME_LIMIT +# define EV_PROCESS_TIME_LIMIT_X2 + + +#define QTESTLOG_INTERNFATAL 99 +#define REMOTE_CONNECT_ERROR 98 +#define QTESTSOCKET_OPEN_ERROR 97 // QTestConnection::open couldn't open a connection + +#define OPENREMOTE_BUG 80 +#define OPENREMOTE_UNKNOWNERROR 79 +#define OPENREMOTE_NETWORKFAILURE 78 +#define OPENREMOTE_CONNECTIONREFUSED 77 +#define OPENREMOTE_NOFILES 76 +#define OPENREMOTE_IMPOSSIBLE 75 +#define OPENREMOTE_INTERNALERROR 74 +#define OPENREMOTE_NORESOURCES 73 +#define OPENREMOTE_INACCESSIBLE 72 +#define OPENREMOTE_ALREADYBOUND 71 + + +// ------------------------------ +// ------------------------------ +// ------------------------------ + +/*! + \enum QTestLog::LearnMode + Possible values: + None: Learnmode is off, + LearnNew: The system asks for verification of new (unknown) and erroneous widget snapshots. + LearnAll: The system asks for verification for each and every widget snapshot. +*/ + +// just a bunch of symbols to prevent polluting the namespace +class QTest +{ +public: + +enum LearnMode { None, LearnNew, LearnAll }; +enum TestFailMode { Abort, Continue }; +enum TestResult { + Invalid, // 0 + PassSelf, // 1 + FailSelf, // 2 + PassCompile, // 3 + FailCompile, // 4 + PassTest, // 5 + FailTest, // 6 + PassUnexpected, // 7 + FailExpected, // 8 + Warn, // 9 + __Error, // 10 Reserved for Test Framework related errors + Msg, // 11 + Note, // 12 used to be a Comment, but the function is named addComment and the string is NOTE + MakeRes, // 13 + SkipAll, // 14 + QDebug, // 15 qDebug message + QFatal, // 16 qFatal message + QWarning, // 17 qWarning message + DetectedConfiguration, // 18 + DetectedStyle, // 19 + Printf, // 20 + IgnoreResult, // 21 + StartingTestcase, // 22 + FinishedTestcase, // 23 + InternFatal, // 24 Internal Fatal error + Totals, // 25 Used to report the totals (passes, failures and skips) + TestFunction_, // 26 Used to report which function is currently being executed + Performance, // 27 Used to report performance values + StartingBuild, // 28 + FinishedBuild, // 29 + TestState, // 30 The current state of the testframework, e.g. initializing, running, cleanup, etc. + StartingScript, // 31 Used to indicate start stop of a script being executed + FinishedScript, // 32 + PassScript, // 33 + FailScript, // 34 + ScriptRes, // 35 + ExpectFailure, // 36 + RemoteIgnoreResult, // 37 + Retest, // 38 + SkipSingle + }; +}; + +#endif diff --git a/libqsystemtest/qtestlog.cpp b/libqsystemtest/qtestlog.cpp new file mode 100644 index 0000000..50c7c8e --- /dev/null +++ b/libqsystemtest/qtestlog.cpp @@ -0,0 +1,596 @@ +/**************************************************************************** + ** + ** Declaration of QTestLog class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "qtest.h" +#include "qtestlog.h" +#include "qkeystring.h" +//#include "qsystem.h" + +#include <QFile> +#include <QTextStream> +#include <QApplication> + +#include <stdlib.h> + +// The 'normal'qDebug can't be used in QTestLog because qDebug is catched and converted into a +// logresult. This is not what we want here because this interferes with the process flow that we are testing. +// So we use a macro to convert qDebug into a call to addResult which bypasses the qDebug catching mechanism. +#if defined(qDebug) +# undef qDebug +#endif +#define qDebug( a ) \ + addResult( QTest::QDebug, QString("%1").arg( a ), "" ); + +QTestLog::QTestLog() +{ + log_file = 0; + log_stream = 0; + logging_disabled = FALSE; + raw_mode = FALSE; + is_gui_mode = FALSE; + cache_enabled = FALSE; +} + +QTestLog::~QTestLog() +{ +} + +void QTestLog::selectGUIMode() +{ + is_gui_mode = TRUE; +} + +QString QTestLog::resultToStr( QTest::TestResult res ) +{ + QString result = QTestResult::resultToStr( res ); + +#ifndef Q_OS_TEMP +/* + Color values: + 0;30 = black + 0;31 = red + 0;32 = green + 0;33 = brown + 0;34 = blue 4 (darkest) + 0;35 = purple + 0;36 = blue 2 + 0;37 = l grey + 1;30 = d grey + 1;31 = l red + 1;32 = l green + 1;33 = yellow :-( + 1;34 = blue 3 + 1;35 = l purple + 1;36 = blue 1 (lightest) + 1;37 = ? +*/ + QString colored_mode; + // FIXME QSystem::getEnvKey( "QTEST_COLORED", colored_mode ); + if (colored_mode.isEmpty()) { + // keep this for backward compatibility + colored_mode = "0"; + if ( (getenv( "QTEST_COLORED" ) != 0) ) + colored_mode = "1"; + } + if ( !isLogging() && (true /*QTestSettings::coloringEnabled()*/) && (colored_mode == "1") ) { + if ( result.startsWith( "FAIL" ) ) + result = "\033[0;31m" + result + "\033[0m"; //red + else if ( result.startsWith( "PASS" ) ) + result = "\033[0;32m" + result + "\033[0m"; //green + else if ( result.startsWith( "SKIP" ) ) + result = "\033[0;37m" + result + "\033[0m"; //gray + else if ( result.startsWith( "PERFORM" ) ) + result = "\033[1;34m" + result + "\033[0m"; //blue 3 + else if ( result.startsWith( "QDEBUG" ) || result.startsWith( "QWARN" ) || + result.startsWith( "WARNING" ) ) + result = "\033[0;33m" + result + "\033[0m"; //brown (I don't like yellow, it's unreadable on my screen) +// result = "\033[1;33m" + result + "\033[0m"; //yellow + else if ( result.startsWith( "QFATAL" ) || result.startsWith( "ERROR" ) || + result.startsWith( "XFAIL" ) || result.startsWith( "XPASS" ) ) + result = "\033[1;31m" + result + "\033[0m"; //light red + } +#endif + + return result; +} + +bool QTestLog::isLoggingToFile() +{ + return log_file != 0; +} + +void QTestLog::cacheResults( bool doCache ) +{ + if (!cache_enabled || !doCache) + cached_results.clear(); + cache_enabled = doCache; +} + +void QTestLog::flushCachedResults() +{ + cache_enabled = FALSE; + for (uint i=0; i<(uint)(cached_results.count()); i++) { + QString s = cached_results[i]; + writeLog( QKeyString(s) ); + } + // make sure we don't append the same results multiple times + cached_results.clear(); +} + +bool QTestLog::setLoggingFile( const QString &fileName, bool append ) +{ + closeLogging(); + + if (log_file != 0) + delete log_file; + log_file = 0; + + if (fileName.isEmpty()) + return FALSE; + + log_file = new QFile( fileName ); + int options = QFile::WriteOnly; + if (append) + options |= QFile::Append; + if ( log_file->open( (QIODevice::OpenModeFlag)options) ) { + log_stream = new QTextStream( log_file ); + flushCachedResults(); + return TRUE; + } + + return FALSE; +} + +/* + Stops the logging of testresults to a logfile and closes the file. + After the call any further logging is send to stdout again. +*/ + +void QTestLog::closeLogging() +{ + if (log_stream != 0) + delete log_stream; + log_stream = 0; + if (log_file != 0) { + log_file->close(); + delete log_file; + } + log_file = 0; +} + +void QTestLog::setConfiguration( const QString &test, + const QString &hostName , + const QString &OSName , + const QString &config , + const QString &makeSpec ) +{ + QString cfg = hostName + + ", " + OSName + + ", " + config + + ", " + makeSpec; + writeLog( QTest::DetectedConfiguration, test, cfg ); +} + +void QTestLog::writeLog( QTestResult::TestResult result, + const QString &test, + const QString &reason, + const QString &dataTag, + const QString &file, + const int line ) +{ + QTestResultRecord rec( result, test, reason, dataTag, file, QString("%1").arg(line), "" ); + writeLog( rec ); +} + +void QTestLog::writeLog( const QKeyString &tmp ) +{ + QTestResultRecord rec( tmp ); + writeLog( rec ); +} + +void QTestLog::writeLog( QTestResultRecord rec ) +{ + if (loggingDisabled()) + return; + + QString test = rec.testName(); + + if (cache_enabled) { + cached_results.append( rec.keyString() ); + return; + } + + if (is_gui_mode) { //qvalidator + if (rec.result == QTest::FailTest || + rec.result == QTest::FailCompile) { + emit logResult( rec.keyString() ); + } + } + + if ( rawLoggingMode() ) { + if (is_gui_mode) { + emit logEvent( rec.keyString() ); + } else { + if (isLogging() ) { + QString key_string = rec.keyString(); // just for debugging... + *log_stream << rec.keyString() << "\n"; + log_stream->flush(); + } else { + // FIXME OutputDebugString( rec.keyString() ); + } + } + } else { + QString S; + if (rec.result == QTest::StartingTestcase || + rec.result == QTest::FinishedTestcase) { + QString insert; + if (rec.result == QTest::StartingTestcase) + insert = "Start testing of "; + else + insert = "Finished testing of "; + insert += test; + int c = (78 - insert.length()) / 2; + if (c < 78) { + while ((int)S.length() < c) + S += "*"; + } + S += " " + insert + " "; + while (S.length() < 78) + S += "*"; + + } else if (rec.result == QTest::StartingBuild || + rec.result == QTest::FinishedBuild) { + QString insert; + if (rec.result == QTest::StartingBuild) + insert = "Start building of "; + else + insert = "Finished building of "; + insert += test; + int c = (78 - insert.length()) / 2; + if (c < 78) { + while ((int)S.length() < c) + S += "*"; + } + S += " " + insert + " "; + while (S.length() < 78) + S += "*"; + + } else if (rec.result == QTest::StartingScript || + rec.result == QTest::FinishedScript) { + QString insert; + if (rec.result == QTest::StartingScript) + insert = "Start script execution on "; + else + insert = "Finished script execution on "; + insert += test; + int c = (78 - insert.length()) / 2; + if (c < 78) { + while ((int)S.length() < c) + S += "*"; + } + S += " " + insert + " "; + while (S.length() < 78) + S += "*"; + + } else { + S = resultToStr( rec.result ) + ": "; + + if (rec.result != QTest::Note && + rec.result != QTest::StartingTestcase && + rec.result != QTest::StartingBuild && + rec.result != QTest::StartingScript && + rec.result != QTest::FinishedTestcase && + rec.result != QTest::FinishedBuild && + rec.result != QTest::FinishedScript && + rec.result != QTest::DetectedConfiguration && + rec.result != QTest::DetectedStyle && + rec.result != QTest::ScriptRes && + rec.result != QTest::MakeRes) { + if (test != "" ) { + S += test; + + if (test.indexOf( "::") > 0) { + if ( rec.result != QTest::PassTest && + rec.result != QTest::IgnoreResult && + rec.dataTag != "" ) { + S += QString( "(%1) " ).arg( rec.dataTag ); + } else { + S += "() "; + } + } else { + S += " "; + } + } + } + + if (rec.result == QTest::IgnoreResult) { + // ignoreResult is a message in a message. + // the actual res is hidden in the dataTag, so we decode it here. + int actual_res = rec.dataTag.toInt(); + // and present it in the string + S += "Ignore a '" + resultToStr( (QTest::TestResult)actual_res ).simplified() + "' with string '" + rec.reason + "'"; + } else { + if ( rec.reason != "" ) + S += rec.reason; + } + + if ( rec.file != "" ) { + if (rec.line > -1) + S += "\n Loc: " + rec.file; + + if (rec.line > -1) + S += QString("::%1").arg(rec.line); + } + + if ( rec.comment != "" ) { + S += " " + rec.comment; + } + } + + if (is_gui_mode) { + emit logEvent( S ); + } else { + if (isLogging() ) { + if (log_stream != 0) { + *log_stream << S << "\n"; + log_stream->flush(); + } + } else { + if (rec.result == QTest::IgnoreResult || + rec.result == QTest::RemoteIgnoreResult) + return; +//FIXME OutputDebugString( QString(S).ucs2() ); + } + } + } +} + +void QTestLog::disableLogging( bool disable ) +{ + logging_disabled = disable; +} + +bool QTestLog::loggingDisabled() +{ + return logging_disabled; +} + +bool QTestLog::isLogging() const +{ + return log_stream != 0; +} + +void QTestLog::setRawLoggingMode( bool enableRaw ) +{ + raw_mode = enableRaw; +} + +bool QTestLog::rawLoggingMode() +{ + return raw_mode; +} + +QString QTestLog::fileName() +{ + if (log_file != 0) + return log_file->fileName(); + return ""; +} + +// ----------------------------------------------- + +bool QTestSettings::coloring_disabled = FALSE; +bool QTestSettings::verbose_mode = FALSE; +bool QTestSettings::wait_at_end = FALSE; +bool QTestSettings::brief_mode = FALSE; +QTest::LearnMode QTestSettings::learn_mode = QTest::None; +bool QTestSettings::debug_mode = FALSE; +bool QTestSettings::is_gui_test = FALSE; +bool QTestSettings::show_performance = TRUE; +bool QTestSettings::show_qdebug = TRUE; +QString QTestSettings::cur_configuration = ""; +bool QTestSettings::logging_to_ftp = FALSE; +bool QTestSettings::prompt_mode = FALSE; + +QTestSettings::QTestSettings() +{ +} + +QTestSettings::~QTestSettings() +{ +} + +void QTestSettings::setPromptMode( bool on ) +{ + prompt_mode = on; +} + +bool QTestSettings::promptMode() +{ + return prompt_mode; +} + +/*! + Returns the learn mode of the instance. + + In 'learn new' mode the user is asked for confirmation when a pixmap/snapshot is + not yet available in the associated testbase or when it does exist but is different + from the pixmap/snapshot taken from the widget under test (e.g. expected and actual + pixmap/snapshot are different). + + In 'learn all' mode the user is asked for confirmation for each and every pixmap/ + snapshot taken from the widget under test. + + In either mode, if the user accepts the pixmap/snapshot it is appended (or replaced) + to the testbase. + + \sa setLearnMode() +*/ + +QTest::LearnMode QTestSettings::learnMode() +{ + return learn_mode; +} + +/*! + Sets the learning mode of the test instance to \a newMode. + The learning mode is typically set by means of a command line parameter and should + never be called directly in your testcase. +*/ + +void QTestSettings::setLearnMode( QTest::LearnMode newMode ) +{ + learn_mode = newMode; +} + +void QTestSettings::setBriefLoggingMode( bool enableBrief ) +{ + brief_mode = enableBrief; +} + +bool QTestSettings::briefMode() +{ + return brief_mode; +} + +void QTestSettings::setDebugMode( bool debugOn ) +{ + debug_mode = debugOn; +} + +bool QTestSettings::debugMode() +{ + return debug_mode; +} + +void QTestSettings::setVerbose( bool verbose ) +{ + verbose_mode = verbose; +} + +bool QTestSettings::verbose() +{ + return verbose_mode; +} + +/*! + If \a doWait is TRUE the testresult will present a "press a key to continue" + message after the testcase has completed. + After the message is shown the testcase will halt until a key is pressed. +*/ + +void QTestSettings::setWaitAtEnd( bool doWait ) +{ + wait_at_end = doWait; +} + +bool QTestSettings::waitAtEnd() +{ + return wait_at_end; +} + +void QTestSettings::enableColoring( bool enable ) +{ + coloring_disabled = !enable; +} + +bool QTestSettings::coloringEnabled() +{ + return !coloring_disabled; +} + +void QTestSettings::setGuiTest( bool isGuiTest ) +{ + is_gui_test = isGuiTest; +} + +bool QTestSettings::isGuiTest() +{ + return is_gui_test; +} + +void QTestSettings::setShowPerformance( bool show ) +{ + show_performance = show; +} + +bool QTestSettings::showPerformance() +{ + return show_performance; +} + +void QTestSettings::setShowQDebug( bool show ) +{ + show_qdebug = show; +} + +bool QTestSettings::showQDebug() +{ + return show_qdebug; +} + +void QTestSettings::setLoggingToFtp( bool isLogging ) +{ + logging_to_ftp = isLogging; +} + +bool QTestSettings::loggingToFtp() +{ + return logging_to_ftp; +} + +void QTestSettings::setConfiguration( const QString &hostName , + const QString &OSName , + const QString &configuration , + const QString &makeSpec ) +{ + cur_configuration = hostName + + ", " + OSName + + ", " + configuration + + ", " + makeSpec; +} + +bool QTestSettings::getConfiguration( const QString &configLine, + QString &hostName , + QString &OSName , + QString &configuration , + QString &makeSpec ) +{ + QString qtVersion, qtConfiguration; + + QStringList sl = configLine.split( "," ); + if (sl.count() == 4) { + hostName = sl[0].simplified(); + OSName = sl[1].simplified(); + qtVersion = ""; + qtConfiguration = sl[2].simplified(); + configuration = qtConfiguration; + makeSpec = sl[3].simplified(); + } else { + qtConfiguration = sl[0].simplified(); + hostName = ""; + OSName = ""; + configuration = ""; + makeSpec = ""; + return FALSE; + } + + return (hostName != "" && OSName != ""); // allow for config string without an actual config and makespec (which is the case for scripts) +} + +QString QTestSettings::curConfiguration() +{ + return cur_configuration; +} + diff --git a/libqsystemtest/qtestlog.h b/libqsystemtest/qtestlog.h new file mode 100644 index 0000000..0662bb0 --- /dev/null +++ b/libqsystemtest/qtestlog.h @@ -0,0 +1,154 @@ +/**************************************************************************** + ** + ** Definition of QTestLog class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef QTESTLOG_H +#define QTESTLOG_H + +#include "qtest.h" +#include "qtestresult.h" + +#include <QString> +#include <QStringList> +#include <QObject> + +class QFile; +class QTextStream; + +class QTestSettings : public QObject +{ +public: + QTestSettings(); + ~QTestSettings(); + + static void enableColoring( bool enable ); + static bool coloringEnabled(); + + static void setLearnMode( QTest::LearnMode newMode ); + static QTest::LearnMode learnMode(); + + static void setVerbose( bool verbose ); + static bool verbose(); + + static void setBriefLoggingMode( bool enableBrief ); + static bool briefMode(); + + static void setDebugMode( bool debugOn ); + static bool debugMode(); + + static void setPromptMode( bool on ); + static bool promptMode(); + + static void setWaitAtEnd( bool doWait ); + static bool waitAtEnd(); + + static void setGuiTest( bool isGuiTest ); + static bool isGuiTest(); + + static void setShowPerformance( bool show ); + static bool showPerformance(); + + static void setShowQDebug( bool show ); + static bool showQDebug(); + + static void setLoggingToFtp( bool isLogging ); + static bool loggingToFtp(); + + static void setConfiguration( const QString &hostName , + const QString &OSName , + const QString &config , + const QString &makeSpec ); + static bool getConfiguration( const QString &configLine, + QString &hostName , + QString &OSName , + QString &configuration , + QString &ccVersion ); + static QString curConfiguration(); + +private: + static bool brief_mode; + static bool coloring_disabled; + static QTest::LearnMode learn_mode; + static bool debug_mode; + static bool verbose_mode; + static bool wait_at_end; + static bool is_gui_test; + static bool show_performance; + static bool show_qdebug; + static bool logging_to_ftp; + static QString cur_configuration; + static bool prompt_mode; +}; + + +class QTestLog : public QObject +{ + Q_OBJECT + +public: + QTestLog(); + virtual ~QTestLog(); + + void selectGUIMode(); + + QString resultToStr( QTest::TestResult res ); + + void writeLog( QTestResultRecord rec ); + void writeLog( QTestResult::TestResult result, + const QString &test, + const QString &reason, + const QString &dataTag = "", + const QString &file = "", + const int line = -1 ); + void writeLog( const QKeyString &tmp ); + + void setConfiguration( const QString &test, + const QString &hostName , + const QString &OSName , + const QString &config , + const QString &makeSpec ); + + QString fileName(); + bool setLoggingFile( const QString &fileName, bool append = FALSE ); + bool isLoggingToFile(); + void closeLogging(); + bool isLogging() const; + + void setRawLoggingMode( bool enableRaw ); + bool rawLoggingMode(); + + void disableLogging( bool disable ); + bool loggingDisabled(); + + void cacheResults( bool doCache ); + void flushCachedResults(); + +signals: + void logEvent( const QString &s ); + void logResult( const QKeyString &s ); + void logScreen( const QString &s ); + +private: + QFile *log_file; + QTextStream *log_stream; + + bool logging_disabled; + bool raw_mode; + + QStringList cached_results; + bool cache_enabled; + bool is_gui_mode; +}; + +#endif + diff --git a/libqsystemtest/qtestresult.cpp b/libqsystemtest/qtestresult.cpp new file mode 100644 index 0000000..c7bfb5f --- /dev/null +++ b/libqsystemtest/qtestresult.cpp @@ -0,0 +1,2570 @@ +/**************************************************************************** + ** + ** Declaration of QTestResult class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#include "qtest.h" +#include "qtestresult.h" +#include "qtestlog.h" +//#include "qsystem.h" + +#include <QDir> +#include <QTextStream> +#include <QTime> + +#ifndef QTEST_SUPPORT +# include "qtestremote.h" +#endif + +QTestResult testResult; + +class QTestResultTestCase; +class QTestResultTestFunc; +class QTestResultTestData; + +class ExpectedFailures +{ +public: + ExpectedFailures(); + virtual ~ExpectedFailures(); + + bool isFailExpected( int dataIndex, QString &comment, bool &abort, bool reset ); + void setFailure( int dataIndex, QString comment, QTest::TestFailMode mode ); + void clear(); + void reset(); + +private: + struct ExpectedFailRec + { + QTest::TestFailMode mode; + int index; + QString comment; + ExpectedFailRec *next; + }; + void append( int index, QString comment, QTest::TestFailMode mode ); + bool getFailure( uint index, ExpectedFailRec *&rec ); + uint count(); + +private: + ExpectedFailRec *first_expected_fail; +}; + +class IgnoreResults +{ +public: + IgnoreResults(); + virtual ~IgnoreResults(); + + void ignoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch ); + bool mustIgnoreResult( QTest::TestResult res, const QString &reason ); + bool resetIgnoreResult( QString &errors ); + bool getIgnoreResultIndex( QTest::TestResult res, const QString &reason, int &index ); + +private: + QStringList ignore_result; +}; + +class QTestResultTestBase : public QObject +{ +public: + QTestResultTestBase( QTestResultTestCase *test_case, QObject *parent ); + virtual ~QTestResultTestBase(); + + void appendResult( bool spontaneous, QTest::TestResult result, + const QString &reason, + const QString &file = "", + int line = -1, + const QString &comment = "" ); + + virtual void ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ); + bool resetIgnoreResult( bool remote, QString &errors ); + + void setRetest( bool busy ); + void resetResult(); + + virtual void setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ); + + QTestResultTestCase* curTestCase(); + + QTest::TestResult result() const; + void setResult( QTest::TestResult res ); + +protected: + ExpectedFailures expected_failures; + IgnoreResults local_ignores; + IgnoreResults remote_ignores; + QString last_result; + uint warning_count; + QTestResultTestCase *test_case; + bool retest_busy; + bool skip_all; + + QTest::TestResult test_result; +}; + +class QTestResultTestCase : public QTestResultTestBase +{ +public: + QTestResultTestCase(); + QTestResultTestCase( const QString &name ); + virtual ~QTestResultTestCase(); + + void setName( const QString &name, bool verbose = TRUE, bool build = FALSE ); + QString name(); + + void setCurrentTestFunction( const QString &name ); + QString curFunctionName( bool fullName ) const; + uint totalTests() const; + uint noOfExecutedTests() const; + + void setCurrentTestData( const QString &dataTag ); + QString curDataTag(); + int curDataIndex() const; + uint curDataCount() const; + + void setCurrentTestLocation(QTestResult::TestLocation loc); + QTestResult::TestLocation currentTestLocation(); + + QTestResultTestFunc* findFunction( const QString &funcName ); + QTestResultTestFunc* curFunction(); + + bool curTestPassed(); + bool curTestVerified(); + bool curTestFailed(); + bool curTestSkipped(); + + // returns the number of skips for the current testfunction + uint curSkipCount() const; + + // returns the total number of skipped tests + uint totalSkipped() const; + + // returns the total number of passed tests + uint totalPassed() const; + + // returns the total number of failed tests + uint totalFailed() const; + + virtual void ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ); + virtual void setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ); + + void addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file = "", + int line = -1, + const QString &comment = "" ); + + QTestLog* testLog(); + void writeLog( QTestResultRecord rec ); + + void retest( bool busy ); + +private: + QTestLog test_log; + QString m_name; + QTestResultTestFunc *m_first; + QTestResultTestFunc *cur_func; + QTime timer; + QTestResult::TestLocation cur_test_location; + + bool build_mode; +}; + +class QTestResultTestFunc : public QTestResultTestBase +{ +public: + QTestResultTestFunc( const QString &funcName, QTestResultTestCase *parent ); + virtual ~QTestResultTestFunc(); + + QString name(); + + QTestResultTestFunc *next(); + void setNext( QTestResultTestFunc *n ); + + void setCurrentTestData( const QString &name ); + QTestResultTestData* findData( const QString &dataName ); + QTestResultTestData* curData(); + QString curDataTag(); + int curDataIndex(); + + uint dataCount(); + uint skipCount(); + uint failCount(); + uint passCount(); + + bool passed( bool current ); + bool verified( bool current ); + bool failed( bool current ); + bool skipped( bool current ); + bool hasFailures(); + + void ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ); + virtual void setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ); + + void addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file = "", + int line = -1, + const QString &comment = "" ); + + void retest( bool busy ); + +protected: + friend class QTestResultTestCase; + +private: + QString func_name; + QTestResultTestData *first_data; + QTestResultTestFunc *next_func; + QTestResultTestData *cur_data; +}; + +class QTestResultTestData : public QTestResultTestBase +{ +public: + QTestResultTestData( const QString &tagName, bool skipAll, QTestResultTestFunc *parent ); + virtual ~QTestResultTestData(); + + QString name(); + QString dataTag(); + + QTestResultTestData* next(); + void setNext( QTestResultTestData *n ); + + void addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file = "", + int line = -1, + const QString &comment = "" ); + + bool verified(); + + void retest( bool busy ); + virtual void setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ); + +protected: + friend class QTestResultTestFunc; + +private: + QString data_name; + QTestResultTestData *next_data; +}; + +/*! + \class QTestResult qtestresult.h + \brief The QTestResult class processes testresults from the executed testcase and + sends these results to a configured output. + + The class is typically called by QTestCase and doesn't need any direct intervention + from a user, but if you have a desire to add additional information into the testresult + you can do so. + + Simply create an instance of QTestResult and call one of it's functions. QTestResult is + a singleton class and will always point at the same instance, i.e. there is only one testresult + to which all messages will be appended. + + If you work inside your testcase you don't even have to create a QTestResult instance: QTestCase + has a predifed instance named testResult that is accessible from your derived testcase. + + \sa QTestCase + \link testing.html Introduction to testing \endlink + \link overview.html QTest overview \endlink +*/ + +class QTestResultSuite : public QObject +{ +public: + QTestResultSuite(); + virtual ~QTestResultSuite(); + + QTestResultTestCase* testCase(); + +private: + QTestResultTestCase *test_case; +}; + +/*! + \enum QTestResult::TestLocation + Possible values can be NoWhere, DataFunc, InitFunc, Func, CleanupFunc, Starting, LaunchApps, InitTestCase, Executing, KillApps, CleanupTestCase. +*/ + +#ifndef QTEST_SUPPORT + + QtMsgHandler oldMsgHandler; + +/* + Message handler that converts qDebug, qWarning and qFatal messages into test results. +*/ + +static void testCaseMsgHandler( QtMsgType type, const char *msg ) +{ + // Avoid infinite recursion: it is possible that one function we call from + // the message handler will call qWarning(), etc. -- ignore such messages. + static bool alreadyCalled = FALSE; + static char *secondMsg = 0; + static QtMsgType secondType; + if ( alreadyCalled ) { + if ( !secondMsg ) { + const int msgSize = 256; + secondMsg = new char[msgSize]; + strncpy( secondMsg, msg, msgSize-1 ); + secondMsg[msgSize-1] = 0; + secondType = type; + } + return; + } + alreadyCalled = TRUE; + + QString s; + s = QString( "%1" ).arg( msg ); + + switch ( type ) { + case QtDebugMsg: + //FIXME This is an ugly hack that I don't like at all. + if (!s.startsWith( "Qt: gdb: -nograb added to command-line options.") && !s.startsWith( "Connected to VFB server:" ) ) + testResult.addResult( QTest::QDebug, s ); + break; + case QtWarningMsg: + if (s.startsWith("ASSERT:")) { + if (testResult.debugMode()) { + testResult.addWarning( s ); // adding a warning to the testresult should be safe... + oldMsgHandler( type, msg ); + } else { + testResult.addFatal( s ); + } + } else { + testResult.addResult( QTest::QWarning, s ); + } + break; + case QtFatalMsg: + if (testResult.debugMode()) { + testResult.addWarning( "<<< QFatal >>> "+ s ); // adding a warning to the testresult should be safe... + if (oldMsgHandler != 0) + oldMsgHandler( type, msg ); + } else { + testResult.addFatal( s ); + } + break; + default: + + { /* do nothing, avoid compiler warnings */ } + } + if ( secondMsg ) { + QString typeStr; + switch ( secondType ) { + case QtDebugMsg: + typeStr = "qDebug()"; + break; + case QtWarningMsg: + typeStr = "qWarning()"; + break; + case QtFatalMsg: + typeStr = "qFatal()"; + break; + default: + { /* do nothing, avoid compiler warnings */ } + } + testResult.addWarning( QString("Received a %1 in the message handler: '%2'").arg(typeStr).arg(secondMsg) ); + delete[] secondMsg; + secondMsg = 0; + } + + alreadyCalled = FALSE; +} +#endif + +QTestResultSuite p; + +// *************************************************** +// *************************************************** + +/*! + The constructor. QTestResult is a singleton class, so all instances of it will end up + with the same private instance. +*/ +QTestResult::QTestResult() +{ +} + +/*! + Destroys the instance. +*/ +QTestResult::~QTestResult() +{ +} + +/*! + \internal +*/ +void QTestResult::installMsgHandler() +{ +#ifndef QTEST_SUPPORT + oldMsgHandler = qInstallMsgHandler( testCaseMsgHandler ); +#endif +} + +/*! + Appends a fatal \a message to the testresult and then aborts the testcase execution. +*/ +void QTestResult::addFatal( const QString &message ) +{ + p.testCase()->addResult( TRUE, QTest::QFatal, message ); +} + +/*! + Appends a \a message to the testresult. +*/ +void QTestResult::addMessage( const QString &message ) +{ + p.testCase()->addResult( TRUE, QTest::Msg, message ); +} + +/*! + Appends an error \a message to the testresult. +*/ +void QTestResult::addError( const QString &message ) +{ + p.testCase()->addResult( TRUE, QTest::__Error, message ); +} + +/*! + Adds a \a note to the testresult. +*/ +void QTestResult::addNote( const QString ¬e ) +{ + p.testCase()->addResult( TRUE, QTest::Note, note ); +} + +/*! + Appends a skip message to the testresult that is clarified by a\a reason. + If \a skipAll is set to TRUE all instances of the current testfunction will be skipped + and only one skip message will be appended to the testresult. If \a skipAll is set to + FALSE every execution of the testfunction will result in a separate skip warning in the + testresult. + The \a file and \a line values will be shown in the testresult to identify the codeline + on which the skip occurred. +*/ +void QTestResult::skip( const QString &reason, bool skipAll, + const QString &file, int line ) +{ + if (skipAll) + p.testCase()->addResult( TRUE, QTest::SkipAll, reason, file, line, "" ); + else + p.testCase()->addResult( TRUE, QTest::SkipSingle, reason, file, line, "" ); +} + +/*! + Appends a warning message described by \a text to the testresult. + The testresult assumes the warning has occurred in the current testcase/testfunction + (as set by setTestFunction()). +*/ +void QTestResult::addWarning( const QString &text ) +{ + p.testCase()->addResult( TRUE, QTest::Warn, text ); + +} + +/*! + Creates a string describing a test failure using the given parameters and appends + the string to the testresult. + The testresult assumes the fail has occurred in the current testcase/testfunction + (as set by setTestFunction()). The \a text describes the problem and \a file and + \a line finally refer to the actual line in the testfunction where the problem occurred. +*/ +void QTestResult::addFailure( const QString &text, const QString &file, int line ) +{ + p.testCase()->addResult( TRUE, QTest::FailTest, text, file, line, "" ); +} + +/*! + \obsolete + Please use ignoreResult() in new code. +*/ +/* +void QTestResult::setIgnoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch ) +{ + ignoreResult( res, reason, exactMatch ); +} +*/ + +/*! + Instructs the testresult to ignore a \a result of the specified \a reason + When the result is reported it will NOT be shown in the testresult + and the test will not abort, even if it is a failure. + ignoreResult may be called multiple times to define a series of results to be ignored. + If \a exactMatch is TRUE, the specified string must match the logged string exactly + else the specified \a result is expected to be the start of a reported result. +*/ +void QTestResult::ignoreResult( const QTest::TestResult result, const QString &reason, bool exactMatch ) +{ + p.testCase()->ignoreResult( FALSE, result, reason, exactMatch ); +} + +/*! + Informs the testresult that \a result with \a reason must be ignored. + This means that the testresult is actually waiting for the specified result to be logged. + When the logging happens the result is ignored (i.e. not shown), but if the logging does not + occur then this is treated as a failure. + If \a exactMatch is TRUE then the specified \a reason must be exactly the same as the + one that is logged, and if \a exactMatch is FALSE then the \a reason is expected to be + start of the logged string. +*/ +void QTestResult::remoteIgnoreResult( const QTest::TestResult result, const QString &reason, bool exactMatch ) +{ + p.testCase()->ignoreResult( TRUE, result, reason, exactMatch ); +} + +/*! + Returns the number of tests that are executed. +*/ +uint QTestResult::noOfExecutedTests() const +{ + return p.testCase()->noOfExecutedTests(); +} + +/*! + Returns the number of detected failures. +*/ +uint QTestResult::noOfFailures() const +{ + return p.testCase()->totalFailed(); +} +/*! + Returns the number of detected passes. +*/ +uint QTestResult::noOfPasses() const +{ + return p.testCase()->totalPassed(); +} + +/*! + Sets the name of the testcase that is currently being executed to \a testCaseName. + If \a verbose is set to TRUE all logging is done in verbose mode, if set to FALSE then + build results are cached and are only shown when the build actually fails. + The \a build parameter determines what information is shown for the testcase, i.e if + \a build is TRUE the "build time" is shown and if FALSE then the "execution time" is shown, etc. +*/ +void QTestResult::setCurrentTestCase( const QString &testCaseName, bool verbose, bool build ) +{ + p.testCase()->setName( testCaseName, verbose, build ); +} + +/*! + Returns the name of the current testcase. +*/ +QString QTestResult::curTestCase() +{ + return p.testCase()->name(); +} + +/*! + Sets the name of the testfunction that is currently being executed to \a testFunc. +*/ +void QTestResult::setCurrentTestFunction( const QString &testFunc ) +{ + p.testCase()->setCurrentTestFunction( testFunc ); +} + +/*! + Sets the name of the testdata that is currently being used to \a dataTag. +*/ +void QTestResult::setCurrentTestData( const QString &dataTag ) +{ + p.testCase()->setCurrentTestData( dataTag ); +} + +/*! + Returns the current data index, i.e. the zero based index of the dataset that is + currently being executed. +*/ +int QTestResult::curDataIndex() const +{ + return p.testCase()->curDataIndex(); +} + +/*! + Sets the current testlocation to \a loc. +*/ +void QTestResult::setCurrentTestLocation(TestLocation loc) +{ + p.testCase()->setCurrentTestLocation( loc ); +} + +/*! + Returns the current testlocation. +*/ +QTestResult::TestLocation QTestResult::currentTestLocation() +{ + return p.testCase()->currentTestLocation(); +} + +/*! + Informs the testresult whether a retest is busy or not. If \a busy is set to TRUE then + logged failures are ignored (not logged). +*/ +void QTestResult::retest( bool busy ) +{ + p.testCase()->retest( busy ); +} + +/*! + \internal + Formats a testresult message using \a resType, \a result, \a dataTag, + \a file, \a line and \a comment and appends the + message to the logging file. If no logging file has been specified the + message will be send to stdout. + The \a resType specifies the type of the result e.g. TestFailure, + TestPass, CompileFailure, etc. + The \a result is the string that describes the result e.g. 'Expected and actual + values are not the same'. + The \a dataTag is the unique string that is associated with the dataset that was + in use when the result was logged. + The \a file and \a line describe the location in the testcase where the problem + occurred. + The \a comment is an additional string that only contains information in special + cases like when an expected failure occurs. + The \a halt variable finally stops the execution of the testfunction if it's value + is set to TRUE. +*/ +void QTestResult::addResult( const TestResult result, + const QString &reason, + const QString &file, + int line, + const QString &comment ) +{ + p.testCase()->addResult( TRUE, result, reason, file, line, comment ); +} + +/*! + Decodes \a keyString and uses the decoded information to add a result. +*/ + +void QTestResult::addResult( const QString &keyString ) +{ + QTestResultRecord rec( keyString ); + p.testCase()->addResult( TRUE, rec.result, rec.reason, rec.file, rec.line, rec.comment ); +} + +/*! + Writes \a dbgtxt to the debugging log output. +*/ +void QTestResult::debugLog( const QString &dbgtxt ) +{ + p.testCase()->addResult( TRUE, QTest::QDebug, dbgtxt ); +} + +/*! + Triggers the testresult to expect a failure when the testfunction is executed. + The \a dataIndex specifies the index to the testdata for which the failure is expected. + A value of -1 means 'all' applied testdata, a value of 0 or higher references a specific dataset. + If the testfunction is executed with the specified dataIndex it 'should' throw a failure. If that + happens the testresult will record the failure as an expected failure and will add the specified + \a comment to the result to give a clue as to why this failure was expected. If the testfunction + doesn't fail (e.g. it passes) this will be recorded as an unexpected pass. + The \a mode can be set to Abort or Continue. Abort means the testfunction will abort and Continue + obviously means the testfunction will continue. +*/ +void QTestResult::setExpectedFailure( int dataIndex, QString comment, TestFailMode mode ) +{ + p.testCase()->setExpectedFailure( dataIndex, comment, mode ); +} + +/*! + Returns the current datatag for the testfunction being executed. +*/ +QString QTestResult::curDataTag() +{ + return p.testCase()->curDataTag(); +} + +/*! + \internal +*/ +void QTestResult::addMakeResult( const QString &result ) +{ + p.testCase()->addResult( TRUE, QTest::MakeRes, result ); +} + +/*! + Returns the current testfunction name. If \a fullName is TRUE a fully qualified + name of testcase::testfunc is returned. +*/ +QString QTestResult::curTestFunc( bool fullName ) const +{ + return p.testCase()->curFunctionName( fullName ); +} + +/*! + Adds a success line (PASS) with the specified \a text to the testresult. +*/ +void QTestResult::addSuccess( const QString &text, const QString &file, int line ) +{ + p.testCase()->addResult( TRUE, QTest::PassTest, text, file, line ); +} + +/*! + \internal + Returns a pointer to the current testlog instance. +*/ +QTestLog* QTestResult::testLog() +{ + return p.testCase()->testLog(); +} + +/*! + Signals the logging module that the specified \a line in \a fileName is + about to be executed. When an error occurs, the given fileName and line + number are used to report the error. +*/ + +bool QTestResult::enterCheckPoint( const QString &fileName, int line ) +{ + // Not using these parameters at the moment. But the intention is that I will use them in future + Q_UNUSED( fileName ); + Q_UNUSED( line ); + + return !checkPointError( 0 ); +} + +/*! + Returns TRUE and a \a reason if an error has occurred since the last call + to enterCheckPoint. +*/ + +bool QTestResult::checkPointError( QString *reason ) +{ + if (p.testCase()->curTestFailed() || p.testCase()->curTestSkipped()) { + if (reason != 0) + *reason = "Sorry, i'll have to figure out what the reason was"; + return TRUE; + } + return FALSE; +} + +/*! + Returns TRUE if the current test has passed, and \a withSkips returns TRUE if one + or more of the datasets where skipped. +*/ + +bool QTestResult::currentTestPassed( bool &withSkips ) const +{ + withSkips = p.testCase()->curTestSkipped(); + return p.testCase()->curTestPassed(); +} + +/*! + Returns the result string that is associated with the given \a res value. +*/ + +QString QTestResult::resultToStr( QTest::TestResult res ) +{ + QString result; + + switch (res) + { + case QTest::PassCompile: + result = "PASS "; + break; + case QTest::PassSelf: + result = "PASS "; + break; + case QTest::PassTest: + result = "PASS "; + break; + case QTest::FailCompile: + result = "FAIL! "; + break; + case QTest::FailSelf: + result = "FAIL! "; + break; + case QTest::FailTest: + result = "FAIL! "; + break; + case QTest::PassUnexpected: + result = "XPASS "; + break; + case QTest::FailExpected: + result = "XFAIL "; + break; + case QTest::SkipAll: + case QTest::SkipSingle: + result = "SKIP "; + break; + case QTest::Warn: + result = "WARNING"; + break; + case QTest::__Error: + result = "ERROR "; + break; + case QTest::MakeRes: + result = "$"; + break; + case QTest::ScriptRes: + result = "$"; + break; + case QTest::Note: + result = "NOTE "; + break; + case QTest::Totals: + result = "TOTALS "; + break; + case QTest::Msg: + result = "MSG "; + break; + case QTest::QDebug: + result = "QDEBUG "; + break; + case QTest::QFatal: + result = "QFATAL "; + break; + case QTest::InternFatal: + result = "FATAL! "; + break; + case QTest::QWarning: + result = "QWARN "; + break; + case QTest::DetectedConfiguration: + result = "CONFIG "; + break; + case QTest::DetectedStyle: + result = "STYLE "; + break; + case QTest::Printf: + result = "PRINTF "; + break; + case QTest::IgnoreResult: + result = "IGNORE "; + break; + case QTest::RemoteIgnoreResult: + result = "IGNORE2"; + break; + case QTest::StartingTestcase: + case QTest::StartingBuild: + case QTest::StartingScript: + case QTest::FinishedTestcase: + case QTest::FinishedBuild: + case QTest::FinishedScript: + result = "NOTE "; + break; + case QTest::TestFunction_: + result = "TF "; + break; + case QTest::TestState: + result = "STATE "; + break; + case QTest::Performance: + result = "PERFORM"; + break; + default: + result = "???? "; + break; + } + return result; +} + +/*! + Returns the learn mode of the testcase. +*/ + +QTest::LearnMode QTestResult::learnMode() +{ + return QTestSettings::learnMode(); +} + +/*! + Returns TRUE if the testcase is running in debug mode (as set on the command line). +*/ + +bool QTestResult::debugMode() const +{ + return QTestSettings::debugMode(); +} + +/*! + Returns the current configuration. +*/ + +QString QTestResult::curConfiguration() +{ + return QTestSettings::curConfiguration(); +} + +/*! + Sets the currently used configuration to \a hostName, \a OSName, \a config and \a makeSpec. +*/ + +void QTestResult::setConfiguration( const QString &hostName, + const QString &OSName, + const QString &config, + const QString &makeSpec ) +{ + QTestSettings::setConfiguration( hostName, OSName, config, makeSpec ); + addResult( QTest::DetectedConfiguration, + QTestSettings::curConfiguration(), + "", + -1, + "" ); +} + +/*! + Returns TRUE of the current test failed. +*/ + +bool QTestResult::currentTestFailed() const +{ + return p.testCase()->curTestFailed(); +} + +#ifdef QTEST_SUPPORT + +bool QTestResult::loadResult( const QString &logging_file, bool &isConsoleApp ) +{ + isConsoleApp = FALSE; + + QDir D; + if (!D.exists( logging_file )) { + return FALSE; + } + + QFile F( logging_file ); + if (!F.open(QFile::ReadOnly)) { + addFailure( "Internal error: Couldn't open logresults", "", -1 ); + return FALSE; + } + + QTextStream ds( &F ); + QString Snext = ""; + QString S = ""; + while (!ds.atEnd()) { + + S = ""; + while (!ds.atEnd()) { + S += Snext; + Snext = ds.readLine(); + if (!Snext.startsWith( "<RESULT>" )) { + // this line is part from the previous line + if (S != "") + S += "\n"; + S += Snext; + Snext = ""; + } else { + if (S != "") + break; + } + } + + if (S != "") { + QTestResultRecord rec; + if (rec.decode( S )) { + if (rec.result == QTest::DetectedStyle) { + isConsoleApp = rec.reason.startsWith( "Console" ); + } + + processLogResult( S ); + } + } + } + + return TRUE; +} + +bool equalConfigs( const QString &c1, const QString &c2 ) +{ + QStringList cl1 = c1.split( "," ); + QStringList cl2 = c2.split( "," ); + + if (cl1.count() < 1 || cl2.count() < 1) + return FALSE; + if (cl1[0] != cl2[0]) + return FALSE; + + if (cl1.count() < 2 || cl2.count() < 2) + return FALSE; + if (cl1[1] != cl2[1]) + return FALSE; + + if (cl1.count() < 3 || cl2.count() < 3) + return FALSE; + QStringList cl3 = cl1[2].split( " " ); + QStringList cl4 = cl2[2].split( " " ); + cl3.sort(); + cl4.sort(); + int cl3_count = 0; + int cl4_count = 0; + while (cl3_count < cl3.count() || cl4_count < cl4.count()) { + QString tmp_c1 = cl3[cl3_count]; + QString tmp_c2 = cl4[cl4_count]; + + if (tmp_c1 != tmp_c2) { + if (tmp_c1 == "-no-xft") { + cl4_count--; + } else if (tmp_c2 == "-no-xft") { + cl3_count--; + } else { +// qDebug( "cl3 = " + tmp_c1 + ", cl4 = " + tmp_c2); + return FALSE; + } + } + cl3_count++; + cl4_count++; + } + return TRUE; +} + +void QTestResult::processLogResult( const QString &S ) +{ + QTestResultRecord rec; + if (!rec.decode( S )) { + return; + } + + if (rec.testCase != curTestCase()) { + setCurrentTestCase( rec.testCase ); + } else { + if (curTestFunc(FALSE) != rec.testFunc) { + if (!curTestFunc(FALSE).isEmpty()) + setCurrentTestFunction( "" ); + setCurrentTestFunction( rec.testFunc ); + } + QString my_data_tag; + if ( rec.dataTag == "") { + my_data_tag = "NO_TESTDATA"; + } else { + my_data_tag = rec.dataTag; + } + if (my_data_tag != curDataTag()) + setCurrentTestData( my_data_tag ); + } + + switch (rec.result) + { + case QTest::ExpectFailure: + { + int dataIndex = rec.line; + int mode = rec.comment.toInt(); + setExpectedFailure( dataIndex, rec.reason, (QTest::TestFailMode)mode ); + break; + } + case QTest::IgnoreResult: + { + int ignore_res = rec.line; + int exact_match = rec.comment.toInt(); + ignoreResult( (QTest::TestResult)ignore_res, rec.reason, exact_match == 1 ); + break; + } + case QTest::RemoteIgnoreResult: + { + int ignore_res = rec.line; + int exact_match = rec.comment.toInt(); + remoteIgnoreResult( (QTest::TestResult)ignore_res, rec.reason, exact_match == 1 ); + break; + } + case QTest::Retest: + { + int busy = rec.comment.toInt(); + retest( busy == 1 ); + break; + } + case QTest::Note: + { + if (!rec.reason.startsWith( "Testcase took " ) && + !rec.reason.startsWith( "Started at" )) + addResult( S ); + break; + } + case QTest::StartingTestcase: + case QTest::FinishedTestcase: + case QTest::StartingBuild: + case QTest::FinishedBuild: + case QTest::StartingScript: + case QTest::FinishedScript: + case QTest::Totals: + case QTest::TestFunction_: + { + // discard result + break; + } + case QTest::DetectedConfiguration: + { + if (curConfiguration() == "") { + // no config detected yet (which is pretty unlikely to happen) + addResult( S ); +// addLogging( S, FALSE ); + } else { + if (!equalConfigs( curConfiguration(), rec.reason)) { + addWarning( "Configuration mismatch: \n actual : '" + rec.reason + "'\n expected: '" + curConfiguration() + "'" ); + } else { + // just ignore the reported configuration. + // We already know and have logged the config. + // This is the only silent case and should be the normal behaviour + } + } + break; + } + default: + { + addResult( S ); + } + } +} +#endif + +// ----------------------------------------------------------------------- + +QTestResultSuite::QTestResultSuite() +{ + test_case = new QTestResultTestCase; +} + +QTestResultSuite::~QTestResultSuite() +{ + delete test_case; + test_case = 0; +} + +QTestResultTestCase* QTestResultSuite::testCase() +{ + return test_case; +} + +// ---------------------------------------------------------------- + +QTestResultRecord::QTestResultRecord() +{ +} + +QTestResultRecord::QTestResultRecord( const QString &keyString ) +{ + decode( keyString ); +} + +QTestResultRecord::QTestResultRecord( QTest::TestResult result, const QString &test, const QString &reason, const QString &dataset, const QString &file, const QString &line, const QString &comment ) +{ + this->result = result; + int pos = test.indexOf( "::" ); + if (pos >= 0) { + testCase = test.left(pos); + testFunc = test.mid(pos+2); + } else { + testCase = test; + testFunc = ""; + } + this->reason = reason; + this->dataTag = dataset; + this->file = file; + this->line = line.toInt(); + this->comment = comment; +} + +QString QTestResultRecord::keyString() +{ + QKeyString key_string; + key_string.addKey( "RESULT", result ); + QString test = testCase; + if (!testFunc.isEmpty()) + test += "::" + testFunc; + key_string.addKey( "TEST", test ); + if (reason != "") + key_string.addKey( "REASON", reason ); + if (dataTag != "") + key_string.addKey( "DATATAG", dataTag ); + if (file != "") + key_string.addKey( "FILE", file ); + if (line != -1) + key_string.addKey( "LINE", line ); + if (comment != "") + key_string.addKey( "COMMENT", comment ); + + return key_string; +} + +QString QTestResultRecord::testName() +{ + QString ret = testCase; + if (testFunc != "") + { + ret+= "::"; + ret += testFunc; + } + return ret; +} + +bool QTestResultRecord::decode( const QString &logText ) +{ + QKeyString key_string = logText; + + QString test; + key_string.findKey( "TEST", test, TRUE ); + int pos = test.indexOf( "::" ); + if (pos >= 0) { + testCase = test.left(pos); + testFunc = test.mid(pos+2); + } else { + testCase = test; + testFunc = ""; + } + + key_string.findKey( "DATATAG", dataTag, TRUE ); + + int i; + if (!key_string.findKey( "RESULT", i )) + return FALSE; + result = (QTest::TestResult)i; + + key_string.findKey( "REASON", reason ); + + key_string.findKey( "COMMENT", comment ); + key_string.findKey( "FILE", file ); + QString sLine; + if (!key_string.findKey( "LINE", sLine )) + line = -1; + else + line = sLine.toInt(); + return TRUE; +} + +bool QTestResultRecord::passed() +{ + if (result == QTest::PassSelf || + result == QTest::PassSelf || + result == QTest::PassCompile || + result == QTest::PassTest || + result == QTest::PassScript ) + return TRUE; + + return FALSE; +} + +// ---------------------------------------------------------------- + +// The max number of warnings we want to log per execution of a testfunction/dataset +#define MAX_WARNINGS_PER_FUNCTION 100 + +QTestResultTestBase::QTestResultTestBase( QTestResultTestCase *testCase, QObject *parent ) : QObject( parent ) +{ + warning_count = 0; + test_case = testCase; + last_result = ""; + test_result = QTest::Invalid; + retest_busy = FALSE; + skip_all = FALSE; +} + +QTestResultTestBase::~QTestResultTestBase() +{ +} + +QTestResultTestCase* QTestResultTestBase::curTestCase() +{ + return test_case; +} + +void QTestResultTestBase::setRetest( bool busy ) +{ + retest_busy = busy; + test_result = QTest::Invalid; + if (testResult.testLog()->rawLoggingMode()) { + appendResult( TRUE, QTest::Retest, "", "", -1, QString( "%1" ).arg( busy ) ); + } +} + +void QTestResultTestBase::resetResult() +{ + retest_busy = FALSE; + test_result = QTest::Invalid; +} + +QTest::TestResult QTestResultTestBase::result() const +{ + return test_result; +} + +void QTestResultTestBase::setResult( QTest::TestResult res ) +{ + // first filter out 'pseudo' results + if (res != QTest::PassSelf && + res != QTest::PassCompile && + res != QTest::PassTest && + res != QTest::PassScript && + res != QTest::PassUnexpected && + res != QTest::SkipAll && + res != QTest::SkipSingle && + res != QTest::FailSelf && + res != QTest::FailCompile && + res != QTest::FailTest && + res != QTest::FailExpected && + res != QTest::FailScript && + res != QTest::QFatal && + res != QTest::__Error && + res != QTest::InternFatal) + return; + + // a result can go from unknown to passed/skipped/failed + // and from passed to skipped/failed + if (test_result == QTest::Invalid || + test_result == QTest::PassSelf || + test_result == QTest::PassCompile || + test_result == QTest::PassUnexpected || + test_result == QTest::PassTest || + test_result == QTest::PassScript ) + test_result = res; +} + +void QTestResultTestBase::appendResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file, + int line, + const QString &comment ) +{ + QString sLine; + sLine.sprintf("%d",line); + QTestResultRecord rec( result, test_case->curFunctionName( TRUE ), reason, test_case->curDataTag(), file, sLine, comment ); + + if (rec.result == QTest::QWarning) { + if ( warning_count < MAX_WARNINGS_PER_FUNCTION ) { + warning_count++; + } else if ( warning_count == MAX_WARNINGS_PER_FUNCTION ) { + warning_count++; // we only want the message once. + rec.reason = "Max amount of warnings are logged, further warnings will be skipped"; + } else { + // The result is skipped (which is what this is all about) + return; + } + } + +#ifndef QTEST_SUPPORT + if ( spontaneous ) { + QTestRemote remote; + remote.postMessage( "RAW_RESULT", rec.keyString() ); + } +#endif + + // filter out equal (repeating) messages + // but let qDebug and qFatal pass unfiltered + if (rec.result != QTest::QDebug && + rec.result != QTest::QFatal && + rec.result != QTest::InternFatal && + (rec.keyString() == last_result) ) { + return; + } + + if ( rec.result == QTest::PassTest ) { + QString comment; + bool abort; + if (expected_failures.isFailExpected( test_case->curDataIndex(), comment, abort, TRUE )) { + appendResult( FALSE, QTest::PassUnexpected, "Expected a failure, but test passed instead", rec.file, rec.line, comment ); + return; + } + } + + if ( rec.result == QTest::FailTest ) { + QString comment; + bool abort; + if (expected_failures.isFailExpected( test_case->curDataIndex(), comment, abort, TRUE )) { + if (local_ignores.mustIgnoreResult( QTest::FailExpected, comment ) ) { + // append a pass so that we know that we have successfully tested something + appendResult( FALSE, QTest::PassTest, "" ); + return; + } else { + // append a new failure and ignore the current one + appendResult( FALSE, QTest::FailExpected, comment, rec.file, rec.line, comment ); + return; + } + } + } + + if ( rec.result == QTest::IgnoreResult || + rec.result == QTest::RemoteIgnoreResult || + rec.result == QTest::ExpectFailure ) { + return; + } + + if (spontaneous && local_ignores.mustIgnoreResult( rec.result, rec.reason ) ) { + // append a pass so that we know that we have successfully tested something + appendResult( FALSE, QTest::PassTest, "" ); + return; + } + + if (remote_ignores.mustIgnoreResult( rec.result, rec.reason ) ) { + // append a pass so that we know that we have successfully tested something + appendResult( FALSE, QTest::PassTest, "" ); + return; + } + + setResult( rec.result ); + + if (!retest_busy) + last_result = rec.keyString(); + + + if ( rec.result == QTest::SkipAll && skip_all ) { + // don't log the same skip a second time. + return; + } + + if (rec.result == QTest::QDebug && !QTestSettings::showQDebug()) + return; + + if (rec.result == QTest::Performance && !QTestSettings::showPerformance()) + return; + + if (QTestSettings::briefMode()) { + if (rec.result == QTest::Warn || + rec.result == QTest::Msg || + rec.result == QTest::Note || + rec.result == QTest::MakeRes || + rec.result == QTest::ScriptRes || + rec.result == QTest::SkipSingle || + rec.result == QTest::SkipAll || + rec.result == QTest::Printf ) + return; + } + + if (rec.passed()) { +//#ifndef QTEST_SUPPORT + if (rec.line >= -1) + return; +//#endif + rec.file = ""; + rec.line = -1; + } + + // Don't log anything if we are re-testing the same testfunction multiple times. + if (retest_busy) { + if (rec.passed()) + retest_busy = FALSE; + } + + if (!retest_busy) { + test_case->writeLog( rec ); + } +} + +void QTestResultTestBase::ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ) +{ + if (remote) { +#ifndef QTEST_SUPPORT + appendResult( TRUE, QTest::RemoteIgnoreResult, reason, "", (int)res, QString( "%1" ).arg( exactMatch ) ); +#else + remote_ignores.ignoreResult( res, reason, exactMatch ); +#endif + } else { + appendResult( TRUE, QTest::IgnoreResult, reason, "", (int)res, QString( "%1" ).arg( exactMatch ) ); + local_ignores.ignoreResult( res, reason, exactMatch ); + } +} + +bool QTestResultTestBase::resetIgnoreResult( bool remote, QString &errors ) +{ + if (remote) + return remote_ignores.resetIgnoreResult( errors ); + else + return local_ignores.resetIgnoreResult( errors ); +} + +IgnoreResults::IgnoreResults() +{ + ignore_result.clear(); +} + +IgnoreResults::~IgnoreResults() +{ +} + +void IgnoreResults::ignoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch ) +{ +# ifndef QT_DEBUG + // Warnings and Debugs are not output in release versions anymore + if ( res == QTest::QWarning || res == QTest::QDebug ) + return; +# endif + + QKeyString k; + k.addKey( "RES", res ); + k.addKey( "TXT", reason ); + int count = 0; + k.addKey( "COUNT", count ); + if (exactMatch) + k.addKey( "EXACT", QString("1") ); + ignore_result.append( k ); +} + +bool IgnoreResults::mustIgnoreResult( QTest::TestResult res, const QString &reason ) +{ + int index; + if (!getIgnoreResultIndex( res, reason, index )) { + return FALSE; + } + + QString S = ignore_result[index]; + QKeyString r(S); + int count; + r.findKey( "COUNT", count ); + count++; + r.setKey( "COUNT", count ); + ignore_result.removeAll( S ); + ignore_result.append( r ); + return TRUE; +} + +bool IgnoreResults::resetIgnoreResult( QString &errors ) +{ + if (ignore_result.count() == 0) { + return FALSE; + } + + bool retValue = FALSE; + + int i; + // first we check how much results we have missed + for (i=0; i<(int)(ignore_result.count()); i++) { + QKeyString k( ignore_result[i] ); + int count; + k.findKey( "COUNT", count ); + if (count == 0) { + retValue = TRUE; + } + } + + // next output all the results we missed as a failure + // and halt after the last failure. + errors = "Ignore result failures:"; + if (retValue == TRUE) { + for (i=0; i<(int)(ignore_result.count()); i++) { + QKeyString k( ignore_result[i] ); + QString reason; + QString res; + int iRes; + k.findKey( "RES", iRes ); + res = QTestResult::resultToStr( (QTest::TestResult)iRes ); + k.findKey( "TXT", reason ); + int count; + k.findKey( "COUNT", count ); + QString exact; + k.findKey( "EXACT", exact ); + bool exact_match = (exact == "1"); + if (count == 0) { + res = res.simplified(); + QString prefix = " that starts with text: '"; + if (exact_match) + prefix = " that matches text: '"; + + errors += "\n Did not catch a " + res + prefix + reason + "'."; + } + } + } + ignore_result.clear(); + return retValue; +} + +bool IgnoreResults::getIgnoreResultIndex( QTest::TestResult res, const QString &reason, int &index ) +{ + index = -1; + + if ( ignore_result.count() == 0) { + return FALSE; + } + + for (int i=0; i<(int)(ignore_result.count()); i++) { + QKeyString SS(ignore_result[i]); + int ires; + SS.findKey( "RES", ires ); + QString ireason; + SS.findKey( "TXT", ireason ); + QString exact; + SS.findKey( "EXACT", exact ); + bool exact_match = (exact == "1"); + + if (ires == res && + ( (!exact_match && reason.startsWith(ireason)) || + (reason == ireason))) { + index = i; + return TRUE; + } + } + + return FALSE; +} + +void QTestResultTestBase::setExpectedFailure( int dataIndex, QString reason, QTest::TestFailMode mode ) +{ + appendResult( TRUE, QTest::ExpectFailure, reason, "", dataIndex, QString( "%1" ).arg( mode ) ); + expected_failures.setFailure( dataIndex, reason, mode ); +} + +ExpectedFailures::ExpectedFailures() +{ + first_expected_fail = 0; +} + +ExpectedFailures::~ExpectedFailures() +{ + clear(); +} + +void ExpectedFailures::clear() +{ + ExpectedFailRec *next; + ExpectedFailRec *cur; + cur = first_expected_fail; + while (cur != 0) { + next = cur->next; + delete cur; + cur = next; + } + first_expected_fail = 0; +} + +uint ExpectedFailures::count() +{ + uint count = 0; + ExpectedFailRec *cur; + cur = first_expected_fail; + while (cur != 0) { + count++; + cur = cur->next; + } + return count; +} + +bool ExpectedFailures::getFailure( uint index, ExpectedFailRec *&rec ) +{ + uint count = 0; + rec = first_expected_fail; + while (rec != 0) { + if (count == index) + return TRUE; + count++; + rec = rec->next; + } + rec = 0; + return FALSE; +} + +/*! + Returns TRUE if the currently executed testfunction is expected to fail when the testdata + referenced to by \a dataIndex is applied to the function. + If the fail is expected, e.g. the function returns TRUE, the \a comment returns a string that + returns information on why the fail is expected. This information is entered in the expectFail + function call that is used to set up the expected failure. + + If \a reset is TRUE, the expected failures will be reset after the first call to a verify, + compare or test, e.g. the next call to a verify, compare or test function will not result in + an expected failure anymore. + + The \a abort parameter returns TRUE if the fail was expected and the execution of the function + should be aborted. +*/ + +bool ExpectedFailures::isFailExpected( int dataIndex, QString &comment, bool &abort, bool reset ) +{ + if (dataIndex == -1) + return FALSE; + + if (count() == 0) + return FALSE; + + // return a TRUE if we expect a fail for every testdata + ExpectedFailRec *rec; + if (getFailure( 0, rec ) && rec->index == -1) { + comment = rec->comment; + abort = rec->mode == QTest::Abort; + if ( reset ) + clear(); + return TRUE; + } + + for (uint i=0; i<count(); i++) { + if (getFailure( i, rec ) && rec->index == dataIndex) { + comment = rec->comment; + abort = rec->mode == QTest::Abort; + if ( reset ) + clear(); + return TRUE; + } + } + + if ( reset ) + clear(); + return FALSE; +} + +void ExpectedFailures::setFailure( int dataIndex, QString comment, QTest::TestFailMode mode ) +{ + if (dataIndex == -1) { + clear(); + append( dataIndex, comment, mode ); + } else { + ExpectedFailRec *rec; + if (count() == 1 && + getFailure( 0, rec ) && + rec->index == -1) + return; + append( dataIndex, comment, mode ); + } +} + +void ExpectedFailures::append( int index, QString comment, QTest::TestFailMode mode ) +{ + ExpectedFailRec *tmp; + tmp = new ExpectedFailRec; + tmp->index = index; + tmp->comment = comment; + tmp->mode = mode; + tmp->next = 0; + + if (first_expected_fail == 0) + first_expected_fail = tmp; + else { + ExpectedFailRec *cur; + cur = first_expected_fail; + while (cur) { + if (cur->next == 0) { + cur->next = tmp; + return; + } + cur = cur->next; + } + } +} + +// ------------------------------------------------------------------- + +QTestResultTestCase::QTestResultTestCase( const QString &name ) : QTestResultTestBase( this, 0 ) +{ + m_name = name; + m_first = 0; + cur_func = 0; + build_mode = FALSE; +} + +QTestResultTestCase::QTestResultTestCase() : QTestResultTestBase( this, 0 ) +{ + m_name = ""; + m_first = 0; + cur_func = 0; + build_mode = FALSE; +} + +QTestResultTestCase::~QTestResultTestCase() +{ + delete m_first; + m_first = 0; +} + +QTestLog* QTestResultTestCase::testLog() +{ + return &test_log; +} + +void QTestResultTestCase::writeLog( QTestResultRecord rec ) +{ + test_log.writeLog( rec ); +} + +void QTestResultTestCase::setName( const QString &name, bool verbose, bool build ) +{ + if (name.isEmpty()) { + if (m_name.isEmpty()) + return; + + if (cur_func != 0) + setCurrentTestFunction( "" ); + + if (!testResult.testLog()->rawLoggingMode()) { + QString errors; + if (local_ignores.resetIgnoreResult( errors )) + addResult( FALSE, QTest::FailTest, errors ); + if (remote_ignores.resetIgnoreResult( errors )) + addResult( FALSE, QTest::FailTest, errors ); + + if (build_mode) { +// FIXME: this fails when building qpe?? +// if (totalFailed() == 0 && totalPassed() == 0) +// addResult( FALSE, QTest::FailTest, "Nothing is tested at all." ); + } else { +#ifndef QTEST_SUPPORT + if (totalTests() == 0) + addResult( FALSE, QTest::FailTest, "Nothing is tested at all." ); +#endif + } + + if (!build_mode) + addResult( TRUE, QTest::Totals, QString("%1 passed, %2 failed, %3 skipped").arg(totalPassed()).arg(totalFailed()).arg(totalSkipped())); + + QString txt = QString("Testcase took %1 ms to ").arg(timer.elapsed()); + if (build_mode) + txt += "compile."; + else + txt += "execute."; + addResult( TRUE, QTest::Note, txt ); + } + + if (build_mode) { + addResult( TRUE, QTest::FinishedBuild, "", "", -1, "" ); + } else { + addResult( TRUE, QTest::FinishedTestcase, "", "", -1, "" ); + } + + if (totalFailed() > 0) { + test_log.flushCachedResults(); + } else { + test_log.cacheResults( FALSE ); + } + + m_name = name; + resetResult(); + } else { + if (m_name == name) { + return; // current testcase is running already + } else if (!m_name.isEmpty() ) { + setName( "" ); // close a previous testcase + } + Q_ASSERT( curFunction() == 0 ); + + test_log.cacheResults( !verbose && !QTestSettings::verbose() ); + + m_name = name; + + build_mode = build; + if (build) { + addResult( TRUE, QTest::StartingBuild, "", "", -1, "" ); + } else { + addResult( TRUE, QTest::StartingTestcase, "", "", -1, "" ); + } + +#ifndef QTEST_SUPPORT + //FIXME addResult( TRUE, QTest::DetectedStyle, QSystem::curStyle() + "-" + QSystem::screenInfo() ); + //FIXME addResult( TRUE, QTest::Note, "Tested with QTest version: " QTEST_VERSION ); +#endif + + QDateTime cur = QDateTime::currentDateTime(); + addResult( TRUE, QTest::Note, "Started at: " + cur.toString() ); + timer.start(); + } + + delete m_first; + m_first = 0; + cur_func = 0; +} + +QString QTestResultTestCase::name() +{ + return m_name; +} + +QString QTestResultTestCase::curFunctionName( bool fullName ) const +{ + QString S; + if (fullName) { + S = m_name; + if (cur_func != 0 && !cur_func->name().isEmpty()) + S+= "::"+ cur_func->name(); + } else { + if (cur_func != 0 && !cur_func->name().isEmpty()) + S = cur_func->name(); + } + + return S; +} + +QTestResultTestFunc* QTestResultTestCase::findFunction( const QString &funcName ) +{ + QTestResultTestFunc *f = m_first; + while (f != 0) { + if (f->name() == funcName) + return f; + else + f = f->next(); + } + + return 0; +} + +void QTestResultTestCase::setCurrentTestFunction( const QString &funcName ) +{ + if (funcName.isEmpty()) { + if ( cur_func == 0 ) + return; + + if (cur_func->cur_data != 0) + cur_func->setCurrentTestData( "" ); + + if (!testResult.testLog()->rawLoggingMode()) { + QString errors; + if (cur_func->resetIgnoreResult( FALSE, errors )) + addResult( FALSE, QTest::FailTest, errors ); + if (cur_func->resetIgnoreResult( TRUE, errors )) + addResult( FALSE, QTest::FailTest, errors ); + + if (!cur_func->hasFailures()) { + if (curDataCount() == curSkipCount() && + curSkipCount() > 0) { + // don't show a PASS + } else { + QString S = ""; + if (curSkipCount() > 0) + S = QString("Passed with %1 skipped tests.").arg( curSkipCount() ); + appendResult( FALSE, QTest::PassTest, S, "", -2 ); + } + } + } + + // blow away the selftest if it was successfull + if (cur_func->name() == "SelfTest" && m_first != 0 && m_first->next() == 0) { + if (cur_func->passed( TRUE )) { + delete cur_func; + cur_func = 0; + m_first = 0; + } + } + + cur_func = 0; + } else { + if (cur_func != 0 && cur_func->name() == funcName) + return; + + QTestResultTestFunc *tmp = findFunction( funcName ); + if (tmp != 0) { + cur_func = tmp; + return; + } + + tmp = new QTestResultTestFunc( funcName, this ); + if (m_first == 0) { + m_first = tmp; + } else { + QTestResultTestFunc *last = m_first; + while (last->next() != 0) + last = last->next(); + last->setNext( tmp ); + } + cur_func = tmp; + } +} + +void QTestResultTestCase::setCurrentTestData( const QString &dataTag ) +{ + if (cur_func != 0) { + cur_func->setCurrentTestData( dataTag ); + } +} + +QTestResultTestFunc* QTestResultTestCase::curFunction() +{ + return cur_func; +} + +QTestResult::TestLocation QTestResultTestCase::currentTestLocation() +{ + return cur_test_location; +} + +void QTestResultTestCase::setCurrentTestLocation( QTestResult::TestLocation loc ) +{ + cur_test_location = loc; +} + + +uint QTestResultTestCase::totalTests() const +{ + uint count = 0; + QTestResultTestFunc *f = m_first; + while (f != 0) { + count++; + f = f->next(); + } + + return count; +} + +uint QTestResultTestCase::noOfExecutedTests() const +{ + // for the moment lets assume these are the same + return totalTests(); +} + +int QTestResultTestCase::curDataIndex() const +{ + if (cur_func != 0) { + return cur_func->curDataIndex(); + } + return -1; +} + +QString QTestResultTestCase::curDataTag() +{ + if (cur_func != 0) { + return cur_func->curDataTag(); + } + return ""; +} + +uint QTestResultTestCase::curDataCount() const +{ + if (cur_func != 0) { + return cur_func->dataCount(); + } + return 0; +} + +uint QTestResultTestCase::curSkipCount() const +{ + if (cur_func != 0) { + return cur_func->skipCount(); + } + return 0; +} + +bool QTestResultTestCase::curTestPassed() +{ + if (cur_func != 0) { + return cur_func->passed( FALSE ); + } + return FALSE; +} + +bool QTestResultTestCase::curTestVerified() +{ + if (cur_func != 0) { + return cur_func->verified( FALSE ); + } + return FALSE; +} + +bool QTestResultTestCase::curTestFailed() +{ + if (cur_func != 0) { + return cur_func->failed( TRUE ); + } else { + if (result() == QTest::FailSelf || + result() == QTest::FailCompile || + result() == QTest::FailTest || + result() == QTest::QFatal || + result() == QTest::FailExpected || + result() == QTest::FailScript ) + return TRUE; + } + return FALSE; +} + +bool QTestResultTestCase::curTestSkipped() +{ + if (cur_func != 0) { + return cur_func->skipped( TRUE ); + } + return FALSE; +} + +uint QTestResultTestCase::totalSkipped() const +{ + uint count = 0; + QTestResultTestFunc *f = m_first; + while (f != 0) { + count+= f->skipCount(); + f = f->next(); + } + + return count; +} + +uint QTestResultTestCase::totalPassed() const +{ + uint count = 0; + + if (result() == QTest::PassSelf || + result() == QTest::PassCompile || + result() == QTest::PassTest || + result() == QTest::PassScript) + count++; + + QTestResultTestFunc *f = m_first; + while (f != 0) { + count+= f->passCount(); + f = f->next(); + } + + return count; +} + +uint QTestResultTestCase::totalFailed() const +{ + uint count = 0; + + if (result() == QTest::FailSelf || + result() == QTest::FailCompile || + result() == QTest::FailTest || + result() == QTest::FailScript || + result() == QTest::QFatal || + result() == QTest::FailExpected || + result() == QTest::PassUnexpected) + count++; + + QTestResultTestFunc *f = m_first; + while (f != 0) { + count+= f->failCount(); + f = f->next(); + } + + return count; +} + +void QTestResultTestCase::ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ) +{ + if (cur_func != 0) + cur_func->ignoreResult( remote, res, reason, exactMatch ); + else + QTestResultTestBase::ignoreResult( remote, res, reason, exactMatch ); +} + +void QTestResultTestCase::setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ) +{ + if (cur_func != 0) + cur_func->setExpectedFailure( dataIndex, comment, mode ); + else + QTestResultTestBase::setExpectedFailure( dataIndex, comment, mode ); +} + +void QTestResultTestCase::addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file, + int line, + const QString &comment ) +{ + if (cur_func != 0) + cur_func->addResult( spontaneous, result, reason, file, line, comment ); + else + appendResult( spontaneous, result, reason, file, line, comment ); +} + +void QTestResultTestCase::retest( bool busy ) +{ + if (cur_func != 0) + cur_func->retest( busy ); + else + setRetest( busy ); +} + +// ******************************************** + +QTestResultTestFunc::QTestResultTestFunc( const QString &funcName, QTestResultTestCase *parent ) : QTestResultTestBase( parent, parent ) +{ + func_name = funcName; + next_func = 0; + first_data = 0; + cur_data = 0; +} + +QTestResultTestFunc::~QTestResultTestFunc() +{ + delete first_data; + first_data = 0; + + delete next_func; + next_func = 0; +} + +QString QTestResultTestFunc::name() +{ + return func_name; +} + +QTestResultTestFunc *QTestResultTestFunc::next() +{ + return next_func; +} + +void QTestResultTestFunc::setNext( QTestResultTestFunc *n ) +{ + next_func = n; +} + +void QTestResultTestFunc::setCurrentTestData( const QString &dataName ) +{ + if (dataName.isEmpty()) { + if (cur_data == 0 ) + return; + + if (!cur_data->verified()) + addResult( TRUE, QTest::FailTest, "Nothing is tested in this testfunction." ); + + if (!testResult.testLog()->rawLoggingMode()) { + QString errors; + if (cur_data->resetIgnoreResult( FALSE, errors )) + addResult( FALSE, QTest::FailTest, errors ); + if (cur_data->resetIgnoreResult( TRUE, errors )) + addResult( FALSE, QTest::FailTest, errors ); + } + + cur_data = 0; + } else { + if (cur_data != 0 && cur_data->name() == dataName) + return; + + QTestResultTestData *tmp = findData( dataName ); + if (tmp != 0) { + cur_data = tmp; + + // wipe the previous result if we want to redo the same test + if (cur_data->retest_busy) + cur_data->resetResult(); +// cur_data->retest( FALSE ); + + return; + } + + tmp = new QTestResultTestData( dataName, skip_all, this ); + if (first_data == 0) { + first_data = tmp; + } else { + QTestResultTestData *last = first_data; + while (last->next() != 0) + last = last->next(); + last->setNext( tmp ); + } + cur_data = tmp; + } +} + +QTestResultTestData* QTestResultTestFunc::findData( const QString &dataName ) +{ + QTestResultTestData *d = first_data; + while (d != 0) { + if (d->dataTag() == dataName) + return d; + else + d = d->next(); + } + + return 0; +} + +uint QTestResultTestFunc::dataCount() +{ + uint count = 0; + QTestResultTestData *d = first_data; + while (d != 0) { + count++; + d = d->next(); + } + return count; +} + +uint QTestResultTestFunc::skipCount() +{ + uint count = 0; + QTestResultTestData *d = first_data; + while (d != 0) { + if (d->result() == QTest::SkipSingle || + d->result() == QTest::SkipAll) + count++; + d = d->next(); + } + if (count == 0 && skip_all) + count = 1; + return count; +} + +uint QTestResultTestFunc::failCount() +{ + uint count = 0; + QTestResultTestData *d = first_data; + while (d != 0) { + if (d->result() == QTest::FailSelf || + d->result() == QTest::FailCompile || + d->result() == QTest::FailTest || + d->result() == QTest::FailScript || + d->result() == QTest::FailExpected || + d->result() == QTest::PassUnexpected) + count++; + d = d->next(); + } + return count; +} + +uint QTestResultTestFunc::passCount() +{ + uint count = 0; + QTestResultTestData *d = first_data; + while (d != 0) { + if (d->result() == QTest::PassSelf || + d->result() == QTest::PassCompile || + d->result() == QTest::PassTest || + d->result() == QTest::PassScript) + count++; + d = d->next(); + } + return count; +} + +QTestResultTestData* QTestResultTestFunc::curData() +{ + return cur_data; +} + +QString QTestResultTestFunc::curDataTag() +{ + if (cur_data != 0) + return cur_data->name(); + return ""; +} + +int QTestResultTestFunc::curDataIndex() +{ + if (cur_data != 0) { + int count = 0; + QTestResultTestData *d = first_data; + while (d != 0 && d != cur_data) { + count++; + d = d->next(); + } + return count; + } + return 0; +} + +bool QTestResultTestFunc::passed( bool current ) +{ + // a test hasn't passed if no test has been executed + if (first_data == 0) + return FALSE; + + QTestResultTestData *d = first_data; + if (current) + d = cur_data; + while (d != 0) { + // a test didn't pass if no result is reported or if it is reported as failed + if (d->result() == QTest::Invalid || + d->result() == QTest::FailSelf || + d->result() == QTest::FailCompile || + d->result() == QTest::FailTest || + d->result() == QTest::QFatal || + d->result() == QTest::FailExpected || + d->result() == QTest::FailScript ) + return FALSE; + if (current) + d = 0; + else + d = d->next(); + } + + return TRUE; +} + +bool QTestResultTestFunc::verified( bool current ) +{ + // a function isn't verified if no test has been executed + if (first_data == 0) + return FALSE; + + QTestResultTestData *d = first_data; + if (current) + d = cur_data; + while (d != 0) { + if (!d->verified()) + return FALSE; + if (current) + d = 0; + else + d = d->next(); + } + + return TRUE; +} + +bool QTestResultTestFunc::hasFailures() +{ + // a test hasn't passed if no test has been executed + if (first_data == 0) + return TRUE; + +// qDebug( func_name ); + + QTestResultTestData *d = first_data; + while (d != 0) { + // a test failed if it didn't pass and isn't skipped + if (d->result() != QTest::PassSelf && + d->result() != QTest::PassCompile && + d->result() != QTest::PassTest && + d->result() != QTest::PassScript && + d->result() != QTest::SkipAll && + d->result() != QTest::SkipSingle) + return TRUE; + d = d->next(); + } + + return FALSE; +} + +bool QTestResultTestFunc::skipped( bool current ) +{ + QTestResultTestData *d = first_data; + if (current) + d = cur_data; + while (d != 0) { + if (d->result() == QTest::SkipAll || + d->result() == QTest::SkipSingle) + return TRUE; + if (current) + d = 0; + else + d = d->next(); + } + if (result() == QTest::SkipAll || + result() == QTest::SkipSingle) + return TRUE; + + return FALSE; +} + +bool QTestResultTestFunc::failed( bool current ) +{ + QTestResultTestData *d = first_data; + if (current) + d = cur_data; + while (d != 0) { + if (d->result() == QTest::FailSelf || + d->result() == QTest::FailCompile || + d->result() == QTest::FailTest || + d->result() == QTest::QFatal || + d->result() == QTest::FailExpected || + d->result() == QTest::FailScript ) + return TRUE; + if (current) + d = 0; + else + d = d->next(); + } + + if (result() == QTest::FailSelf || + result() == QTest::FailCompile || + result() == QTest::FailTest || + result() == QTest::QFatal || + result() == QTest::FailExpected || + result() == QTest::FailScript ) + return TRUE; + + return FALSE; +} + +void QTestResultTestFunc::ignoreResult( bool remote, const QTest::TestResult res, const QString &reason, bool exactMatch ) +{ + if (cur_data != 0) + cur_data->ignoreResult( remote, res, reason, exactMatch ); + else + QTestResultTestBase::ignoreResult( remote, res, reason, exactMatch ); +} + +void QTestResultTestFunc::addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file, + int line, + const QString &comment) +{ + if (comment == "SKIP_ALL") { + skip_all = TRUE; + } + if (cur_data != 0) + cur_data->addResult( spontaneous, result, reason, file, line, comment ); + else + appendResult( spontaneous, result, reason, file, line, comment ); +} + +void QTestResultTestFunc::retest( bool busy ) +{ + if (cur_data != 0) + cur_data->retest( busy ); + else + setRetest( busy ); +} + +void QTestResultTestFunc::setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ) +{ + if (cur_data != 0) + cur_data->setExpectedFailure( dataIndex, comment, mode ); + else + QTestResultTestBase::setExpectedFailure( dataIndex, comment, mode ); +} + +// ******************************************** + +QTestResultTestData::QTestResultTestData( const QString &dataName, bool skipAll, QTestResultTestFunc *parent ) : QTestResultTestBase( parent->curTestCase(), parent ) +{ + data_name = dataName; + next_data = 0; + skip_all = skipAll; +} + +QTestResultTestData::~QTestResultTestData() +{ +// qDebug( QString( "QTestResultTestData::~QTestResultTestData()... %1").arg(data_name).toLatin1() ); + + delete next_data; + next_data = 0; +} + +QString QTestResultTestData::name() +{ + return data_name; +} + +QString QTestResultTestData::dataTag() +{ + return data_name; +} + +QTestResultTestData* QTestResultTestData::next() +{ + return next_data; +} + +void QTestResultTestData::setNext( QTestResultTestData *n ) +{ + next_data = n; +} + +void QTestResultTestData::addResult( bool spontaneous, + QTest::TestResult result, + const QString &reason, + const QString &file, + int line, + const QString &comment) +{ + appendResult( spontaneous, result, reason, file, line, comment ); +} + +bool QTestResultTestData::verified() +{ + // a test isn't verified if no result is reported + if (result() == QTest::Invalid) + return FALSE; + return TRUE; +} + +void QTestResultTestData::retest( bool busy ) +{ + setRetest( busy ); +} + +void QTestResultTestData::setExpectedFailure( int dataIndex, QString comment, QTest::TestFailMode mode ) +{ + QTestResultTestBase::setExpectedFailure( dataIndex, comment, mode ); +} + +// ---------------------------------------------------------------- + diff --git a/libqsystemtest/qtestresult.h b/libqsystemtest/qtestresult.h new file mode 100644 index 0000000..a8e4a98 --- /dev/null +++ b/libqsystemtest/qtestresult.h @@ -0,0 +1,131 @@ +/**************************************************************************** + ** + ** Definition of QTestResult class. + ** + ** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. + ** + ** This file is part of the QTest library. + ** EDITIONS: NONE + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#ifndef QTESTRESULT_H +#define QTESTRESULT_H + +#include "qtest.h" +#include "qkeystring.h" + +#include <QString> + +#define QTEST_SUPPORT + +class QTestLog; +class QTestResult; + +class QTestResultRecord : public QTest +{ +public: + QTestResultRecord(); + QTestResultRecord( const QString &keyString ); + QTestResultRecord( TestResult result, const QString &test, const QString &reason, const QString &dataset, const QString &file, const QString &line, const QString &comment ); + + QString testName(); + bool passed(); + + QString keyString(); + bool decode( const QString &logText ); + + QString testCase; + QString testFunc; + QString dataTag; + TestResult result; + QString reason; + QString file; + int line; + QString comment; +}; + +class QTestResult : public QTest +{ +public: + QTestResult(); + virtual ~QTestResult(); + + static QTestLog* testLog(); + static void installMsgHandler(); + + enum TestLocation { NoWhere = 0, DataFunc = 1, InitFunc = 2, Func = 3, CleanupFunc = 4, Starting = 100, LaunchApps, InitTestCase, Executing, KillApps, CleanupTestCase }; + void setCurrentTestLocation(TestLocation loc); + TestLocation currentTestLocation(); + + void setCurrentTestCase( const QString &testCaseName, bool verbose = TRUE, bool build = FALSE ); + QString curTestCase(); + + void setCurrentTestFunction( const QString &testFunc ); + QString curTestFunc( bool fullName ) const; + + void setCurrentTestData( const QString &dataTag ); + QString curDataTag(); + int curDataIndex() const; + + void setConfiguration( const QString &hostName, + const QString &OSName, + const QString &config, + const QString &makeSpec ); + QString curConfiguration(); + + bool enterCheckPoint( const QString &fileName, int line ); + bool checkPointError( QString *reason = 0 ); + + void skip( const QString &reason, bool skipAll, const QString &file, int line ); + void addNote( const QString &comment ); + void addMessage( const QString &message ); + void addWarning( const QString &text ); + void addError( const QString &text ); + void addFatal( const QString &message ); + void addFailure( const QString &text, const QString &file, int line ); + void addSuccess( const QString &text, const QString &file, int line ); + void addMakeResult( const QString &result ); + + void addResult( const TestResult result, + const QString &reason, + const QString &file = "", + int line = -1, + const QString &extraInfo = "" ); + void addResult( const QString &keyString ); + + void debugLog( const QString &dbgtxt ); + + void setExpectedFailure( int dataIndex, QString comment, TestFailMode mode ); + + void ignoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch = FALSE ); + void remoteIgnoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch = FALSE ); + + uint noOfExecutedTests() const; + uint noOfFailures() const; + uint noOfPasses() const; + bool currentTestPassed( bool &withSkips ) const; + bool currentTestFailed() const; + + bool debugMode() const; + QTest::LearnMode learnMode(); +// void showQDebug( bool show ); +// void showPerformance( bool show ); + + // obsolete function that is kept for backward compatibility. +// void setIgnoreResult( const QTest::TestResult res, const QString &reason, bool exactMatch = FALSE ); + + static QString resultToStr( QTest::TestResult res ); + void retest( bool busy ); + +#ifdef QTEST_SUPPORT + bool loadResult( const QString &logging_file, bool &isConsoleApp ); + void processLogResult( const QString &S ); +#endif +}; + +#endif + diff --git a/qtuitest-host.pri b/qtuitest-host.pri index 7f579a2..2abb783 100644 --- a/qtuitest-host.pri +++ b/qtuitest-host.pri @@ -39,6 +39,9 @@ SEMI_PRIVATE_HEADERS += \ $$QTUITEST_SRC/libqsystemtest/qtestverifydlg_p.h HEADERS +=\ + $$QTUITEST_SRC/libqsystemtest/qtestlog.h \ + $$QTUITEST_SRC/libqsystemtest/qtestresult.h \ + $$QTUITEST_SRC/libqsystemtest/qkeystring.h \ $$QTUITEST_SRC/libqsystemtest/qabstracttest.h \ $$QTUITEST_SRC/libqsystemtest/qsystemtest.h \ $$QTUITEST_SRC/libqsystemtest/qtestide.h \ @@ -50,6 +53,9 @@ HEADERS +=\ $$QTUITEST_SRC/libqsystemtest/meegotestcontrol.h SOURCES +=\ + $$QTUITEST_SRC/libqsystemtest/qtestlog.cpp \ + $$QTUITEST_SRC/libqsystemtest/qtestresult.cpp \ + $$QTUITEST_SRC/libqsystemtest/qkeystring.cpp \ # $$QTUITEST_SRC/libqsystemtest/gracefulquit.cpp \ $$QTUITEST_SRC/libqsystemtest/qtestide.cpp \ $$QTUITEST_SRC/libqsystemtest/qabstracttest.cpp \ |