diff options
Diffstat (limited to 'tests/auto/qml')
64 files changed, 2082 insertions, 210 deletions
diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index ccb2d71c53..a50411e18b 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -20,6 +20,6 @@ PRIVATETESTS += \ SUBDIRS += $$PUBLICTESTS -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS -} + diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 4568d25dc1..40e19d375d 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -1223,16 +1223,15 @@ void tst_QQmlEngineDebugService::queryObjectTree() int main(int argc, char *argv[]) { int _argc = argc + 1; - char **_argv = new char*[_argc]; + QScopedArrayPointer<char *>_argv(new char*[_argc]); for (int i = 0; i < argc; ++i) _argv[i] = argv[i]; char arg[] = "-qmljsdebugger=port:3768,services:QmlDebugger"; _argv[_argc - 1] = arg; - QGuiApplication app(_argc, _argv); + QGuiApplication app(_argc, _argv.data()); tst_QQmlEngineDebugService tc; - return QTest::qExec(&tc, _argc, _argv); - delete _argv; + return QTest::qExec(&tc, _argc, _argv.data()); } #include "tst_qqmlenginedebugservice.moc" diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index c1de5ff594..c4b17aa60a 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -64,7 +64,8 @@ class QQmlProfilerTestClient : public QQmlProfilerClient Q_OBJECT public: - QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection) {} + QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection), + lastTimestamp(-1) {} QVector<QQmlProfilerData> qmlMessages; QVector<QQmlProfilerData> javascriptMessages; @@ -72,6 +73,8 @@ public: QVector<QQmlProfilerData> asynchronousMessages; QVector<QQmlProfilerData> pixmapMessages; + qint64 lastTimestamp; + signals: void recordingFinished(); @@ -114,6 +117,8 @@ void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId) void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= startTime); + lastTimestamp = startTime; QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -125,6 +130,8 @@ void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, const QString &string) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -137,6 +144,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); QVERIFY(location.line >= -2); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename); data.line = location.line; data.column = location.column; @@ -149,6 +158,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= endTime); + lastTimestamp = endTime; QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -161,6 +172,8 @@ void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int anim QVERIFY(threadId >= 0); QVERIFY(frameRate != -1); QVERIFY(animationCount != -1); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::Event, QQmlProfilerDefinitions::AnimationFrame); data.framerate = frameRate; @@ -178,6 +191,8 @@ void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraph Q_UNUSED(numericData3); Q_UNUSED(numericData4); Q_UNUSED(numericData5); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame, type)); } @@ -186,6 +201,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve qint64 time, const QString &url, int numericData1, int numericData2) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url); switch (type) { case QQmlProfilerDefinitions::PixmapSizeKnown: @@ -205,6 +222,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type); data.amount = amount; jsHeapMessages.append(data); @@ -213,6 +232,8 @@ void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryTyp void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type, QString::number(a) + QLatin1Char('x') + QString::number(b))); @@ -588,12 +609,11 @@ void tst_QQmlProfilerService::scenegraphData() checkTraceReceived(); checkJsHeap(); - - // check that at least one frame was rendered - // there should be a SGPolishAndSync + SGRendererFrame + SGRenderLoopFrame sequence - // (though we can't be sure to get the SGRenderLoopFrame in the threaded renderer) + // Check that at least one frame was rendered. + // There should be a SGContextFrame + SGRendererFrame + SGRenderLoopFrame sequence, + // but we can't be sure to get the SGRenderLoopFrame in the threaded renderer. // - // since the rendering happens in a different thread, there could be other unrelated events + // Since the rendering happens in a different thread, there could be other unrelated events // interleaved. Also, events could carry the same time stamps and be sorted in an unexpected way // if the clocks are acting up. qint64 contextFrameTime = -1; @@ -622,8 +642,13 @@ void tst_QQmlProfilerService::scenegraphData() foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) { - QVERIFY(msg.time >= renderFrameTime); - break; + if (msg.time >= contextFrameTime) { + // Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and + // SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is + // OK as the scene graph might decide to do an initial rendering. + QVERIFY(msg.time >= renderFrameTime); + break; + } } } } diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 3f89913f3b..a23b7e37eb 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -326,7 +326,7 @@ private slots: private: QV4Debugger *debugger() const { - return static_cast<QV4Debugger *>(m_v4->debugger); + return static_cast<QV4Debugger *>(m_v4->debugger()); } void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1) { @@ -444,7 +444,7 @@ void tst_qv4debugger::addBreakPointWhilePaused() static QV4::ReturnedValue someCall(QV4::CallContext *ctx) { - static_cast<QV4Debugger *>(ctx->d()->engine->debugger) + static_cast<QV4Debugger *>(ctx->d()->engine->debugger()) ->removeBreakPoint("removeBreakPointForNextInstruction", 2); return QV4::Encode::undefined(); } diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 6fe5d897e0..1ec0a6513d 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -135,8 +135,6 @@ public: int lastResponseId; bool lastResult; -public slots: - void recordResponse(int requestId, bool result) { lastResponseId = requestId; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 99c18f91a1..f4eb17f1ca 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -73,6 +73,7 @@ private slots: void newQObject(); void newQObject_ownership(); void newQObject_deletedEngine(); + void newQMetaObject(); void exceptionInSlot(); void globalObjectProperties(); void globalObjectEquals(); @@ -188,6 +189,8 @@ private slots: void v4FunctionWithoutQML(); + void withNoContext(); + signals: void testSignal(); }; @@ -712,6 +715,104 @@ void tst_QJSEngine::newQObject_deletedEngine() QTRY_VERIFY(spy.count()); } +class TestQMetaObject : public QObject { + Q_OBJECT + Q_PROPERTY(int called READ called) +public: + enum Enum1 { + Zero = 0, + One, + Two + }; + enum Enum2 { + A = 0, + B, + C + }; + Q_ENUMS(Enum1 Enum2) + + Q_INVOKABLE TestQMetaObject() + : m_called(1) { + } + Q_INVOKABLE TestQMetaObject(int) + : m_called(2) { + } + Q_INVOKABLE TestQMetaObject(QString) + : m_called(3) { + } + Q_INVOKABLE TestQMetaObject(QString, int) + : m_called(4) { + } + int called() const { + return m_called; + } +private: + int m_called; +}; + +void tst_QJSEngine::newQMetaObject() { + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.isNull(), false); + QCOMPARE(metaObject.isObject(), true); + QCOMPARE(metaObject.isQObject(), false); + QCOMPARE(metaObject.isCallable(), true); + QCOMPARE(metaObject.isQMetaObject(), true); + + QCOMPARE(metaObject.toQMetaObject(), &TestQMetaObject::staticMetaObject); + + QVERIFY(metaObject.strictlyEquals(engine.newQMetaObject<TestQMetaObject>())); + + + { + auto result = metaObject.callAsConstructor(); + if (result.isError()) + qDebug() << result.toString(); + QCOMPARE(result.isError(), false); + QCOMPARE(result.isNull(), false); + QCOMPARE(result.isObject(), true); + QCOMPARE(result.isQObject(), true); + QVERIFY(result.property("constructor").strictlyEquals(metaObject)); + QVERIFY(result.prototype().strictlyEquals(metaObject)); + + + QCOMPARE(result.property("called").toInt(), 1); + + } + + QJSValue integer(42); + QJSValue string("foo"); + + { + auto result = metaObject.callAsConstructor({integer}); + QCOMPARE(result.property("called").toInt(), 2); + } + + { + auto result = metaObject.callAsConstructor({string}); + QCOMPARE(result.property("called").toInt(), 3); + } + + { + auto result = metaObject.callAsConstructor({string, integer}); + QCOMPARE(result.property("called").toInt(), 4); + } + } + + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.property("Zero").toInt(), 0); + QCOMPARE(metaObject.property("One").toInt(), 1); + QCOMPARE(metaObject.property("Two").toInt(), 2); + QCOMPARE(metaObject.property("A").toInt(), 0); + QCOMPARE(metaObject.property("B").toInt(), 1); + QCOMPARE(metaObject.property("C").toInt(), 2); + } + +} + void tst_QJSEngine::exceptionInSlot() { QJSEngine engine; @@ -1343,6 +1444,7 @@ void tst_QJSEngine::valueConversion_QVariant() QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123)); QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull()); + QVERIFY(eng.toScriptValue(QVariant::fromValue(nullptr)).isNull()); { QVariantMap map; @@ -2040,18 +2142,42 @@ void tst_QJSEngine::jsNumberClass() QJSValue ret = eng.evaluate("new Number(123).toExponential()"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2")); + ret = eng.evaluate("new Number(123).toExponential(1)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("1.2e+2")); + ret = eng.evaluate("new Number(123).toExponential(2)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2")); + ret = eng.evaluate("new Number(123).toExponential(3)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("1.230e+2")); } QVERIFY(proto.property("toFixed").isCallable()); { QJSValue ret = eng.evaluate("new Number(123).toFixed()"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("123")); + ret = eng.evaluate("new Number(123).toFixed(1)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123.0")); + ret = eng.evaluate("new Number(123).toFixed(2)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123.00")); } QVERIFY(proto.property("toPrecision").isCallable()); { QJSValue ret = eng.evaluate("new Number(123).toPrecision()"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("123")); + ret = eng.evaluate("new Number(42).toPrecision(1)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("4e+1")); + ret = eng.evaluate("new Number(42).toPrecision(2)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("42")); + ret = eng.evaluate("new Number(42).toPrecision(3)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("42.0")); } } @@ -3886,6 +4012,13 @@ void tst_QJSEngine::v4FunctionWithoutQML() QVERIFY(obj.called); } +void tst_QJSEngine::withNoContext() +{ + // Don't crash (QTBUG-53794) + QJSEngine engine; + engine.evaluate("with (noContext) true"); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 859caf72c7..d28bbc1ffa 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -431,6 +431,12 @@ void tst_QJSValue::toString() QVERIFY(!o.engine()); QCOMPARE(o.toString(), QStringLiteral("1,2,3")); } + + { + QByteArray hello = QByteArrayLiteral("Hello World"); + QJSValue jsValue = eng.toScriptValue(hello); + QCOMPARE(jsValue.toString(), QString::fromUtf8(hello)); + } } void tst_QJSValue::toNumber() @@ -978,8 +984,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast<QVariant>(undefined), QVariant()); QJSValue null = eng.evaluate("null"); - QCOMPARE(null.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(null.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant::fromValue(nullptr)); { QJSValue number = eng.toScriptValue(123.0); @@ -1055,8 +1061,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast<QVariant>(undef), QVariant()); QJSValue nil = QJSValue(QJSValue::NullValue); - QCOMPARE(nil.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant::fromValue(nullptr)); } // array diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 28f04be5d7..68a2eace19 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -8,7 +8,6 @@ PUBLICTESTS += \ qjsvalueiterator \ qjsonbinding \ qmlmin \ - qmlplugindump \ qqmlcomponent \ qqmlconsole \ qqmlengine \ @@ -61,7 +60,9 @@ PRIVATETESTS += \ v4misc \ qqmltranslation \ qqmlimport \ - qqmlobjectmodel + qqmlobjectmodel \ + qmldiskcache \ + qv4mm qtHaveModule(widgets) { PUBLICTESTS += \ @@ -72,14 +73,13 @@ qtHaveModule(widgets) { SUBDIRS += $$PUBLICTESTS \ qqmlextensionplugin SUBDIRS += $$METATYPETESTS -!winrt { # no QProcess on winrt +!uikit:!winrt { # no QProcess on uikit/winrt !contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger - SUBDIRS += qmllint + SUBDIRS += qmllint qmlplugindump } -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS -} qtNomakeTools( \ qmlplugindump \ diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro new file mode 100644 index 0000000000..f98a157b6a --- /dev/null +++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro @@ -0,0 +1,9 @@ +CONFIG += testcase +TARGET = tst_qmldiskcache +osx:CONFIG -= app_bundle + +SOURCES += tst_qmldiskcache.cpp + +RESOURCES += test.qml + +QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmldiskcache/test.qml b/tests/auto/qml/qmldiskcache/test.qml new file mode 100644 index 0000000000..d632422616 --- /dev/null +++ b/tests/auto/qml/qmldiskcache/test.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int value: 20 +} diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp new file mode 100644 index 0000000000..8ccf4714ba --- /dev/null +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -0,0 +1,562 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> + +#include <private/qv4compileddata_p.h> +#include <private/qv4compiler_p.h> +#include <private/qv4jsir_p.h> +#include <private/qv4isel_p.h> +#include <private/qv8engine_p.h> +#include <private/qv4engine_p.h> +#include <QQmlComponent> +#include <QQmlEngine> +#include <QQmlFileSelector> +#include <QThread> +#include <QCryptographicHash> +#include <QStandardPaths> +#include <QDirIterator> + +class tst_qmldiskcache: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void regenerateAfterChange(); + void registerImportForImplicitComponent(); + void basicVersionChecks(); + void recompileAfterChange(); + void fileSelectors(); + void localAliases(); + void cacheResources(); +}; + +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + +static void waitForFileSystem() +{ + // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that + // the newly written file has a modification date newer than an existing cache file, we must + // wait. + // Similar effects of lacking precision have been observed on some Linux systems. + QThread::sleep(1); +} + +struct TestCompiler +{ + TestCompiler(QQmlEngine *engine) + : engine(engine) + , tempDir() + , testFilePath(tempDir.path() + QStringLiteral("/test.qml")) + , cacheFilePath(tempDir.path() + QStringLiteral("/test.qmlc")) + , mappedFile(cacheFilePath) + , currentMapping(nullptr) + { + } + + bool compile(const QByteArray &contents) + { + closeMapping(); + engine->clearComponentCache(); + + waitForFileSystem(); + + { + QFile f(testFilePath); + if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + lastErrorString = f.errorString(); + return false; + } + if (f.write(contents) != contents.size()) { + lastErrorString = f.errorString(); + return false; + } + } + + CleanlyLoadingComponent component(engine, testFilePath); + if (!component.isReady()) { + lastErrorString = component.errorString(); + return false; + } + + return true; + } + + const QV4::CompiledData::Unit *mapUnit() + { + if (!mappedFile.open(QIODevice::ReadOnly)) { + lastErrorString = mappedFile.errorString(); + return nullptr; + } + + currentMapping = mappedFile.map(/*offset*/0, mappedFile.size()); + if (!currentMapping) { + lastErrorString = mappedFile.errorString(); + return nullptr; + } + QV4::CompiledData::Unit *unitPtr; + memcpy(&unitPtr, ¤tMapping, sizeof(unitPtr)); + return unitPtr; + } + + typedef void (*HeaderTweakFunction)(QV4::CompiledData::Unit *header); + bool tweakHeader(HeaderTweakFunction function) + { + closeMapping(); + + QFile f(cacheFilePath); + if (!f.open(QIODevice::ReadWrite)) + return false; + QV4::CompiledData::Unit header; + if (f.read(reinterpret_cast<char *>(&header), sizeof(header)) != sizeof(header)) + return false; + function(&header); + f.seek(0); + return f.write(reinterpret_cast<const char *>(&header), sizeof(header)) == sizeof(header); + } + + bool verify() + { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), v4->iselFactory.data(), &lastErrorString); + } + + void closeMapping() + { + if (currentMapping) { + mappedFile.unmap(currentMapping); + currentMapping = nullptr; + } + mappedFile.close(); + } + + void clearCache() + { + closeMapping(); + QFile::remove(cacheFilePath); + } + + QQmlEngine *engine; + const QTemporaryDir tempDir; + const QString testFilePath; + const QString cacheFilePath; + QString lastErrorString; + QFile mappedFile; + uchar *currentMapping; +}; + +void tst_qmldiskcache::initTestCase() +{ + qputenv("QML_FORCE_DISK_CACHE", "1"); +} + +void tst_qmldiskcache::regenerateAfterChange() +{ + QQmlEngine engine; + TestCompiler testCompiler(&engine); + + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + { + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(quint32(testUnit->nObjects), quint32(1)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(quint32(obj->nBindings), quint32(1)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script)); + QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(1)); + + QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); + + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); + QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + } + + { + const QByteArray newContents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + " property int secondProperty: 42;\n" + "}"); + + QVERIFY2(testCompiler.compile(newContents), qPrintable(testCompiler.lastErrorString)); + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(quint32(testUnit->nObjects), quint32(1)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(quint32(obj->nBindings), quint32(2)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); + QCOMPARE(obj->bindingTable()->valueAsNumber(), double(42)); + + QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); + + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); + QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + } +} + +void tst_qmldiskcache::registerImportForImplicitComponent() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQuick 2.0\n" + "Loader {\n" + " sourceComponent: Item {}\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + { + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(quint32(testUnit->nImports), quint32(2)); + QCOMPARE(testUnit->stringAt(testUnit->importAt(0)->uriIndex), QStringLiteral("QtQuick")); + + QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + + QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->uriIndex), QString(componentType->module())); + QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->qualifierIndex), QStringLiteral("QmlInternals")); + + QCOMPARE(quint32(testUnit->nObjects), quint32(3)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(quint32(obj->nBindings), quint32(1)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object)); + + const QV4::CompiledData::Object *implicitComponent = testUnit->objectAt(obj->bindingTable()->value.objectIndex); + QCOMPARE(testUnit->stringAt(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType->elementName()); + } +} + +void tst_qmldiskcache::basicVersionChecks() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->qtVersion = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Qt version mismatch. Found 0 expected %1").arg(QT_VERSION, 0, 16)); + testCompiler.clearCache(); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->version = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16)); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->architectureIndex = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Architecture mismatch. Found expected %1").arg(QSysInfo::buildAbi())); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->codeGeneratorIndex = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName)); + } +} + +class TypeVersion1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged); +public: + + + int m_value = 0; + int value() const { return m_value; } + void setValue(int v) { m_value = v; emit valueChanged(); } + +signals: + void valueChanged(); +}; + +// Same as TypeVersion1 except the property type changed! +class TypeVersion2 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged); +public: + + + QString m_value; + QString value() const { return m_value; } + void setValue(QString v) { m_value = v; emit valueChanged(); } + +signals: + void valueChanged(); +}; + +void tst_qmldiskcache::recompileAfterChange() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import TypeTest 1.0\n" + "TypeThatWillChange {\n" + "}"); + + qmlRegisterType<TypeVersion1>("TypeTest", 1, 0, "TypeThatWillChange"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + QDateTime initialCacheTimeStamp = QFileInfo(testCompiler.cacheFilePath).lastModified(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<TypeVersion1> obj(qobject_cast<TypeVersion1*>(component.create())); + QVERIFY(!obj.isNull()); + QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<TypeVersion1> obj(qobject_cast<TypeVersion1*>(component.create())); + QVERIFY(!obj.isNull()); + QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp); + } + + engine.clearComponentCache(); + qmlClearTypeRegistrations(); + qmlRegisterType<TypeVersion2>("TypeTest", 1, 0, "TypeThatWillChange"); + + waitForFileSystem(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<TypeVersion2> obj(qobject_cast<TypeVersion2*>(component.create())); + QVERIFY(!obj.isNull()); + QVERIFY(QFileInfo(testCompiler.cacheFilePath).lastModified() > initialCacheTimeStamp); + } +} + +void tst_qmldiskcache::fileSelectors() +{ + QQmlEngine engine; + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const QString testFilePath = tempDir.path() + "/test.qml"; + { + QFile f(testFilePath); + QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString())); + f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 42 }")); + } + + const QString selector = QStringLiteral("testSelector"); + const QString selectorPath = tempDir.path() + "/+" + selector; + const QString selectedTestFilePath = selectorPath + "/test.qml"; + { + QVERIFY(QDir::root().mkpath(selectorPath)); + QFile f(selectorPath + "/test.qml"); + QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString())); + f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 100 }")); + } + + { + QQmlComponent component(&engine, testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); + + QFile cacheFile(testFilePath + "c"); + QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); + } + + QQmlFileSelector qmlSelector(&engine); + qmlSelector.setExtraSelectors(QStringList() << selector); + + engine.clearComponentCache(); + + { + QQmlComponent component(&engine, testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 100); + + QFile cacheFile(selectedTestFilePath + "c"); + QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); + } +} + +void tst_qmldiskcache::localAliases() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " id: root\n" + " property int prop: 100\n" + " property alias dummy1: root.prop\n" + " property alias dummy2: root.prop\n" + " property alias dummy3: root.prop\n" + " property alias dummy4: root.prop\n" + " property alias dummy5: root.prop\n" + " property alias foo: root.prop\n" + " property alias bar: root.foo\n" + "}"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("bar").toInt(), 100); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("bar").toInt(), 100); + } +} + +void tst_qmldiskcache::cacheResources() +{ + const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + QVERIFY(QDir::root().mkpath(cacheDirectory)); + + const QString qmlCacheDirectory = cacheDirectory + QLatin1String("/qmlcache/"); + QVERIFY(QDir(qmlCacheDirectory).removeRecursively()); + QVERIFY(QDir::root().mkpath(qmlCacheDirectory)); + QVERIFY(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot).isEmpty()); + + + QQmlEngine engine; + + { + CleanlyLoadingComponent component(&engine, QUrl("qrc:/test.qml")); + qDebug() << component.errorString(); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 20); + } + + QCOMPARE(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files).count(), 1); +} + +QTEST_MAIN(tst_qmldiskcache) + +#include "tst_qmldiskcache.moc" diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml new file mode 100644 index 0000000000..d19b6ecc41 --- /dev/null +++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 + +Item { + id:root + + LoggingCategory { + id: testCategory + name: "qt.test" + } + + LoggingCategory { + id: emptyCategory + } + + Component.onCompleted: { + console.debug(testCategory, "console.debug"); + console.log(testCategory, "console.log"); + console.info(testCategory, "console.info"); + console.warn(testCategory, "console.warn"); + console.error(testCategory, "console.error"); + + testCategory.name = "qt.test2"; + + console.error(emptyCategory, "console.error"); + } +} diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp index f12656c5fe..f832143935 100644 --- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp +++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp @@ -40,6 +40,7 @@ public: private slots: void logging(); + void categorized_logging(); void tracing(); void profiling(); void testAssert(); @@ -87,6 +88,41 @@ void tst_qqmlconsole::logging() delete object; } +void tst_qqmlconsole::categorized_logging() +{ + QUrl testUrl = testFileUrl("categorized_logging.qml"); + QQmlTestMessageHandler messageHandler; + messageHandler.setIncludeCategoriesEnabled(true); + + QLoggingCategory testCategory("qt.test"); + testCategory.setEnabled(QtDebugMsg, true); + QVERIFY(testCategory.isDebugEnabled()); + QVERIFY(testCategory.isWarningEnabled()); + QVERIFY(testCategory.isCriticalEnabled()); + + QQmlComponent component(&engine, testUrl); + QObject *object = component.create(); + QVERIFY2(object != 0, component.errorString().toUtf8()); + + QVERIFY(messageHandler.messages().contains("qt.test: console.info")); + QVERIFY(messageHandler.messages().contains("qt.test: console.warn")); + QVERIFY(messageHandler.messages().contains("qt.test: console.error")); + + QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) + + "QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"; + QVERIFY(messageHandler.messages().contains(emptyCategory)); + + QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) + + "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created"; + QVERIFY(messageHandler.messages().contains(changedCategory)); + + QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(63) + + "Error: A QmlLoggingCatgory was provided without a valid name"; + QVERIFY(messageHandler.messages().contains(useEmptyCategory)); + + delete object; +} + void tst_qqmlconsole::tracing() { QUrl testUrl = testFileUrl("tracing.qml"); diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index e0cde6c86f..f49fd391ac 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -61,6 +61,7 @@ private slots: void qtbug_22535(); void evalAfterInvalidate(); void qobjectDerived(); + void qtbug_49232(); private: QQmlEngine engine; @@ -200,6 +201,8 @@ class TestObject : public QObject Q_PROPERTY(int a READ a NOTIFY aChanged) Q_PROPERTY(int b READ b NOTIFY bChanged) Q_PROPERTY(int c READ c NOTIFY cChanged) + Q_PROPERTY(char d READ d NOTIFY dChanged) + Q_PROPERTY(uchar e READ e NOTIFY eChanged) public: TestObject() : _a(10), _b(10), _c(10) {} @@ -213,15 +216,25 @@ public: int c() const { return _c; } void setC(int c) { _c = c; emit cChanged(); } + char d() const { return _d; } + void setD(char d) { _d = d; emit dChanged(); } + + uchar e() const { return _e; } + void setE(uchar e) { _e = e; emit eChanged(); } + signals: void aChanged(); void bChanged(); void cChanged(); + void dChanged(); + void eChanged(); private: int _a; int _b; int _c; + char _d; + uchar _e; }; #define TEST_CONTEXT_PROPERTY(ctxt, name, value) \ @@ -694,6 +707,22 @@ void tst_qqmlcontext::qobjectDerived() QCOMPARE(command.count, 2); } +void tst_qqmlcontext::qtbug_49232() +{ + TestObject testObject; + testObject.setD('a'); + testObject.setE(97); + + QQmlEngine engine; + engine.rootContext()->setContextProperty("TestObject", &testObject); + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0; QtObject { property int valueOne: TestObject.d; property int valueTwo: TestObject.e }", QUrl()); + QScopedPointer<QObject> obj(component.create()); + + QCOMPARE(obj->property("valueOne"), QVariant('a')); + QCOMPARE(obj->property("valueTwo"), QVariant(97)); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" diff --git a/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml b/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml new file mode 100644 index 0000000000..8bb0a3554e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml @@ -0,0 +1,3 @@ +import Test 1.0 +WeakReferenceMutator { +} diff --git a/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml new file mode 100644 index 0000000000..4006a2a782 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +QtObject { + property Component factory: Component { QtObject {} } + property QtObject testProperty: factory.createObject() +} diff --git a/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml b/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml new file mode 100644 index 0000000000..57d3ae2f14 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml @@ -0,0 +1,12 @@ +import QtQml 2.0 +QtObject { + property int value: 100 + property bool success: { + unrelatedFunction(); + return value == 42; + } + property int unrelatedValue: 100 + function unrelatedFunction() { + return unrelatedValue; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml new file mode 100644 index 0000000000..d59f8f99f9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 +NullObjectInitializerBase { + testProperty: null + property bool success: false + Component.onCompleted: { + success = testProperty === null; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml new file mode 100644 index 0000000000..32bc62c9f2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property QtObject testProperty: null +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml new file mode 100644 index 0000000000..8f7d5f2a70 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +QtObject { + function checkPropertyDeletion() { + var o = { + x: 1, + y: 2 + }; + o.z = 3 + delete o.y; + + return (o.x === 1 && o.y === undefined && o.z === 3) + } + + property bool result: checkPropertyDeletion() +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2f78df1f11..c734a0a011 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -44,6 +44,8 @@ #include <private/qv4scopedvalue_p.h> #include <private/qv4alloca_p.h> #include <private/qv4runtime_p.h> +#include <private/qv4object_p.h> +#include <private/qqmlcomponentattached_p.h> #ifdef Q_CC_MSVC #define NO_INLINE __declspec(noinline) @@ -82,6 +84,7 @@ private slots: void arrayExpressions(); void contextPropertiesTriggerReeval(); void objectPropertiesTriggerReeval(); + void dependenciesWithFunctions(); void deferredProperties(); void deferredPropertiesErrors(); void deferredPropertiesInComponents(); @@ -208,6 +211,7 @@ private slots: void dynamicCreationOwnership(); void regExpBug(); void nullObjectBinding(); + void nullObjectInitializer(); void deletedEngine(); void libraryScriptAssert(); void variantsAssignedUndefined(); @@ -282,6 +286,7 @@ private slots: void replaceBinding(); void deleteRootObjectInCreation(); void onDestruction(); + void onDestructionViaGC(); void bindingSuppression(); void signalEmitted(); void threadSignal(); @@ -323,6 +328,8 @@ private slots: void switchExpression(); void qtbug_46022(); void qtbug_52340(); + void qtbug_54589(); + void qtbug_54687(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -875,6 +882,18 @@ void tst_qqmlecmascript::objectPropertiesTriggerReeval() } } +void tst_qqmlecmascript::dependenciesWithFunctions() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("dependenciesWithFunctions.qml")); + + QScopedPointer<QObject> object(component.create()); + QVERIFY2(object, qPrintable(component.errorString())); + QVERIFY(!object->property("success").toBool()); + object->setProperty("value", 42); + QVERIFY(object->property("success").toBool()); +} + void tst_qqmlecmascript::deferredProperties() { QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); @@ -2312,7 +2331,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - function->call(d); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return true; @@ -2338,16 +2357,15 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, if (!function) return false; - QV4::ScopedValue value(scope); QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - value = function->call(d); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return false; } - return QV4::Runtime::method_strictEqual(value, result); + return QV4::Runtime::method_strictEqual(scope.result, result); } static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o, @@ -2371,12 +2389,12 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - QV4::ScopedValue result(scope, function->call(d)); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return QV4::Encode::undefined(); } - return result->asReturnedValue(); + return scope.result.asReturnedValue(); } #define EVALUATE_ERROR(source) evaluate_error(engine, object, source) @@ -5708,6 +5726,49 @@ void tst_qqmlecmascript::nullObjectBinding() delete object; } +void tst_qqmlecmascript::nullObjectInitializer() +{ + { + QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false); + QVERIFY(ddata); + + { + const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty"); + QVERIFY(propertyIndex > 0); + QVERIFY(!ddata->hasBindingBit(propertyIndex)); + } + + QVariant value = obj->property("testProperty"); + QVERIFY(value.userType() == qMetaTypeId<QObject*>()); + QVERIFY(!value.value<QObject*>()); + } + + { + QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.2.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false); + QVERIFY(ddata); + + { + const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty"); + QVERIFY(propertyIndex > 0); + QVERIFY(ddata->hasBindingBit(propertyIndex)); + } + + QVERIFY(obj->property("success").toBool()); + + QVariant value = obj->property("testProperty"); + QVERIFY(value.userType() == qMetaTypeId<QObject*>()); + QVERIFY(!value.value<QObject*>()); + } +} + // Test that bindings don't evaluate once the engine has been destroyed void tst_qqmlecmascript::deletedEngine() { @@ -5769,7 +5830,7 @@ void tst_qqmlecmascript::variants() QVERIFY(object != 0); QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid); - QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::VoidStar)); + QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr)); QCOMPARE(object->property("intVariant").type(), QVariant::Int); QCOMPARE(object->property("doubleVariant").type(), QVariant::Double); @@ -7155,6 +7216,105 @@ void tst_qqmlecmascript::onDestruction() } } +class WeakReferenceMutator : public QObject +{ + Q_OBJECT +public: + WeakReferenceMutator() + : resultPtr(Q_NULLPTR) + , weakRef(Q_NULLPTR) + {} + + void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr) + { + QV4::QObjectWrapper::wrap(v4, this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); + + this->resultPtr = resultPtr; + this->weakRef = weakRef; + + QObject::connect(QQmlComponent::qmlAttachedProperties(this), &QQmlComponentAttached::destruction, this, &WeakReferenceMutator::reviveFirstWeakReference); + } + +private slots: + void reviveFirstWeakReference() { + *resultPtr = weakRef->valueRef() && weakRef->isNullOrUndefined(); + if (!*resultPtr) + return; + QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(this)); + weakRef->set(v4, v4->newObject()); + *resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined(); + } + +public: + bool *resultPtr; + + QV4::WeakValue *weakRef; +}; + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { +struct WeakReferenceSentinel : public Object { + WeakReferenceSentinel(WeakValue *weakRef, bool *resultPtr) + : weakRef(weakRef) + , resultPtr(resultPtr) { + + } + + ~WeakReferenceSentinel() { + *resultPtr = weakRef->isNullOrUndefined(); + } + + WeakValue *weakRef; + bool *resultPtr; +}; +} // namespace Heap + +struct WeakReferenceSentinel : public Object { + V4_OBJECT2(WeakReferenceSentinel, Object) + V4_NEEDS_DESTROY +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +DEFINE_OBJECT_VTABLE(QV4::WeakReferenceSentinel); + +void tst_qqmlecmascript::onDestructionViaGC() +{ + qmlRegisterType<WeakReferenceMutator>("Test", 1, 0, "WeakReferenceMutator"); + + QQmlEngine engine; + QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + + QQmlComponent component(&engine, testFileUrl("DestructionHelper.qml")); + + QScopedPointer<QV4::WeakValue> weakRef; + + bool mutatorResult = false; + bool sentinelResult = false; + + { + weakRef.reset(new QV4::WeakValue); + weakRef->set(v4, v4->newObject()); + QVERIFY(!weakRef->isNullOrUndefined()); + + QPointer<WeakReferenceMutator> weakReferenceMutator = qobject_cast<WeakReferenceMutator *>(component.create()); + QVERIFY2(!weakReferenceMutator.isNull(), qPrintable(component.errorString())); + weakReferenceMutator->init(v4, weakRef.data(), &mutatorResult); + + v4->memoryManager->allocObject<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult); + } + gc(engine); + + QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC"); + QVERIFY2(sentinelResult, "The weak reference was not cleared properly"); +} + struct EventProcessor : public QObject { Q_OBJECT @@ -7425,8 +7585,11 @@ void tst_qqmlecmascript::negativeYear() QVariant q; QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); - // Strip the timezone. It should be irrelevant as the date was created with the same one. - QCOMPARE(q.toString().left(32), QStringLiteral("result: Sat Jan 1 00:00:00 -2001")); + + // Only check for the year. We hope that every language writes the year in arabic numerals and + // in relation to a specific dude's date of birth. We also hope that no language adds a "-2001" + // junk string somewhere in the middle. + QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1); QMetaObject::invokeMethod(object, "check_negative_toisostring", Q_RETURN_ARG(QVariant, q)); QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-")); @@ -7922,6 +8085,22 @@ void tst_qqmlecmascript::qtbug_52340() QVERIFY(returnValue.toBool()); } +void tst_qqmlecmascript::qtbug_54589() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_54589.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->property("result").toBool(), true); +} + +void tst_qqmlecmascript::qtbug_54687() +{ + QJSEngine e; + // it's simple: this shouldn't crash. + engine.evaluate("12\n----12"); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 3af1cf46b3..9c155eda5b 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -275,6 +275,12 @@ void tst_qqmlengine::clearComponentCache() // Modify qml file { + // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that + // the newly written file has a modification date newer than an existing cache file, we must + // wait. + // Similar effects of lacking precision have been observed on some Linux systems. + QThread::sleep(1); + QFile file("temp.qml"); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 11\n}\n"); @@ -317,6 +323,11 @@ public: QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); QCoreApplication::processEvents(); + // There might be JS function objects around that hold a last ref to the compilation unit that's + // keeping the type compilation data (CompilationUnit) around. Let's collect them as well so that + // trim works well. + engine->collectGarbage(); + engine->trimComponentCache(); } diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp index ab5e958323..124a107a37 100644 --- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp +++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp @@ -80,6 +80,10 @@ void tst_qqmlextensionplugin::iidCheck_data() QList<QString> files; for (QDirIterator it(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), QDirIterator::Subdirectories); it.hasNext(); ) { QString file = it.next(); +#if defined(Q_OS_DARWIN) + if (file.contains(QLatin1String(".dSYM/"))) + continue; +#endif if (file.endsWith(SUFFIX)) { files << file; } @@ -105,7 +109,7 @@ void tst_qqmlextensionplugin::iidCheck() QFETCH(QString, filePath); QPluginLoader loader(filePath); - QVERIFY(loader.load()); + QVERIFY2(loader.load(), qPrintable(loader.errorString())); QVERIFY(loader.instance() != Q_NULLPTR); if (qobject_cast<QQmlExtensionPlugin *>(loader.instance())) { diff --git a/tests/auto/qml/qqmllanguage/data/alias.12.qml b/tests/auto/qml/qqmllanguage/data/alias.12.qml new file mode 100644 index 0000000000..cc17318092 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.12.qml @@ -0,0 +1,15 @@ +import QtQml 2.0 + +QtObject { + id: root + property alias topLevelAlias: subObject.targetProperty + + property QtObject referencingSubObject: QtObject { + property alias success: root.topLevelAlias + } + + property QtObject foo: QtObject { + id: subObject + property bool targetProperty: true + } +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.13.qml b/tests/auto/qml/qqmllanguage/data/alias.13.qml new file mode 100644 index 0000000000..cff8a72d9a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.13.qml @@ -0,0 +1,16 @@ +import QtQml 2.0 + +QtObject { + id: root + property bool targetProperty: true + + property QtObject foo: QtObject { + id: otherSubObject + property alias theAlias: root.targetProperty + } + + property QtObject referencingSubObject: QtObject { + property alias success: otherSubObject.theAlias + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt new file mode 100644 index 0000000000..90a3ea4317 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt @@ -0,0 +1 @@ +10:34:References to other aliases within the same object are not supported at the moment diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.qml b/tests/auto/qml/qqmllanguage/data/alias.14.qml new file mode 100644 index 0000000000..ff3c36d990 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.14.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +QtObject { + id: root + property bool targetProperty: true + + property QtObject foo: QtObject { + id: otherSubObject + property alias theAliasOrigin: root.targetProperty + property alias theAlias: otherSubObject.theAliasOrigin + } + + property QtObject referencingSubObject: QtObject { + property alias success: otherSubObject.theAlias + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml b/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml index c91cf581b3..52027232db 100644 --- a/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml +++ b/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml @@ -26,7 +26,9 @@ MyTypeObject { boolProperty: true variantProperty: "Hello World!" vectorProperty: "10,1,2.2" + vector2Property: "2, 3" vector4Property: "10,1,2.2,2.3" + quaternionProperty: "4,5,6,7" urlProperty: "main.qml?with%3cencoded%3edata" objectProperty: MyTypeObject { intProperty: 8 } diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt index 7a75447a62..acf0d1da84 100644 --- a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt @@ -1,2 +1,2 @@ 3:1:Type RegisteredCompositeType2 unavailable --1:-1:File not found +-1:-1:No such file or directory diff --git a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml index e1daf3b78f..efedf2b14a 100644 --- a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml +++ b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml @@ -1,4 +1,5 @@ import Test 1.0 MyNamespacedType { + myEnum: MyNamespace.Key5 } diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt new file mode 100644 index 0000000000..b79b660c46 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt @@ -0,0 +1 @@ +5:5:Circular alias reference detected diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml new file mode 100644 index 0000000000..9b50b48df8 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +QtObject { + id: root + property alias a: subObject.b + property QtObject foo: QtObject { + id: subObject + property alias b: root.a + } +} diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt index 716cf5709a..b3082d80e6 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt +++ b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt @@ -1,2 +1,2 @@ 5:5:Type RegisteredCompositeType unavailable -2:1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType +-1:-1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt index 77c26df310..ebeab6987b 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt +++ b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt @@ -1 +1 @@ -2:1:No matching type found, pragma Singleton files cannot be used by QQmlComponent. +-1:-1:No matching type found, pragma Singleton files cannot be used by QQmlComponent. diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index ccfcfc098e..3af7645ff7 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -27,8 +27,6 @@ ****************************************************************************/ #include "testtypes.h" -#include <private/qqmlcompiler_p.h> - static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) @@ -47,6 +45,7 @@ void registerTypes() qmlRegisterType<MyDotPropertyObject>("Test",1,0,"MyDotPropertyObject"); qmlRegisterType<MyNamespace::MyNamespacedType>("Test",1,0,"MyNamespacedType"); qmlRegisterType<MyNamespace::MySecondNamespacedType>("Test",1,0,"MySecondNamespacedType"); + qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "Test", 1, 0, "MyNamespace", "Access to enums & flags only"); qmlRegisterType<MyParserStatus>("Test",1,0,"MyParserStatus"); qmlRegisterType<MyGroupedObject>(); qmlRegisterType<MyRevisionedClass>("Test",1,0,"MyRevisionedClass"); @@ -131,9 +130,10 @@ void CustomBinding::componentComplete() QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, compilationUnit->runtimeFunctions[bindingId])); - QQmlBinding *qmlBinding = new QQmlBinding(function, m_target, context); QQmlProperty property(m_target, name, qmlContext(this)); + QQmlBinding *qmlBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, + function, m_target, context); qmlBinding->setTarget(property); QQmlPropertyPrivate::setBinding(property, qmlBinding); } diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 788ae42726..6c62bcf7b9 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -33,15 +33,16 @@ #include <QtCore/qdatetime.h> #include <QtGui/qmatrix.h> #include <QtGui/qcolor.h> +#include <QtGui/qvector2d.h> #include <QtGui/qvector3d.h> #include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> #include <QtQml/qqml.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlparserstatus.h> #include <QtQml/qqmlpropertyvaluesource.h> #include <QtQml/qqmlscriptstring.h> #include <QtQml/qqmlproperty.h> -#include <private/qqmlcompiler_p.h> #include <private/qqmlcustomparser_p.h> QVariant myCustomVariantTypeConverter(const QString &data); @@ -242,8 +243,10 @@ class MyTypeObject : public QObject Q_PROPERTY(QRectF rectFProperty READ rectFProperty WRITE setRectFProperty NOTIFY rectFPropertyChanged) Q_PROPERTY(bool boolProperty READ boolProperty WRITE setBoolProperty NOTIFY boolPropertyChanged) Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty NOTIFY variantPropertyChanged) + Q_PROPERTY(QVector2D vector2Property READ vector2Property WRITE setVector2Property NOTIFY vector2PropertyChanged) Q_PROPERTY(QVector3D vectorProperty READ vectorProperty WRITE setVectorProperty NOTIFY vectorPropertyChanged) Q_PROPERTY(QVector4D vector4Property READ vector4Property WRITE setVector4Property NOTIFY vector4PropertyChanged) + Q_PROPERTY(QQuaternion quaternionProperty READ quaternionProperty WRITE setQuaternionProperty NOTIFY quaternionPropertyChanged) Q_PROPERTY(QUrl urlProperty READ urlProperty WRITE setUrlProperty NOTIFY urlPropertyChanged) Q_PROPERTY(QQmlScriptString scriptProperty READ scriptProperty WRITE setScriptProperty) @@ -524,6 +527,15 @@ public: emit vectorPropertyChanged(); } + QVector2D vector2PropertyValue; + QVector2D vector2Property() const { + return vector2PropertyValue; + } + void setVector2Property(const QVector2D &v) { + vector2PropertyValue = v; + emit vector2PropertyChanged(); + } + QVector4D vector4PropertyValue; QVector4D vector4Property() const { return vector4PropertyValue; @@ -533,6 +545,15 @@ public: emit vector4PropertyChanged(); } + QQuaternion quaternionPropertyValue; + QQuaternion quaternionProperty() const { + return quaternionPropertyValue; + } + void setQuaternionProperty(const QQuaternion &v) { + quaternionPropertyValue = v; + emit quaternionPropertyChanged(); + } + QUrl urlPropertyValue; QUrl urlProperty() const { return urlPropertyValue; @@ -586,7 +607,9 @@ signals: void boolPropertyChanged(); void variantPropertyChanged(); void vectorPropertyChanged(); + void vector2PropertyChanged(); void vector4PropertyChanged(); + void quaternionPropertyChanged(); void urlPropertyChanged(); }; @@ -707,9 +730,19 @@ private: namespace MyNamespace { + Q_NAMESPACE + enum MyNSEnum { + Key1 = 1, + Key2, + Key5 = 5 + }; + Q_ENUM_NS(MyNSEnum); + class MyNamespacedType : public QObject { Q_OBJECT + Q_PROPERTY(MyNamespace::MyNSEnum myEnum MEMBER m_myEnum) + MyNamespace::MyNSEnum m_myEnum = MyNSEnum::Key1; }; class MySecondNamespacedType : public QObject diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 58a7c39760..ad06946b0b 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -494,6 +494,7 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidAlias.8") << "invalidAlias.8.qml" << "invalidAlias.8.errors.txt" << false; QTest::newRow("invalidAlias.9") << "invalidAlias.9.qml" << "invalidAlias.9.errors.txt" << false; QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false; + QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false; QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false; QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false; @@ -681,6 +682,7 @@ void tst_qqmllanguage::assignBasicTypes() QCOMPARE(object->boolProperty(), true); QCOMPARE(object->variantProperty(), QVariant("Hello World!")); QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f)); + QCOMPARE(object->vector2Property(), QVector2D(2, 3)); QCOMPARE(object->vector4Property(), QVector4D(10, 1, 2.2f, 2.3f)); const QUrl encoded = QUrl::fromEncoded("main.qml?with%3cencoded%3edata", QUrl::TolerantMode); QCOMPARE(object->urlProperty(), component.url().resolved(encoded)); @@ -1803,6 +1805,48 @@ void tst_qqmllanguage::aliasProperties() delete object; } + + // Nested aliases with a qml file + { + QQmlComponent component(&engine, testFileUrl("alias.12.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); + } + + // Nested aliases with a qml file with reverse ordering + { + // This is known to fail at the moment. + QQmlComponent component(&engine, testFileUrl("alias.13.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); + } + + // "Nested" aliases within an object that require iterative resolution + { + // This is known to fail at the moment. + QQmlComponent component(&engine, testFileUrl("alias.14.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); + } } // QTBUG-13374 Test that alias properties and signals can coexist @@ -2074,8 +2118,13 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlTypeData *td = eng->typeLoader.getType(url); Q_ASSERT(td); - QV4::CompiledData::Unit *qmlUnit = td->compiledData()->compilationUnit->data; - Q_ASSERT(qmlUnit); + const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->data; + Q_ASSERT(readOnlyQmlUnit); + QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); + memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); + qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + td->compilationUnit()->data = qmlUnit; + const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; @@ -2552,7 +2601,7 @@ void tst_qqmllanguage::basicRemote_data() QTest::newRow("no need for qmldir") << QUrl(serverdir+"Test.qml") << "" << ""; QTest::newRow("absent qmldir") << QUrl(serverdir+"/noqmldir/Test.qml") << "" << ""; - QTest::newRow("need qmldir") << QUrl(serverdir+"TestLocal.qml") << "" << ""; + QTest::newRow("need qmldir") << QUrl(serverdir+"TestNamed.qml") << "" << ""; } void tst_qqmllanguage::basicRemote() @@ -2592,6 +2641,8 @@ void tst_qqmllanguage::importsRemote_data() << ""; QTest::newRow("remote import with local") << "import \""+serverdir+"\"\nTestLocal {}" << "QQuickImage" << ""; + QTest::newRow("remote import with qualifier") << "import \""+serverdir+"\" as NS\nNS.NamedLocal {}" << "QQuickImage" + << ""; QTest::newRow("wrong remote import with undeclared local") << "import \""+serverdir+"\"\nWrongTestLocal {}" << "" << "WrongTestLocal is not a type"; QTest::newRow("wrong remote import of internal local") << "import \""+serverdir+"\"\nLocalInternal {}" << "" @@ -2898,7 +2949,7 @@ void tst_qqmllanguage::importIncorrectCase() QCOMPARE(errors.count(), 1); const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? - QStringLiteral("File not found") : + QStringLiteral("No such file or directory") : QStringLiteral("File name case mismatch"); QCOMPARE(errors.at(0).description(), expectedError); @@ -4077,7 +4128,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() QVERIFY(subCache); QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/0, /*context*/0); QVERIFY(pd); - QCOMPARE(pd->propType, qMetaTypeId<int>()); + QCOMPARE(pd->propType(), qMetaTypeId<int>()); } void tst_qqmllanguage::propertyCacheInSync() diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index b3d199f80f..cd497cbd79 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -105,6 +105,7 @@ private slots: void get_nested(); void get_nested_data(); void crash_model_with_multiple_roles(); + void crash_model_with_unknown_roles(); void set_model_cache(); void property_changes(); void property_changes_data(); @@ -963,6 +964,21 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles() delete rootItem; } +void tst_qqmllistmodel::crash_model_with_unknown_roles() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); + QScopedPointer<QObject> rootItem(component.create()); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); + QVERIFY(model != 0); + + // used to cause a crash in debug builds + model->index(0, 0, QModelIndex()).data(Qt::DisplayRole); + model->index(0, 0, QModelIndex()).data(Qt::UserRole); +} + //QTBUG-15190 void tst_qqmllistmodel::set_model_cache() { diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 5a8f2747a9..fe73610bcc 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -157,7 +157,7 @@ void tst_qqmlproperty::qmlmetaproperty() QObject *obj = new QObject; - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -194,7 +194,8 @@ void tst_qqmlproperty::qmlmetaproperty() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -326,11 +327,8 @@ class PropertyObject : public QObject Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject) Q_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) - Q_PROPERTY(char charProperty READ charProperty WRITE setCharProperty) Q_PROPERTY(QChar qcharProperty READ qcharProperty WRITE setQcharProperty) Q_PROPERTY(QChar constQChar READ constQChar STORED false CONSTANT FINAL) - Q_PROPERTY(char constChar READ constChar STORED false CONSTANT FINAL) - Q_PROPERTY(int constInt READ constInt STORED false CONSTANT FINAL) Q_CLASSINFO("DefaultProperty", "defaultProperty") public: @@ -367,15 +365,11 @@ public: } QString stringProperty() const { return m_stringProperty;} - char charProperty() const { return m_charProperty; } QChar qcharProperty() const { return m_qcharProperty; } QChar constQChar() const { return 0x25cf; /* Unicode: black circle */ } - char constChar() const { return 'A'; } - int constInt() const { return 123456; } void setStringProperty(QString arg) { m_stringProperty = arg; } - void setCharProperty(char arg) { m_charProperty = arg; } void setQcharProperty(QChar arg) { m_qcharProperty = arg; } signals: @@ -392,7 +386,6 @@ private: MyQmlObject m_qmlObject; MyQObject *m_qObject; QString m_stringProperty; - char m_charProperty; QChar m_qcharProperty; }; @@ -406,7 +399,7 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&object); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -445,7 +438,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -453,7 +447,7 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&dobject); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -495,7 +489,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -509,7 +504,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&object, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -548,7 +543,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -556,7 +552,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -598,7 +594,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -606,7 +603,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onClicked")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -647,7 +644,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -655,7 +653,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -696,7 +694,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -710,7 +709,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&object, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -749,7 +748,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -757,7 +757,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&dobject, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -799,7 +799,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -813,7 +814,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -852,7 +853,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -860,7 +862,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -902,7 +904,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -910,7 +913,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -951,7 +954,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -959,7 +963,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -1000,7 +1004,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -1405,23 +1410,14 @@ void tst_qqmlproperty::write() // Char/string-property { PropertyObject o; - QQmlProperty charProperty(&o, "charProperty"); QQmlProperty qcharProperty(&o, "qcharProperty"); QQmlProperty stringProperty(&o, "stringProperty"); const int black_circle = 0x25cf; - QCOMPARE(charProperty.write(QString("foo")), false); - QCOMPARE(charProperty.write('Q'), true); - QCOMPARE(charProperty.read(), QVariant('Q')); - QCOMPARE(charProperty.write(QString("t")), true); - QCOMPARE(charProperty.read(), QVariant('t')); - QCOMPARE(qcharProperty.write(QString("foo")), false); QCOMPARE(qcharProperty.write('Q'), true); QCOMPARE(qcharProperty.read(), QVariant('Q')); - QCOMPARE(qcharProperty.write(QString("t")), true); - QCOMPARE(qcharProperty.read(), QVariant('t')); QCOMPARE(qcharProperty.write(QChar(black_circle)), true); QCOMPARE(qcharProperty.read(), QVariant(QChar(black_circle))); @@ -1430,19 +1426,10 @@ void tst_qqmlproperty::write() QCOMPARE(o.stringProperty(), QString("bar")); QCOMPARE(stringProperty.write(QVariant(1234)), true); QCOMPARE(stringProperty.read().toString(), QString::number(1234)); + QCOMPARE(stringProperty.write('A'), true); + QCOMPARE(stringProperty.read().toString(), QString::number('A')); QCOMPARE(stringProperty.write(QChar(black_circle)), true); - QCOMPARE(stringProperty.read(), QVariant(QString(QChar(black_circle)))); - - { // char -> QString - QQmlComponent component(&engine); - component.setData("import Test 1.0\nPropertyObject { stringProperty: constChar }", QUrl()); - PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != 0); - if (obj) { - QQmlProperty stringProperty(obj, "stringProperty"); - QCOMPARE(stringProperty.read(), QVariant(QString(obj->constChar()))); - } - } + QCOMPARE(stringProperty.read(), QVariant(QChar(black_circle))); { // QChar -> QString QQmlComponent component(&engine); @@ -1455,16 +1442,6 @@ void tst_qqmlproperty::write() } } - { // int -> QString - QQmlComponent component(&engine); - component.setData("import Test 1.0\nPropertyObject { stringProperty: constInt }", QUrl()); - PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != 0); - if (obj) { - QQmlProperty stringProperty(obj, "stringProperty"); - QCOMPARE(stringProperty.read(), QVariant(QString::number(obj->constInt()))); - } - } } // VariantMap-property @@ -2078,15 +2055,15 @@ void tst_qqmlproperty::floatToStringPrecision() QFETCH(QString, qtString); QFETCH(QString, jsString); - const char *name = propertyName.toLatin1().constData(); + QByteArray name = propertyName.toLatin1(); QCOMPARE(obj->property(name).toDouble(), number); QCOMPARE(obj->property(name).toString(), qtString); - const char *name1 = (propertyName + QLatin1Char('1')).toLatin1().constData(); + QByteArray name1 = (propertyName + QLatin1Char('1')).toLatin1(); QCOMPARE(obj->property(name1).toDouble(), number); QCOMPARE(obj->property(name1).toString(), qtString); - const char *name2 = (propertyName + QLatin1Char('2')).toLatin1().constData(); + QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1(); QCOMPARE(obj->property(name2).toDouble(), number); QCOMPARE(obj->property(name2).toString(), jsString); diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 5f15afff85..824fe445c0 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -30,6 +30,8 @@ #include <private/qqmlpropertycache_p.h> #include <QtQml/qqmlengine.h> #include <private/qv8engine_p.h> +#include <private/qmetaobjectbuilder_p.h> +#include <QCryptographicHash> #include "../../shared/util.h" class tst_qqmlpropertycache : public QObject @@ -45,6 +47,9 @@ private slots: void methodsDerived(); void signalHandlers(); void signalHandlersDerived(); + void metaObjectSize_data(); + void metaObjectSize(); + void metaObjectChecksum(); private: QQmlEngine engine; @@ -105,17 +110,17 @@ void tst_qqmlpropertycache::properties() QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "propertyA")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QVERIFY((data = cacheProperty(cache, "propertyA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); - QVERIFY(data = cacheProperty(cache, "propertyB")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QVERIFY((data = cacheProperty(cache, "propertyB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); - QVERIFY(data = cacheProperty(cache, "propertyC")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QVERIFY((data = cacheProperty(cache, "propertyC"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); - QVERIFY(data = cacheProperty(cache, "propertyD")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QVERIFY((data = cacheProperty(cache, "propertyD"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::propertiesDerived() @@ -129,17 +134,17 @@ void tst_qqmlpropertycache::propertiesDerived() QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "propertyA")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QVERIFY((data = cacheProperty(cache, "propertyA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); - QVERIFY(data = cacheProperty(cache, "propertyB")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QVERIFY((data = cacheProperty(cache, "propertyB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); - QVERIFY(data = cacheProperty(cache, "propertyC")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QVERIFY((data = cacheProperty(cache, "propertyC"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); - QVERIFY(data = cacheProperty(cache, "propertyD")); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QVERIFY((data = cacheProperty(cache, "propertyD"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::methods() @@ -152,29 +157,29 @@ void tst_qqmlpropertycache::methods() QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "slotA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QVERIFY((data = cacheProperty(cache, "slotA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); - QVERIFY(data = cacheProperty(cache, "slotB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QVERIFY((data = cacheProperty(cache, "slotB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); - QVERIFY(data = cacheProperty(cache, "signalA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QVERIFY((data = cacheProperty(cache, "signalA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "signalB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QVERIFY((data = cacheProperty(cache, "signalB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "propertyAChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyBChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyCChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyDChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::methodsDerived() @@ -188,29 +193,29 @@ void tst_qqmlpropertycache::methodsDerived() QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "slotA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QVERIFY((data = cacheProperty(cache, "slotA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); - QVERIFY(data = cacheProperty(cache, "slotB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QVERIFY((data = cacheProperty(cache, "slotB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); - QVERIFY(data = cacheProperty(cache, "signalA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QVERIFY((data = cacheProperty(cache, "signalA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "signalB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QVERIFY((data = cacheProperty(cache, "signalB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "propertyAChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyBChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyCChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyDChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlers() @@ -223,23 +228,23 @@ void tst_qqmlpropertycache::signalHandlers() QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "onSignalA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QVERIFY((data = cacheProperty(cache, "onSignalA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "onSignalB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QVERIFY((data = cacheProperty(cache, "onSignalB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "onPropertyAChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyBChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyCChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyDChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlersDerived() @@ -253,25 +258,145 @@ void tst_qqmlpropertycache::signalHandlersDerived() QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "onSignalA")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QVERIFY((data = cacheProperty(cache, "onSignalA"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "onSignalB")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QVERIFY((data = cacheProperty(cache, "onSignalB"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "onPropertyAChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyBChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyCChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyDChanged")); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } -QTEST_MAIN(tst_qqmlpropertycache) +class TestClass : public QObject +{ + Q_OBJECT + Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged) + int m_prop; + +public: + enum MyEnum { + First, Second + }; + Q_ENUM(MyEnum) + + Q_CLASSINFO("Foo", "Bar") + + TestClass() {} + + int prop() const + { + return m_prop; + } + +public slots: + void setProp(int prop) + { + if (m_prop == prop) + return; + + m_prop = prop; + emit propChanged(prop); + } +signals: + void propChanged(int prop); +}; + +class TestClassWithParameters : public QObject +{ + Q_OBJECT + +public: + Q_INVOKABLE void slotWithArguments(int firstArg) { + Q_UNUSED(firstArg); + } +}; + +class TestClassWithClassInfo : public QObject +{ + Q_OBJECT + Q_CLASSINFO("Key", "Value") +}; #include "tst_qqmlpropertycache.moc" + +#define ARRAY_SIZE(arr) \ + int(sizeof(arr) / sizeof(arr[0])) + +#define TEST_CLASS(Class) \ + QTest::newRow(#Class) << &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) << ARRAY_SIZE(qt_meta_stringdata_##Class.data) + +Q_DECLARE_METATYPE(const QMetaObject*); + +void tst_qqmlpropertycache::metaObjectSize_data() +{ + QTest::addColumn<const QMetaObject*>("metaObject"); + QTest::addColumn<int>("expectedFieldCount"); + QTest::addColumn<int>("expectedStringCount"); + + TEST_CLASS(TestClass); + TEST_CLASS(TestClassWithParameters); + TEST_CLASS(TestClassWithClassInfo); +} + +void tst_qqmlpropertycache::metaObjectSize() +{ + QFETCH(const QMetaObject *, metaObject); + QFETCH(int, expectedFieldCount); + QFETCH(int, expectedStringCount); + + int size = 0; + int stringDataSize = 0; + bool valid = QQmlPropertyCache::determineMetaObjectSizes(*metaObject, &size, &stringDataSize); + QVERIFY(valid); + + QCOMPARE(size, expectedFieldCount - 1); // Remove trailing zero field until fixed in moc. + QCOMPARE(stringDataSize, expectedStringCount); +} + +void tst_qqmlpropertycache::metaObjectChecksum() +{ + QMetaObjectBuilder builder; + builder.setClassName("Test"); + builder.addClassInfo("foo", "bar"); + + QCryptographicHash hash(QCryptographicHash::Md5); + + QScopedPointer<QMetaObject, QScopedPointerPodDeleter> mo(builder.toMetaObject()); + QVERIFY(!mo.isNull()); + + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray initialHash = hash.result(); + QVERIFY(!initialHash.isEmpty()); + hash.reset(); + + { + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray nextHash = hash.result(); + QVERIFY(!nextHash.isEmpty()); + hash.reset(); + QCOMPARE(initialHash, nextHash); + } + + builder.addProperty("testProperty", "int", -1); + + mo.reset(builder.toMetaObject()); + { + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray nextHash = hash.result(); + QVERIFY(!nextHash.isEmpty()); + hash.reset(); + QVERIFY(initialHash != nextHash); + } +} + +QTEST_MAIN(tst_qqmlpropertycache) diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index e0c2324dc6..b8ea98df2b 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -60,6 +60,7 @@ private slots: void QTBUG_35233(); void disallowExtending(); void QTBUG_35906(); + void QTBUG_48136(); }; class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus @@ -462,6 +463,40 @@ void tst_QQmlPropertyMap::QTBUG_35906() QCOMPARE(value.toInt(), 42); } +void tst_QQmlPropertyMap::QTBUG_48136() +{ + static const char key[] = "mykey"; + QQmlPropertyMap map; + + // + // Test that the notify signal is emitted correctly + // + + const int propIndex = map.metaObject()->indexOfProperty(key); + const QMetaProperty prop = map.metaObject()->property(propIndex); + QSignalSpy notifySpy(&map, QByteArray::number(QSIGNAL_CODE) + prop.notifySignal().methodSignature()); + + map.insert(key, 42); + QCOMPARE(notifySpy.count(), 1); + map.insert(key, 43); + QCOMPARE(notifySpy.count(), 2); + map.insert(key, 43); + QCOMPARE(notifySpy.count(), 2); + map.insert(key, 44); + QCOMPARE(notifySpy.count(), 3); + + // + // Test that the valueChanged signal is emitted correctly + // + QSignalSpy valueChangedSpy(&map, &QQmlPropertyMap::valueChanged); + map.setProperty(key, 44); + QCOMPARE(valueChangedSpy.count(), 0); + map.setProperty(key, 45); + QCOMPARE(valueChangedSpy.count(), 1); + map.setProperty(key, 45); + QCOMPARE(valueChangedSpy.count(), 1); +} + QTEST_MAIN(tst_QQmlPropertyMap) #include "tst_qqmlpropertymap.moc" diff --git a/tests/auto/qml/qqmlqt/data/exit.qml b/tests/auto/qml/qqmlqt/data/exit.qml new file mode 100644 index 0000000000..12727d9f04 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/exit.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +QtObject { + property int returnCode: -1 + Component.onCompleted: Qt.exit(returnCode) +} + diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml new file mode 100644 index 0000000000..9d73640c87 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: { + var t = tp.time; + tp.time = t; + } +} diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 8150241e4a..0576650d01 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -46,6 +46,15 @@ #include <QFont> #include "../../shared/util.h" +// Copied from tst_qdatetime.cpp +#ifdef Q_OS_WIN +# include <qt_windows.h> +# include <time.h> +# if defined(Q_OS_WINRT) +# define tzset() +# endif +#endif + class tst_qqmlqt : public QQmlDataTest { Q_OBJECT @@ -87,11 +96,15 @@ private slots: void atob(); void fontFamilies(); void quit(); + void exit(); void resolvedUrl(); void later_data(); void later(); void qtObjectContents(); + void timeRoundtrip_data(); + void timeRoundtrip(); + private: QQmlEngine engine; }; @@ -872,8 +885,6 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTime time(11, 16, 39, 755); temporary = QDateTime(QDate(1970,1,1), time); - QTest::newRow("formatDate, qtime") << "formatDate" << QVariant::fromValue(time) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); - QTest::newRow("formatDateTime, qtime") << "formatDateTime" << QVariant::fromValue(time) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); QDate date(2011,5,31); @@ -980,6 +991,20 @@ void tst_qqmlqt::quit() delete object; } +void tst_qqmlqt::exit() +{ + QQmlComponent component(&engine, testFileUrl("exit.qml")); + + QSignalSpy spy(&engine, &QQmlEngine::exit); + QObject *object = component.create(); + QVERIFY(object != Q_NULLPTR); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QVERIFY(arguments.at(0).toInt() == object->property("returnCode").toInt()); + + delete object; +} + void tst_qqmlqt::resolvedUrl() { QQmlComponent component(&engine, testFileUrl("resolvedUrl.qml")); @@ -1139,6 +1164,104 @@ void tst_qqmlqt::qtObjectContents() delete object; } +class TimeProvider: public QObject +{ + Q_OBJECT + Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged) + +public: + TimeProvider(const QTime &t) + : m_getTime(t) + {} + + QTime time() const { return m_getTime; } + void setTime(const QTime &t) { m_putTime = t; emit timeChanged(); } + +signals: + void timeChanged(); + +public: + QTime m_getTime, m_putTime; +}; + +class TimeZoneSwitch +{ +public: + TimeZoneSwitch(const char *newZone) + : doChangeZone(qstrcmp(newZone, "localtime") == 0) + { + if (!doChangeZone) + return; + + hadOldZone = qEnvironmentVariableIsSet("TZ"); + if (hadOldZone) { + oldZone = qgetenv("TZ"); + } + qputenv("TZ", newZone); + tzset(); + } + + ~TimeZoneSwitch() + { + if (!doChangeZone) + return; + + if (hadOldZone) + qputenv("TZ", oldZone); + else + qunsetenv("TZ"); + tzset(); + } + +private: + bool doChangeZone; + bool hadOldZone; + QByteArray oldZone; +}; + +void tst_qqmlqt::timeRoundtrip_data() +{ + QTest::addColumn<QTime>("time"); + + // Local timezone: + QTest::newRow("localtime") << QTime(0, 0, 0); + + // No DST: + QTest::newRow("UTC") << QTime(0, 0, 0); + QTest::newRow("Europe/Amsterdam") << QTime(1, 0, 0); + QTest::newRow("Asia/Jakarta") << QTime(7, 0, 0); + + // DST: + QTest::newRow("Namibia/Windhoek") << QTime(1, 0, 0); + QTest::newRow("Australia/Adelaide") << QTime(10, 0, 0); + QTest::newRow("Australia/Hobart") << QTime(10, 0, 0); + QTest::newRow("Pacific/Auckland") << QTime(12, 0, 0); + QTest::newRow("Pacific/Samoa") << QTime(13, 0, 0); +} + +void tst_qqmlqt::timeRoundtrip() +{ +#ifdef Q_OS_WIN + QSKIP("On Windows, the DateObject doesn't handle DST transitions correctly when the timezone is not localtime."); // I.e.: for this test. +#endif + + TimeZoneSwitch tzs(QTest::currentDataTag()); + QFETCH(QTime, time); + + TimeProvider tp(time); + + QQmlEngine eng; + eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); + QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); + QObject *obj = component.create(); + QVERIFY(obj != 0); + + // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without + // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back + // again. + QCOMPARE(tp.m_getTime, tp.m_putTime); +} + QTEST_MAIN(tst_qqmlqt) #include "tst_qqmlqt.moc" diff --git a/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js b/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js new file mode 100644 index 0000000000..322a7aea03 --- /dev/null +++ b/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js @@ -0,0 +1,24 @@ +.import QtQuick.LocalStorage 2.0 as Sql + +function test() { + var db = Sql.LocalStorage.openDatabaseSync("QmlTestDB-nullvalues", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS NullValues(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO NullValues VALUES(?, ?)', [ 'hello', null ]); + var firstRow = tx.executeSql("SELECT * FROM NullValues").rows.item(0); + if (firstRow.salutation !== "hello") + return + if (firstRow.salutee === "") { + r = "wrong_data_type" + return + } + if (firstRow.salutee === null) + r = "passed"; + } + ); + + return r; +} diff --git a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp index b1e5cc4e7a..e16bfb08a2 100644 --- a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp +++ b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp @@ -122,7 +122,7 @@ void tst_qqmlsqldatabase::checkDatabasePath() QVERIFY(engine->offlineStoragePath().contains("OfflineStorage")); } -static const int total_databases_created_by_tests = 12; +static const int total_databases_created_by_tests = 13; void tst_qqmlsqldatabase::testQml_data() { QTest::addColumn<QString>("jsfile"); // The input file @@ -144,6 +144,7 @@ void tst_qqmlsqldatabase::testQml_data() QTest::newRow("error-outsidetransaction") << "error-outsidetransaction.js"; // reuse above QTest::newRow("reopen1") << "reopen1.js"; QTest::newRow("reopen2") << "reopen2.js"; // re-uses above DB + QTest::newRow("null-values") << "nullvalues.js"; // If you add a test, you should usually use a new database in the // test - in which case increment total_databases_created_by_tests above. diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index bf255ba6a0..1fc803a395 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -31,7 +31,6 @@ #include <QQmlComponent> #include <QTranslator> #include <QQmlContext> -#include <private/qqmlcompiler_p.h> #include <private/qqmlengine_p.h> #include "../../shared/util.h" @@ -77,15 +76,15 @@ void tst_qqmltranslation::translation() QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); - QQmlCompiledData *cdata = typeData->compiledData(); - QVERIFY(cdata); + QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); + QVERIFY(compilationUnit); QSet<QString> compiledTranslations; compiledTranslations << QStringLiteral("basic") << QStringLiteral("disambiguation") << QStringLiteral("singular") << QStringLiteral("plural"); - const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *unit = compilationUnit->data; const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { @@ -96,7 +95,7 @@ void tst_qqmltranslation::translation() if (expectCompiledTranslation) { if (binding->type != QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_Translation)); + QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation)); } else { if (binding->type == QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation"; @@ -137,10 +136,10 @@ void tst_qqmltranslation::idTranslation() QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); - QQmlCompiledData *cdata = typeData->compiledData(); - QVERIFY(cdata); + QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); + QVERIFY(compilationUnit); - const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *unit = compilationUnit->data; const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { @@ -148,7 +147,7 @@ void tst_qqmltranslation::idTranslation() if (propertyName == "idTranslation") { if (binding->type != QV4::CompiledData::Binding::Type_TranslationById) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_TranslationById)); + QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById)); } else { QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); } diff --git a/tests/auto/qml/qqmltypeloader/data/MyComponent.qml b/tests/auto/qml/qqmltypeloader/data/MyComponent.qml new file mode 100644 index 0000000000..a642518199 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/MyComponent.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Rectangle { + width: 100 + height: 62 + + MyComponent3 {} + + MyComponent2 {} +} + diff --git a/tests/auto/qml/qqmltypeloader/data/MyComponent2.qml b/tests/auto/qml/qqmltypeloader/data/MyComponent2.qml new file mode 100644 index 0000000000..02cf5cb5dd --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/MyComponent2.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Rectangle { + width: 100 + height: 62 +} + diff --git a/tests/auto/qml/qqmltypeloader/data/MyComponent3.qml b/tests/auto/qml/qqmltypeloader/data/MyComponent3.qml new file mode 100644 index 0000000000..ad5d569197 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/MyComponent3.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Rectangle { + width: 100 + height: 62 + + MyComponent4 {} +} + diff --git a/tests/auto/qml/qqmltypeloader/data/trim_cache2.qml b/tests/auto/qml/qqmltypeloader/data/trim_cache2.qml new file mode 100644 index 0000000000..326a720a87 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/trim_cache2.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + width: 400 + height: 400 + + Component.onCompleted: { + var component = Qt.createComponent("MyComponent.qml") + if (component.status == Component.Error) + console.log(component.errorString()) + } +} + diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index ef1ea3a897..3d3a7ff725 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -32,7 +32,6 @@ #include <QtQuick/qquickitem.h> #include <QtQml/private/qqmlengine_p.h> #include <QtQml/private/qqmltypeloader_p.h> -#include <QtQml/private/qqmlcompiler_p.h> #include "../../shared/util.h" class tst_QQMLTypeLoader : public QQmlDataTest @@ -43,6 +42,7 @@ private slots: void testLoadComplete(); void loadComponentSynchronously(); void trimCache(); + void trimCache2(); }; void tst_QQMLTypeLoader::testLoadComplete() @@ -81,10 +81,19 @@ void tst_QQMLTypeLoader::trimCache() url.setQuery(QString::number(i)); QQmlTypeData *data = loader.getType(url); - if (i % 5 == 0) // keep references to some of them so that they aren't trimmed - data->compiledData()->addref(); + // Run an event loop to receive the callback that release()es. + QTRY_COMPARE(data->count(), 2); - data->release(); + // keep references to some of them so that they aren't trimmed. References to either the + // QQmlTypeData or its compiledData() should prevent the trimming. + if (i % 10 == 0) { + // keep ref on data, don't add ref on data->compiledData() + } else if (i % 5 == 0) { + data->compilationUnit()->addref(); + data->release(); + } else { + data->release(); + } } for (int i = 0; i < 256; ++i) { @@ -98,6 +107,19 @@ void tst_QQMLTypeLoader::trimCache() } } +void tst_QQMLTypeLoader::trimCache2() +{ + QQuickView *window = new QQuickView(); + window->setSource(testFileUrl("trim_cache2.qml")); + QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader; + // in theory if gc has already run this could be false + // QCOMPARE(loader.isTypeLoaded(testFileUrl("MyComponent2.qml")), true); + window->engine()->collectGarbage(); + QTest::qWait(1); // force event loop + window->engine()->trimComponentCache(); + QCOMPARE(loader.isTypeLoaded(testFileUrl("MyComponent2.qml")), false); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml index bc92b1e5f9..73d2b921a7 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml @@ -5,5 +5,11 @@ MyTypeObject { property real v_g: color.g property real v_b: color.b property real v_a: color.a + property real hsv_h: color.hsvHue + property real hsv_s: color.hsvSaturation + property real hsv_v: color.hsvValue + property real hsl_h: color.hslHue + property real hsl_s: color.hslSaturation + property real hsl_l: color.hslLightness property variant copy: color } diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml new file mode 100644 index 0000000000..0034163bbe --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml @@ -0,0 +1,8 @@ +import Test 1.0 + +MyTypeObject { + color.hslHue: if (true) 0.43 + color.hslSaturation: if (true) 0.74 + color.hslLightness: if (true) 0.54 + color.a: if (true) 0.7 +} diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml new file mode 100644 index 0000000000..1fc47d460e --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml @@ -0,0 +1,8 @@ +import Test 1.0 + +MyTypeObject { + color.hsvHue: if (true) 0.43 + color.hsvSaturation: if (true) 0.77 + color.hsvValue: if (true) 0.88 + color.a: if (true) 0.7 +} diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h index 77d723fbd4..bcfe4028c6 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h @@ -194,7 +194,7 @@ class MyOffsetValueInterceptor : public QObject, public QQmlPropertyValueInterce Q_INTERFACES(QQmlPropertyValueInterceptor) public: virtual void setTarget(const QQmlProperty &p) { prop = p; } - virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyPrivate::BypassInterceptor); } + virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyData::BypassInterceptor); } private: QQmlProperty prop; @@ -215,7 +215,7 @@ public: c.getRgb(&r, &g, &b, &a); c.setRgb(a, b, g, r); - QQmlPropertyPrivate::write(prop, c, QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::write(prop, c, QQmlPropertyData::BypassInterceptor); } private: @@ -230,7 +230,7 @@ public: virtual void setTarget(const QQmlProperty &p) { prop = p; } virtual void write(const QVariant &) { - QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyData::BypassInterceptor); } private: diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index f506d0f53a..803bad197a 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -907,6 +907,15 @@ void tst_qqmlvaluetypes::color() QCOMPARE((float)object->property("v_g").toDouble(), (float)0.88); QCOMPARE((float)object->property("v_b").toDouble(), (float)0.6); QCOMPARE((float)object->property("v_a").toDouble(), (float)0.34); + + QCOMPARE(qRound(object->property("hsv_h").toDouble() * 100), 43); + QCOMPARE(qRound(object->property("hsv_s").toDouble() * 100), 77); + QCOMPARE(qRound(object->property("hsv_v").toDouble() * 100), 88); + + QCOMPARE(qRound(object->property("hsl_h").toDouble() * 100), 43); + QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74); + QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54); + QColor comparison; comparison.setRedF(0.2); comparison.setGreenF(0.88); @@ -933,6 +942,30 @@ void tst_qqmlvaluetypes::color() } { + QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + + QColor newColor; + newColor.setHsvF(0.43, 0.77, 0.88, 0.7); + QCOMPARE(object->color(), newColor); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + + QColor newColor; + newColor.setHslF(0.43, 0.74, 0.54, 0.7); + QCOMPARE(object->color(), newColor); + + delete object; + } + + { QQmlComponent component(&engine, testFileUrl("color_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); QVERIFY(object != 0); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect new file mode 100644 index 0000000000..8c13977462 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect @@ -0,0 +1,17 @@ +PATCH /qqmlxmlhttprequest.cpp HTTP/1.1 +Accept-Language: en-US +If-Match: "ETagNumber" +Content-Type: application/example +Content-Length: 247 +Connection: Keep-Alive +Accept-Encoding: gzip, deflate +User-Agent: Mozilla/5.0 +Host: {{ServerHostUrl}} + +--- a/qqmlxmlhttprequest.cpp ++++ b/qqmlxmlhttprequest.cpp +@@ -1238,11 +1238,13 @@ +- } else if (m_method == QLatin1String("OPTIONS")) { ++ } else if (m_method == QLatin1String("OPTIONS") || ++ (m_method == QLatin1String("PATCH"))) { + diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml new file mode 100644 index 0000000000..2abf1c60a8 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Canonical Limited and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +QtObject { + property string url + + property bool dataOK: false + property bool headerOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("PATCH", url); + x.setRequestHeader("Accept-Language","en-US"); + x.setRequestHeader("If-Match","\"ETagNumber\""); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.HEADERS_RECEIVED) { + headerOK = (x.getResponseHeader("Content-Location") == "/qqmlxmlhttprequest.cpp") && + (x.getResponseHeader("ETag") == "\"ETagNumber\"") && + (x.status == "204"); + } else if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText === ""); + } + } + + var body = "--- a/qqmlxmlhttprequest.cpp\n" + + "+++ b/qqmlxmlhttprequest.cpp\n" + + "@@ -1238,11 +1238,13 @@\n" + + "- } else if (m_method == QLatin1String(\"OPTIONS\")) {\n" + + "+ } else if (m_method == QLatin1String(\"OPTIONS\") ||\n" + + "+ (m_method == QLatin1String(\"PATCH\"))) {\n" + + x.send(body); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply new file mode 100644 index 0000000000..cece41ced1 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply @@ -0,0 +1,3 @@ +HTTP/1.1 204 No Content +Content-Location: /qqmlxmlhttprequest.cpp +ETag: "ETagNumber" diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 425e12677f..1ce07ecdab 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -71,6 +71,7 @@ private slots: void send_withdata_data(); void send_options(); void send_options_data(); + void send_patch(); void abort(); void abort_unsent(); void abort_opened(); @@ -639,6 +640,26 @@ void tst_qqmlxmlhttprequest::send_options_data() QTest::newRow("OPTIONS (with data)") << "testdocument.html" << "send_data.10.expect" << "send_data.9.qml" << "send_data.2.reply"; } +void tst_qqmlxmlhttprequest::send_patch() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("send_patch.expect"), + testFileUrl("send_patch.reply"), + // the content of response file will be ignored due to 204 status code + testFileUrl("testdocument.html"))); + + QQmlComponent component(&engine, testFileUrl("send_patch.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/qqmlxmlhttprequest.cpp")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + QTRY_VERIFY(object->property("headerOK").toBool()); +} + + // Test abort() has no effect in unsent state void tst_qqmlxmlhttprequest::abort_unsent() { diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index f3ba3e8971..f19e82032a 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -56,6 +56,7 @@ public slots: } private slots: + void initTestCase(); void basicProperties(); void showFiles(); void resetFiltering(); @@ -97,6 +98,15 @@ void tst_qquickfolderlistmodel::checkNoErrors(const QQmlComponent& component) QVERIFY(!component.isError()); } +void tst_qquickfolderlistmodel::initTestCase() +{ + // The tests rely on a fixed number of files in the directory with the qml files + // (the data dir), so disable the disk cache to avoid creating .qmlc files and + // confusing the test. + qputenv("QML_DISABLE_DISK_CACHE", "1"); + QQmlDataTest::initTestCase(); +} + void tst_qquickfolderlistmodel::basicProperties() { QQmlComponent component(&engine, testFileUrl("basic.qml")); diff --git a/tests/auto/qml/qv4mm/qv4mm.pro b/tests/auto/qml/qv4mm/qv4mm.pro new file mode 100644 index 0000000000..d9b749af4a --- /dev/null +++ b/tests/auto/qml/qv4mm/qv4mm.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qv4mm +osx:CONFIG -= app_bundle + +SOURCES += tst_qv4mm.cpp + +QT += qml qml-private testlib + diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp new file mode 100644 index 0000000000..d4ba363d00 --- /dev/null +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 basysKom GmbH. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QQmlEngine> +#include <private/qv4mm_p.h> + +class tst_qv4mm : public QObject +{ + Q_OBJECT + +private slots: + void gcStats(); + void tweaks(); +}; + +void tst_qv4mm::gcStats() +{ + qputenv(QV4_MM_STATS, "1"); + QQmlEngine engine; + engine.collectGarbage(); +} + +void tst_qv4mm::tweaks() +{ + qputenv(QV4_MM_MAXBLOCK_SHIFT, "5"); + qputenv(QV4_MM_MAX_CHUNK_SIZE, "65536"); + QQmlEngine engine; +} + +QTEST_MAIN(tst_qv4mm) + +#include "tst_qv4mm.moc" |