Remove ability to run tests in random order.

Remove the undocumented feature that allows test functions to be
executed in random order.  The feature was designed to expose unintended
dependencies between test functions -- test functions are only supposed
to depend on the initTestCase() and init() functions.

Aside from the lack of documentation, there are a number of problems
with this feature.  Most importantly, running the tests in random order
has only a 50% chance of exposing dependencies between test functions.
A better strategy would be to run the test functions in reverse order
and complain if that produces different results to running the tests in
the normal order.

Additionally, the random order is not deterministic, so even if a
dependency is exposed during a test run, there's no guarantee that it
will be exposed again.  The feature allows the user to optionally
supply a random seed to make the "random" order deterministic, but as
rand() implementations are not identical across platforms, even that
does not guarantee that dependencies between test functions will be
exposed deterministically.

Change-Id: I39eac34c532ccb988116778bbc5ab05d835874c5
Reviewed-on: http://codereview.qt.nokia.com/3720
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Rohan McGovern <rohan.mcgovern@nokia.com>
This commit is contained in:
Jason McDonald 2011-08-29 16:50:52 +10:00 committed by Qt by Nokia
parent 9be3851eb9
commit 527317748d
12 changed files with 11 additions and 171 deletions

View File

@ -96,8 +96,6 @@ public:
virtual void addMessage(MessageTypes type, const char *message, virtual void addMessage(MessageTypes type, const char *message,
const char *file = 0, int line = 0) = 0; const char *file = 0, int line = 0) = 0;
virtual void registerRandomSeed(unsigned int seed) = 0;
void outputString(const char *msg); void outputString(const char *msg);
private: private:

View File

