diff options
author | Jøger Hansegård <[email protected]> | 2023-08-11 17:03:01 +0200 |
---|---|---|
committer | Jøger Hansegård <[email protected]> | 2023-11-15 11:07:09 +0000 |
commit | 80a14c86b2739492d7f7fbdb1cbde1da85d1341d (patch) | |
tree | 93ff671e91100fc43d71d59593cfcf0ff3daf2f3 | |
parent | f67499baab77e287a54d5c2abf0e8e6088ef5930 (diff) |
Add QTest option for repeating the entire test execution
Repeated test execution can be useful, under a debugger, to catch an
intermittent failure or, under memory instrumentation, to make memory
leaks easier to recognize.
The new -repeat flag allows running the entire test suite multiple times
within the same process. It works by executing all tests sequentially
before repeating the execution again.
This switch is a developer tool, and is not intended for CI. It can only
be used with the plain text logger.
Change-Id: I2439462c5c44d1c8aa3d3b5656de3eef44898c68
Reviewed-by: Edward Welbourne <[email protected]>
Reviewed-by: Tor Arne Vestbø <[email protected]>
-rw-r--r-- | src/testlib/doc/src/qttestlib-manual.qdoc | 4 | ||||
-rw-r--r-- | src/testlib/qabstracttestlogger.cpp | 13 | ||||
-rw-r--r-- | src/testlib/qabstracttestlogger_p.h | 2 | ||||
-rw-r--r-- | src/testlib/qplaintestlogger.cpp | 9 | ||||
-rw-r--r-- | src/testlib/qplaintestlogger_p.h | 2 | ||||
-rw-r--r-- | src/testlib/qtestcase.cpp | 35 | ||||
-rw-r--r-- | src/testlib/qtestlog.cpp | 15 | ||||
-rw-r--r-- | src/testlib/qtestlog_p.h | 1 | ||||
-rw-r--r-- | tests/auto/testlib/selftests/tst_selftests.cpp | 10 |
9 files changed, 84 insertions, 7 deletions
diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc index 0498e170f16..5fe2b116517 100644 --- a/src/testlib/doc/src/qttestlib-manual.qdoc +++ b/src/testlib/doc/src/qttestlib-manual.qdoc @@ -348,6 +348,10 @@ Disables the crash handler on Unix platforms. On Windows, it re-enables the Windows Error Reporting dialog, which is turned off by default. This is useful for debugging crashes. + \li \c -repeat \e n \br + Run the testsuite n times or until the test fails. Useful for finding + flaky tests. If negative, the tests are repeated forever. This is intended + as a developer tool, and is only supported with the plain text logger. \li \c -platform \e name \br This command line argument applies to all Qt applications, but might be diff --git a/src/testlib/qabstracttestlogger.cpp b/src/testlib/qabstracttestlogger.cpp index 596799f5c44..de6fb635607 100644 --- a/src/testlib/qabstracttestlogger.cpp +++ b/src/testlib/qabstracttestlogger.cpp @@ -148,6 +148,19 @@ QAbstractTestLogger::~QAbstractTestLogger() } /*! + Returns true if the logger supports repeated test runs. + + Repetition of test runs is disabled by default, and can be enabled only for + test loggers that support it. Even if the logger may create syntactically + correct test reports, log-file analyzers may assume that test names are + unique within one report file. +*/ +bool QAbstractTestLogger::isRepeatSupported() const +{ + return false; +} + +/*! Returns true if the \c output stream is standard output. */ bool QAbstractTestLogger::isLoggingToStdout() const diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h index 188967981cc..b4a66cd12af 100644 --- a/src/testlib/qabstracttestlogger_p.h +++ b/src/testlib/qabstracttestlogger_p.h @@ -76,6 +76,8 @@ public: virtual void addMessage(MessageTypes type, const QString &message, const char *file = nullptr, int line = 0) = 0; + virtual bool isRepeatSupported() const; + bool isLoggingToStdout() const; void outputString(const char *msg); diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp index 4bafacb10fa..58c7f6bf5be 100644 --- a/src/testlib/qplaintestlogger.cpp +++ b/src/testlib/qplaintestlogger.cpp @@ -492,4 +492,13 @@ void QPlainTestLogger::addMessage(MessageTypes type, const QString &message, printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line); } +bool QPlainTestLogger::isRepeatSupported() const +{ + // The plain text logger creates unstructured reports. Such reports are not + // parser friendly, and are unlikely to be parsed by any test reporting + // tools. We can therefore allow repeated test runs with minimum risk that + // any parsers fails to handle repeated test names. + return true; +} + QT_END_NAMESPACE diff --git a/src/testlib/qplaintestlogger_p.h b/src/testlib/qplaintestlogger_p.h index 13228a7631f..819a54fd507 100644 --- a/src/testlib/qplaintestlogger_p.h +++ b/src/testlib/qplaintestlogger_p.h @@ -43,6 +43,8 @@ public: void addMessage(MessageTypes type, const QString &message, const char *file = nullptr, int line = 0) override; + bool isRepeatSupported() const override; + private: enum class MessageSource { Incident, diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 1ea6c30a48c..7540c9a2475 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -539,6 +539,8 @@ static int eventDelay = -1; static int timeout = -1; #endif static bool noCrashHandler = false; +static int repetitions = 1; +static bool repeatForever = false; /*! \internal Invoke a method of the object without generating warning if the method does not exist @@ -710,6 +712,9 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool int logFormat = -1; // Not set const char *logFilename = nullptr; + repetitions = 1; + repeatForever = false; + QTest::testFunctions.clear(); QTest::testTags.clear(); @@ -764,6 +769,10 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool " -maxwarnings n : Sets the maximum amount of messages to output.\n" " 0 means unlimited, default: 2000\n" " -nocrashhandler : Disables the crash handler. Useful for debugging crashes.\n" + " -repeat n : Run the testsuite n times or until the test fails.\n" + " Useful for finding flaky tests. If negative, the tests are\n" + " repeated forever. This is intended as a developer tool, and\n" + " is only supported with the plain text logger.\n" "\n" " Benchmarking options:\n" #if QT_CONFIG(valgrind) @@ -913,6 +922,14 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool } else { QTestLog::setMaxWarnings(qToInt(argv[++i])); } + } else if (strcmp(argv[i], "-repeat") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "-repeat needs an extra parameter for the number of repetitions\n"); + exit(1); + } else { + repetitions = qToInt(argv[++i]); + repeatForever = repetitions < 0; + } } else if (strcmp(argv[i], "-nocrashhandler") == 0) { QTest::noCrashHandler = true; #if QT_CONFIG(valgrind) @@ -1066,6 +1083,11 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool if (addFallbackLogger) QTestLog::addLogger(QTestLog::Plain, logFilename); + + if (repetitions != 1 && !QTestLog::isRepeatSupported()) { + fprintf(stderr, "-repeat is only supported with plain text logger\n"); + exit(1); + } } // Temporary, backwards compatibility, until qtdeclarative's use of it is converted @@ -2330,10 +2352,7 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) #if QT_CONFIG(valgrind) if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess) #endif - { - QTestTable::globalTestTable(); QTestLog::startLogging(); - } } /*! \internal @@ -2398,7 +2417,12 @@ int QTest::qRun() return 1; } TestMethods test(currentTestObject, std::move(commandLineMethods)); - test.invokeTests(currentTestObject); + + while (QTestLog::failCount() == 0 && (repeatForever || repetitions-- > 0)) { + QTestTable::globalTestTable(); + test.invokeTests(currentTestObject); + QTestTable::clearGlobalTestTable(); + } } #ifndef QT_NO_EXCEPTIONS @@ -2435,10 +2459,7 @@ void QTest::qCleanup() #if QT_CONFIG(valgrind) if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess) #endif - { QTestLog::stopLogging(); - QTestTable::clearGlobalTestTable(); - } delete QBenchmarkGlobalData::current; QBenchmarkGlobalData::current = nullptr; diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index e01da379115..127cefe50ec 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -559,6 +559,21 @@ bool QTestLog::hasLoggers() return !QTest::loggers()->empty(); } +/*! + \internal + + Returns true if all loggers support repeated test runs +*/ +bool QTestLog::isRepeatSupported() +{ + FOREACH_TEST_LOGGER { + if (!logger->isRepeatSupported()) + return false; + } + + return true; +} + bool QTestLog::loggerUsingStdout() { FOREACH_TEST_LOGGER { diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index 9717858afb6..f9bbfa158de 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -91,6 +91,7 @@ public: static void addLogger(QAbstractTestLogger *logger); static bool hasLoggers(); + static bool isRepeatSupported(); static bool loggerUsingStdout(); static void setVerboseLevel(int level); diff --git a/tests/auto/testlib/selftests/tst_selftests.cpp b/tests/auto/testlib/selftests/tst_selftests.cpp index 06c61e8ff2d..6354ff7052c 100644 --- a/tests/auto/testlib/selftests/tst_selftests.cpp +++ b/tests/auto/testlib/selftests/tst_selftests.cpp @@ -1233,6 +1233,7 @@ SCENARIO("Exit code is as expected") { 0, "globaldata testGlobal:global=true" }, { 0, "globaldata testGlobal:local=true" }, { 0, "globaldata testGlobal:global=true:local=true" }, + { 0, "globaldata testGlobal -repeat 2" }, { 1, "globaldata testGlobal:local=true:global=true" }, { 1, "globaldata testGlobal:global=true:blah" }, { 1, "globaldata testGlobal:blah:local=true" }, @@ -1244,6 +1245,15 @@ SCENARIO("Exit code is as expected") { 1, "globaldata testGlobal:blah skipSingle:global=true:local=true" }, { 1, "globaldata testGlobal:global=true skipSingle:blah" }, { 2, "globaldata testGlobal:blah skipSingle:blue" }, + // Passing -repeat argument + { 1, "pass testNumber1 -repeat" }, + { 0, "pass testNumber1 -repeat 1" }, + { 0, "pass testNumber1 -repeat 1 -o out.xml,xml" }, + { 0, "pass testNumber1 -repeat 2" }, + { 0, "pass testNumber1 -repeat 2 -o -,txt" }, + { 0, "pass testNumber1 -repeat 2 -o -,txt -o log.txt,txt" }, + { 1, "pass testNumber1 -repeat 2 -o log.xml,xml" }, + { 1, "pass testNumber1 -repeat 2 -o -,txt -o -,xml" }, }; size_t n_testCases = sizeof(testCases) / sizeof(*testCases); |