diff options
Diffstat (limited to 'src/qmltest')
-rw-r--r-- | src/qmltest/doc/qtqmltest.qdocconf | 42 | ||||
-rw-r--r-- | src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp | 81 | ||||
-rw-r--r-- | src/qmltest/doc/src/qtquicktest-cppapi.qdoc | 59 | ||||
-rw-r--r-- | src/qmltest/doc/src/qtquicktest-index.qdoc | 203 | ||||
-rw-r--r-- | src/qmltest/doc/src/qtquicktest-qmltypes.qdoc | 46 | ||||
-rw-r--r-- | src/qmltest/doc/src/qtquicktest.qdoc | 79 | ||||
-rw-r--r-- | src/qmltest/qmltest.pro | 12 | ||||
-rw-r--r-- | src/qmltest/qtestoptions_p.h | 6 | ||||
-rw-r--r-- | src/qmltest/quicktest.cpp | 355 | ||||
-rw-r--r-- | src/qmltest/quicktest.h | 32 | ||||
-rw-r--r-- | src/qmltest/quicktestevent.cpp | 33 | ||||
-rw-r--r-- | src/qmltest/quicktestevent_p.h | 10 | ||||
-rw-r--r-- | src/qmltest/quicktestresult.cpp | 78 | ||||
-rw-r--r-- | src/qmltest/quicktestresult_p.h | 11 |
14 files changed, 962 insertions, 85 deletions
diff --git a/src/qmltest/doc/qtqmltest.qdocconf b/src/qmltest/doc/qtqmltest.qdocconf new file mode 100644 index 0000000000..9a3e16b64d --- /dev/null +++ b/src/qmltest/doc/qtqmltest.qdocconf @@ -0,0 +1,42 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtQmlTest +description = Qt Quick Test Reference Documentation +version = $QT_VERSION +moduleheader = QtQuickTest +qhp.projects = QtQmlTest + +qhp.QtQmlTest.file = qtqmltest.qhp +qhp.QtQmlTest.namespace = org.qt-project.qtqmltest.$QT_VERSION_TAG +qhp.QtQmlTest.virtualFolder = qtqmltest +qhp.QtQmlTest.indexTitle = Qt Quick Test +qhp.QtQmlTest.indexRoot = + +qhp.QtQmlTest.filterAttributes = qtqmltest $QT_VERSION qtrefdoc +qhp.QtQmlTest.customFilters.Qt.name = QtQmlTest $QT_VERSION +qhp.QtQmlTest.customFilters.Qt.filterAttributes = qtqmltest $QT_VERSION + +qhp.QtQmlTest.subprojects = qmltypes classes +qhp.QtQmlTest.subprojects.classes.title = C++ API +qhp.QtQmlTest.subprojects.classes.indexTitle = Qt Quick Test C++ API +qhp.QtQmlTest.subprojects.classes.selectors = class namespace doc:headerfile +qhp.QtQmlTest.subprojects.classes.sortPages = true +qhp.QtQmlTest.subprojects.qmltypes.title = QML Types +qhp.QtQmlTest.subprojects.qmltypes.indexTitle = Qt Quick Test QML Types +qhp.QtQmlTest.subprojects.qmltypes.selectors = qmlclass +qhp.QtQmlTest.subprojects.qmltypes.sortPages = true + +tagfile = qtqmltest.tags + +depends += qtcore qtgui qttestlib qtqml qtquick qtdoc + +headerdirs += .. + +sourcedirs += .. \ + ../../imports/testlib + +exampledirs += snippets + +navigation.landingpage = "Qt Quick Test" +navigation.cppclassespage = "Qt Quick Test C++ API" +navigation.qmltypespage = "Qt Quick Test QML Types" diff --git a/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp b/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp new file mode 100644 index 0000000000..191f693a9a --- /dev/null +++ b/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [1] +//! [0] +#include <QtQuickTest> +//! [0] +QUICK_TEST_MAIN(example) +//! [1] + +//! [2] +// tst_mytest.cpp +#include <QtQuickTest> +#include <QQmlEngine> +#include <QQmlContext> + +class Setup : public QObject +{ + Q_OBJECT + +public: + Setup() {} + +public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + engine->rootContext()->setContextProperty("myContextProperty", QVariant(true)); + } +}; + +QUICK_TEST_MAIN_WITH_SETUP(mytest, Setup) + +#include "tst_mytest.moc" +//! [2] diff --git a/src/qmltest/doc/src/qtquicktest-cppapi.qdoc b/src/qmltest/doc/src/qtquicktest-cppapi.qdoc new file mode 100644 index 0000000000..0e5dab8887 --- /dev/null +++ b/src/qmltest/doc/src/qtquicktest-cppapi.qdoc @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtQuickTest + \keyword Qt Quick Test C++ API + \title Qt Quick Test C++ API + \ingroup modules + + \brief Provides macros and functions for tests. + + The C++ macros and functions can be included into your application using + the following include statement: + + \snippet src_qmltest_qquicktest.cpp 0 + + There are two ways to link against the corresponding C++ library. If your + test project uses a QML \l TestCase, you should already have the following + line in your project file: + + \badcode + CONFIG += qmltestcase + \endcode + + This will cause the test to link to the C++ \QtQuickTest library. + + If you have a C++-only test project, you can add the following line + to your project file: + + \badcode + QT += qmltest + \endcode + + \sa {Executing C++ Before QML Tests} +*/ diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc new file mode 100644 index 0000000000..4c0124689b --- /dev/null +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtquicktest-index.html + \title Qt Quick Test + \brief Unit testing framework for QML. + + \target Introduction to Qt Quick Test + \section1 Introduction + + \l {Qt Quick Test QML Types}{Qt Quick Test} is a unit test framework for QML applications. + Test cases are written as JavaScript functions within a \l [QML] TestCase + type: + + \qml + import QtQuick 2.3 + import QtTest 1.0 + + TestCase { + name: "MathTests" + + function test_math() { + compare(2 + 2, 4, "2 + 2 = 4") + } + + function test_fail() { + compare(2 + 2, 5, "2 + 2 = 5") + } + } + \endqml + + Functions whose names start with \c{test_} are treated as test cases + to be executed. See the documentation for the \l [QML] TestCase and + \l [QML] SignalSpy types for more information on writing test cases. + + \note There is no binary compatibility guarantee for the Qt Quick Test + module. This means that an application that uses Qt Quick Test is + only guaranteed to work with the Qt version it was developed against. + However, source compatibility is guaranteed. + + \target Running Qt Quick Tests + \section1 Running Tests + + Test cases are launched by a C++ harness that consists of + the following code: + + \snippet src_qmltest_qquicktest.cpp 1 + + Where "example" is the identifier to use to uniquely identify + this set of tests. Finally, add \c{CONFIG += qmltestcase} to the project + file: + + \badcode + TEMPLATE = app + TARGET = tst_example + CONFIG += warn_on qmltestcase + SOURCES += tst_example.cpp + \endcode + + The test harness scans the specified source directory recursively + for "tst_*.qml" files. If \c{QUICK_TEST_SOURCE_DIR} is not defined, + then the current directory will be scanned when the harness is run. + Other *.qml files may appear for auxillary QML components that are + used by the test. + + The \c{-input} command-line option can be set at runtime to run + test cases from a different directory. This may be needed to run + tests on a target device where the compiled-in directory name refers + to a host. For example: + + \badcode + tst_example -input /mnt/SDCard/qmltests + \endcode + + It is also possible to run a single file using the \c{-input} option. + For example: + + \badcode + tst_example -input data/test.qml + \endcode + + \badcode + tst_example -input <full_path>/test.qml + \endcode + + \note Specifying the full path to the qml test file is for example + needed for shadow builds. + + If your test case needs QML imports, then you can add them as + \c{-import} options to the test program command-line. + + If \c IMPORTPATH is specified in your .pro file, each import path added to \c IMPORTPATH + will be passed as a command-line argument when the test is run using "make check": + + \badcode + IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2 + \endcode + + The \c{-functions} command-line option will return a list of the current + tests functions. It is possible to run a single test function using the name + of the test function as an argument. For example: + + \badcode + tst_example Test_Name::function1 + \endcode + + The \c{-help} command-line option will return all the options available. + + \badcode + tst_example -help + \endcode + + \section1 Executing C++ Before QML Tests + + To execute C++ code before any of the QML tests are run, the + \l QUICK_TEST_MAIN_WITH_SETUP macro can be used. This can be useful for + setting context properties on the QML engine, amongst other things. + + The macro is identical to \c QUICK_TEST_MAIN, except that it takes an + additional \c QObject* argument. The test framework will call slots and + invokable functions with the following names: + + \table + \header + \li Name + \li Purpose + \row + \li \c {void applicationAvailable()} + \li Called right after the QApplication object was instantiated. + Use this function to setup everything that is not related + to QML directly. + \row + \li \c {void qmlEngineAvailable(QQmlEngine *)} + \li Called when the QML engine is available. + Any \l {QQmlEngine::addImportPath}{import paths}, + \l {QQmlEngine::addPluginPath}{plugin paths}, + and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors} + will have been set on the engine by this point. + \row + \li \c {void cleanupTestCase()} + \li Called right after the test execution has finished. + Use this function to clean up before everything will start to be destructed. + \endtable + + Each function will be called once for each \c {tst_*.qml} file, so any + arguments are unique to that test. For example, this means that each QML + test file will have its own QML engine. + + The following example demonstrates how the macro can be used to set context + properties on the QML engine: + + \snippet src_qmltest_qquicktest.cpp 2 + + The \c .moc include is based on the file name of the \c .cpp file. + For example, in the example above, the \c .cpp file is named + \c tst_mytest.cpp. If the file was named \c MyTest.cpp, the include would + be: + + \code + #include "MyTest.moc" + \endcode + + \section1 Reference + + \list + \li \l{Qt Quick Test QML Types}{QML Types} + \li \l{Qt Quick Test C++ API}{C++ API} + \endlist + + \section1 Licenses + + Qt Quick Tests is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under free software licenses. Since Qt 5.4, + these free software licenses are + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. +*/ diff --git a/src/qmltest/doc/src/qtquicktest-qmltypes.qdoc b/src/qmltest/doc/src/qtquicktest-qmltypes.qdoc new file mode 100644 index 0000000000..0ed8c6faa0 --- /dev/null +++ b/src/qmltest/doc/src/qtquicktest-qmltypes.qdoc @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtTest 1.\QtMinorVersion + \title Qt Quick Test QML Types + \brief Provides QML types to unit test your QML application. + \ingroup qmlmodules + + You can import this module using the following statement: + + \qml \QtMinorVersion + import QtTest 1.\1 + \endqml + + \section1 QML Types + \generatelist {qmltypesbymodule QtTest} + \noautolist + + For more information about how to use these types, see + \l{Qt Quick Test}. +*/ diff --git a/src/qmltest/doc/src/qtquicktest.qdoc b/src/qmltest/doc/src/qtquicktest.qdoc new file mode 100644 index 0000000000..31c097ed76 --- /dev/null +++ b/src/qmltest/doc/src/qtquicktest.qdoc @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \namespace QQuickTest + \inmodule QtQuickTest + + \brief The QQuickTest namespace contains all the functions and + macros related to Qt Quick Test. + + See the \l{Introduction to Qt Quick Test} for information about how to write + Qt Quick unit tests. + + To link to the Qt Quick Test C++ library, see \l {Qt Quick Test C++ API}. + + \sa {Executing C++ Before QML Tests} +*/ + +/*! + \macro QUICK_TEST_MAIN(name) + \relates QQuickTest + + \brief Sets up the entry point for a Qt Quick Test application. + The \a name argument uniquely identifies this set of tests. + + \snippet src_qmltest_qquicktest.cpp 1 + + \note The macro assumes that your test sources are in the current + directory, unless the \c QUICK_TEST_SOURCE_DIR environment variable is set. + + \sa QUICK_TEST_MAIN_WITH_SETUP(), {Running Qt Quick Tests} + +*/ + +/*! + \macro QUICK_TEST_MAIN_WITH_SETUP(name, QuickTestSetupClass) + \relates QQuickTest + + \brief Sets up the entry point for a Qt Quick Test application. + The \a name argument uniquely identifies this set of tests. + + This macro is identical to QUICK_TEST_MAIN(), except that it takes an + additional argument \a QuickTestSetupClass, a pointer to a QObject-derived + class. With this class it is possible to define additional setup code to + execute before running the QML test. + + \note The macro assumes that your test sources are in the current + directory, unless the \c QUICK_TEST_SOURCE_DIR environment variable is set. + + The following snippet demonstrates the use of this macro: + + \snippet src_qmltest_qquicktest.cpp 2 + + \sa QUICK_TEST_MAIN(), {Running Qt Quick Tests} +*/ diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index d13e162ff4..0bf05093be 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -1,8 +1,10 @@ TARGET = QtQuickTest +QMAKE_DOCS = $$PWD/doc/qtqmltest.qdocconf + DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_FOREACH QT = core testlib-private -QT_PRIVATE = quick qml-private gui core-private gui-private +QT_PRIVATE = quick quick-private qml-private gui core-private gui-private # Testlib is only a private dependency, which results in our users not # inheriting testlibs's MODULE_CONFIG transitively. Make it explicit. @@ -13,12 +15,6 @@ qtHaveModule(widgets) { DEFINES += QT_QMLTEST_WITH_WIDGETS } -# Install qmltestcase.prf into the Qt mkspecs so that "CONFIG += qmltestcase" -# can be used in customer applications to build against QtQuickTest. -feature.path = $$[QT_INSTALL_DATA]/mkspecs/features -feature.files = $$PWD/features/qmltestcase.prf -INSTALLS += feature - SOURCES += \ $$PWD/quicktest.cpp \ $$PWD/quicktestevent.cpp \ @@ -30,6 +26,6 @@ HEADERS += \ $$PWD/quicktestresult_p.h \ $$PWD/qtestoptions_p.h -!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING +qtConfig(qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING load(qt_module) diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h index 02609a6189..7be5c88590 100644 --- a/src/qmltest/qtestoptions_p.h +++ b/src/qmltest/qtestoptions_p.h @@ -51,7 +51,11 @@ // We mean it. // -#include <QtTest/qtest_global.h> +#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) +# include <QtTest/qtest_global.h> +#else +# include <QtTest/qttestglobal.h> +#endif #include <QtCore/qstring.h> #include <QtCore/qstringlist.h> diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index f62f66170e..9a73726797 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -44,10 +44,14 @@ #include <QtQml/qqml.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <QtQml/qjsvalue.h> #include <QtQml/qjsengine.h> #include <QtQml/qqmlpropertymap.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/qquickitem.h> #include <QtGui/qopengl.h> #include <QtCore/qurl.h> #include <QtCore/qfileinfo.h> @@ -57,11 +61,15 @@ #include <QtCore/qdebug.h> #include <QtCore/qeventloop.h> #include <QtCore/qtextstream.h> +#include <QtCore/qtimer.h> #include <QtGui/qtextdocument.h> #include <stdio.h> #include <QtGui/QGuiApplication> #include <QtCore/QTranslator> #include <QtTest/QSignalSpy> +#include <QtQml/QQmlFileSelector> + +#include <private/qqmlcomponent_p.h> #ifdef QT_QMLTEST_WITH_WIDGETS #include <QtWidgets/QApplication> @@ -69,6 +77,61 @@ QT_BEGIN_NAMESPACE +/*! + \since 5.13 + + Returns \c true if \l {QQuickItem::}{updatePolish()} has not been called + on \a item since the last call to \l {QQuickItem::}{polish()}, + otherwise returns \c false. + + When assigning values to properties in QML, any layouting the item + must do as a result of the assignment might not take effect immediately, + but can instead be postponed until the item is polished. For these cases, + you can use this function to ensure that the item has been polished + before the execution of the test continues. For example: + + \code + QVERIFY(QQuickTest::qIsPolishScheduled(item)); + QVERIFY(QQuickTest::qWaitForItemPolished(item)); + \endcode + + Without the call to \c qIsPolishScheduled() above, the + call to \c qWaitForItemPolished() might see that no polish + was scheduled and therefore pass instantly, assuming that + the item had already been polished. This function + makes it obvious why an item wasn't polished and allows tests to + fail early under such circumstances. + + The QML equivalent of this function is + \l {TestCase::}{isPolishScheduled()}. + + \sa QQuickItem::polish(), QQuickItem::updatePolish() +*/ +bool QQuickTest::qIsPolishScheduled(const QQuickItem *item) +{ + return QQuickItemPrivate::get(item)->polishScheduled; +} + +/*! + \since 5.13 + + Waits for \a timeout milliseconds or until + \l {QQuickItem::}{updatePolish()} has been called on \a item. + + Returns \c true if \c updatePolish() was called on \a item within + \a timeout milliseconds, otherwise returns \c false. + + The QML equivalent of this function is + \l {TestCase::}{waitForItemPolished()}. + + \sa QQuickItem::polish(), QQuickItem::updatePolish(), + QQuickTest::qIsPolishScheduled() +*/ +bool QQuickTest::qWaitForItemPolished(const QQuickItem *item, int timeout) +{ + return QTest::qWaitFor([&]() { return !QQuickItemPrivate::get(item)->polishScheduled; }, timeout); +} + class QTestRootObject : public QObject { Q_OBJECT @@ -76,7 +139,7 @@ class QTestRootObject : public QObject Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged) Q_PROPERTY(QObject *defined READ defined) public: - QTestRootObject(QObject *parent = 0) + QTestRootObject(QObject *parent = nullptr) : QObject(parent), hasQuit(false), m_windowShown(false), m_hasTestCase(false) { m_defined = new QQmlPropertyMap(this); #if defined(QT_OPENGL_ES_2_ANGLE) @@ -87,7 +150,7 @@ public: static QTestRootObject *instance() { static QPointer<QTestRootObject> object = new QTestRootObject; if (!object) { - qWarning("A new test root object has been created, the behavior may be compromised"); + // QTestRootObject was deleted when previous test ended, create a new one object = new QTestRootObject; } return object; @@ -188,15 +251,166 @@ bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000) if (remaining <= 0) break; QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QTest::qSleep(10); } return spy.size(); } +void maybeInvokeSetupMethod(QObject *setupObject, const char *member, QGenericArgument val0 = QGenericArgument(nullptr)) +{ + // It's OK if it doesn't exist: since we have more than one callback that + // can be called, it makes sense if the user only implements one of them. + // We do this the long way rather than just calling the static + // QMetaObject::invokeMethod(), because that will issue a warning if the + // function doesn't exist, which we don't want. + const QMetaObject *setupMetaObject = setupObject->metaObject(); + const int methodIndex = setupMetaObject->indexOfMethod(member); + if (methodIndex != -1) { + const QMetaMethod method = setupMetaObject->method(methodIndex); + method.invoke(setupObject, val0); + } +} + +using namespace QV4::CompiledData; + +class TestCaseCollector +{ +public: + typedef QList<QString> TestCaseList; + + TestCaseCollector(const QFileInfo &fileInfo, QQmlEngine *engine) + { + QString path = fileInfo.absoluteFilePath(); + if (path.startsWith(QLatin1String(":/"))) + path.prepend(QLatin1String("qrc")); + + QQmlComponent component(engine, path); + m_errors += component.errors(); + + if (component.isReady()) { + QQmlRefPointer<CompilationUnit> rootCompilationUnit = QQmlComponentPrivate::get(&component)->compilationUnit; + TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit.data()); + m_testCases = result.testCases + result.finalizedPartialTestCases(); + m_errors += result.errors; + } + } + + TestCaseList testCases() const { return m_testCases; } + QList<QQmlError> errors() const { return m_errors; } + +private: + TestCaseList m_testCases; + QList<QQmlError> m_errors; + + struct TestCaseEnumerationResult + { + TestCaseList testCases; + QList<QQmlError> errors; + + // Partially constructed test cases + bool isTestCase = false; + TestCaseList testFunctions; + QString testCaseName; + + TestCaseList finalizedPartialTestCases() const + { + TestCaseList result; + for (const QString &function : testFunctions) + result << QString(QStringLiteral("%1::%2")).arg(testCaseName).arg(function); + return result; + } + + TestCaseEnumerationResult &operator<<(const TestCaseEnumerationResult &other) + { + testCases += other.testCases + other.finalizedPartialTestCases(); + errors += other.errors; + return *this; + } + }; + + TestCaseEnumerationResult enumerateTestCases(CompilationUnit *compilationUnit, const Object *object = nullptr) + { + QQmlType testCaseType; + for (quint32 i = 0, count = compilationUnit->importCount(); i < count; ++i) { + const Import *import = compilationUnit->importAt(i); + if (compilationUnit->stringAt(import->uriIndex) != QLatin1Literal("QtTest")) + continue; + + QString testCaseTypeName(QStringLiteral("TestCase")); + QString typeQualifier = compilationUnit->stringAt(import->qualifierIndex); + if (!typeQualifier.isEmpty()) + testCaseTypeName = typeQualifier % QLatin1Char('.') % testCaseTypeName; + + testCaseType = compilationUnit->typeNameCache->query(testCaseTypeName).type; + if (testCaseType.isValid()) + break; + } + + TestCaseEnumerationResult result; + + if (!object) // Start at root of compilation unit if not enumerating a specific child + object = compilationUnit->objectAt(0); + + if (CompilationUnit *superTypeUnit = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)->compilationUnit.data()) { + // We have a non-C++ super type, which could indicate we're a subtype of a TestCase + if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl()) + result.isTestCase = true; + else + result = enumerateTestCases(superTypeUnit); + + if (result.isTestCase) { + // Look for override of name in this type + for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { + if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1Literal("name")) { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + result.testCaseName = compilationUnit->stringAt(binding->stringIndex); + } else { + QQmlError error; + error.setUrl(compilationUnit->url()); + error.setLine(binding->location.line); + error.setColumn(binding->location.column); + error.setDescription(QStringLiteral("the 'name' property of a TestCase must be a literal string")); + result.errors << error; + } + break; + } + } + + // Look for additional functions in this type + auto functionsEnd = compilationUnit->objectFunctionsEnd(object); + for (auto function = compilationUnit->objectFunctionsBegin(object); function != functionsEnd; ++function) { + QString functionName = compilationUnit->stringAt(function->nameIndex); + if (!(functionName.startsWith(QLatin1Literal("test_")) || functionName.startsWith(QLatin1Literal("benchmark_")))) + continue; + + if (functionName.endsWith(QLatin1Literal("_data"))) + continue; + + result.testFunctions << functionName; + } + } + } + + for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + const Object *child = compilationUnit->objectAt(binding->value.objectIndex); + result << enumerateTestCases(compilationUnit, child); + } + } + + return result; + } +}; + int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir) { + return quick_test_main_with_setup(argc, argv, name, sourceDir, nullptr); +} + +int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup) +{ // Peek at arguments to check for '-widgets' argument #ifdef QT_QMLTEST_WITH_WIDGETS bool withWidgets = false; @@ -208,7 +422,7 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD } #endif - QCoreApplication *app = 0; + QCoreApplication *app = nullptr; if (!QCoreApplication::instance()) { #ifdef QT_QMLTEST_WITH_WIDGETS if (withWidgets) @@ -220,15 +434,20 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD } } + if (setup) + maybeInvokeSetupMethod(setup, "applicationAvailable()"); + // Look for QML-specific command-line options. // -import dir Specify an import directory. // -plugins dir Specify a directory where to search for plugins. // -input dir Specify the input directory for test cases. // -translation file Specify the translation file. + // -file-selector Specify a file selector QStringList imports; QStringList pluginPaths; QString testPath; QString translationFile; + QStringList fileSelectors; int index = 1; QScopedArrayPointer<char *> testArgV(new char *[argc + 1]); testArgV[0] = argv[0]; @@ -253,6 +472,9 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD } else if (strcmp(argv[index], "-translation") == 0 && (index + 1) < argc) { translationFile = stripQuotes(QString::fromLocal8Bit(argv[index + 1])); index += 2; + } else if (strcmp(argv[index], "-file-selector") == 0 && (index + 1) < argc) { + fileSelectors += stripQuotes(QString::fromLocal8Bit(argv[index + 1])); + index += 2; } else { testArgV[testArgC++] = argv[index++]; } @@ -277,6 +499,11 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD } #endif +#if defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) + if (testPath.isEmpty()) + testPath = QLatin1String(":/"); +#endif + // Determine where to look for the test data. if (testPath.isEmpty() && sourceDir) { const QString s = QString::fromLocal8Bit(sourceDir); @@ -326,43 +553,77 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD // Register the test object qmlRegisterSingletonType<QTestRootObject>("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject); + + QSet<QString> commandLineTestFunctions = QTest::testFunctions.toSet(); + const bool filteringTestFunctions = !commandLineTestFunctions.isEmpty(); + // Scan through all of the "tst_*.qml" files and run each of them - // in turn with a QQuickView. - QQuickView *view = new QQuickView; - view->setFlags(Qt::Window | Qt::WindowSystemMenuHint - | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint - | Qt::WindowCloseButtonHint); - QEventLoop eventLoop; - QObject::connect(view->engine(), SIGNAL(quit()), - QTestRootObject::instance(), SLOT(quit())); - QObject::connect(view->engine(), SIGNAL(quit()), - &eventLoop, SLOT(quit())); - view->rootContext()->setContextProperty - (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead - for (const QString &path : qAsConst(imports)) - view->engine()->addImportPath(path); - for (const QString &path : qAsConst(pluginPaths)) - view->engine()->addPluginPath(path); + // in turn with a separate QQuickView (for test isolation). for (const QString &file : qAsConst(files)) { const QFileInfo fi(file); if (!fi.exists()) continue; - view->setObjectName(fi.baseName()); - view->setTitle(view->objectName()); + QQmlEngine engine; + for (const QString &path : qAsConst(imports)) + engine.addImportPath(path); + for (const QString &path : qAsConst(pluginPaths)) + engine.addPluginPath(path); + + if (!fileSelectors.isEmpty()) { + QQmlFileSelector* const qmlFileSelector = new QQmlFileSelector(&engine, &engine); + qmlFileSelector->setExtraSelectors(fileSelectors); + } + + TestCaseCollector testCaseCollector(fi, &engine); + if (!testCaseCollector.errors().isEmpty()) { + for (const QQmlError &error : testCaseCollector.errors()) + qWarning() << error; + exit(1); + } + + TestCaseCollector::TestCaseList availableTestFunctions = testCaseCollector.testCases(); + if (QTest::printAvailableFunctions) { + for (const QString &function : availableTestFunctions) + qDebug("%s()", qPrintable(function)); + continue; + } + + const QSet<QString> availableTestSet = availableTestFunctions.toSet(); + if (filteringTestFunctions && !availableTestSet.intersects(commandLineTestFunctions)) + continue; + commandLineTestFunctions.subtract(availableTestSet); + + QQuickView view(&engine, nullptr); + view.setFlags(Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint + | Qt::WindowCloseButtonHint); + QEventLoop eventLoop; + QObject::connect(view.engine(), SIGNAL(quit()), + QTestRootObject::instance(), SLOT(quit())); + QObject::connect(view.engine(), SIGNAL(quit()), + &eventLoop, SLOT(quit())); + view.rootContext()->setContextProperty + (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead + + // Do this down here so that import paths, plugin paths, + // file selectors, etc. are available in case the user needs access to them. + if (setup) + maybeInvokeSetupMethod(setup, "qmlEngineAvailable(QQmlEngine*)", Q_ARG(QQmlEngine*, view.engine())); + + view.setObjectName(fi.baseName()); + view.setTitle(view.objectName()); QTestRootObject::instance()->init(); QString path = fi.absoluteFilePath(); if (path.startsWith(QLatin1String(":/"))) - view->setSource(QUrl(QLatin1String("qrc:") + path.midRef(2))); + view.setSource(QUrl(QLatin1String("qrc:") + path.midRef(1))); else - view->setSource(QUrl::fromLocalFile(path)); + view.setSource(QUrl::fromLocalFile(path)); - if (QTest::printAvailableFunctions) - continue; - while (view->status() == QQuickView::Loading) + while (view.status() == QQuickView::Loading) QTest::qWait(10); - if (view->status() == QQuickView::Error) { - handleCompileErrors(fi, view); + if (view.status() == QQuickView::Error) { + handleCompileErrors(fi, &view); continue; } if (!QTestRootObject::instance()->hasQuit) { @@ -371,22 +632,25 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD // an asynchronous test and we need to show the window // and wait for the first frame to be rendered // and then wait for quit indication. - view->setFramePosition(QPoint(50, 50)); - if (view->size().isEmpty()) { // Avoid hangs with empty windows. - view->resize(200, 200); + view.setFramePosition(QPoint(50, 50)); + if (view.size().isEmpty()) { // Avoid hangs with empty windows. + view.resize(200, 200); } - view->show(); - if (!QTest::qWaitForWindowExposed(view)) { + view.show(); + if (!QTest::qWaitForWindowExposed(&view)) { qWarning().nospace() << "Test '" << QDir::toNativeSeparators(path) << "' window not exposed after show()."; } - view->requestActivate(); - if (!QTest::qWaitForWindowActive(view)) { + view.requestActivate(); + if (!QTest::qWaitForWindowActive(&view)) { qWarning().nospace() << "Test '" << QDir::toNativeSeparators(path) << "' window not active after requestActivate()."; } - if (view->isExposed()) { - QTestRootObject::instance()->setWindowShown(true); + if (view.isExposed()) { + // Defer property update until event loop has started + QTimer::singleShot(0, []() { + QTestRootObject::instance()->setWindowShown(true); + }); } else { qWarning().nospace() << "Test '" << QDir::toNativeSeparators(path) << "' window was never exposed! " @@ -394,15 +658,24 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD } if (!QTestRootObject::instance()->hasQuit && QTestRootObject::instance()->hasTestCase()) eventLoop.exec(); - // view->hide(); Causes a crash in Qt 3D due to deletion of the GL context, see QTBUG-27696 } } + if (setup) + maybeInvokeSetupMethod(setup, "cleanupTestCase()"); + // Flush the current logging stream. - QuickTestResult::setProgramName(0); - delete view; + QuickTestResult::setProgramName(nullptr); delete app; + // Check that all test functions passed on the command line were found + if (!commandLineTestFunctions.isEmpty()) { + qWarning() << "Could not find the following test functions:"; + for (const QString &functionName : qAsConst(commandLineTestFunctions)) + qWarning(" %s()", qUtf8Printable(functionName)); + return commandLineTestFunctions.count(); + } + // Return the number of failures as the exit code. return QuickTestResult::exitCode(); } diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h index 6486accb9e..8e3510c9a5 100644 --- a/src/qmltest/quicktest.h +++ b/src/qmltest/quicktest.h @@ -45,16 +45,16 @@ QT_BEGIN_NAMESPACE -QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS +class QQuickItem; Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir); +Q_QUICK_TEST_EXPORT int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup); #ifdef QUICK_TEST_SOURCE_DIR #define QUICK_TEST_MAIN(name) \ int main(int argc, char **argv) \ { \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, QUICK_TEST_SOURCE_DIR); \ } @@ -62,31 +62,49 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, #define QUICK_TEST_OPENGL_MAIN(name) \ int main(int argc, char **argv) \ { \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, QUICK_TEST_SOURCE_DIR); \ } +#define QUICK_TEST_MAIN_WITH_SETUP(name, QuickTestSetupClass) \ + int main(int argc, char **argv) \ + { \ + QTEST_SET_MAIN_SOURCE_PATH \ + QuickTestSetupClass setup; \ + return quick_test_main_with_setup(argc, argv, #name, QUICK_TEST_SOURCE_DIR, &setup); \ + } + #else #define QUICK_TEST_MAIN(name) \ int main(int argc, char **argv) \ { \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ - return quick_test_main(argc, argv, #name, 0); \ + return quick_test_main(argc, argv, #name, nullptr); \ } #define QUICK_TEST_OPENGL_MAIN(name) \ int main(int argc, char **argv) \ { \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ - return quick_test_main(argc, argv, #name, 0); \ + return quick_test_main(argc, argv, #name, nullptr); \ + } + +#define QUICK_TEST_MAIN_WITH_SETUP(name, QuickTestSetupClass) \ + int main(int argc, char **argv) \ + { \ + QTEST_SET_MAIN_SOURCE_PATH \ + QuickTestSetupClass setup; \ + return quick_test_main_with_setup(argc, argv, #name, nullptr, &setup); \ } #endif +namespace QQuickTest { +Q_QUICK_TEST_EXPORT bool qIsPolishScheduled(const QQuickItem *item); +Q_QUICK_TEST_EXPORT bool qWaitForItemPolished(const QQuickItem *item, int timeout = 5000); +} + QT_END_NAMESPACE #endif diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp index dc7b917bc4..5b07220c29 100644 --- a/src/qmltest/quicktestevent.cpp +++ b/src/qmltest/quicktestevent.cpp @@ -121,12 +121,34 @@ bool QuickTestEvent::keyClickChar(const QString &character, int modifiers, int d return true; } +#if QT_CONFIG(shortcut) +// valueToKeySequence() is copied from qquickshortcut.cpp +static QKeySequence valueToKeySequence(const QVariant &value) +{ + if (value.type() == QVariant::Int) + return QKeySequence(static_cast<QKeySequence::StandardKey>(value.toInt())); + return QKeySequence::fromString(value.toString()); +} +#endif + +bool QuickTestEvent::keySequence(const QVariant &keySequence) +{ + QWindow *window = activeWindow(); + if (!window) + return false; +#if QT_CONFIG(shortcut) + QTest::keySequence(window, valueToKeySequence(keySequence)); +#endif + return true; +} + namespace QtQuickTest { enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove, MouseDoubleClickSequence }; int lastMouseTimestamp = 0; + // TODO should be Qt::MouseButtons buttons in case multiple buttons are pressed static void mouseEvent(MouseAction action, QWindow *window, QObject *item, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, const QPointF &_pos, int delay=-1) @@ -156,7 +178,7 @@ namespace QtQuickTest return; } - QPoint pos; + QPoint pos = _pos.toPoint(); QQuickItem *sgitem = qobject_cast<QQuickItem *>(item); if (sgitem) pos = sgitem->mapToScene(_pos).toPoint(); @@ -173,7 +195,7 @@ namespace QtQuickTest me.setTimestamp(++lastMouseTimestamp); break; case MouseRelease: - me = QMouseEvent(QEvent::MouseButtonRelease, pos, window->mapToGlobal(pos), button, 0, stateKey); + me = QMouseEvent(QEvent::MouseButtonRelease, pos, window->mapToGlobal(pos), button, nullptr, stateKey); me.setTimestamp(++lastMouseTimestamp); lastMouseTimestamp += 500; // avoid double clicks being generated break; @@ -235,6 +257,7 @@ bool QuickTestEvent::mousePress QWindow *view = eventWindow(item); if (!view) return false; + m_pressedButtons.setFlag(Qt::MouseButton(button), true); QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item, Qt::MouseButton(button), Qt::KeyboardModifiers(modifiers), @@ -264,6 +287,7 @@ bool QuickTestEvent::mouseRelease QWindow *view = eventWindow(item); if (!view) return false; + m_pressedButtons.setFlag(Qt::MouseButton(button), false); QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item, Qt::MouseButton(button), Qt::KeyboardModifiers(modifiers), @@ -319,8 +343,9 @@ bool QuickTestEvent::mouseMove QWindow *view = eventWindow(item); if (!view) return false; + const Qt::MouseButtons effectiveButtons = buttons ? Qt::MouseButtons(buttons) : m_pressedButtons; QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item, - Qt::MouseButton(buttons), Qt::NoModifier, + Qt::MouseButton(int(effectiveButtons)), Qt::NoModifier, QPointF(x, y), delay); return true; } @@ -338,7 +363,7 @@ QWindow *QuickTestEvent::eventWindow(QObject *item) QQuickItem *testParentitem = qobject_cast<QQuickItem *>(parent()); if (testParentitem) return testParentitem->window(); - return 0; + return nullptr; } QWindow *QuickTestEvent::activeWindow() diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h index 89065b8880..5208c03a82 100644 --- a/src/qmltest/quicktestevent_p.h +++ b/src/qmltest/quicktestevent_p.h @@ -81,8 +81,8 @@ class Q_QUICK_TEST_EXPORT QuickTestEvent : public QObject Q_OBJECT Q_PROPERTY(int defaultMouseDelay READ defaultMouseDelay FINAL) public: - QuickTestEvent(QObject *parent = 0); - ~QuickTestEvent(); + QuickTestEvent(QObject *parent = nullptr); + ~QuickTestEvent() override; int defaultMouseDelay() const; public Q_SLOTS: @@ -94,6 +94,8 @@ public Q_SLOTS: bool keyReleaseChar(const QString &character, int modifiers, int delay); bool keyClickChar(const QString &character, int modifiers, int delay); + Q_REVISION(2) bool keySequence(const QVariant &keySequence); + bool mousePress(QObject *item, qreal x, qreal y, int button, int modifiers, int delay); bool mouseRelease(QObject *item, qreal x, qreal y, int button, @@ -113,10 +115,12 @@ public Q_SLOTS: QQuickTouchEventSequence *touchEvent(QObject *item = nullptr); private: - QWindow *eventWindow(QObject *item = 0); + QWindow *eventWindow(QObject *item = nullptr); QWindow *activeWindow(); QTouchDevice *touchDevice(); + Qt::MouseButtons m_pressedButtons; + friend class QQuickTouchEventSequence; }; diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 3650df8f3a..49552e1901 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -39,6 +39,7 @@ ****************************************************************************/ #include "quicktestresult_p.h" +#include "quicktest.h" #include <QtTest/qtestcase.h> #include <QtTest/qtestsystem.h> #include <QtTest/private/qtestblacklist_p.h> @@ -57,9 +58,13 @@ #include <QtCore/qmap.h> #include <QtCore/qbytearray.h> #include <QtCore/qcoreapplication.h> +#include <QtCore/qdatetime.h> #include <QtCore/qdebug.h> #include <QtCore/QUrl> #include <QtCore/QDir> +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif #include <QtQuick/qquickwindow.h> #include <QtGui/qvector3d.h> #include <QtGui/qimagewriter.h> @@ -72,7 +77,7 @@ QT_BEGIN_NAMESPACE -static const char *globalProgramName = 0; +static const char *globalProgramName = nullptr; static bool loggingStarted = false; static QBenchmarkGlobalData globalBenchmarkData; @@ -87,7 +92,7 @@ class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject Q_PROPERTY(QSize size READ size CONSTANT) public: - QuickTestImageObject(const QImage& img, QObject *parent = 0) + QuickTestImageObject(const QImage& img, QObject *parent = nullptr) : QObject(parent) , m_image(img) { @@ -142,7 +147,7 @@ public Q_SLOTS: QImageWriter writer(filePath); if (!writer.write(m_image)) { QQmlEngine *engine = qmlContext(this)->engine(); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *v4 = engine->handle(); v4->throwError(QStringLiteral("Can't save to %1: %2").arg(filePath, writer.errorString())); } } @@ -171,9 +176,9 @@ class QuickTestResultPrivate { public: QuickTestResultPrivate() - : table(0) - , benchmarkIter(0) - , benchmarkData(0) + : table(nullptr) + , benchmarkIter(nullptr) + , benchmarkData(nullptr) , iterCount(0) { } @@ -260,10 +265,10 @@ void QuickTestResult::setFunctionName(const QString &name) QString fullName = d->testCaseName + QLatin1String("::") + name; QTestResult::setCurrentTestFunction (d->intern(fullName).constData()); - QTestPrivate::checkBlackLists(fullName.toUtf8().constData(), 0); + QTestPrivate::checkBlackLists(fullName.toUtf8().constData(), nullptr); } } else { - QTestResult::setCurrentTestFunction(0); + QTestResult::setCurrentTestFunction(nullptr); } d->functionName = name; emit functionNameChanged(); @@ -292,7 +297,7 @@ void QuickTestResult::setDataTag(const QString &tag) QTestPrivate::checkBlackLists((testCaseName() + QLatin1String("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData()); emit dataTagChanged(); } else { - QTestResult::setCurrentTestData(0); + QTestResult::setCurrentTestData(nullptr); } } @@ -379,6 +384,16 @@ QStringList QuickTestResult::functionsToRun() const } /*! + \qmlproperty list<string> TestResult::tagsToRun + + This property returns the list of test function's data tags to be run +*/ +QStringList QuickTestResult::tagsToRun() const +{ + return QTest::testTags; +} + +/*! \qmlmethod TestResult::reset() Resets all pass/fail/skip counters and prepare for testing. @@ -437,7 +452,7 @@ void QuickTestResult::clearTestTable() { Q_D(QuickTestResult); delete d->table; - d->table = 0; + d->table = nullptr; } void QuickTestResult::finishTestData() @@ -548,6 +563,18 @@ void QuickTestResult::stringify(QQmlV4Function *args) result = QString::fromLatin1("Qt.vector3d(%1, %2, %3)").arg(v3d.x()).arg(v3d.y()).arg(v3d.z()); break; } + case QVariant::Url: + { + QUrl url = v.value<QUrl>(); + result = QString::fromLatin1("Qt.url(%1)").arg(url.toString()); + break; + } + case QVariant::DateTime: + { + QDateTime dt = v.value<QDateTime>(); + result = dt.toString(Qt::ISODateWithMs); + break; + } default: result = v.toString(); } @@ -612,9 +639,15 @@ void QuickTestResult::warn(const QString &message, const QUrl &location, int lin QTestLog::warn(message.toLatin1().constData(), qtestFixUrl(location).toLatin1().constData(), line); } -void QuickTestResult::ignoreWarning(const QString &message) +void QuickTestResult::ignoreWarning(const QJSValue &message) { - QTestLog::ignoreMessage(QtWarningMsg, message.toLatin1().constData()); + if (message.isRegExp()) { +#if QT_CONFIG(regularexpression) + QTestLog::ignoreMessage(QtWarningMsg, message.toVariant().toRegularExpression()); +#endif + } else { + QTestLog::ignoreMessage(QtWarningMsg, message.toString().toLatin1()); + } } void QuickTestResult::wait(int ms) @@ -732,7 +765,7 @@ void QuickTestResult::stopBenchmark() { Q_D(QuickTestResult); delete d->benchmarkIter; - d->benchmarkIter = 0; + d->benchmarkIter = nullptr; } QObject *QuickTestResult::grabImage(QQuickItem *item) @@ -746,7 +779,7 @@ QObject *QuickTestResult::grabImage(QQuickItem *item) QQmlEngine::setContextForObject(o, qmlContext(this)); return o; } - return 0; + return nullptr; } QObject *QuickTestResult::findChild(QObject *parent, const QString &objectName) @@ -754,6 +787,16 @@ QObject *QuickTestResult::findChild(QObject *parent, const QString &objectName) return parent ? parent->findChild<QObject*>(objectName) : 0; } +bool QuickTestResult::isPolishScheduled(QQuickItem *item) const +{ + return QQuickTest::qIsPolishScheduled(item); +} + +bool QuickTestResult::waitForItemPolished(QQuickItem *item, int timeout) +{ + return QQuickTest::qWaitForItemPolished(item, timeout); +} + namespace QTest { void qtest_qParseArgs(int argc, char *argv[], bool qml); }; @@ -769,12 +812,11 @@ void QuickTestResult::setProgramName(const char *name) { if (name) { QTestPrivate::parseBlackList(); - QTestPrivate::parseGpuBlackList(); QTestResult::reset(); } else if (!name && loggingStarted) { QTestResult::setCurrentTestObject(globalProgramName); QTestLog::stopLogging(); - QTestResult::setCurrentTestObject(0); + QTestResult::setCurrentTestObject(nullptr); } globalProgramName = name; QTestResult::setCurrentTestObject(globalProgramName); @@ -796,7 +838,7 @@ int QuickTestResult::exitCode() #endif } +QT_END_NAMESPACE + #include "quicktestresult.moc" #include "moc_quicktestresult_p.cpp" - -QT_END_NAMESPACE diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h index af13299ee5..3643826e5d 100644 --- a/src/qmltest/quicktestresult_p.h +++ b/src/qmltest/quicktestresult_p.h @@ -76,9 +76,10 @@ class Q_QUICK_TEST_EXPORT QuickTestResult : public QObject Q_PROPERTY(int failCount READ failCount) Q_PROPERTY(int skipCount READ skipCount) Q_PROPERTY(QStringList functionsToRun READ functionsToRun) + Q_PROPERTY(QStringList tagsToRun READ tagsToRun) public: - QuickTestResult(QObject *parent = 0); - ~QuickTestResult(); + QuickTestResult(QObject *parent = nullptr); + ~QuickTestResult() override; // Values must match QBenchmarkIterationController::RunMode. enum RunMode @@ -107,6 +108,7 @@ public: int skipCount() const; QStringList functionsToRun() const; + QStringList tagsToRun() const; public Q_SLOTS: void reset(); @@ -137,7 +139,7 @@ public Q_SLOTS: const QUrl &location, int line); void warn(const QString &message, const QUrl &location, int line); - void ignoreWarning(const QString &message); + void ignoreWarning(const QJSValue &message); void wait(int ms); void sleep(int ms); @@ -158,6 +160,9 @@ public Q_SLOTS: Q_REVISION(1) QObject *findChild(QObject *parent, const QString &objectName); + Q_REVISION(13) bool isPolishScheduled(QQuickItem *item) const; + Q_REVISION(13) bool waitForItemPolished(QQuickItem *item, int timeout); + public: // Helper functions for the C++ main() shell. static void parseArgs(int argc, char *argv[]); |