@ -345,7 +345,6 @@ void QPlainTestLogger::printBenchmarkResult(const QBenchmarkResult &result)
} }
QPlainTestLogger::QPlainTestLogger() QPlainTestLogger::QPlainTestLogger()
: randomSeed(9), hasRandomSeed(false)
{ {
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
InitializeCriticalSection(&QTest::outputCriticalSection); InitializeCriticalSection(&QTest::outputCriticalSection);
@ -368,17 +367,10 @@ void QPlainTestLogger::startLogging(const char *filename)
QTest::qt_snprintf(buf, sizeof(buf), "Testing %s\n", QTest::qt_snprintf(buf, sizeof(buf), "Testing %s\n",
QTestResult::currentTestObjectName()); QTestResult::currentTestObjectName());
} else { } else {
if (hasRandomSeed) { QTest::qt_snprintf(buf, sizeof(buf),
QTest::qt_snprintf(buf, sizeof(buf), "********* Start testing of %s *********\n"
"********* Start testing of %s *********\n" "Config: Using QTest library " QTEST_VERSION_STR
"Config: Using QTest library " QTEST_VERSION_STR ", Qt %s\n", QTestResult::currentTestObjectName(), qVersion());
", Qt %s, Random seed %d\n", QTestResult::currentTestObjectName(), qVersion(), randomSeed);
} else {
QTest::qt_snprintf(buf, sizeof(buf),
"********* Start testing of %s *********\n"
"Config: Using QTest library " QTEST_VERSION_STR
", Qt %s\n", QTestResult::currentTestObjectName(), qVersion());
}
} }
outputMessage(buf); outputMessage(buf);
} }
@ -440,10 +432,4 @@ void QPlainTestLogger::addMessage(MessageTypes type, const char *message,
printMessage(QTest::messageType2String(type), message, file, line); printMessage(QTest::messageType2String(type), message, file, line);
} }
void QPlainTestLogger::registerRandomSeed(unsigned int seed)
{
randomSeed = seed;
hasRandomSeed = true;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -75,11 +75,8 @@ public:
void addMessage(MessageTypes type, const char *message, void addMessage(MessageTypes type, const char *message,
const char *file = 0, int line = 0); const char *file = 0, int line = 0);
void registerRandomSeed(unsigned int seed);
private:
unsigned int randomSeed;
bool hasRandomSeed;
private:
void printMessage(const char *type, const char *msg, const char *file = 0, int line = 0); void printMessage(const char *type, const char *msg, const char *file = 0, int line = 0);
void outputMessage(const char *str); void outputMessage(const char *str);
void printBenchmarkResult(const QBenchmarkResult &result); void printBenchmarkResult(const QBenchmarkResult &result);

View File

@ -869,10 +869,7 @@ namespace QTest
static int keyDelay = -1; static int keyDelay = -1;
static int mouseDelay = -1; static int mouseDelay = -1;
static int eventDelay = -1; static int eventDelay = -1;
static bool randomOrder = false;
static int keyVerbose = -1; static int keyVerbose = -1;
static unsigned int seed = 0;
static bool seedSet = false;
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
static bool noCrashHandler = false; static bool noCrashHandler = false;
#endif #endif
@ -958,37 +955,6 @@ int Q_TESTLIB_EXPORT defaultKeyDelay()
return keyDelay; return keyDelay;
} }
void seedRandom()
{
static bool randomSeeded = false;
if (!randomSeeded) {
if (!QTest::seedSet) {
QElapsedTimer timer;
timer.start();
QTest::seed = timer.msecsSinceReference();
}
qsrand(QTest::seed);
randomSeeded = true;
}
}
template<typename T>
void swap(T * array, int pos, int otherPos)
{
T tmp = array[pos];
array[pos] = array[otherPos];
array[otherPos] = tmp;
}
template<typename T>
static void randomizeList(T * array, int size)
{
for (int i = 0; i != size; i++) {
int pos = qrand() % size;
swap(array, pos, i);
}
}
static bool isValidSlot(const QMetaMethod &sl) static bool isValidSlot(const QMetaMethod &sl)
{ {
if (sl.access() != QMetaMethod::Private || !sl.parameterTypes().isEmpty() if (sl.access() != QMetaMethod::Private || !sl.parameterTypes().isEmpty()
@ -1046,9 +1012,6 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
" -v1 : Print enter messages for each testfunction\n" " -v1 : Print enter messages for each testfunction\n"
" -v2 : Also print out each QVERIFY/QCOMPARE/QTEST\n" " -v2 : Also print out each QVERIFY/QCOMPARE/QTEST\n"
" -vs : Print every signal emitted\n" " -vs : Print every signal emitted\n"
" -random : Run testcases within each test in random order\n"
" -seed n : Positive integer to be used as seed for -random. If not specified,\n"
" the current time will be used as seed.\n"
" -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n" " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n"
" -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n" " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n"
" -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n" " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n"
@ -1177,22 +1140,6 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
#endif #endif
} else if (strcmp(argv[i], "-eventcounter") == 0) { } else if (strcmp(argv[i], "-eventcounter") == 0) {
QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::EventCounter); QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::EventCounter);
} else if (strcmp(argv[i], "-random") == 0) {
QTest::randomOrder = true;
} else if (strcmp(argv[i], "-seed") == 0) {
bool argumentOk = false;
if (i + 1 < argc) {
char * endpt = 0;
long longSeed = strtol(argv[++i], &endpt, 10);
argumentOk = (*endpt == '\0' && longSeed >= 0);
QTest::seed = longSeed;
}
if (!argumentOk) {
fprintf(stderr, "-seed needs an extra positive integer parameter to specify the seed\n");
exit(1);
} else {
QTest::seedSet = true;
}
} else if (strcmp(argv[i], "-minimumvalue") == 0) { } else if (strcmp(argv[i], "-minimumvalue") == 0) {
if (i + 1 >= argc) { if (i + 1 >= argc) {
fprintf(stderr, "-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n"); fprintf(stderr, "-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n");
@ -1297,11 +1244,6 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
QTEST_ASSERT(QTest::testFuncCount < 512); QTEST_ASSERT(QTest::testFuncCount < 512);
} }
} }
if (QTest::seedSet && !QTest::randomOrder) {
fprintf(stderr, "-seed requires -random\n");
exit(1);
}
} }
QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container) QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container)
@ -1596,11 +1538,7 @@ static void qInvokeTestMethods(QObject *testObject)
{ {
const QMetaObject *metaObject = testObject->metaObject(); const QMetaObject *metaObject = testObject->metaObject();
QTEST_ASSERT(metaObject); QTEST_ASSERT(metaObject);
if (QTest::randomOrder) { QTestLog::startLogging();
QTestLog::startLogging(QTest::seed);
} else {
QTestLog::startLogging();
}
QTestResult::setCurrentTestFunction("initTestCase"); QTestResult::setCurrentTestFunction("initTestCase");
QTestResult::setCurrentTestLocation(QTestResult::DataFunc); QTestResult::setCurrentTestLocation(QTestResult::DataFunc);
QTestTable::globalTestTable(); QTestTable::globalTestTable();
@ -1617,8 +1555,6 @@ static void qInvokeTestMethods(QObject *testObject)
if(!QTestResult::skipCurrentTest() && !previousFailed) { if(!QTestResult::skipCurrentTest() && !previousFailed) {
if (QTest::testFuncs) { if (QTest::testFuncs) {
if (QTest::randomOrder)
randomizeList(QTest::testFuncs, QTest::testFuncCount);
for (int i = 0; i != QTest::testFuncCount; i++) { for (int i = 0; i != QTest::testFuncCount; i++) {
if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).signature(), if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).signature(),
QTest::testFuncs[i].data())) { QTest::testFuncs[i].data())) {
@ -1631,8 +1567,6 @@ static void qInvokeTestMethods(QObject *testObject)
QMetaMethod *testMethods = new QMetaMethod[methodCount]; QMetaMethod *testMethods = new QMetaMethod[methodCount];
for (int i = 0; i != methodCount; i++) for (int i = 0; i != methodCount; i++)
testMethods[i] = metaObject->method(i); testMethods[i] = metaObject->method(i);
if (QTest::randomOrder)
randomizeList(testMethods, methodCount);
for (int i = 0; i != methodCount; i++) { for (int i = 0; i != methodCount; i++) {
if (!isValidSlot(testMethods[i])) if (!isValidSlot(testMethods[i]))
continue; continue;
@ -1840,9 +1774,6 @@ int QTest::qExec(QObject *testObject, int argc, char **argv)
QTestResult::setCurrentTestObject(metaObject->className()); QTestResult::setCurrentTestObject(metaObject->className());
qtest_qParseArgs(argc, argv, false); qtest_qParseArgs(argc, argv, false);
if (QTest::randomOrder) {
seedRandom();
}
#ifdef QTESTLIB_USE_VALGRIND #ifdef QTESTLIB_USE_VALGRIND
if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) { if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) {
const QStringList origAppArgs(QCoreApplication::arguments()); const QStringList origAppArgs(QCoreApplication::arguments());

View File

@ -165,13 +165,8 @@ void QTestLightXmlStreamer::formatBeforeAttributes(const QTestElement *element,
void QTestLightXmlStreamer::output(QTestElement *element) const void QTestLightXmlStreamer::output(QTestElement *element) const
{ {
QTestCharBuffer buf; QTestCharBuffer buf;
if (logger()->hasRandomSeed()) { QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n",
QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n <RandomSeed>%d</RandomSeed>\n",
qVersion(), QTEST_VERSION_STR, logger()->randomSeed() );
} else {
QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n",
qVersion(), QTEST_VERSION_STR ); qVersion(), QTEST_VERSION_STR );
}
outputString(buf.constData()); outputString(buf.constData());
QTest::qt_asprintf(&buf, "</Environment>\n"); QTest::qt_asprintf(&buf, "</Environment>\n");

View File

@ -281,15 +281,6 @@ void QTestLog::addBenchmarkResult(const QBenchmarkResult &result)
QTest::testLogger->addBenchmarkResult(result); QTest::testLogger->addBenchmarkResult(result);
} }
void QTestLog::startLogging(unsigned int randomSeed)
{
QTEST_ASSERT(!QTest::testLogger);
QTest::initLogger();
QTest::testLogger->registerRandomSeed(randomSeed);
QTest::testLogger->startLogging(QTest::outFile);
QTest::oldMessageHandler = qInstallMsgHandler(QTest::messageHandler);
}
void QTestLog::startLogging() void QTestLog::startLogging()
{ {
QTEST_ASSERT(!QTest::testLogger); QTEST_ASSERT(!QTest::testLogger);

View File

@ -82,7 +82,6 @@ public:
static void info(const char *msg, const char *file, int line); static void info(const char *msg, const char *file, int line);
static void startLogging(); static void startLogging();
static void startLogging(unsigned int randomSeed);
static void stopLogging(); static void stopLogging();
static void setLogMode(LogMode mode); static void setLogMode(LogMode mode);

View File

@ -62,8 +62,6 @@ QTestLogger::QTestLogger(int fm)
, testCounter(0) , testCounter(0)
, failureCounter(0) , failureCounter(0)
, errorCounter(0) , errorCounter(0)
, randomSeed_(0)
, hasRandomSeed_(false)
{ {
} }
@ -128,14 +126,6 @@ void QTestLogger::stopLogging()
property->addAttribute(QTest::AI_PropertyValue, qVersion()); property->addAttribute(QTest::AI_PropertyValue, qVersion());
properties->addLogElement(property); properties->addLogElement(property);
if (hasRandomSeed()) {
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "RandomSeed");
QTest::qt_snprintf(buf, sizeof(buf), "%i", randomSeed());
property->addAttribute(QTest::AI_PropertyValue, buf);
properties->addLogElement(property);
}
currentLogElement->addLogElement(properties); currentLogElement->addLogElement(properties);
currentLogElement->addLogElement(iterator); currentLogElement->addLogElement(iterator);
@ -349,21 +339,5 @@ void QTestLogger::addMessage(MessageTypes type, const char *message, const char
} }
} }
void QTestLogger::registerRandomSeed(unsigned int seed)
{
randomSeed_ = seed;
hasRandomSeed_ = true;
}
unsigned int QTestLogger::randomSeed() const
{
return randomSeed_;
}
bool QTestLogger::hasRandomSeed() const
{
return hasRandomSeed_;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -87,10 +87,6 @@ class QTestLogger : public QAbstractTestLogger
void addMessage(MessageTypes type, const char *message, void addMessage(MessageTypes type, const char *message,
const char *file = 0, int line = 0); const char *file = 0, int line = 0);
void registerRandomSeed(unsigned int seed);
unsigned int randomSeed() const;
bool hasRandomSeed() const;
private: private:
QTestElement *listOfTestcases; QTestElement *listOfTestcases;
QTestElement *currentLogElement; QTestElement *currentLogElement;
@ -101,8 +97,6 @@ class QTestLogger : public QAbstractTestLogger
int testCounter; int testCounter;
int failureCounter; int failureCounter;
int errorCounter; int errorCounter;
unsigned int randomSeed_;
bool hasRandomSeed_;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -205,13 +205,8 @@ void QTestXmlStreamer::output(QTestElement *element) const
quotedTc.constData()); quotedTc.constData());
outputString(buf.constData()); outputString(buf.constData());
if (logger()->hasRandomSeed()) { QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n",
QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n <RandomSeed>%d</RandomSeed>\n", qVersion(), QTEST_VERSION_STR );
qVersion(), QTEST_VERSION_STR, logger()->randomSeed() );
} else {
QTest::qt_asprintf(&buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n",
qVersion(), QTEST_VERSION_STR );
}
outputString(buf.constData()); outputString(buf.constData());
QTest::qt_asprintf(&buf, "</Environment>\n"); QTest::qt_asprintf(&buf, "</Environment>\n");

