diff options
Diffstat (limited to 'tests/auto/qml/qmlcachegen')
19 files changed, 571 insertions, 5 deletions
diff --git a/tests/auto/qml/qmlcachegen/Enums.qml b/tests/auto/qml/qmlcachegen/Enums.qml new file mode 100644 index 0000000000..830babb73e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/Enums.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 +QtObject { + enum Test { + First = 100, + Second = 200 + } + property int value: 0 + Component.onCompleted: value = Enums.Second +} diff --git a/tests/auto/qml/qmlcachegen/Retain.qml b/tests/auto/qml/qmlcachegen/Retain.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/Retain.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/qml/qmlcachegen/jsimport.qml b/tests/auto/qml/qmlcachegen/jsimport.qml new file mode 100644 index 0000000000..9c40878e60 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/jsimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.js" as Script + +QtObject { + property int value: Script.getter() +} diff --git a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml b/tests/auto/qml/qmlcachegen/jsmoduleimport.qml new file mode 100644 index 0000000000..c1fad7fee2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/jsmoduleimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.mjs" as Script + +QtObject { + property bool ok: Script.ok() +} diff --git a/tests/auto/qml/qmlcachegen/library.js b/tests/auto/qml/qmlcachegen/library.js new file mode 100644 index 0000000000..51fb41dc23 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/library.js @@ -0,0 +1,4 @@ + +function getter() { + return 42; +} diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index 8d8b37be15..c7820ac1cd 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -1,7 +1,27 @@ -CONFIG += testcase +CONFIG += testcase qtquickcompiler TARGET = tst_qmlcachegen macos:CONFIG -= app_bundle SOURCES += tst_qmlcachegen.cpp +workerscripts_test.files = worker.js worker.qml +workerscripts_test.prefix = /workerscripts +RESOURCES += workerscripts_test + +RESOURCES += versionchecks.qml + +RESOURCES += trickypaths.qrc + +RESOURCES += jsimport.qml script.js library.js + +RESOURCES += Enums.qml + +# QTBUG-46375 +!win32: RESOURCES += trickypaths_umlaut.qrc + +RESOURCES += jsmoduleimport.qml script.mjs + +RESOURCES += retain.qrc +QTQUICK_COMPILER_RETAINED_RESOURCES += retain.qrc + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/retain.qrc b/tests/auto/qml/qmlcachegen/retain.qrc new file mode 100644 index 0000000000..af042b25d8 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/retain.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>Retain.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/script.js b/tests/auto/qml/qmlcachegen/script.js new file mode 100644 index 0000000000..fa55f9069e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/script.js @@ -0,0 +1,6 @@ + +.import "library.js" as Library + +function getter() { + return Library.getter() +} diff --git a/tests/auto/qml/qmlcachegen/script.mjs b/tests/auto/qml/qmlcachegen/script.mjs new file mode 100644 index 0000000000..459c336125 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/script.mjs @@ -0,0 +1,4 @@ + +export function ok() { + return true +} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qml b/tests/auto/qml/qmlcachegen/trickypaths.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qrc b/tests/auto/qml/qmlcachegen/trickypaths.qrc new file mode 100644 index 0000000000..57977ccf6d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/directory with spaces"> +<file alias="file name with spaces.qml">trickypaths.qml</file> +<file>versionStyleSuffix-1.2-core-yc.qml</file> +<file>versionStyleSuffix-1.2-more.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc new file mode 100644 index 0000000000..9ca889d692 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> +<file alias="Bäh.qml">umlaut.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b7e616a050..8cfa4cb6af 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -32,7 +32,11 @@ #include <QQmlEngine> #include <QProcess> #include <QLibraryInfo> +#include <QStandardPaths> #include <QSysInfo> +#include <QLoggingCategory> +#include <private/qqmlcomponent_p.h> +#include <qtranslator.h> class tst_qmlcachegen: public QObject { @@ -43,6 +47,25 @@ private slots: void loadGeneratedFile(); void translationExpressionSupport(); + void signalHandlerParameters(); + void errorOnArgumentsInSignalHandler(); + void aheadOfTimeCompilation(); + void functionExpressions(); + void versionChecksForAheadOfTimeUnits(); + void retainedResources(); + + void workerScripts(); + + void trickyPaths_data(); + void trickyPaths(); + + void qrcScriptImport(); + void fsScriptImport(); + void moduleScriptImport(); + + void enums(); + + void sourceFileIndices(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -67,15 +90,20 @@ public: } }; -static bool generateCache(const QString &qmlFileName) +static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr = nullptr) { QProcess proc; - proc.setProcessChannelMode(QProcess::ForwardedChannels); + if (capturedStderr == nullptr) + proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QLatin1String("qmlcachegen")); - proc.setArguments(QStringList() << (QLatin1String("--target-architecture=") + QSysInfo::buildCpuArchitecture()) << (QLatin1String("--target-abi=") + QSysInfo::buildAbi()) << qmlFileName); + proc.setArguments(QStringList() << qmlFileName); proc.start(); if (!proc.waitForFinished()) return false; + + if (capturedStderr) + *capturedStderr = proc.readAllStandardError(); + if (proc.exitStatus() != QProcess::NormalExit) return false; return proc.exitCode() == 0; @@ -84,6 +112,13 @@ static bool generateCache(const QString &qmlFileName) void tst_qmlcachegen::initTestCase() { qputenv("QML_FORCE_DISK_CACHE", "1"); + QStandardPaths::setTestModeEnabled(true); + + // make sure there's no pre-existing cache dir + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + if (!cacheDir.isEmpty()) + //QDir(cacheDir).removeRecursively(); + qDebug() << cacheDir; } void tst_qmlcachegen::loadGeneratedFile() @@ -108,6 +143,17 @@ 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); + QCOMPARE(uint(cacheUnit->sourceFileIndex), uint(0)); + } + QVERIFY(QFile::remove(testFilePath)); QQmlEngine engine; @@ -115,13 +161,36 @@ 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); + auto unitData = compilationUnit->unitData(); + QVERIFY(unitData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::StaticData); } +class QTestTranslator : public QTranslator +{ +public: + QString translate(const char *context, const char *sourceText, const char */*disambiguation*/, int /*n*/) const override + { + m_lastContext = QString::fromUtf8(context); + return QString::fromUtf8(sourceText).toUpper(); + } + bool isEmpty() const override { return true; } + mutable QString m_lastContext; +}; + void tst_qmlcachegen::translationExpressionSupport() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); + QTestTranslator translator; + qApp->installTranslator(&translator); + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { QFile f(tempDir.path() + '/' + fileName); const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); @@ -155,7 +224,395 @@ void tst_qmlcachegen::translationExpressionSupport() CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); QScopedPointer<QObject> obj(component.create()); QVERIFY(!obj.isNull()); - QCOMPARE(obj->property("text").toString(), QString("All Ok")); + QCOMPARE(obj->property("text").toString(), QString("ALL Ok")); + QCOMPARE(translator.m_lastContext, QStringLiteral("test")); +} + +void tst_qmlcachegen::signalHandlerParameters() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " property real result: 0\n" + " signal testMe(real value);\n" + " onTestMe: result = value;\n" + " function runTest() { testMe(42); }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + { + 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); + } + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QMetaObject::invokeMethod(obj.data(), "runTest"); + QCOMPARE(obj->property("result").toInt(), 42); + + { + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + QVERIFY(compilationUnit->unitData()); + + // Verify that the QML objects don't come from the original data. + QVERIFY(compilationUnit->objectAt(0) != compilationUnit->unitData()->qmlUnit()->objectAt(0)); + + // Typically the final file name is one of those strings that is not in the original + // pre-compiled qml file's string table, while for example the signal parameter + // name ("value") is. + const auto isStringIndexInStringTable = [compilationUnit](uint index) { + return index < compilationUnit->unitData()->stringTableSize; + }; + + QVERIFY(isStringIndexInStringTable(compilationUnit->objectAt(0)->signalAt(0)->parameterAt(0)->nameIndex)); + QVERIFY(!compilationUnit->dynamicStrings.isEmpty()); + } +} + +void tst_qmlcachegen::errorOnArgumentsInSignalHandler() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.2\n" + "QtObject {\n" + " signal mySignal(var arguments);\n" + " onMySignal: console.log(arguments);\n" + "}"); + + + QByteArray errorOutput; + QVERIFY(!generateCache(testFilePath, &errorOutput)); + QVERIFY2(errorOutput.contains("error: The use of eval() or the use of the arguments object in signal handlers is"), errorOutput); +} + +void tst_qmlcachegen::aheadOfTimeCompilation() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " function runTest() { var x = 0; while (x < 42) { ++x }; return x; }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVariant result; + QMetaObject::invokeMethod(obj.data(), "runTest", Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result.toInt(), 42); +} + +static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr; + +void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() +{ + QVERIFY(QFile::exists(":/versionchecks.qml")); + QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + + Q_ASSERT(!temporaryModifiedCachedUnit); + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + QVERIFY(originalUnit); + QV4::CompiledData::Unit *tweakedUnit = (QV4::CompiledData::Unit *)malloc(originalUnit->unitSize); + memcpy(reinterpret_cast<void *>(tweakedUnit), reinterpret_cast<const void *>(originalUnit), originalUnit->unitSize); + tweakedUnit->version = QV4_DATA_STRUCTURE_VERSION - 1; + temporaryModifiedCachedUnit = new QQmlPrivate::CachedQmlUnit{tweakedUnit, nullptr, nullptr}; + + auto testHandler = [](const QUrl &url) -> const QQmlPrivate::CachedQmlUnit * { + if (url == QUrl("qrc:/versionchecks.qml")) + return temporaryModifiedCachedUnit; + return nullptr; + }; + QQmlMetaType::prependCachedUnitLookupFunction(testHandler); + + { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error)); + QCOMPARE(error, QQmlMetaType::CachedUnitLookupError::VersionMismatch); + } + + { + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc:/versionchecks.qml")); + QCOMPARE(component.status(), QQmlComponent::Error); + QCOMPARE(component.errorString(), QString("qrc:/versionchecks.qml:-1 File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile\n")); + } + + Q_ASSERT(temporaryModifiedCachedUnit); + free(const_cast<QV4::CompiledData::Unit *>(temporaryModifiedCachedUnit->qmlData)); + delete temporaryModifiedCachedUnit; + temporaryModifiedCachedUnit = nullptr; + + QQmlMetaType::removeCachedUnitLookupFunction(testHandler); +} + +void tst_qmlcachegen::retainedResources() +{ + QFile file(":/Retain.qml"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(file.readAll().startsWith("import QtQml 2.0")); +} + +void tst_qmlcachegen::workerScripts() +{ + QVERIFY(QFile::exists(":/workerscripts/worker.js")); + QVERIFY(QFile::exists(":/workerscripts/worker.qml")); + QCOMPARE(QFileInfo(":/workerscripts/worker.js").size(), 0); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/worker.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("success").toBool()); +} + +void tst_qmlcachegen::functionExpressions() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile( + "test.qml", + "import QtQuick 2.0\n" + "Item {\n" + " id: di\n" + " \n" + " property var f\n" + " property bool f_called: false\n" + " f : function() { f_called = true }\n" + " \n" + " signal g\n" + " property bool g_handler_called: false\n" + " onG: function() { g_handler_called = true }\n" + " \n" + " signal h(int i)\n" + " property bool h_connections_handler_called: false\n" + " Connections {\n" + " target: di\n" + " onH: function(magic) { h_connections_handler_called = (magic == 42)\n }\n" + " }\n" + " \n" + " function runTest() { \n" + " f()\n" + " g()\n" + " h(42)\n" + " }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QCOMPARE(obj->property("f_called").toBool(), false); + QCOMPARE(obj->property("g_handler_called").toBool(), false); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), false); + + QMetaObject::invokeMethod(obj.data(), "runTest"); + + QCOMPARE(obj->property("f_called").toBool(), true); + QCOMPARE(obj->property("g_handler_called").toBool(), true); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), true); +} + +void tst_qmlcachegen::trickyPaths_data() +{ + QTest::addColumn<QString>("filePath"); + QTest::newRow("path with spaces") << QStringLiteral(":/directory with spaces/file name with spaces.qml"); + QTest::newRow("version style suffix 1") << QStringLiteral(":/directory with spaces/versionStyleSuffix-1.2-core-yc.qml"); + QTest::newRow("version style suffix 2") << QStringLiteral(":/directory with spaces/versionStyleSuffix-1.2-more.qml"); + + // QTBUG-46375 +#if !defined(Q_OS_WIN) + QTest::newRow("path with umlaut") << QStringLiteral(":/Bäh.qml"); +#endif +} + +void tst_qmlcachegen::trickyPaths() +{ + QFETCH(QString, filePath); + QVERIFY2(QFile::exists(filePath), qPrintable(filePath)); + QCOMPARE(QFileInfo(filePath).size(), 0); + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc" + filePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("success").toInt(), 42); +} + +void tst_qmlcachegen::qrcScriptImport() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsimport.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_COMPARE(obj->property("value").toInt(), 42); +} + +void tst_qmlcachegen::fsScriptImport() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile( + "test.qml", + "import QtQml 2.0\n" + "import \"test.js\" as ScriptTest\n" + "QtObject {\n" + " property int value: ScriptTest.value\n" + "}\n"); + + const QString scriptFilePath = writeTempFile( + "test.js", + "var value = 42" + ); + + QVERIFY(generateCache(scriptFilePath)); + QVERIFY(generateCache(testFilePath)); + + const QString scriptCacheFilePath = scriptFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(scriptFilePath)); + + { + QFile cache(scriptCacheFilePath); + 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)); + QCOMPARE(uint(cacheUnit->sourceFileIndex), uint(0)); + } + + // Remove source code to make sure that when loading succeeds, it is because we loaded + // the existing cache files. + QVERIFY(QFile::remove(testFilePath)); + QVERIFY(QFile::remove(scriptFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); +} + +void tst_qmlcachegen::moduleScriptImport() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsmoduleimport.qml")); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("ok").toBool()); + + QVERIFY(QFile::exists(":/script.mjs")); + QCOMPARE(QFileInfo(":/script.mjs").size(), 0); + + { + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit(); + QVERIFY(compilationUnit); + auto unitData = compilationUnit->unitData(); + QVERIFY(unitData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::IsESModule); + + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/script.mjs"), &error); + QVERIFY(unitFromResources); + + QCOMPARE(unitFromResources, compilationUnit->unitData()); + } +} + +void tst_qmlcachegen::enums() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///Enums.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_COMPARE(obj->property("value").toInt(), 200); +} + +void tst_qmlcachegen::sourceFileIndices() +{ + QVERIFY(QFile::exists(":/versionchecks.qml")); + QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + QVERIFY(unitFromResources); + QVERIFY(unitFromResources->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + QCOMPARE(uint(unitFromResources->sourceFileIndex), uint(0)); } QTEST_GUILESS_MAIN(tst_qmlcachegen) diff --git a/tests/auto/qml/qmlcachegen/umlaut.qml b/tests/auto/qml/qmlcachegen/umlaut.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/umlaut.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionchecks.qml b/tests/auto/qml/qmlcachegen/versionchecks.qml new file mode 100644 index 0000000000..77d67e7da4 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionchecks.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property bool ok: true +} diff --git a/tests/auto/qml/qmlcachegen/worker.js b/tests/auto/qml/qmlcachegen/worker.js new file mode 100644 index 0000000000..dd2d0b843d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/worker.js @@ -0,0 +1,3 @@ +WorkerScript.onMessage = function(message) { + WorkerScript.sendMessage({ reply: message }); +} diff --git a/tests/auto/qml/qmlcachegen/worker.qml b/tests/auto/qml/qmlcachegen/worker.qml new file mode 100644 index 0000000000..1f1c9d1ac7 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/worker.qml @@ -0,0 +1,12 @@ +import QtQml 2.0 +import QtQuick 2.0 +QtObject { + property bool success: false + property WorkerScript worker: WorkerScript { + source: "worker.js" + onMessage: { + success = true + } + } + Component.onCompleted: worker.sendMessage("Hello") +} |