diff options
Diffstat (limited to 'tests')
10 files changed, 302 insertions, 2 deletions
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 7e81df93b9..d8afcd6946 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -33,6 +33,7 @@ #include <QProcess> #include <QLibraryInfo> #include <QSysInfo> +#include <private/qqmlcomponent_p.h> class tst_qmlcachegen: public QObject { @@ -115,6 +116,16 @@ void tst_qmlcachegen::loadGeneratedFile() const QString cacheFilePath = testFilePath + QLatin1Char('c'); QVERIFY(QFile::exists(cacheFilePath)); + + { + QFile cache(cacheFilePath); + QVERIFY(cache.open(QIODevice::ReadOnly)); + const QV4::CompiledData::Unit *cacheUnit = reinterpret_cast<const QV4::CompiledData::Unit *>(cache.map(/*offset*/0, sizeof(QV4::CompiledData::Unit))); + QVERIFY(cacheUnit); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + } + QVERIFY(QFile::remove(testFilePath)); QQmlEngine engine; @@ -122,6 +133,13 @@ void tst_qmlcachegen::loadGeneratedFile() QScopedPointer<QObject> obj(component.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); + + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + QVERIFY(compilationUnit->data); + QVERIFY(!(compilationUnit->data->flags & QV4::CompiledData::Unit::StaticData)); } void tst_qmlcachegen::translationExpressionSupport() diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 72e1a2915d..03d53b755d 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -279,6 +279,8 @@ private slots: void accessDeletedObject(); + void lowercaseTypeNames(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -4903,6 +4905,12 @@ void tst_qqmllanguage::accessDeletedObject() QVERIFY(!o.isNull()); } +void tst_qqmllanguage::lowercaseTypeNames() +{ + QCOMPARE(qmlRegisterType<QObject>("Test", 1, 0, "lowerCaseTypeName"), -1); + QCOMPARE(qmlRegisterSingletonType<QObject>("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir new file mode 100644 index 0000000000..104c4bf673 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithStaticPlugin +plugin secondStaticPlugin diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir new file mode 100644 index 0000000000..45a02b2ffe --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithWaitingPlugin +plugin pluginThatWaits diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 8600e1e8ab..9abff7b2f6 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -29,6 +29,9 @@ #include <qdir.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlextensionplugin.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonarray.h> #include <QDebug> #if defined(Q_OS_MAC) @@ -73,12 +76,80 @@ private slots: void importsChildPlugin(); void importsChildPlugin2(); void importsChildPlugin21(); + void parallelPluginImport(); private: QString m_importsDirectory; QString m_dataImportsDirectory; }; +class PluginThatWaits : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + static QMutex initializeEngineEntered; + static QWaitCondition waitingForInitializeEngineEntry; + static QMutex leavingInitializeEngine; + static QWaitCondition waitingForInitializeEngineLeave; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + initializeEngineEntered.lock(); + leavingInitializeEngine.lock(); + waitingForInitializeEngineEntry.wakeOne(); + initializeEngineEntered.unlock(); + waitingForInitializeEngineLeave.wait(&leavingInitializeEngine); + leavingInitializeEngine.unlock(); + } +}; +QByteArray PluginThatWaits::metaData; +QMutex PluginThatWaits::initializeEngineEntered; +QWaitCondition PluginThatWaits::waitingForInitializeEngineEntry; +QMutex PluginThatWaits::leavingInitializeEngine; +QWaitCondition PluginThatWaits::waitingForInitializeEngineLeave; + +class SecondStaticPlugin : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } +}; +QByteArray SecondStaticPlugin::metaData; + +template <typename PluginType> +void registerStaticPlugin(const char *uri) +{ + QStaticPlugin plugin; + plugin.instance = []() { + static PluginType plugin; + return static_cast<QObject*>(&plugin); + }; + + QJsonObject md; + md.insert(QStringLiteral("IID"), QQmlExtensionInterface_iid); + QJsonArray uris; + uris.append(uri); + md.insert(QStringLiteral("uri"), uris); + + PluginType::metaData.append(QLatin1String("QTMETADATA ")); + PluginType::metaData.append(QJsonDocument(md).toBinaryData()); + + plugin.rawMetaData = []() { + return PluginType::metaData.constData(); + }; + qRegisterStaticPluginFunction(plugin); +}; + void tst_qqmlmoduleplugin::initTestCase() { QQmlDataTest::initTestCase(); @@ -88,6 +159,9 @@ void tst_qqmlmoduleplugin::initTestCase() m_dataImportsDirectory = directory() + QStringLiteral("/imports"); QVERIFY2(QFileInfo(m_dataImportsDirectory).isDir(), qPrintable(QString::fromLatin1("Imports directory '%1' does not exist.").arg(m_dataImportsDirectory))); + + registerStaticPlugin<PluginThatWaits>("moduleWithWaitingPlugin"); + registerStaticPlugin<SecondStaticPlugin>("moduleWithStaticPlugin"); } #define VERIFY_ERRORS(errorfile) \ @@ -635,6 +709,51 @@ void tst_qqmlmoduleplugin::importsChildPlugin21() delete object; } +void tst_qqmlmoduleplugin::parallelPluginImport() +{ + QMutexLocker locker(&PluginThatWaits::initializeEngineEntered); + + QThread worker; + QObject::connect(&worker, &QThread::started, [&worker](){ + // Engines in separate threads are tricky, but as long as we do not create a graphical + // object and move objects created by the engines across thread boundaries, this is safe. + // At the same time this allows us to place the engine's loader thread into the position + // where, without the fix for this bug, the global lock is acquired. + QQmlEngine engineInThread; + + QQmlComponent component(&engineInThread); + component.setData("import moduleWithWaitingPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + worker.quit(); + }); + worker.start(); + + PluginThatWaits::waitingForInitializeEngineEntry.wait(&PluginThatWaits::initializeEngineEntered); + + { + // After acquiring this lock, the engine in the other thread as well as its type loader + // thread are blocked. However they should not hold the global plugin lock + // qmlEnginePluginsWithRegisteredTypes()->mutex in qqmllimports.cpp, allowing for the load + // of a component in a different engine with its own plugin to proceed. + QMutexLocker continuationLock(&PluginThatWaits::leavingInitializeEngine); + + QQmlEngine secondEngine; + QQmlComponent secondComponent(&secondEngine); + secondComponent.setData("import moduleWithStaticPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + QScopedPointer<QObject> o(secondComponent.create()); + QVERIFY(!o.isNull()); + + PluginThatWaits::waitingForInitializeEngineLeave.wakeOne(); + } + + worker.wait(); +} + QTEST_MAIN(tst_qqmlmoduleplugin) #include "tst_qqmlmoduleplugin.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro index 43bd112415..118ca26ee9 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro @@ -10,4 +10,12 @@ include (../../shared/util.pri) TESTDATA = data/* imports/* $$OUT_PWD/imports/* +waitingPlugin.files = moduleWithWaitingPlugin +waitingPlugin.prefix = /qt-project.org/imports/ +RESOURCES += waitingPlugin + +staticPlugin.files = moduleWithStaticPlugin +staticPlugin.prefix = /qt-project.org/imports/ +RESOURCES += staticPlugin + QT += core-private gui-private qml-private network testlib diff --git a/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml new file mode 100644 index 0000000000..d4c5daecab --- /dev/null +++ b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 +import QtQuick.Window 2.1 + +Item { + width: 400 + height: 400 + objectName: "root Item" + + Loader { + sourceComponent: Rectangle { + objectName: "yellow rectangle" + x: 50; y: 50; width: 300; height: 300 + color: "yellow" + Window { + objectName: "red transient Window" + width: 100 + height: 100 + visible: true // makes it harder, because it wants to become visible before root has a window + color: "red" + title: "red" + flags: Qt.Dialog + onVisibilityChanged: console.log("visibility " + visibility) + onVisibleChanged: console.log("visible " + visible) + } + } + } +} diff --git a/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml new file mode 100644 index 0000000000..69421448e0 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import QtQuick.Window 2.1 + +Item { + width: 400 + height: 400 + objectName: "root Item" + + Loader { + sourceComponent: Window { + objectName: "red transient Window" + width: 100 + height: 100 + visible: true // makes it harder, because it wants to become visible before root has a window + color: "red" + title: "red" + flags: Qt.Dialog + onVisibilityChanged: console.log("visibility " + visibility) + onVisibleChanged: console.log("visible " + visible) + } + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index e557b592ee..65493f52e2 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -32,11 +32,15 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlincubator.h> +#include <QtQuick/qquickview.h> #include <private/qquickloader_p.h> +#include <private/qquickwindowmodule_p.h> #include "testhttpserver.h" #include "../../shared/util.h" #include "../shared/geometrytestutil.h" +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + class SlowComponent : public QQmlComponent { Q_OBJECT @@ -112,6 +116,8 @@ private slots: void parented(); void sizeBound(); void QTBUG_30183(); + void transientWindow(); + void nestedTransientWindow(); void sourceComponentGarbageCollection(); @@ -1159,6 +1165,86 @@ void tst_QQuickLoader::QTBUG_30183() QCOMPARE(rect->height(), 120.0); } +void tst_QQuickLoader::transientWindow() // QTBUG-52944 +{ + QQuickView view; + view.setSource(testFileUrl("itemLoaderWindow.qml")); + QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject()); + QVERIFY(root); + QQuickLoader *loader = root->findChild<QQuickLoader *>(); + QVERIFY(loader); + QTRY_COMPARE(loader->status(), QQuickLoader::Ready); + QQuickWindowQmlImpl *loadedWindow = qobject_cast<QQuickWindowQmlImpl *>(loader->item()); + QVERIFY(loadedWindow); + QCOMPARE(loadedWindow->visibility(), QWindow::Hidden); + + QElapsedTimer timer; + qint64 viewVisibleTime = -1; + qint64 loadedWindowVisibleTime = -1; + connect(&view, &QWindow::visibleChanged, + [&viewVisibleTime, &timer]() { viewVisibleTime = timer.elapsed(); } ); + connect(loadedWindow, &QQuickWindowQmlImpl::visibilityChanged, + [&loadedWindowVisibleTime, &timer]() { loadedWindowVisibleTime = timer.elapsed(); } ); + timer.start(); + view.show(); + + QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(loadedWindowVisibleTime >= 0); + QVERIFY(viewVisibleTime >= 0); + + // now that we're sure they are both visible, which one became visible first? + qCDebug(lcTests) << "transient Window became visible" << (loadedWindowVisibleTime - viewVisibleTime) << "ms after the root Item"; + QVERIFY((loadedWindowVisibleTime - viewVisibleTime) >= 0); + + QWindowList windows = QGuiApplication::topLevelWindows(); + QTRY_COMPARE(windows.size(), 2); + + // TODO Ideally we would now close the outer window and make sure the transient window closes too. + // It works during manual testing because of QWindowPrivate::maybeQuitOnLastWindowClosed() + // but quitting an autotest doesn't make sense. +} + +void tst_QQuickLoader::nestedTransientWindow() // QTBUG-52944 +{ + QQuickView view; + view.setSource(testFileUrl("itemLoaderItemWindow.qml")); + QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject()); + QVERIFY(root); + QQuickLoader *loader = root->findChild<QQuickLoader *>(); + QVERIFY(loader); + QTRY_COMPARE(loader->status(), QQuickLoader::Ready); + QQuickItem *loadedItem = qobject_cast<QQuickItem *>(loader->item()); + QVERIFY(loadedItem); + QQuickWindowQmlImpl *loadedWindow = loadedItem->findChild<QQuickWindowQmlImpl *>(); + QVERIFY(loadedWindow); + QCOMPARE(loadedWindow->visibility(), QWindow::Hidden); + + QElapsedTimer timer; + qint64 viewVisibleTime = -1; + qint64 loadedWindowVisibleTime = -1; + connect(&view, &QWindow::visibleChanged, + [&viewVisibleTime, &timer]() { viewVisibleTime = timer.elapsed(); } ); + connect(loadedWindow, &QQuickWindowQmlImpl::visibilityChanged, + [&loadedWindowVisibleTime, &timer]() { loadedWindowVisibleTime = timer.elapsed(); } ); + timer.start(); + view.show(); + + QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(loadedWindowVisibleTime >= 0); + QVERIFY(viewVisibleTime >= 0); + + // now that we're sure they are both visible, which one became visible first? + qCDebug(lcTests) << "transient Window became visible" << (loadedWindowVisibleTime - viewVisibleTime) << "ms after the root Item"; + QVERIFY((loadedWindowVisibleTime - viewVisibleTime) >= 0); + + QWindowList windows = QGuiApplication::topLevelWindows(); + QTRY_COMPARE(windows.size(), 2); + + // TODO Ideally we would now close the outer window and make sure the transient window closes too. + // It works during manual testing because of QWindowPrivate::maybeQuitOnLastWindowClosed() + // but quitting an autotest doesn't make sense. +} + void tst_QQuickLoader::sourceComponentGarbageCollection() { QQmlEngine engine; diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 9b526f80c5..5b2c585a92 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -6143,9 +6143,17 @@ void tst_qquicktextinput::keypress_inputMask_withValidator_data() KeyList keys; // inserting '1111.11' then two backspaces keys << Qt::Key_Home << "1111.11" << Qt::Key_Backspace << Qt::Key_Backspace; - QTest::newRow("backspaceWithRegExp") << QString("9999.99;_") << 0.0 << 0.0 << 0 + QTest::newRow("backspaceWithRegExp") << QString("9999;_") << 0.0 << 0.0 << 0 << QString("/^[-]?((\\.\\d+)|(\\d+(\\.\\d+)?))$/") - << keys << QString("1111.") << QString("1111.__"); + << keys << QString("11") << QString("11__"); + } + { + KeyList keys; + // inserting '99' - QTBUG-64616 + keys << Qt::Key_Home << "99"; + QTest::newRow("invalidTextWithRegExp") << QString("X9;_") << 0.0 << 0.0 << 0 + << QString("/[+-][0+9]/") + << keys << QString("") << QString("__"); } } |