View File

@ -93,9 +93,8 @@ namespace QTest {
QXmlTestLogger::QXmlTestLogger(XmlMode mode ) QXmlTestLogger::QXmlTestLogger(XmlMode mode )
:xmlmode(mode), randomSeed(0), hasRandomSeed(false) : xmlmode(mode)
{ {
} }
QXmlTestLogger::~QXmlTestLogger() QXmlTestLogger::~QXmlTestLogger()
@ -116,20 +115,11 @@ void QXmlTestLogger::startLogging(const char *filename)
outputString(buf.constData()); outputString(buf.constData());
} }
if (hasRandomSeed) { QTest::qt_asprintf(&buf,
QTest::qt_asprintf(&buf,
"<Environment>\n"
" <QtVersion>%s</QtVersion>\n"
" <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n"
" <RandomSeed>%d</RandomSeed>\n"
"</Environment>\n", qVersion(), randomSeed);
} else {
QTest::qt_asprintf(&buf,
"<Environment>\n" "<Environment>\n"
" <QtVersion>%s</QtVersion>\n" " <QtVersion>%s</QtVersion>\n"
" <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n" " <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n"
"</Environment>\n", qVersion()); "</Environment>\n", qVersion());
}
outputString(buf.constData()); outputString(buf.constData());
} }
@ -450,10 +440,4 @@ int QXmlTestLogger::xmlCdata(QTestCharBuffer* str, char const* src)
return allocateStringFn(str, src, QXmlTestLogger::xmlCdata); return allocateStringFn(str, src, QXmlTestLogger::xmlCdata);
} }
void QXmlTestLogger::registerRandomSeed(unsigned int seed)
{
randomSeed = seed;
hasRandomSeed = true;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -79,8 +79,6 @@ public:
void addMessage(MessageTypes type, const char *message, void addMessage(MessageTypes type, const char *message,
const char *file = 0, int line = 0); const char *file = 0, int line = 0);
void registerRandomSeed(unsigned int seed);
static int xmlCdata(QTestCharBuffer *dest, char const* src); static int xmlCdata(QTestCharBuffer *dest, char const* src);
static int xmlQuote(QTestCharBuffer *dest, char const* src); static int xmlQuote(QTestCharBuffer *dest, char const* src);
static int xmlCdata(QTestCharBuffer *dest, char const* src, size_t n); static int xmlCdata(QTestCharBuffer *dest, char const* src, size_t n);
@ -88,8 +86,6 @@ public:
private: private:
XmlMode xmlmode; XmlMode xmlmode;
unsigned int randomSeed;
bool hasRandomSeed;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE