diff options
Diffstat (limited to 'src/testlib/qtestcase.cpp')
-rw-r--r-- | src/testlib/qtestcase.cpp | 1496 |
1 files changed, 119 insertions, 1377 deletions
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 286da52be2..9acddcc7ea 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -97,6 +97,8 @@ #include <IOKit/pwr_mgt/IOPMLib.h> #endif +#include <vector> + QT_BEGIN_NAMESPACE using QtMiscUtils::toHexUpper; @@ -136,1261 +138,6 @@ static void stackTrace() #endif } -/*! - \namespace QTest - \inmodule QtTest - - \brief The QTest namespace contains all the functions and - declarations that are related to Qt Test. - - See the \l{Qt Test Overview} for information about how to write unit tests. -*/ - -/*! - \namespace QTest::Internal - \internal -*/ - -/*! \macro QVERIFY(condition) - - \relates QTest - - The QVERIFY() macro checks whether the \a condition is true or not. If it is - true, execution continues. If not, a failure is recorded in the test log - and the test won't be executed further. - - \b {Note:} This macro can only be used in a test function that is invoked - by the test framework. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 0 - - \sa QCOMPARE(), QTRY_VERIFY() -*/ - -/*! \macro QVERIFY2(condition, message) - - \relates QTest - - The QVERIFY2() macro behaves exactly like QVERIFY(), except that it outputs - a verbose \a message when \a condition is false. The \a message is a plain - C string. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 1 - - \sa QVERIFY(), QCOMPARE() -*/ - -/*! \macro QCOMPARE(actual, expected) - - \relates QTest - - The QCOMPARE macro compares an \a actual value to an \a expected value using - the equals operator. If \a actual and \a expected are identical, execution - continues. If not, a failure is recorded in the test log and the test - won't be executed further. - - In the case of comparing floats and doubles, qFuzzyCompare() is used for - comparing. This means that comparing to 0 will likely fail. One solution - to this is to compare to 1, and add 1 to the produced output. - - QCOMPARE tries to output the contents of the values if the comparison fails, - so it is visible from the test log why the comparison failed. - - QCOMPARE is very strict on the data types. Both \a actual and \a expected - have to be of the same type, otherwise the test won't compile. This prohibits - unspecified behavior from being introduced; that is behavior that usually - occurs when the compiler implicitly casts the argument. - - For your own classes, you can use \l QTest::toString() to format values for - outputting into the test log. - - \note This macro can only be used in a test function that is invoked - by the test framework. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 2 - - \sa QVERIFY(), QTRY_COMPARE(), QTest::toString() -*/ - -/*! \macro QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) - \since 5.3 - - \relates QTest - - The QVERIFY_EXCEPTION_THROWN macro executes an \a expression and tries - to catch an exception thrown from the \a expression. If the \a expression - throws an exception and its type is the same as \a exceptiontype - or \a exceptiontype is substitutable with the type of thrown exception - (i.e. usually the type of thrown exception is publically derived - from \a exceptiontype) then execution will be continued. If not-substitutable - type of exception is thrown or the \a expression doesn't throw an exception - at all, then a failure will be recorded in the test log and - the test won't be executed further. - - \note This macro can only be used in a test function that is invoked - by the test framework. -*/ - -/*! \macro QTRY_VERIFY_WITH_TIMEOUT(condition, timeout) - \since 5.0 - - \relates QTest - - The QTRY_VERIFY_WITH_TIMEOUT() macro is similar to QVERIFY(), but checks the \a condition - repeatedly, until either the condition becomes true or the \a timeout is - reached. Between each evaluation, events will be processed. If the timeout - is reached, a failure is recorded in the test log and the test won't be - executed further. - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_VERIFY(), QTRY_VERIFY2_WITH_TIMEOUT(), QVERIFY(), QCOMPARE(), QTRY_COMPARE() -*/ - - -/*! \macro QTRY_VERIFY(condition) - \since 5.0 - - \relates QTest - - Checks the \a condition by invoking QTRY_VERIFY_WITH_TIMEOUT() with a timeout of five seconds. - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_VERIFY_WITH_TIMEOUT(), QTRY_VERIFY2(), QVERIFY(), QCOMPARE(), QTRY_COMPARE() -*/ - -/*! \macro QTRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout) - \since 5.6 - - \relates QTest - - The QTRY_VERIFY2_WITH_TIMEOUT macro is similar to QTRY_VERIFY_WITH_TIMEOUT() - except that it outputs a verbose \a message when \a condition is still false - after the specified \a timeout. The \a message is a plain C string. - - Example: - \code - QTRY_VERIFY2_WITH_TIMEOUT(list.size() > 2, QByteArray::number(list.size()).constData(), 10000); - \endcode - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_VERIFY(), QTRY_VERIFY_WITH_TIMEOUT(), QVERIFY(), QCOMPARE(), QTRY_COMPARE() -*/ - -/*! \macro QTRY_VERIFY2(condition, message) - \since 5.6 - - \relates QTest - - Checks the \a condition by invoking QTRY_VERIFY2_WITH_TIMEOUT() with a timeout - of five seconds. If \a condition is then still false, \a message is output. - The \a message is a plain C string. - - Example: - \code - QTRY_VERIFY2_WITH_TIMEOUT(list.size() > 2, QByteArray::number(list.size()).constData()); - \endcode - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_VERIFY2_WITH_TIMEOUT(), QTRY_VERIFY2(), QVERIFY(), QCOMPARE(), QTRY_COMPARE() -*/ - -/*! \macro QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout) - \since 5.0 - - \relates QTest - - The QTRY_COMPARE_WITH_TIMEOUT() macro is similar to QCOMPARE(), but performs the comparison - of the \a actual and \a expected values repeatedly, until either the two values - are equal or the \a timeout is reached. Between each comparison, events - will be processed. If the timeout is reached, a failure is recorded in the - test log and the test won't be executed further. - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_COMPARE(), QCOMPARE(), QVERIFY(), QTRY_VERIFY() -*/ - -/*! \macro QTRY_COMPARE(actual, expected) - \since 5.0 - - \relates QTest - - Performs a comparison of the \a actual and \a expected values by - invoking QTRY_COMPARE_WITH_TIMEOUT() with a timeout of five seconds. - - \note This macro can only be used in a test function that is invoked - by the test framework. - - \sa QTRY_COMPARE_WITH_TIMEOUT(), QCOMPARE(), QVERIFY(), QTRY_VERIFY() -*/ - -/*! \macro QFETCH(type, name) - - \relates QTest - - The fetch macro creates a local variable named \a name with the type \a type - on the stack. \a name has to match the element name from the test's data. - If no such element exists, the test will assert. - - Assuming a test has the following data: - - \snippet code/src_qtestlib_qtestcase.cpp 3 - - The test data has two elements, a QString called \c aString and an integer - called \c expected. To fetch these values in the actual test: - - \snippet code/src_qtestlib_qtestcase.cpp 4 - - \c aString and \c expected are variables on the stack that are initialized with - the current test data. - - \b {Note:} This macro can only be used in a test function that is invoked - by the test framework. The test function must have a _data function. -*/ - -/*! \macro QWARN(message) - - \relates QTest - \threadsafe - - Appends \a message as a warning to the test log. This macro can be used anywhere - in your tests. -*/ - -/*! \macro QFAIL(message) - - \relates QTest - - This macro can be used to force a test failure. The test stops - executing and the failure \a message is appended to the test log. - - \b {Note:} This macro can only be used in a test function that is invoked - by the test framework. - - Example: - - \snippet code/src_qtestlib_qtestcase.cpp 5 -*/ - -/*! \macro QTEST(actual, testElement) - - \relates QTest - - QTEST() is a convenience macro for \l QCOMPARE() that compares - the value \a actual with the element \a testElement from the test's data. - If there is no such element, the test asserts. - - Apart from that, QTEST() behaves exactly as \l QCOMPARE(). - - Instead of writing: - - \snippet code/src_qtestlib_qtestcase.cpp 6 - - you can write: - - \snippet code/src_qtestlib_qtestcase.cpp 7 - - \sa QCOMPARE() -*/ - -/*! \macro QSKIP(description) - - \relates QTest - - If called from a test function, the QSKIP() macro stops execution of the test - without adding a failure to the test log. You can use it to skip tests that - wouldn't make sense in the current configuration. The text \a description is - appended to the test log and should contain an explanation of why the test - couldn't be executed. - - If the test is data-driven, each call to QSKIP() will skip only the current - row of test data, so an unconditional call to QSKIP will produce one skip - message in the test log for each row of test data. - - If called from an _data function, the QSKIP() macro will stop execution of - the _data function and will prevent execution of the associated test - function. - - If called from initTestCase() or initTestCase_data(), the QSKIP() macro will - skip all test and _data functions. - - \b {Note:} This macro can only be used in a test function or _data - function that is invoked by the test framework. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 8 -*/ - -/*! \macro QEXPECT_FAIL(dataIndex, comment, mode) - - \relates QTest - - The QEXPECT_FAIL() macro marks the next \l QCOMPARE() or \l QVERIFY() as an - expected failure. Instead of adding a failure to the test log, an expected - failure will be reported. - - If a \l QVERIFY() or \l QCOMPARE() is marked as an expected failure, - but passes instead, an unexpected pass (XPASS) is written to the test log. - - The parameter \a dataIndex describes for which entry in the test data the - failure is expected. Pass an empty string (\c{""}) if the failure - is expected for all entries or if no test data exists. - - \a comment will be appended to the test log for the expected failure. - - \a mode is a \l QTest::TestFailMode and sets whether the test should - continue to execute or not. - - \b {Note:} This macro can only be used in a test function that is invoked - by the test framework. - - Example 1: - \snippet code/src_qtestlib_qtestcase.cpp 9 - - In the example above, an expected fail will be written into the test output - if the variable \c i is not 42. If the variable \c i is 42, an unexpected pass - is written instead. The QEXPECT_FAIL() has no influence on the second QCOMPARE() - statement in the example. - - Example 2: - \snippet code/src_qtestlib_qtestcase.cpp 10 - - The above testfunction will not continue executing for the test data - entry \c{data27}. - - \sa QTest::TestFailMode, QVERIFY(), QCOMPARE() -*/ - -/*! \macro QFINDTESTDATA(filename) - \since 5.0 - - \relates QTest - - Returns a QString for the testdata file referred to by \a filename, or an - empty QString if the testdata file could not be found. - - This macro allows the test to load data from an external file without - hardcoding an absolute filename into the test, or using relative paths - which may be error prone. - - The returned path will be the first path from the following list which - resolves to an existing file or directory: - - \list - \li \a filename relative to QCoreApplication::applicationDirPath() - (only if a QCoreApplication or QApplication object has been created). - \li \a filename relative to the test's standard install directory - (QLibraryInfo::TestsPath with the lowercased testcase name appended). - \li \a filename relative to the directory containing the source file from which - QFINDTESTDATA is invoked. - \endlist - - If the named file/directory does not exist at any of these locations, - a warning is printed to the test log. - - For example, in this code: - \snippet code/src_qtestlib_qtestcase.cpp 26 - - The testdata file will be resolved as the first existing file from: - - \list - \li \c{/home/user/build/myxmlparser/tests/tst_myxmlparser/testxml/simple1.xml} - \li \c{/usr/local/Qt-5.0.0/tests/tst_myxmlparser/testxml/simple1.xml} - \li \c{/home/user/sources/myxmlparser/tests/tst_myxmlparser/testxml/simple1.xml} - \endlist - - This allows the test to find its testdata regardless of whether the - test has been installed, and regardless of whether the test's build tree - is equal to the test's source tree. - - \b {Note:} reliable detection of testdata from the source directory requires - either that qmake is used, or the \c{QT_TESTCASE_BUILDDIR} macro is defined to - point to the working directory from which the compiler is invoked, or only - absolute paths to the source files are passed to the compiler. Otherwise, the - absolute path of the source directory cannot be determined. - - \b {Note:} For tests that use the \l QTEST_APPLESS_MAIN() macro to generate a - \c{main()} function, \c{QFINDTESTDATA} will not attempt to find test data - relative to QCoreApplication::applicationDirPath(). In practice, this means that - tests using \c{QTEST_APPLESS_MAIN()} will fail to find their test data - if run from a shadow build tree. -*/ - -/*! \macro QTEST_MAIN(TestClass) - - \relates QTest - - Implements a main() function that instantiates an application object and - the \a TestClass, and executes all tests in the order they were defined. - Use this macro to build stand-alone executables. - - If \c QT_WIDGETS_LIB is defined, the application object will be a QApplication, - if \c QT_GUI_LIB is defined, the application object will be a QGuiApplication, - otherwise it will be a QCoreApplication. If qmake is used and the configuration - includes \c{QT += widgets}, then \c QT_WIDGETS_LIB will be defined automatically. - Similarly, if qmake is used and the configuration includes \c{QT += gui}, then - \c QT_GUI_LIB will be defined automatically. - - \b {Note:} On platforms that have keypad navigation enabled by default, - this macro will forcefully disable it if \c QT_WIDGETS_LIB is defined. This is done - to simplify the usage of key events when writing autotests. If you wish to write a - test case that uses keypad navigation, you should enable it either in the - \c {initTestCase()} or \c {init()} functions of your test case by calling - \l {QApplication::setNavigationMode()}. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 11 - - \sa QTEST_APPLESS_MAIN(), QTEST_GUILESS_MAIN(), QTest::qExec(), - QApplication::setNavigationMode() -*/ - -/*! \macro QTEST_APPLESS_MAIN(TestClass) - - \relates QTest - - Implements a main() function that executes all tests in \a TestClass. - - Behaves like \l QTEST_MAIN(), but doesn't instantiate a QApplication - object. Use this macro for really simple stand-alone non-GUI tests. - - \sa QTEST_MAIN() -*/ - -/*! \macro QTEST_GUILESS_MAIN(TestClass) - \since 5.0 - - \relates QTest - - Implements a main() function that instantiates a QCoreApplication object - and the \a TestClass, and executes all tests in the order they were - defined. Use this macro to build stand-alone executables. - - Behaves like \l QTEST_MAIN(), but instantiates a QCoreApplication instead - of the QApplication object. Use this macro if your test case doesn't need - functionality offered by QApplication, but the event loop is still necessary. - - \sa QTEST_MAIN() -*/ - -/*! - \macro QBENCHMARK - - \relates QTest - - This macro is used to measure the performance of code within a test. - The code to be benchmarked is contained within a code block following - this macro. - - For example: - - \snippet code/src_qtestlib_qtestcase.cpp 27 - - \sa {Qt Test Overview#Creating a Benchmark}{Creating a Benchmark}, - {Chapter 5: Writing a Benchmark}{Writing a Benchmark} -*/ - -/*! - \macro QBENCHMARK_ONCE - \since 4.6 - - \relates QTest - - \brief The QBENCHMARK_ONCE macro is for measuring performance of a - code block by running it once. - - This macro is used to measure the performance of code within a test. - The code to be benchmarked is contained within a code block following - this macro. - - Unlike QBENCHMARK, the contents of the contained code block is only run - once. The elapsed time will be reported as "0" if it's to short to - be measured by the selected backend. (Use) - - \sa {Qt Test Overview#Creating a Benchmark}{Creating a Benchmark}, - {Chapter 5: Writing a Benchmark}{Writing a Benchmark} -*/ - -/*! \enum QTest::TestFailMode - - This enum describes the modes for handling an expected failure of the - \l QVERIFY() or \l QCOMPARE() macros. - - \value Abort Aborts the execution of the test. Use this mode when it - doesn't make sense to execute the test any further after the - expected failure. - - \value Continue Continues execution of the test after the expected failure. - - \sa QEXPECT_FAIL() -*/ - -/*! \enum QTest::KeyAction - - This enum describes possible actions for key handling. - - \value Press The key is pressed. - \value Release The key is released. - \value Click The key is clicked (pressed and released). -*/ - -/*! \enum QTest::MouseAction - - This enum describes possible actions for mouse handling. - - \value MousePress A mouse button is pressed. - \value MouseRelease A mouse button is released. - \value MouseClick A mouse button is clicked (pressed and released). - \value MouseDClick A mouse button is double clicked (pressed and released twice). - \value MouseMove The mouse pointer has moved. -*/ - -/*! \fn void QTest::keyClick(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - - Simulates clicking of \a key with an optional \a modifier on a \a widget. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before clicking the key. - - Examples: - \snippet code/src_qtestlib_qtestcase.cpp 14 - - The first example above simulates clicking the \c escape key on \c - myWidget without any keyboard modifiers and without delay. The - second example simulates clicking \c shift-escape on \c myWidget - following a 200 ms delay of the test. - - \sa QTest::keyClicks() -*/ - -/*! \fn void QTest::keyClick(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - - Simulates clicking of \a key with an optional \a modifier on a \a widget. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before clicking the key. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 13 - - The example above simulates clicking \c a on \c myWidget without - any keyboard modifiers and without delay of the test. - - \sa QTest::keyClicks() -*/ - -/*! \fn void QTest::keyClick(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates clicking of \a key with an optional \a modifier on a \a window. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before clicking the key. - - Examples: - \snippet code/src_qtestlib_qtestcase.cpp 29 - - The first example above simulates clicking the \c escape key on \c - myWindow without any keyboard modifiers and without delay. The - second example simulates clicking \c shift-escape on \c myWindow - following a 200 ms delay of the test. - - \sa QTest::keyClicks() -*/ - -/*! \fn void QTest::keyClick(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates clicking of \a key with an optional \a modifier on a \a window. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before clicking the key. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 28 - - The example above simulates clicking \c a on \c myWindow without - any keyboard modifiers and without delay of the test. - - \sa QTest::keyClicks() -*/ - -/*! \fn void QTest::keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - - Sends a Qt key event to \a widget with the given \a key and an associated \a action. - Optionally, a keyboard \a modifier can be specified, as well as a \a delay - (in milliseconds) of the test before sending the event. -*/ - -/*! \fn void QTest::keyEvent(KeyAction action, QWidget *widget, char ascii, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - - Sends a Qt key event to \a widget with the given key \a ascii and an associated \a action. - Optionally, a keyboard \a modifier can be specified, as well as a \a delay - (in milliseconds) of the test before sending the event. -*/ - -/*! \fn void QTest::keyEvent(KeyAction action, QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Sends a Qt key event to \a window with the given \a key and an associated \a action. - Optionally, a keyboard \a modifier can be specified, as well as a \a delay - (in milliseconds) of the test before sending the event. -*/ - -/*! \fn void QTest::keyEvent(KeyAction action, QWindow *window, char ascii, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Sends a Qt key event to \a window with the given key \a ascii and an associated \a action. - Optionally, a keyboard \a modifier can be specified, as well as a \a delay - (in milliseconds) of the test before sending the event. -*/ - -/*! \fn void QTest::keyPress(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - - Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay - is larger than 0, the test will wait for \a delay milliseconds before pressing the key. - - \b {Note:} At some point you should release the key using \l keyRelease(). - - \sa QTest::keyRelease(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyPress(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - - Simulates pressing a \a key with an optional \a modifier on a \a widget. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before pressing the key. - - \b {Note:} At some point you should release the key using \l keyRelease(). - - \sa QTest::keyRelease(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyPress(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates pressing a \a key with an optional \a modifier on a \a window. If \a delay - is larger than 0, the test will wait for \a delay milliseconds before pressing the key. - - \b {Note:} At some point you should release the key using \l keyRelease(). - - \sa QTest::keyRelease(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyPress(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates pressing a \a key with an optional \a modifier on a \a window. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before pressing the key. - - \b {Note:} At some point you should release the key using \l keyRelease(). - - \sa QTest::keyRelease(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyRelease(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - - Simulates releasing a \a key with an optional \a modifier on a \a widget. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before releasing the key. - - \sa QTest::keyPress(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyRelease(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - - Simulates releasing a \a key with an optional \a modifier on a \a widget. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before releasing the key. - - \sa QTest::keyClick() -*/ - -/*! \fn void QTest::keyRelease(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates releasing a \a key with an optional \a modifier on a \a window. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before releasing the key. - - \sa QTest::keyPress(), QTest::keyClick() -*/ - -/*! \fn void QTest::keyRelease(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - \overload - \since 5.0 - - Simulates releasing a \a key with an optional \a modifier on a \a window. - If \a delay is larger than 0, the test will wait for \a delay milliseconds - before releasing the key. - - \sa QTest::keyClick() -*/ - -/*! \fn void QTest::keyClicks(QWidget *widget, const QString &sequence, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) - - Simulates clicking a \a sequence of keys on a \a - widget. Optionally, a keyboard \a modifier can be specified as - well as a \a delay (in milliseconds) of the test before each key - click. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 15 - - The example above simulates clicking the sequence of keys - representing "hello world" on \c myWidget without any keyboard - modifiers and without delay of the test. - - \sa QTest::keyClick() -*/ - -/*! \fn void QTest::waitForEvents() - \internal -*/ - -/*! \fn void QTest::mouseEvent(MouseAction action, QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1) - \internal -*/ - -/*! \fn void QTest::mouseEvent(MouseAction action, QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1) - \internal -*/ - -/*! \fn void QTest::mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) - - Simulates pressing a mouse \a button with an optional \a modifier - on a \a widget. The position is defined by \a pos; the default - position is the center of the widget. If \a delay is specified, - the test will wait for the specified amount of milliseconds before - the press. - - \sa QTest::mouseRelease(), QTest::mouseClick() -*/ - -/*! \fn void QTest::mousePress(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1) - \overload - \since 5.0 - - Simulates pressing a mouse \a button with an optional \a stateKey modifier - on a \a window. The position is defined by \a pos; the default - position is the center of the window. If \a delay is specified, - the test will wait for the specified amount of milliseconds before - the press. - - \sa QTest::mouseRelease(), QTest::mouseClick() -*/ - -/*! \fn void QTest::mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) - - Simulates releasing a mouse \a button with an optional \a modifier - on a \a widget. The position of the release is defined by \a pos; - the default position is the center of the widget. If \a delay is - specified, the test will wait for the specified amount of - milliseconds before releasing the button. - - \sa QTest::mousePress(), QTest::mouseClick() -*/ - -/*! \fn void QTest::mouseRelease(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1) - \overload - \since 5.0 - - Simulates releasing a mouse \a button with an optional \a stateKey modifier - on a \a window. The position of the release is defined by \a pos; - the default position is the center of the window. If \a delay is - specified, the test will wait for the specified amount of - milliseconds before releasing the button. - - \sa QTest::mousePress(), QTest::mouseClick() -*/ - -/*! \fn void QTest::mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) - - Simulates clicking a mouse \a button with an optional \a modifier - on a \a widget. The position of the click is defined by \a pos; - the default position is the center of the widget. If \a delay is - specified, the test will wait for the specified amount of - milliseconds before pressing and before releasing the button. - - \sa QTest::mousePress(), QTest::mouseRelease() -*/ - -/*! \fn void QTest::mouseClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1) - \overload - \since 5.0 - - Simulates clicking a mouse \a button with an optional \a stateKey modifier - on a \a window. The position of the click is defined by \a pos; - the default position is the center of the window. If \a delay is - specified, the test will wait for the specified amount of - milliseconds before pressing and before releasing the button. - - \sa QTest::mousePress(), QTest::mouseRelease() -*/ - -/*! \fn void QTest::mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) - - Simulates double clicking a mouse \a button with an optional \a - modifier on a \a widget. The position of the click is defined by - \a pos; the default position is the center of the widget. If \a - delay is specified, the test will wait for the specified amount of - milliseconds before each press and release. - - \sa QTest::mouseClick() -*/ - -/*! \fn void QTest::mouseDClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1) - \overload - \since 5.0 - - Simulates double clicking a mouse \a button with an optional \a stateKey - modifier on a \a window. The position of the click is defined by - \a pos; the default position is the center of the window. If \a - delay is specified, the test will wait for the specified amount of - milliseconds before each press and release. - - \sa QTest::mouseClick() -*/ - -/*! \fn void QTest::mouseMove(QWidget *widget, QPoint pos = QPoint(), int delay=-1) - - Moves the mouse pointer to a \a widget. If \a pos is not - specified, the mouse pointer moves to the center of the widget. If - a \a delay (in milliseconds) is given, the test will wait before - moving the mouse pointer. -*/ - -/*! \fn void QTest::mouseMove(QWindow *window, QPoint pos = QPoint(), int delay=-1) - \overload - \since 5.0 - - Moves the mouse pointer to a \a window. If \a pos is not - specified, the mouse pointer moves to the center of the window. If - a \a delay (in milliseconds) is given, the test will wait before - moving the mouse pointer. -*/ - -/*! - \fn char *QTest::toString(const T &value) - - Returns a textual representation of \a value. This function is used by - \l QCOMPARE() to output verbose information in case of a test failure. - - You can add specializations or overloads of this function to your test to enable - verbose output. - - \b {Note:} Starting with Qt 5.5, you should prefer to provide a toString() function - in the type's namespace instead of specializing this template. - If your code needs to continue to work with the QTestLib from Qt 5.4 or - earlier, you need to continue to use specialization. - - \b {Note:} The caller of toString() must delete the returned data - using \c{delete[]}. Your implementation should return a string - created with \c{new[]} or qstrdup(). The easiest way to do so is to - create a QByteArray or QString and calling QTest::toString() on it - (see second example below). - - Example for specializing (Qt ≤ 5.4): - - \snippet code/src_qtestlib_qtestcase.cpp 16 - - The example above defines a toString() specialization for a class - called \c MyPoint. Whenever a comparison of two instances of \c - MyPoint fails, \l QCOMPARE() will call this function to output the - contents of \c MyPoint to the test log. - - Same example, but with overloading (Qt ≥ 5.5): - - \snippet code/src_qtestlib_qtestcase.cpp toString-overload - - \sa QCOMPARE() -*/ - -/*! - \fn char *QTest::toString(const QLatin1String &string) - \overload - - Returns a textual representation of the given \a string. -*/ - -/*! - \fn char *QTest::toString(const QString &string) - \overload - - Returns a textual representation of the given \a string. -*/ - -/*! - \fn char *QTest::toString(const QByteArray &ba) - \overload - - Returns a textual representation of the byte array \a ba. - - \sa QTest::toHexRepresentation() -*/ - -/*! - \fn char *QTest::toString(const QTime &time) - \overload - - Returns a textual representation of the given \a time. -*/ - -/*! - \fn char *QTest::toString(const QDate &date) - \overload - - Returns a textual representation of the given \a date. -*/ - -/*! - \fn char *QTest::toString(const QDateTime &dateTime) - \overload - - Returns a textual representation of the date and time specified by - \a dateTime. -*/ - -/*! - \fn char *QTest::toString(const QChar &character) - \overload - - Returns a textual representation of the given \a character. -*/ - -/*! - \fn char *QTest::toString(const QPoint &point) - \overload - - Returns a textual representation of the given \a point. -*/ - -/*! - \fn char *QTest::toString(const QSize &size) - \overload - - Returns a textual representation of the given \a size. -*/ - -/*! - \fn char *QTest::toString(const QRect &rectangle) - \overload - - Returns a textual representation of the given \a rectangle. -*/ - -/*! - \fn char *QTest::toString(const QUrl &url) - \since 4.4 - \overload - - Returns a textual representation of the given \a url. -*/ - -/*! - \fn char *QTest::toString(const QPointF &point) - \overload - - Returns a textual representation of the given \a point. -*/ - -/*! - \fn char *QTest::toString(const QSizeF &size) - \overload - - Returns a textual representation of the given \a size. -*/ - -/*! - \fn char *QTest::toString(const QRectF &rectangle) - \overload - - Returns a textual representation of the given \a rectangle. -*/ - -/*! - \fn char *QTest::toString(const QVariant &variant) - \overload - - Returns a textual representation of the given \a variant. -*/ - -/*! - \fn char *QTest::toString(QSizePolicy::ControlType ct) - \overload - \since 5.5 - - Returns a textual representation of control type \a ct. -*/ - -/*! - \fn char *QTest::toString(QSizePolicy::ControlTypes cts) - \overload - \since 5.5 - - Returns a textual representation of control types \a cts. -*/ - -/*! - \fn char *QTest::toString(QSizePolicy::Policy p) - \overload - \since 5.5 - - Returns a textual representation of policy \a p. -*/ - -/*! - \fn char *QTest::toString(QSizePolicy sp) - \overload - \since 5.5 - - Returns a textual representation of size policy \a sp. -*/ - -/*! \fn void QTest::qWait(int ms) - - Waits for \a ms milliseconds. While waiting, events will be processed and - your test will stay responsive to user interface events or network communication. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 17 - - The code above will wait until the network server is responding for a - maximum of about 12.5 seconds. - - \sa QTest::qSleep(), QSignalSpy::wait() -*/ - -/*! \fn bool QTest::qWaitForWindowExposed(QWindow *window, int timeout) - \since 5.0 - - Waits for \a timeout milliseconds or until the \a window is exposed. - Returns \c true if \c window is exposed within \a timeout milliseconds, otherwise returns \c false. - - This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen some - time after being asked to show itself on the screen. - - \sa QTest::qWaitForWindowActive(), QWindow::isExposed() -*/ - -/*! \fn bool QTest::qWaitForWindowActive(QWindow *window, int timeout) - \since 5.0 - - Waits for \a timeout milliseconds or until the \a window is active. - - Returns \c true if \c window is active within \a timeout milliseconds, otherwise returns \c false. - - \sa QTest::qWaitForWindowExposed(), QWindow::isActive() -*/ - -/*! \fn bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout) - \since 5.0 - - Waits for \a timeout milliseconds or until the \a widget's window is exposed. - Returns \c true if \c widget's window is exposed within \a timeout milliseconds, otherwise returns \c false. - - This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen some - time after being asked to show itself on the screen. - - \sa QTest::qWaitForWindowActive() -*/ - -/*! \fn bool QTest::qWaitForWindowActive(QWidget *widget, int timeout) - \since 5.0 - - Waits for \a timeout milliseconds or until the \a widget's window is active. - - Returns \c true if \c widget's window is active within \a timeout milliseconds, otherwise returns \c false. - - \sa QTest::qWaitForWindowExposed(), QWidget::isActiveWindow() -*/ - -/*! \fn bool QTest::qWaitForWindowShown(QWidget *widget, int timeout) - \since 5.0 - \deprecated - - Waits for \a timeout milliseconds or until the \a widget's window is exposed. - Returns \c true if \c widget's window is exposed within \a timeout milliseconds, otherwise returns \c false. - - This function does the same as qWaitForWindowExposed(). - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 24 - - \sa QTest::qWaitForWindowActive(), QTest::qWaitForWindowExposed() -*/ - -/*! - \class QTest::QTouchEventSequence - \inmodule QtTest - \since 4.6 - - \brief The QTouchEventSequence class is used to simulate a sequence of touch events. - - To simulate a sequence of touch events on a specific device for a window or widget, call - QTest::touchEvent to create a QTouchEventSequence instance. Add touch events to - the sequence by calling press(), move(), release() and stationary(), and let the - instance run out of scope to commit the sequence to the event system. - - Example: - \snippet code/src_qtestlib_qtestcase.cpp 25 -*/ - -/*! - \fn QTest::QTouchEventSequence::~QTouchEventSequence() - - Commits this sequence of touch events, unless autoCommit was disabled, and frees allocated resources. -*/ - -/*! - \fn void QTest::QTouchEventSequence::commit(bool processEvents) - - Commits this sequence of touch events to the event system. Normally there is no need to call this - function because it is called from the destructor. However, if autoCommit is disabled, the events - only get committed upon explicitly calling this function. - - In special cases tests may want to disable the processing of the events. This can be achieved by - setting \a processEvents to false. This results in merely queuing the events, the event loop will - not be forced to process them. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::press(int touchId, const QPoint &pt, QWindow *window) - \since 5.0 - - Adds a press event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a window. If \a window is the null pointer, then - \a pt is interpreted as relative to the window provided when instantiating this QTouchEventSequence. - - Simulates that the user pressed the touch screen or pad with the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::press(int touchId, const QPoint &pt, QWidget *widget) - - Adds a press event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a widget. If \a widget is the null pointer, then - \a pt is interpreted as relative to the widget provided when instantiating this QTouchEventSequence. - - Simulates that the user pressed the touch screen or pad with the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::move(int touchId, const QPoint &pt, QWindow *window) - \since 5.0 - - Adds a move event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a window. If \a window is the null pointer, then - \a pt is interpreted as relative to the window provided when instantiating this QTouchEventSequence. - - Simulates that the user moved the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::move(int touchId, const QPoint &pt, QWidget *widget) - - Adds a move event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a widget. If \a widget is the null pointer, then - \a pt is interpreted as relative to the widget provided when instantiating this QTouchEventSequence. - - Simulates that the user moved the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::release(int touchId, const QPoint &pt, QWindow *window) - \since 5.0 - - Adds a release event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a window. If \a window is the null pointer, then - \a pt is interpreted as relative to the window provided when instantiating this QTouchEventSequence. - - Simulates that the user lifted the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::release(int touchId, const QPoint &pt, QWidget *widget) - - Adds a release event for touchpoint \a touchId at position \a pt to this sequence and returns - a reference to this QTouchEventSequence. - - The position \a pt is interpreted as relative to \a widget. If \a widget is the null pointer, then - \a pt is interpreted as relative to the widget provided when instantiating this QTouchEventSequence. - - Simulates that the user lifted the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence &QTest::QTouchEventSequence::stationary(int touchId) - - Adds a stationary event for touchpoint \a touchId to this sequence and returns - a reference to this QTouchEventSequence. - - Simulates that the user did not move the finger identified by \a touchId. -*/ - -/*! - \fn QTouchEventSequence QTest::touchEvent(QWindow *window, QTouchDevice *device, bool autoCommit) - \since 5.0 - - Creates and returns a QTouchEventSequence for the \a device to - simulate events for \a window. - - When adding touch events to the sequence, \a window will also be used to translate - the position provided to screen coordinates, unless another window is provided in the - respective calls to press(), move() etc. - - The touch events are committed to the event system when the destructor of the - QTouchEventSequence is called (ie when the object returned runs out of scope), unless - \a autoCommit is set to false. When \a autoCommit is false, commit() has to be called - manually. -*/ - -/*! - \fn QTouchEventSequence QTest::touchEvent(QWidget *widget, QTouchDevice *device, bool autoCommit) - - Creates and returns a QTouchEventSequence for the \a device to - simulate events for \a widget. - - When adding touch events to the sequence, \a widget will also be used to translate - the position provided to screen coordinates, unless another widget is provided in the - respective calls to press(), move() etc. - - The touch events are committed to the event system when the destructor of the - QTouchEventSequence is called (ie when the object returned runs out of scope), unless - \a autoCommit is set to false. When \a autoCommit is false, commit() has to be called - manually. -*/ - static bool installCoverageTool(const char * appname, const char * testname) { #ifdef __COVERAGESCANNER__ @@ -1412,43 +159,74 @@ static bool installCoverageTool(const char * appname, const char * testname) #endif } +static bool isValidSlot(const QMetaMethod &sl) +{ + if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0 + || sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot) + return false; + const QByteArray name = sl.name(); + return !(name.isEmpty() || name.endsWith("_data") + || name == "initTestCase" || name == "cleanupTestCase" + || name == "init" || name == "cleanup"); +} + namespace QTest { + class WatchDog; + static QObject *currentTestObject = 0; static QString mainSourcePath; - class TestFunction { + class TestMethods { + Q_DISABLE_COPY(TestMethods) public: - TestFunction() : function_(-1), data_(0) {} - void set(int function, char *data) { function_ = function; data_ = data; } - char *data() const { return data_; } - int function() const { return function_; } - ~TestFunction() { delete[] data_; } + typedef std::vector<QMetaMethod> MetaMethods; + + explicit TestMethods(const QObject *o, const MetaMethods &m = MetaMethods()); + + void invokeTests(QObject *testObject) const; + + static QMetaMethod findMethod(const QObject *obj, const char *signature); + private: - int function_; - char *data_; + bool invokeTest(int index, const char *data, WatchDog *watchDog) const; + void invokeTestOnData(int index) const; + + QMetaMethod m_initTestCaseMethod; // might not exist, check isValid(). + QMetaMethod m_initTestCaseDataMethod; + QMetaMethod m_cleanupTestCaseMethod; + QMetaMethod m_initMethod; + QMetaMethod m_cleanupMethod; + + MetaMethods m_methods; }; - /** - * Contains the list of test functions that was supplied - * on the command line, if any. Hence, if not empty, - * those functions should be run instead of - * all appearing in the test case. - */ - static TestFunction * testFuncs = 0; - static int testFuncCount = 0; - - /** Don't leak testFuncs on exit even on error */ - static struct TestFuncCleanup + + TestMethods::TestMethods(const QObject *o, const MetaMethods &m) + : m_initTestCaseMethod(TestMethods::findMethod(o, "initTestCase()")) + , m_initTestCaseDataMethod(TestMethods::findMethod(o, "initTestCase_data()")) + , m_cleanupTestCaseMethod(TestMethods::findMethod(o, "cleanupTestCase()")) + , m_initMethod(TestMethods::findMethod(o, "init()")) + , m_cleanupMethod(TestMethods::findMethod(o, "cleanup()")) + , m_methods(m) { - void cleanup() - { - delete[] testFuncs; - testFuncCount = 0; - testFuncs = 0; + if (m.empty()) { + const QMetaObject *metaObject = o->metaObject(); + const int count = metaObject->methodCount(); + m_methods.reserve(count); + for (int i = 0; i < count; ++i) { + const QMetaMethod me = metaObject->method(i); + if (isValidSlot(me)) + m_methods.push_back(me); + } } + } - ~TestFuncCleanup() { cleanup(); } - } testFuncCleaner; + QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature) + { + const QMetaObject *metaObject = obj->metaObject(); + const int funcIndex = metaObject->indexOfMethod(signature); + return funcIndex >= 0 ? metaObject->method(funcIndex) : QMetaMethod(); + } static int keyDelay = -1; static int mouseDelay = -1; @@ -1504,22 +282,6 @@ int Q_TESTLIB_EXPORT defaultKeyDelay() return keyDelay; } -static bool isValidSlot(const QMetaMethod &sl) -{ - if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0 - || sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot) - return false; - QByteArray name = sl.name(); - if (name.isEmpty()) - return false; - if (name.endsWith("_data")) - return false; - if (name == "initTestCase" || name == "cleanupTestCase" - || name == "cleanup" || name == "init") - return false; - return true; -} - Q_TESTLIB_EXPORT bool printAvailableFunctions = false; Q_TESTLIB_EXPORT QStringList testFunctions; Q_TESTLIB_EXPORT QStringList testTags; @@ -1617,6 +379,9 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) QTestLog::LogMode logFormat = QTestLog::Plain; const char *logFilename = 0; + QTest::testFunctions.clear(); + QTest::testTags.clear(); + #if defined(Q_OS_MAC) && defined(HAVE_XCTEST) if (QXcodeTestLogger::canLogTestProgress()) logFormat = QTestLog::XCTest; @@ -1897,7 +662,7 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) fprintf(stderr, "\n" " -help : This help\n"); exit(1); - } else if (qml) { + } else { // We can't check the availability of test functions until // we load the QML files. So just store the data for now. int colon = -1; @@ -1923,36 +688,6 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) QTest::testTags += QString::fromLatin1(argv[i] + colon + 1); } - } else { - if (!QTest::testFuncs) { - QTest::testFuncs = new QTest::TestFunction[512]; - } - - int colon = -1; - char buf[512], *data=0; - int off; - for (off = 0; *(argv[i]+off); ++off) { - if (*(argv[i]+off) == ':') { - colon = off; - break; - } - } - if (colon != -1) { - data = qstrdup(argv[i]+colon+1); - } - qsnprintf(buf, qMin(512, off + 1), "%s", argv[i]); // copy text before the ':' into buf - qsnprintf(buf + off, qMin(512 - off, 3), "()"); // append "()" - int idx = QTest::currentTestObject->metaObject()->indexOfMethod(buf); - if (idx < 0 || !isValidSlot(QTest::currentTestObject->metaObject()->method(idx))) { - fprintf(stderr, "Unknown test function: '%s'. Possible matches:\n", buf); - buf[off] = 0; - qPrintTestSlots(stderr, buf); - fprintf(stderr, "\n%s -functions\nlists all available test functions.\n", argv[0]); - exit(1); - } - testFuncs[testFuncCount].set(idx, data); - testFuncCount++; - QTEST_ASSERT(QTest::testFuncCount < 512); } } @@ -2005,7 +740,7 @@ qreal addResult(qreal current, const QBenchmarkResult& r) } -static void qInvokeTestMethodDataEntry(char *slot) +void TestMethods::invokeTestOnData(int index) const { /* Benchmarking: for each median iteration*/ @@ -2020,7 +755,8 @@ static void qInvokeTestMethodDataEntry(char *slot) /* Benchmarking: for each accumulation iteration*/ bool invokeOk; do { - invokeMethod(QTest::currentTestObject, "init()"); + if (m_initMethod.isValid()) + m_initMethod.invoke(QTest::currentTestObject, Qt::DirectConnection); if (QTestResult::skipCurrentTest() || QTestResult::currentTestFailed()) break; @@ -2032,8 +768,7 @@ static void qInvokeTestMethodDataEntry(char *slot) QTestResult::currentDataTag() ? QTestResult::currentDataTag() : ""); - invokeOk = QMetaObject::invokeMethod(QTest::currentTestObject, slot, - Qt::DirectConnection); + invokeOk = m_methods[index].invoke(QTest::currentTestObject, Qt::DirectConnection); if (!invokeOk) QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__); @@ -2041,7 +776,8 @@ static void qInvokeTestMethodDataEntry(char *slot) QTestResult::finishedCurrentTestData(); - invokeMethod(QTest::currentTestObject, "cleanup()"); + if (m_cleanupMethod.isValid()) + m_cleanupMethod.invoke(QTest::currentTestObject, Qt::DirectConnection); // If the test isn't a benchmark, finalize the result after cleanup() has finished. if (!isBenchmark) @@ -2132,7 +868,7 @@ public: int t = timeout.load(); if (!t) break; - if (!waitCondition.wait(&mutex, t)) { + if (Q_UNLIKELY(!waitCondition.wait(&mutex, t))) { stackTrace(); qFatal("Test function timed out"); } @@ -2155,21 +891,18 @@ private: If the function was successfully called, true is returned, otherwise false. */ -static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog *watchDog) +bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) const { - QTEST_ASSERT(slotName); - QBenchmarkTestMethodData benchmarkData; QBenchmarkTestMethodData::current = &benchmarkData; - QBenchmarkGlobalData::current->context.slotName = QLatin1String(slotName); + const QByteArray &name = m_methods[index].name(); + QBenchmarkGlobalData::current->context.slotName = QLatin1String(name) + QStringLiteral("()"); char member[512]; QTestTable table; - char *slot = qstrdup(slotName); - slot[strlen(slot) - 2] = '\0'; - QTestResult::setCurrentTestFunction(slot); + QTestResult::setCurrentTestFunction(name.constData()); const QTestTable *gTable = QTestTable::globalTestTable(); const int globalDataCount = gTable->dataCount(); @@ -2181,7 +914,7 @@ static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog * QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex)); if (curGlobalDataIndex == 0) { - qsnprintf(member, 512, "%s_data()", slot); + qsnprintf(member, 512, "%s_data()", name.constData()); invokeMethod(QTest::currentTestObject, member); } @@ -2196,7 +929,7 @@ static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog * if (!*data) data = 0; else { - fprintf(stderr, "Unknown testdata for function %s: '%s'\n", slotName, data); + fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), data); fprintf(stderr, "Function has no testdata.\n"); return false; } @@ -2209,14 +942,14 @@ static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog * if (!data || !qstrcmp(data, table.testData(curDataIndex)->dataTag())) { foundFunction = true; - QTestPrivate::checkBlackLists(slot, dataCount ? table.testData(curDataIndex)->dataTag() : 0); + QTestPrivate::checkBlackLists(name.constData(), dataCount ? table.testData(curDataIndex)->dataTag() : 0); QTestDataSetter s(curDataIndex >= dataCount ? static_cast<QTestData *>(0) : table.testData(curDataIndex)); if (watchDog) watchDog->beginTest(); - qInvokeTestMethodDataEntry(slot); + invokeTestOnData(index); if (watchDog) watchDog->testFinished(); @@ -2228,7 +961,7 @@ static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog * } if (data && !foundFunction) { - fprintf(stderr, "Unknown testdata for function %s: '%s'\n", slotName, data); + fprintf(stderr, "Unknown testdata for function %s: '%s()'\n", name.constData(), data); fprintf(stderr, "Available testdata:\n"); for (int i = 0; i < table.dataCount(); ++i) fprintf(stderr, "%s\n", table.testData(i)->dataTag()); @@ -2243,7 +976,6 @@ static bool qInvokeTestMethod(const char *slotName, const char *data, WatchDog * QTestResult::setSkipCurrentTest(false); QTestResult::setBlacklistCurrentTest(false); QTestResult::setCurrentTestData(0); - delete[] slot; return true; } @@ -2256,12 +988,12 @@ void *fetchData(QTestData *data, const char *tagName, int typeId) int idx = data->parent()->indexOf(tagName); - if (idx == -1 || idx >= data->dataCount()) { + if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) { qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.", tagName); } - if (typeId != data->parent()->elementTypeId(idx)) { + if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) { qFatal("Requested type '%s' does not match available type '%s'.", QMetaType::typeName(typeId), QMetaType::typeName(data->parent()->elementTypeId(idx))); @@ -2521,21 +1253,23 @@ static bool debuggerPresent() #endif } -static void qInvokeTestMethods(QObject *testObject) +void TestMethods::invokeTests(QObject *testObject) const { const QMetaObject *metaObject = testObject->metaObject(); QTEST_ASSERT(metaObject); QTestLog::startLogging(); QTestResult::setCurrentTestFunction("initTestCase"); QTestTable::globalTestTable(); - invokeMethod(testObject, "initTestCase_data()"); + if (m_initTestCaseDataMethod.isValid()) + m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection); QScopedPointer<WatchDog> watchDog; if (!debuggerPresent()) watchDog.reset(new WatchDog); if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) { - invokeMethod(testObject, "initTestCase()"); + if (m_initTestCaseMethod.isValid()) + m_initTestCaseMethod.invoke(testObject, Qt::DirectConnection); // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy. const bool previousFailed = QTestResult::currentTestFailed(); @@ -2544,35 +1278,22 @@ static void qInvokeTestMethods(QObject *testObject) QTestResult::finishedCurrentTestFunction(); if (!QTestResult::skipCurrentTest() && !previousFailed) { - - if (QTest::testFuncs) { - for (int i = 0; i != QTest::testFuncCount; i++) { - if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).methodSignature().constData(), - QTest::testFuncs[i].data(), watchDog.data())) { - break; - } - } - testFuncCleaner.cleanup(); - } else { - int methodCount = metaObject->methodCount(); - QMetaMethod *testMethods = new QMetaMethod[methodCount]; - for (int i = 0; i != methodCount; i++) - testMethods[i] = metaObject->method(i); - for (int i = 0; i != methodCount; i++) { - if (!isValidSlot(testMethods[i])) - continue; - if (!qInvokeTestMethod(testMethods[i].methodSignature().constData(), 0, watchDog.data())) - break; - } - delete[] testMethods; - testMethods = 0; + for (int i = 0, count = int(m_methods.size()); i < count; ++i) { + const char *data = Q_NULLPTR; + if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty()) + data = qstrdup(QTest::testTags.at(i).toLatin1().constData()); + const bool ok = invokeTest(i, data, watchDog.data()); + delete [] data; + if (!ok) + break; } } QTestResult::setSkipCurrentTest(false); QTestResult::setBlacklistCurrentTest(false); QTestResult::setCurrentTestFunction("cleanupTestCase"); - invokeMethod(testObject, "cleanupTestCase()"); + if (m_cleanupTestCaseMethod.isValid()) + m_cleanupTestCaseMethod.invoke(testObject, Qt::DirectConnection); QTestResult::finishedCurrentTestData(); QTestResult::finishedCurrentTestDataCleanup(); } @@ -2940,7 +1661,7 @@ int QTest::qExec(QObject *testObject, int argc, char **argv) #ifdef QTESTLIB_USE_VALGRIND if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) { - if (!qApp) + if (Q_UNLIKELY(!qApp)) qFatal("QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN"); const QStringList origAppArgs(QCoreApplication::arguments()); @@ -2957,7 +1678,23 @@ int QTest::qExec(QObject *testObject, int argc, char **argv) if (!noCrashHandler) handler.reset(new FatalSignalHandler); #endif - qInvokeTestMethods(testObject); + TestMethods::MetaMethods commandLineMethods; + if (!QTest::testFunctions.isEmpty()) { + foreach (const QString &tf, QTest::testFunctions) { + const QByteArray tfB = tf.toLatin1(); + const QByteArray signature = tfB + QByteArrayLiteral("()"); + QMetaMethod m = TestMethods::findMethod(testObject, signature.constData()); + if (!m.isValid() || !isValidSlot(m)) { + fprintf(stderr, "Unknown test function: '%s'. Possible matches:\n", tfB.constData()); + qPrintTestSlots(stderr, tfB.constData()); + fprintf(stderr, "\n%s -functions\nlists all available test functions.\n", argv[0]); + exit(1); + } + commandLineMethods.push_back(m); + } + } + TestMethods test(testObject, commandLineMethods); + test.invokeTests(testObject); } #ifndef QT_NO_EXCEPTIONS @@ -3578,6 +2315,11 @@ bool QTest::compare_string_helper(const char *t1, const char *t2, const char *ac toString(t1), toString(t2), actual, expected, file, line); } +/*! + \namespace QTest::Internal + \internal +*/ + /*! \fn bool QTest::compare_ptr_helper(const void *t1, const void *t2, const char *actual, const char *expected, const char *file, int line); \internal */ |